Initial import
[ciopfs.git] / ciopfs.c
blob0a1361e95d284cb1f6f0e1b451e4b508d681a4c5
1 /*
2 * CIOPFS - The Case Insensitive On Purpose Filesystem for FUSE
4 * (c) 2008 Marc Andre Tanner <mat at brain-dump dot org>
5 * (c) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
7 * This program can be distributed under the terms of the GNU GPLv2.
9 * How it works:
10 * Before any operation takes place all filenames are
11 * converted to lower case. The original filenames are stored
12 * in extended attributes named user.filename. This value
13 * is returned upon request.
15 * Requirements:
16 * In order to compile ciopfs, you will need both
17 * libfuse and libattr. Furthermore if you want a case
18 * preserving filesystem you have to make sure that the
19 * underlaying filesystem supports extended attributes
20 * (for example for ext{2,3} you need a kernel with
21 * CONFIG_EXT{2,3}_FS_XATTR enabled. You probably also
22 * want to mount the underlaying filesystem with the
23 * user_xattr option which allows non root users to create
24 * extended attributes.
26 * Compile & Install:
27 * $EDITOR config.mk
28 * make
29 * sudo make install
31 * Mount:
32 * ciopfs directory mountpoint [options]
36 #ifdef __linux__
37 #define _XOPEN_SOURCE 500 /* For pread()/pwrite() */
38 #endif
40 #define _BSD_SOURCE /* for vsyslog() */
42 #include <fuse.h>
43 #include <ulockmgr.h>
44 #include <sys/xattr.h>
45 #include <assert.h>
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <stdlib.h>
49 #include <stdbool.h>
50 #include <stdarg.h>
51 #include <string.h>
52 #include <errno.h>
53 #include <dirent.h>
54 #include <unistd.h>
55 #include <limits.h>
56 #include <syslog.h>
58 #define logp(format, args...) (*dolog)(format, ## args)
60 #ifdef NDEBUG
61 # define debug(format, args...)
62 #else
63 # define debug logp
64 #endif
66 #define CIOPFS_ATTR_NAME "user.filename"
68 #ifndef PATH_MAX
69 #define PATH_MAX 4096
70 #endif
72 #ifndef FILENAME_MAX
73 #define FILENAME_MAX 4096
74 #endif
77 static const char *dirname;
79 void stderr_print(const char *fmt, ...)
81 va_list ap;
82 va_start(ap, fmt);
83 fputs("ciopfs: ", stderr);
84 vfprintf(stderr, fmt, ap);
85 va_end(ap);
88 void syslog_print(const char *fmt, ...)
90 va_list ap;
91 va_start(ap, fmt);
92 vsyslog(LOG_NOTICE, fmt, ap);
93 va_end(ap);
96 static void (*dolog)(const char *fmt, ...) = syslog_print;
98 static inline char *strtolower(char *s)
100 char *t = s;
101 while(*t) {
102 *t = tolower(*t);
103 t++;
105 return s;
108 static char* map_path(const char *path)
110 // XXX: malloc failure, memory fragmentation?
111 if (path[0] == '/') {
112 if (path[1] == '\0')
113 return strdup(".");
114 path++;
116 char *p = strdup(path);
117 strtolower(p);
118 debug("%s => %s\n", path, p);
119 return p;
122 static ssize_t ciopfs_get_orig_name(const char *path, char *value, size_t size)
124 ssize_t attrlen;
125 debug("looking up original file name of %s ", path);
126 attrlen = lgetxattr(path, CIOPFS_ATTR_NAME, value, size);
127 if (attrlen > 0) {
128 value[attrlen] = '\0';
129 debug("found %s\n", value);
130 } else {
131 debug("nothing found\n");
133 return attrlen;
136 static int ciopfs_set_orig_name_fd(int fd, const char *origpath)
138 char *filename = strrchr(origpath, '/');
139 if (!filename)
140 filename = (char *)origpath;
141 else
142 filename++;
143 //XXX: map_path memory leak
144 debug("storing original name '%s' in '%s'\n", filename, map_path(origpath));
145 if (fsetxattr(fd, CIOPFS_ATTR_NAME, filename, strlen(filename), 0)) {
146 debug("%s\n", strerror(errno));
147 return -errno;
149 return 0;
152 static int ciopfs_set_orig_name_path(const char *path, const char *origpath)
154 char *filename = strrchr(origpath, '/');
155 if (!filename)
156 filename = (char *)origpath;
157 else
158 filename++;
159 debug("storing original name '%s' in '%s'\n", filename, path);
160 // XXX: setting an extended attribute on a symlink doesn't seem to work (EPERM)
161 if (lsetxattr(path, CIOPFS_ATTR_NAME, filename, strlen(filename), 0)) {
162 debug("%s\n", strerror(errno));
163 return -errno;
165 return 0;
168 static int ciopfs_getattr(const char *path, struct stat *st_data)
170 char *p = map_path(path);
171 int res = lstat(p, st_data);
172 free(p);
173 return (res == -1) ? -errno : 0;
176 static int ciopfs_fgetattr(const char *path, struct stat *stbuf,
177 struct fuse_file_info *fi)
179 int res = fstat(fi->fh, stbuf);
180 if (res == -1)
181 return -errno;
182 return 0;
186 static int ciopfs_readlink(const char *path, char *buf, size_t size)
188 char *p = map_path(path);
189 int res = readlink(p, buf, size - 1);
190 free(p);
191 if (res == -1)
192 return -errno;
193 buf[res] = '\0';
194 return 0;
197 static int ciopfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
198 off_t offset, struct fuse_file_info *fi)
200 int ret = 0;
201 DIR *dp;
202 struct dirent *de;
203 char *p = map_path(path);
204 size_t pathlen = strlen(p);
205 char dnamebuf[PATH_MAX];
206 char attrbuf[FILENAME_MAX];
208 if (pathlen > PATH_MAX) {
209 ret = -ENAMETOOLONG;
210 goto out;
213 strcpy(dnamebuf, p);
215 (void) offset;
216 (void) fi;
218 dp = opendir(p);
219 if (dp == NULL) {
220 ret = -errno;
221 goto out;
224 while ((de = readdir(dp)) != NULL) {
225 struct stat st;
226 char *dname;
228 memset(&st, 0, sizeof(st));
229 st.st_ino = de->d_ino;
230 st.st_mode = de->d_type << 12;
232 if (!strcmp(".", de->d_name) || !strcmp("..", de->d_name))
233 dname = de->d_name;
234 else {
235 snprintf(dnamebuf, sizeof dnamebuf, "%s/%s", p, de->d_name);
236 debug("dnamebuf: %s de->d_name: %s\n", dnamebuf, de->d_name);
237 if (ciopfs_get_orig_name(dnamebuf, attrbuf, sizeof attrbuf) > 0)
238 dname = attrbuf;
239 else
240 dname = de->d_name;
242 debug("dname: %s\n", dname);
243 if (filler(buf, dname, &st, 0))
244 break;
247 closedir(dp);
248 out:
249 free(p);
250 return ret;
253 static int ciopfs_mknod(const char *path, mode_t mode, dev_t rdev)
255 int res;
256 char *p = map_path(path);
257 /* On Linux this could just be 'mknod(p, mode, rdev)' but this
258 is more portable */
259 if (S_ISREG(mode)) {
260 res = open(p, O_CREAT | O_EXCL | O_WRONLY, mode);
261 if (res >= 0) {
262 ciopfs_set_orig_name_fd(res, path);
263 close(res);
265 } else if (S_ISFIFO(mode)) {
266 res = mkfifo(p, mode);
267 } else
268 res = mknod(p, mode, rdev);
269 free(p);
270 if (res == -1)
271 return -errno;
273 return 0;
276 static int ciopfs_mkdir(const char *path, mode_t mode)
278 int ret = 0;
279 char *p = map_path(path);
280 int res = mkdir(p, mode);
282 if (res == -1) {
283 ret = -errno;
284 goto out;
287 ciopfs_set_orig_name_path(p, path);
288 out:
289 free(p);
290 return ret;
293 static int ciopfs_unlink(const char *path)
295 char *p = map_path(path);
296 int res = unlink(p);
297 free(p);
298 if (res == -1)
299 return -errno;
300 return 0;
303 static int ciopfs_rmdir(const char *path)
305 char *p = map_path(path);
306 int res = rmdir(p);
307 free(p);
308 if (res == -1)
309 return -errno;
310 return 0;
313 static int ciopfs_symlink(const char *from, const char *to)
315 int ret = 0;
316 char *f = map_path(from);
317 char *t = map_path(to);
318 int res = symlink(f, t);
319 debug("symlink from %s to %s\n", from, to);
320 if (res == -1) {
321 ret = -errno;
322 goto out;
324 ciopfs_set_orig_name_path(t, to);
325 out:
326 free(f);
327 free(t);
328 return ret;
331 static int ciopfs_rename(const char *from, const char *to)
333 int ret = 0;
334 char *f = map_path(from);
335 char *t = map_path(to);
336 int res = rename(f, t);
337 if (res == -1) {
338 ret = -errno;
339 goto out;
341 ciopfs_set_orig_name_path(t, to);
342 out:
343 free(f);
344 free(t);
345 return ret;
348 static int ciopfs_link(const char *from, const char *to)
350 int ret = 0;
351 char *f = map_path(from);
352 char *t = map_path(to);
353 int res = link(from, to);
354 if (res == -1) {
355 ret = -errno;
356 goto out;
358 ciopfs_set_orig_name_path(t, to);
359 out:
360 free(f);
361 free(t);
362 return ret;
365 static int ciopfs_chmod(const char *path, mode_t mode)
367 char *p = map_path(path);
368 int res = chmod(p, mode);
369 free(p);
370 if (res == -1)
371 return -errno;
372 return 0;
375 static int ciopfs_chown(const char *path, uid_t uid, gid_t gid)
377 char *p = map_path(path);
378 int res = lchown(p, uid, gid);
379 free(p);
380 if (res == -1)
381 return -errno;
382 return 0;
385 static int ciopfs_truncate(const char *path, off_t size)
387 char *p = map_path(path);
388 int res = truncate(p, size);
389 free(p);
390 if (res == -1)
391 return -errno;
392 return 0;
395 static int ciopfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi)
397 int res = ftruncate(fi->fh, size);
398 if (res == -1)
399 return -errno;
401 return 0;
404 static int ciopfs_utime(const char *path, struct utimbuf *buf)
406 char *p = map_path(path);
407 int res = utime(p, buf);
408 free(p);
409 if (res == -1)
410 return -errno;
411 return 0;
414 static int ciopfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
416 char *p = map_path(path);
417 int fd = open(p, fi->flags, mode);
418 free(p);
419 if(fd == -1)
420 return -errno;
421 ciopfs_set_orig_name_fd(fd, path);
422 fi->fh = fd;
423 return 0;
426 static int ciopfs_open(const char *path, struct fuse_file_info *fi)
428 char *p = map_path(path);
429 int fd = open(p, fi->flags);
430 free(p);
431 if (fd == -1)
432 return -errno;
433 if (fi->flags & O_CREAT)
434 ciopfs_set_orig_name_fd(fd, path);
435 fi->fh = fd;
436 return 0;
439 static int ciopfs_read(const char *path, char *buf, size_t size, off_t offset,
440 struct fuse_file_info *fi)
442 int res = pread(fi->fh, buf, size, offset);
443 if (res == -1)
444 res = -errno;
445 return res;
448 static int ciopfs_write(const char *path, const char *buf, size_t size,
449 off_t offset, struct fuse_file_info *fi)
451 int res = pwrite(fi->fh, buf, size, offset);
452 if (res == -1)
453 res = -errno;
454 return res;
457 static int ciopfs_statfs(const char *path, struct statvfs *stbuf)
459 char *p = map_path(path);
460 int res = statvfs(p, stbuf);
461 free(p);
462 if (res == -1)
463 return -errno;
465 return 0;
468 static int ciopfs_flush(const char *path, struct fuse_file_info *fi)
470 /* This is called from every close on an open file, so call the
471 close on the underlying filesystem. But since flush may be
472 called multiple times for an open file, this must not really
473 close the file. This is important if used on a network
474 filesystem like NFS which flush the data/metadata on close() */
475 int res = close(dup(fi->fh));
476 if (res == -1)
477 return -errno;
479 return 0;
482 static int ciopfs_release(const char *path, struct fuse_file_info *fi)
484 close(fi->fh);
485 return 0;
488 static int ciopfs_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
490 int res;
491 #ifdef HAVE_FDATASYNC
492 if (isdatasync)
493 res = fdatasync(fi->fh);
494 else
495 #endif
496 res = fsync(fi->fh);
497 if (res == -1)
498 return -errno;
499 return 0;
502 static int ciopfs_access(const char *path, int mode)
504 char *p = map_path(path);
505 int res = access(p, mode);
506 free(p);
507 if (res == -1)
508 return -errno;
509 return 0;
512 static int ciopfs_setxattr(const char *path, const char *name, const char *value,
513 size_t size, int flags)
515 if (!strcmp(name, CIOPFS_ATTR_NAME)) {
516 debug("denying setting value of extended attribute '%s'\n", CIOPFS_ATTR_NAME);
517 return -EPERM;
519 char *p = map_path(path);
520 int res = lsetxattr(p, name, value, size, flags);
521 free(p);
522 if (res == -1)
523 return -errno;
524 return 0;
527 static int ciopfs_getxattr(const char *path, const char *name, char *value, size_t size)
529 char *p = map_path(path);
530 int res = lgetxattr(p, name, value, size);
531 free(p);
532 if (res == -1)
533 return -errno;
534 return res;
537 static int ciopfs_listxattr(const char *path, char *list, size_t size)
539 char *p = map_path(path);
540 int res = llistxattr(p, list, size);
541 free(p);
542 if (res == -1)
543 return -errno;
544 return res;
547 static int ciopfs_removexattr(const char *path, const char *name)
549 if (!strcmp(name, CIOPFS_ATTR_NAME)) {
550 debug("denying removal of extended attribute '%s'\n", CIOPFS_ATTR_NAME);
551 return -EPERM;
553 char *p = map_path(path);
554 int res = lremovexattr(p, name);
555 free(p);
556 if (res == -1)
557 return -errno;
558 return 0;
561 static int ciopfs_lock(const char *path, struct fuse_file_info *fi, int cmd,
562 struct flock *lock)
564 return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
565 sizeof(fi->lock_owner));
569 struct fuse_operations ciopfs_operations = {
570 .getattr = ciopfs_getattr,
571 .fgetattr = ciopfs_fgetattr,
572 .readlink = ciopfs_readlink,
573 .readdir = ciopfs_readdir,
574 .mknod = ciopfs_mknod,
575 .mkdir = ciopfs_mkdir,
576 .symlink = ciopfs_symlink,
577 .unlink = ciopfs_unlink,
578 .rmdir = ciopfs_rmdir,
579 .rename = ciopfs_rename,
580 .link = ciopfs_link,
581 .chmod = ciopfs_chmod,
582 .chown = ciopfs_chown,
583 .truncate = ciopfs_truncate,
584 .ftruncate = ciopfs_ftruncate,
585 .utime = ciopfs_utime,
586 .create = ciopfs_create,
587 .open = ciopfs_open,
588 .read = ciopfs_read,
589 .write = ciopfs_write,
590 .statfs = ciopfs_statfs,
591 .flush = ciopfs_flush,
592 .release = ciopfs_release,
593 .fsync = ciopfs_fsync,
594 .access = ciopfs_access,
595 .setxattr = ciopfs_setxattr,
596 .getxattr = ciopfs_getxattr,
597 .listxattr = ciopfs_listxattr,
598 .removexattr = ciopfs_removexattr,
599 .lock = ciopfs_lock
601 * what about:
603 * opendir
604 * releasedir
608 static void usage(const char *name)
610 fprintf(stderr, "usage: %s directory mountpoint [options]\n"
611 "\n"
612 "Mounts the content of directory at mountpoint in case insensitiv fashion.\n"
613 "\n"
614 "general options:\n"
615 " -o opt,[opt...] mount options\n"
616 " -h|--help print help\n"
617 " --version print version\n"
618 "\n", name);
622 enum {
623 CIOPFS_OPT_HELP,
624 CIOPFS_OPT_VERSION
627 static int ciopfs_opt_parse(void *data, const char *arg, int key, struct fuse_args *outargs)
629 switch(key) {
630 case FUSE_OPT_KEY_NONOPT:
631 if (!dirname) {
632 // XXX: realpath(char *s, NULL) is a glibc extension
633 if (!(dirname = realpath(arg, NULL)) || chdir(arg)) {
634 perror(outargs->argv[0]);
635 exit(1);
637 return 0;
639 return 1;
640 case FUSE_OPT_KEY_OPT:
641 if (arg[0] == '-') {
642 switch(arg[1]) {
643 case 'd':
644 case 'f':
645 dolog = stderr_print;
648 return 1;
649 case CIOPFS_OPT_HELP:
650 usage(outargs->argv[0]);
651 fuse_opt_add_arg(outargs, "-ho");
652 fuse_main(outargs->argc, outargs->argv, &ciopfs_operations, NULL);
653 exit(0);
654 case CIOPFS_OPT_VERSION:
655 fprintf(stderr, "%s: "VERSION" fuse: %d\n", outargs->argv[0], fuse_version());
656 exit(0);
657 default:
658 fprintf(stderr, "see `%s -h' for usage\n", outargs->argv[0]);
659 exit(1);
661 return 1;
664 static struct fuse_opt ciopfs_opts[] = {
665 FUSE_OPT_KEY("-h", CIOPFS_OPT_HELP),
666 FUSE_OPT_KEY("--help", CIOPFS_OPT_HELP),
667 FUSE_OPT_KEY("--version", CIOPFS_OPT_VERSION),
668 FUSE_OPT_END
671 int main(int argc, char *argv[])
673 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
674 if (fuse_opt_parse(&args, &dirname, ciopfs_opts, ciopfs_opt_parse)) {
675 fprintf(stderr, "Invalid arguments, see `%s -h' for usage\n", argv[0]);
676 exit(1);
678 debug("dir: %s\n", dirname);
679 fuse_main(args.argc, args.argv, &ciopfs_operations, NULL);
680 return 0;