3 * This is offered under a BSD-style license. This means you can use the code for whatever you
4 * desire in any way you may want but you MUST NOT forget to give me appropriate credits when
5 * spreading your work which is based on mine. Something like "original implementation by Radek
6 * Podgorny" should be fine.
8 * License: BSD-style license
9 * Copyright: Radek Podgorny <radek@podgorny.cz>,
10 * Bernd Schubert <bernd-schubert@gmx.de>
14 // For pread()/pwrite()
15 #define _XOPEN_SOURCE 500
27 #include <sys/types.h>
32 #include <sys/statvfs.h>
36 #include <sys/xattr.h>
43 #include "findbranch.h"
52 static struct fuse_opt unionfs_opts[] = {
53 FUSE_OPT_KEY("--help", KEY_HELP),
54 FUSE_OPT_KEY("--version", KEY_VERSION),
55 FUSE_OPT_KEY("-h", KEY_HELP),
56 FUSE_OPT_KEY("-V", KEY_VERSION),
57 FUSE_OPT_KEY("stats", KEY_STATS),
58 FUSE_OPT_KEY("cow", KEY_COW),
59 FUSE_OPT_KEY("noinitgroups", KEY_NOINITGROUPS),
60 FUSE_OPT_KEY("statfs_omit_ro", KEY_STATFS_OMIT_RO),
61 FUSE_OPT_KEY("chroot=%s,", KEY_CHROOT),
62 FUSE_OPT_KEY("max_files=%s", KEY_MAX_FILES),
67 static int unionfs_chmod(const char *path, mode_t mode) {
70 int i = find_rw_branch_cow(path);
71 if (i == -1) return -errno;
74 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
76 int res = chmod(p, mode);
77 if (res == -1) return -errno;
82 static int unionfs_chown(const char *path, uid_t uid, gid_t gid) {
85 int i = find_rw_branch_cow(path);
86 if (i == -1) return -errno;
89 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
91 int res = lchown(p, uid, gid);
92 if (res == -1) return -errno;
98 * unionfs implementation of the create call
99 * libfuse will call this to create regular files
101 static int unionfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) {
104 int i = find_rw_branch_cutlast(path);
105 if (i == -1) return -errno;
108 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
110 // NOTE: We should do:
111 // Create the file with mode=0 first, otherwise we might create
112 // a file as root + x-bit + suid bit set, which might be used for
114 int res = open(p, fi->flags, 0);
115 if (res == -1) return -errno;
117 set_owner(p); // no error check, since creating the file succeeded
119 // NOW, that the file has the proper owner we may set the requested mode
123 remove_hidden(path, i);
130 * flush may be called multiple times for an open file, this must not really
131 * close the file. This is important if used on a network filesystem like NFS
132 * which flush the data/metadata on close()
134 static int unionfs_flush(const char *path, struct fuse_file_info *fi) {
137 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
139 int fd = dup(fi->fh);
143 if (fsync(fi->fh) == -1) return -EIO;
149 if (res == -1) return -errno;
155 * Just a stub. This method is optional and can safely be left unimplemented
157 static int unionfs_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) {
160 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
164 #if _POSIX_SYNCHRONIZED_IO + 0 > 0
165 res = fdatasync(fi->fh);
173 if (res == -1) return -errno;
178 static int unionfs_getattr(const char *path, struct stat *stbuf) {
181 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) {
182 memset(stbuf, 0, sizeof(stbuf));
183 stbuf->st_mode = S_IFREG | 0444;
185 stbuf->st_size = STATS_SIZE;
189 int i = find_rorw_branch(path);
190 if (i == -1) return -errno;
193 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
195 int res = lstat(p, stbuf);
196 if (res == -1) return -errno;
198 /* This is a workaround for broken gnu find implementations. Actually,
199 * n_links is not defined at all for directories by posix. However, it
200 * seems to be common for filesystems to set it to one if the actual value
201 * is unknown. Since nlink_t is unsigned and since these broken implementations
202 * always substract 2 (for . and ..) this will cause an underflow, setting
203 * it to max(nlink_t).
205 if (S_ISDIR(stbuf->st_mode)) stbuf->st_nlink = 1;
212 * called before first access to the filesystem
214 static void * unionfs_init(struct fuse_conn_info *conn) {
215 // just to prevent the compiler complaining about unused variables
216 (void) conn->max_readahead;
218 // we only now (from unionfs_init) may go into the chroot, since otherwise
219 // fuse_main() will fail to open /dev/fuse and to call mount
221 int res = chroot(uopt.chroot);
223 usyslog("Chdir to %s failed: %s ! Aborting!\n",
224 uopt.chroot, strerror(errno));
231 static int unionfs_link(const char *from, const char *to) {
234 // hardlinks do not work across different filesystems so we need a copy of from first
235 int i = find_rw_branch_cow(from);
236 if (i == -1) return -errno;
238 // FIXME, we actually MUST COW to i
239 int j = find_rw_branch_cutlast(to);
240 if (j == -1) return -errno;
242 char f[PATHLEN_MAX], t[PATHLEN_MAX];
243 snprintf(f, PATHLEN_MAX, "%s%s", uopt.branches[i].path, from);
244 snprintf(t, PATHLEN_MAX, "%s%s", uopt.branches[j].path, to);
246 int res = link(f, t);
247 if (res == -1) return -errno;
249 // no need for set_owner(), since owner and permissions are copied over by link()
251 remove_hidden(to, i); // remove hide file (if any)
256 * unionfs mkdir() implementation
258 * NOTE: Never delete whiteouts directories here, since this will just
259 * make already hidden sub-branches visible again.
261 static int unionfs_mkdir(const char *path, mode_t mode) {
264 int i = find_rw_branch_cutlast(path);
265 if (i == -1) return -errno;
268 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
270 int res = mkdir(p, 0);
271 if (res == -1) return -errno;
273 set_owner(p); // no error check, since creating the file succeeded
274 // NOW, that the file has the proper owner we may set the requested mode
280 static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) {
283 int i = find_rw_branch_cutlast(path);
284 if (i == -1) return -errno;
287 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
289 int file_type = mode & S_IFMT;
290 int file_perm = mode & (S_PROT_MASK);
293 if ((file_type) == S_IFREG) {
294 // under FreeBSD, only the super-user can create ordinary files using mknod
295 // Actually this workaround should not be required any more
296 // since we now have the unionfs_create() method
297 // So can we remove it?
299 usyslog (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n");
302 if (res > 0 && close(res) == -1) usyslog(LOG_WARNING, "Warning, cannot close file\n");
304 res = mknod(p, file_type, rdev);
307 if (res == -1) return -errno;
309 set_owner(p); // no error check, since creating the file succeeded
310 // NOW, that the file has the proper owner we may set the requested mode
313 remove_hidden(path, i);
318 static int unionfs_open(const char *path, struct fuse_file_info *fi) {
321 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) {
322 if ((fi->flags & 3) == O_RDONLY) {
323 // This makes exec() fail
331 if (fi->flags & (O_WRONLY | O_RDWR)) {
332 i = find_rw_branch_cutlast(path);
334 i = find_rorw_branch(path);
337 if (i == -1) return -errno;
340 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
342 int fd = open(p, fi->flags);
343 if (fd == -1) return -errno;
345 if (fi->flags & (O_WRONLY | O_RDWR)) {
346 // There might have been a hide file, but since we successfully
347 // wrote to the real file, a hide file must not exist anymore
348 remove_hidden(path, i);
351 // This makes exec() fail
353 fi->fh = (unsigned long)fd;
358 static int unionfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
361 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) {
362 char out[STATS_SIZE] = "";
363 stats_sprint(&stats, out);
366 if (offset < (off_t) strlen(out)) {
367 if (s > (off_t) strlen(out) - offset) s = strlen(out)-offset;
368 memcpy(buf, out+offset, s);
376 int res = pread(fi->fh, buf, size, offset);
378 if (res == -1) return -errno;
380 if (uopt.stats_enabled) stats_add_read(&stats, size);
385 static int unionfs_readlink(const char *path, char *buf, size_t size) {
388 int i = find_rorw_branch(path);
389 if (i == -1) return -errno;
392 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
394 int res = readlink(p, buf, size - 1);
396 if (res == -1) return -errno;
403 static int unionfs_release(const char *path, struct fuse_file_info *fi) {
406 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
408 int res = close(fi->fh);
409 if (res == -1) return -errno;
415 * unionfs rename function
416 * TODO: If we rename a directory on a read-only branch, we need to copy over
417 * all files to the renamed directory on the read-write branch.
419 static int unionfs_rename(const char *from, const char *to) {
422 bool is_dir = false; // is 'from' a file or directory
424 int j = find_rw_branch_cutlast(to);
425 if (j == -1) return -errno;
427 int i = find_rorw_branch(from);
428 if (i == -1) return -errno;
430 if (!uopt.branches[i].rw) {
431 i = find_rw_branch_cow(from);
432 if (i == -1) return -errno;
436 usyslog(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which"
437 "is not supported yet.\n", __func__, i, j);
441 char f[PATHLEN_MAX], t[PATHLEN_MAX];
442 snprintf(f, PATHLEN_MAX, "%s%s", uopt.branches[i].path, from);
443 snprintf(t, PATHLEN_MAX, "%s%s", uopt.branches[i].path, to);
445 filetype_t ftype = path_is_dir(f);
446 if (ftype == NOT_EXISTING)
448 else if (ftype == IS_DIR)
452 if (!uopt.branches[i].rw) {
453 // since original file is on a read-only branch, we copied the from file to a writable branch,
454 // but since we will rename from, we also need to hide the from file on the read-only branch
456 res = hide_dir(from, i);
458 res = hide_file(from, i);
459 if (res) return -errno;
465 int err = errno; // unlink() might overwrite errno
466 // if from was on a read-only branch we copied it, but now rename failed so we need to delete it
467 if (!uopt.branches[i].rw) {
469 usyslog(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
470 "also unlink() failed\n", __func__, from);
472 if (remove_hidden(from, i))
473 usyslog(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
474 "also removing the whiteout failed\n", __func__, from);
479 if (uopt.branches[i].rw) {
480 // A lower branch still *might* have a file called 'from', we need to delete this.
481 // We only need to do this if we have been on a rw-branch, since we created
482 // a whiteout for read-only branches anyway.
484 maybe_whiteout(from, i, WHITEOUT_DIR);
486 maybe_whiteout(from, i, WHITEOUT_FILE);
489 remove_hidden(to, i); // remove hide file (if any)
494 * Wrapper function to convert the result of statfs() to statvfs()
495 * libfuse uses statvfs, since it conforms to POSIX. Unfortunately,
496 * glibc's statvfs parses /proc/mounts, which then results in reading
497 * the filesystem itself again - which would result in a deadlock.
500 static int statvfs_local(const char *path, struct statvfs *stbuf) {
502 /* glibc's statvfs walks /proc/mounts and stats entries found there
503 * in order to extract their mount flags, which may deadlock if they
504 * are mounted under the unionfs. As a result, we have to do this
508 int res = statfs(path, &stfs);
509 if (res == -1) return res;
511 memset(stbuf, 0, sizeof(*stbuf));
512 stbuf->f_bsize = stfs.f_bsize;
514 stbuf->f_frsize = stfs.f_frsize;
516 stbuf->f_frsize = stfs.f_bsize;
517 stbuf->f_blocks = stfs.f_blocks;
518 stbuf->f_bfree = stfs.f_bfree;
519 stbuf->f_bavail = stfs.f_bavail;
520 stbuf->f_files = stfs.f_files;
521 stbuf->f_ffree = stfs.f_ffree;
522 stbuf->f_favail = stfs.f_ffree; /* nobody knows */
523 stbuf->f_fsid = stfs.f_fsid.__val[0];
525 /* We don't worry about flags, exactly because this would
526 * require reading /proc/mounts, and avoiding that and the
527 * resulting deadlocks is exactly what we're trying to avoid
528 * by doing this rather than using statvfs.
531 stbuf->f_namemax = stfs.f_namelen;
535 return statvfs(path, stbuf);
542 * statvs implementation
543 * TODO: fsid: It would be optimal, if we would store a once generated random
544 * fsid. But what if the same branch with the fsid used for different
545 * unions? Is the present way ok for most cases?
547 static int unionfs_statfs(const char *path, struct statvfs *stbuf) {
554 dev_t devno[uopt.nbranches];
557 for (i = 0; i < uopt.nbranches; i++) {
559 int res = statvfs_local(uopt.branches[i].path, &stb);
560 if (res == -1) continue;
563 res = stat(uopt.branches[i].path, &st);
564 if (res == -1) continue;
565 devno[i] = st.st_dev;
568 memcpy(stbuf, &stb, sizeof(*stbuf));
570 stbuf->f_fsid = stb.f_fsid << 8;
574 // Eliminate same devices
576 for (j = 0; j < i; j ++) {
577 if (st.st_dev == devno[j]) break;
581 // Filesystem can have different block sizes -> normalize to first's block size
582 double ratio = (double)stb.f_bsize / (double)stbuf->f_bsize;
584 if (uopt.branches[i].rw) {
585 stbuf->f_blocks += stb.f_blocks * ratio;
586 stbuf->f_bfree += stb.f_bfree * ratio;
587 stbuf->f_bavail += stb.f_bavail * ratio;
589 stbuf->f_files += stb.f_files;
590 stbuf->f_ffree += stb.f_ffree;
591 stbuf->f_favail += stb.f_favail;
592 } else if (!uopt.statfs_omit_ro) {
593 // omitting the RO branches is not correct regarding
594 // the block counts but it actually fixes the
595 // percentage of free space. so, let the user decide.
596 stbuf->f_blocks += stb.f_blocks * ratio;
597 stbuf->f_files += stb.f_files;
600 if (!(stb.f_flag & ST_RDONLY)) stbuf->f_flag &= ~ST_RDONLY;
601 if (!(stb.f_flag & ST_NOSUID)) stbuf->f_flag &= ~ST_NOSUID;
603 if (stb.f_namemax < stbuf->f_namemax) stbuf->f_namemax = stb.f_namemax;
605 // we don't care about overflows, the fsid just should be different
607 stbuf->f_fsid += stb.f_fsid;
614 static int unionfs_symlink(const char *from, const char *to) {
617 int i = find_rw_branch_cutlast(to);
618 if (i == -1) return -errno;
621 snprintf(t, PATHLEN_MAX, "%s%s", uopt.branches[i].path, to);
623 int res = symlink(from, t);
624 if (res == -1) return -errno;
626 set_owner(t); // no error check, since creating the file succeeded
628 remove_hidden(to, i); // remove hide file (if any)
632 static int unionfs_truncate(const char *path, off_t size) {
635 int i = find_rw_branch_cow(path);
636 if (i == -1) return -errno;
639 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
641 int res = truncate(p, size);
643 if (res == -1) return -errno;
648 static int unionfs_utimens(const char *path, const struct timespec ts[2]) {
651 if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
653 int i = find_rw_branch_cow(path);
654 if (i == -1) return -errno;
657 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
659 struct timeval tv[2];
660 tv[0].tv_sec = ts[0].tv_sec;
661 tv[0].tv_usec = ts[0].tv_nsec / 1000;
662 tv[1].tv_sec = ts[1].tv_sec;
663 tv[1].tv_usec = ts[1].tv_nsec / 1000;
665 int res = utimes(p, tv);
667 if (res == -1) return -errno;
672 static int unionfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
677 int res = pwrite(fi->fh, buf, size, offset);
678 if (res == -1) return -errno;
680 if (uopt.stats_enabled) stats_add_written(&stats, size);
686 static int unionfs_getxattr(const char *path, const char *name, char *value, size_t size) {
689 int i = find_rorw_branch(path);
690 if (i == -1) return -errno;
693 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
695 int res = lgetxattr(p, name, value, size);
697 if (res == -1) return -errno;
702 static int unionfs_listxattr(const char *path, char *list, size_t size) {
705 int i = find_rorw_branch(path);
706 if (i == -1) return -errno;
709 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
711 int res = llistxattr(p, list, size);
713 if (res == -1) return -errno;
718 static int unionfs_removexattr(const char *path, const char *name) {
721 int i = find_rw_branch_cow(path);
722 if (i == -1) return -errno;
725 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
727 int res = lremovexattr(p, name);
729 if (res == -1) return -errno;
734 static int unionfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) {
737 int i = find_rw_branch_cow(path);
738 if (i == -1) return -errno;
741 snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
743 int res = lsetxattr(p, name, value, size, flags);
745 if (res == -1) return -errno;
749 #endif // HAVE_SETXATTR
751 static struct fuse_operations unionfs_oper = {
752 .chmod = unionfs_chmod,
753 .chown = unionfs_chown,
754 .create = unionfs_create,
755 .flush = unionfs_flush,
756 .fsync = unionfs_fsync,
757 .getattr = unionfs_getattr,
758 .init = unionfs_init,
759 .link = unionfs_link,
760 .mkdir = unionfs_mkdir,
761 .mknod = unionfs_mknod,
762 .open = unionfs_open,
763 .read = unionfs_read,
764 .readlink = unionfs_readlink,
765 .readdir = unionfs_readdir,
766 .release = unionfs_release,
767 .rename = unionfs_rename,
768 .rmdir = unionfs_rmdir,
769 .statfs = unionfs_statfs,
770 .symlink = unionfs_symlink,
771 .truncate = unionfs_truncate,
772 .unlink = unionfs_unlink,
773 .utimens = unionfs_utimens,
774 .write = unionfs_write,
776 .getxattr = unionfs_getxattr,
777 .listxattr = unionfs_listxattr,
778 .removexattr = unionfs_removexattr,
779 .setxattr = unionfs_setxattr,
783 int main(int argc, char *argv[]) {
784 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
786 int res = debug_init();
787 if (res != 0) return res;
791 if (fuse_opt_parse(&args, NULL, unionfs_opts, unionfs_opt_proc) == -1) return 1;
794 if (uopt.nbranches == 0) {
795 printf("You need to specify at least one branch!\n");
799 if (uopt.stats_enabled) stats_init(&stats);
802 // enable fuse permission checks, we need to set this, even we we are
803 // not root, since we don't have our own access() function
804 if (fuse_opt_add_arg(&args, "-odefault_permissions")) {
805 fprintf(stderr, "Severe failure, can't enable permssion checks, aborting!\n");
811 res = fuse_main(args.argc, args.argv, &unionfs_oper, NULL);
812 return uopt.doexit ? uopt.retval : res;