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.
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.
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.
32 * ciopfs directory mountpoint [options]
37 #define _XOPEN_SOURCE 500 /* For pread()/pwrite() */
40 #define _BSD_SOURCE /* for vsyslog() */
44 #include <sys/xattr.h>
58 #define logp(format, args...) (*dolog)(format, ## args)
61 # define debug(format, args...)
66 #define CIOPFS_ATTR_NAME "user.filename"
73 #define FILENAME_MAX 4096
77 static const char *dirname
;
79 void stderr_print(const char *fmt
, ...)
83 fputs("ciopfs: ", stderr
);
84 vfprintf(stderr
, fmt
, ap
);
88 void syslog_print(const char *fmt
, ...)
92 vsyslog(LOG_NOTICE
, fmt
, ap
);
96 static void (*dolog
)(const char *fmt
, ...) = syslog_print
;
98 static inline char *strtolower(char *s
)
108 static char* map_path(const char *path
)
110 // XXX: malloc failure, memory fragmentation?
111 if (path
[0] == '/') {
116 char *p
= strdup(path
);
118 debug("%s => %s\n", path
, p
);
122 static ssize_t
ciopfs_get_orig_name(const char *path
, char *value
, size_t size
)
125 debug("looking up original file name of %s ", path
);
126 attrlen
= lgetxattr(path
, CIOPFS_ATTR_NAME
, value
, size
);
128 value
[attrlen
] = '\0';
129 debug("found %s\n", value
);
131 debug("nothing found\n");
136 static int ciopfs_set_orig_name_fd(int fd
, const char *origpath
)
138 char *filename
= strrchr(origpath
, '/');
140 filename
= (char *)origpath
;
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
));
152 static int ciopfs_set_orig_name_path(const char *path
, const char *origpath
)
154 char *filename
= strrchr(origpath
, '/');
156 filename
= (char *)origpath
;
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
));
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
);
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
);
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);
197 static int ciopfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
198 off_t offset
, struct fuse_file_info
*fi
)
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
) {
224 while ((de
= readdir(dp
)) != NULL
) {
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
))
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)
242 debug("dname: %s\n", dname
);
243 if (filler(buf
, dname
, &st
, 0))
253 static int ciopfs_mknod(const char *path
, mode_t mode
, dev_t rdev
)
256 char *p
= map_path(path
);
257 /* On Linux this could just be 'mknod(p, mode, rdev)' but this
260 res
= open(p
, O_CREAT
| O_EXCL
| O_WRONLY
, mode
);
262 ciopfs_set_orig_name_fd(res
, path
);
265 } else if (S_ISFIFO(mode
)) {
266 res
= mkfifo(p
, mode
);
268 res
= mknod(p
, mode
, rdev
);
276 static int ciopfs_mkdir(const char *path
, mode_t mode
)
279 char *p
= map_path(path
);
280 int res
= mkdir(p
, mode
);
287 ciopfs_set_orig_name_path(p
, path
);
293 static int ciopfs_unlink(const char *path
)
295 char *p
= map_path(path
);
303 static int ciopfs_rmdir(const char *path
)
305 char *p
= map_path(path
);
313 static int ciopfs_symlink(const char *from
, const char *to
)
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
);
324 ciopfs_set_orig_name_path(t
, to
);
331 static int ciopfs_rename(const char *from
, const char *to
)
334 char *f
= map_path(from
);
335 char *t
= map_path(to
);
336 int res
= rename(f
, t
);
341 ciopfs_set_orig_name_path(t
, to
);
348 static int ciopfs_link(const char *from
, const char *to
)
351 char *f
= map_path(from
);
352 char *t
= map_path(to
);
353 int res
= link(from
, to
);
358 ciopfs_set_orig_name_path(t
, to
);
365 static int ciopfs_chmod(const char *path
, mode_t mode
)
367 char *p
= map_path(path
);
368 int res
= chmod(p
, mode
);
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
);
385 static int ciopfs_truncate(const char *path
, off_t size
)
387 char *p
= map_path(path
);
388 int res
= truncate(p
, size
);
395 static int ciopfs_ftruncate(const char *path
, off_t size
, struct fuse_file_info
*fi
)
397 int res
= ftruncate(fi
->fh
, size
);
404 static int ciopfs_utime(const char *path
, struct utimbuf
*buf
)
406 char *p
= map_path(path
);
407 int res
= utime(p
, buf
);
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
);
421 ciopfs_set_orig_name_fd(fd
, path
);
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
);
433 if (fi
->flags
& O_CREAT
)
434 ciopfs_set_orig_name_fd(fd
, path
);
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
);
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
);
457 static int ciopfs_statfs(const char *path
, struct statvfs
*stbuf
)
459 char *p
= map_path(path
);
460 int res
= statvfs(p
, stbuf
);
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
));
482 static int ciopfs_release(const char *path
, struct fuse_file_info
*fi
)
488 static int ciopfs_fsync(const char *path
, int isdatasync
, struct fuse_file_info
*fi
)
491 #ifdef HAVE_FDATASYNC
493 res
= fdatasync(fi
->fh
);
502 static int ciopfs_access(const char *path
, int mode
)
504 char *p
= map_path(path
);
505 int res
= access(p
, mode
);
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
);
519 char *p
= map_path(path
);
520 int res
= lsetxattr(p
, name
, value
, size
, flags
);
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
);
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
);
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
);
553 char *p
= map_path(path
);
554 int res
= lremovexattr(p
, name
);
561 static int ciopfs_lock(const char *path
, struct fuse_file_info
*fi
, int cmd
,
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
,
581 .chmod
= ciopfs_chmod
,
582 .chown
= ciopfs_chown
,
583 .truncate
= ciopfs_truncate
,
584 .ftruncate
= ciopfs_ftruncate
,
585 .utime
= ciopfs_utime
,
586 .create
= ciopfs_create
,
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
,
608 static void usage(const char *name
)
610 fprintf(stderr
, "usage: %s directory mountpoint [options]\n"
612 "Mounts the content of directory at mountpoint in case insensitiv fashion.\n"
615 " -o opt,[opt...] mount options\n"
616 " -h|--help print help\n"
617 " --version print version\n"
627 static int ciopfs_opt_parse(void *data
, const char *arg
, int key
, struct fuse_args
*outargs
)
630 case FUSE_OPT_KEY_NONOPT
:
632 // XXX: realpath(char *s, NULL) is a glibc extension
633 if (!(dirname
= realpath(arg
, NULL
)) || chdir(arg
)) {
634 perror(outargs
->argv
[0]);
640 case FUSE_OPT_KEY_OPT
:
645 dolog
= stderr_print
;
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
);
654 case CIOPFS_OPT_VERSION
:
655 fprintf(stderr
, "%s: "VERSION
" fuse: %d\n", outargs
->argv
[0], fuse_version());
658 fprintf(stderr
, "see `%s -h' for usage\n", outargs
->argv
[0]);
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
),
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]);
678 debug("dir: %s\n", dirname
);
679 fuse_main(args
.argc
, args
.argv
, &ciopfs_operations
, NULL
);