/** * unionfs implementation of the create call * libfuse will call this to create regular files */ static int unionfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) { DBG("%s\n", path); int i = find_rw_branch_cutlast(path); if (i == -1) RETURN(-errno); char p[PATHLEN_MAX]; if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG); // NOTE: We should do: // Create the file with mode=0 first, otherwise we might create // a file as root + x-bit + suid bit set, which might be used for // security racing! int res = open(p, fi->flags, 0); if (res == -1) RETURN(-errno); set_owner(p); // no error check, since creating the file succeeded // NOW, that the file has the proper owner we may set the requested mode fchmod(res, mode); fi->fh = res; remove_hidden(path, i); DBG("fd = %" PRIx64 "\n", fi->fh); RETURN(0); }
static int unionfs_open(const char *path, struct fuse_file_info *fi) { DBG("%s\n", path); int i; if (fi->flags & (O_WRONLY | O_RDWR)) { i = find_rw_branch_cutlast(path); } else { i = find_rorw_branch(path); } if (i == -1) RETURN(-errno); char p[PATHLEN_MAX]; if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG); int fd = open(p, fi->flags); if (fd == -1) RETURN(-errno); if (fi->flags & (O_WRONLY | O_RDWR)) { // There might have been a hide file, but since we successfully // wrote to the real file, a hide file must not exist anymore remove_hidden(path, i); } // This makes exec() fail //fi->direct_io = 1; fi->fh = (unsigned long)fd; DBG("fd = %"PRIx64"\n", fi->fh); RETURN(0); }
/** * copy-one-write * Find path in a union branch and if this branch is read-only, * copy the file to a read-write branch. */ int find_rw_root_cow(const char *path) { int root_rorw = find_rorw_root(path); // not found anywhere if (root_rorw < 0) return -1; // the found root is writable, good! if (uopt.roots[root_rorw].rw) return root_rorw; // cow is disabled, return whatever was found if (!uopt.cow_enabled) return root_rorw; int root_rw = find_lowest_rw_root(root_rorw); if (root_rw < 0) { // no writable root found errno = EACCES; return -1; } if (cow_cp(path, root_rorw, root_rw)) return -1; // remove a file that might hide the copied file remove_hidden(path, root_rw); return root_rw; }
static int unionfs_symlink(const char *from, const char *to) { DBG("from %s to %s\n", from, to); int i = find_rw_branch_cutlast(to); if (i == -1) RETURN(-errno); char t[PATHLEN_MAX]; if (BUILD_PATH(t, uopt.branches[i].path, to)) RETURN(-ENAMETOOLONG); int res = symlink(from, t); if (res == -1) RETURN(-errno); set_owner(t); // no error check, since creating the file succeeded remove_hidden(to, i); // remove hide file (if any) RETURN(0); }
static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) { DBG("%s\n", path); int i = find_rw_branch_cutlast(path); if (i == -1) RETURN(-errno); char p[PATHLEN_MAX]; if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG); int file_type = mode & S_IFMT; int file_perm = mode & (S_PROT_MASK); int res = -1; if ((file_type) == S_IFREG) { // under FreeBSD, only the super-user can create ordinary files using mknod // Actually this workaround should not be required any more // since we now have the unionfs_create() method // So can we remove it? USYSLOG (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n"); res = creat(p, 0); if (res > 0 && close(res) == -1) USYSLOG(LOG_WARNING, "Warning, cannot close file\n"); } else { res = mknod(p, file_type, rdev); } if (res == -1) RETURN(-errno); set_owner(p); // no error check, since creating the file succeeded // NOW, that the file has the proper owner we may set the requested mode chmod(p, file_perm); remove_hidden(path, i); RETURN(0); }
static int unionfs_link(const char *from, const char *to) { DBG("from %s to %s\n", from, to); // hardlinks do not work across different filesystems so we need a copy of from first int i = find_rw_branch_cow(from); if (i == -1) RETURN(-errno); int j = __find_rw_branch_cutlast(to, i); if (j == -1) RETURN(-errno); DBG("from branch: %d to branch: %d\n", i, j); char f[PATHLEN_MAX], t[PATHLEN_MAX]; if (BUILD_PATH(f, uopt.branches[i].path, from)) RETURN(-ENAMETOOLONG); if (BUILD_PATH(t, uopt.branches[j].path, to)) RETURN(-ENAMETOOLONG); int res = link(f, t); if (res == -1) RETURN(-errno); // no need for set_owner(), since owner and permissions are copied over by link() remove_hidden(to, i); // remove hide file (if any) RETURN(0); }
/** * unionfs rename function * TODO: If we rename a directory on a read-only branch, we need to copy over * all files to the renamed directory on the read-write branch. */ static int unionfs_rename(const char *from, const char *to) { DBG("from %s to %s\n", from, to); bool is_dir = false; // is 'from' a file or directory int j = find_rw_branch_cutlast(to); if (j == -1) RETURN(-errno); int i = find_rorw_branch(from); if (i == -1) RETURN(-errno); if (!uopt.branches[i].rw) { i = find_rw_branch_cow_common(from, true); if (i == -1) RETURN(-errno); } if (i != j) { USYSLOG(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which" "is not supported yet.\n", __func__, i, j); RETURN(-EXDEV); } char f[PATHLEN_MAX], t[PATHLEN_MAX]; if (BUILD_PATH(f, uopt.branches[i].path, from)) RETURN(-ENAMETOOLONG); if (BUILD_PATH(t, uopt.branches[i].path, to)) RETURN(-ENAMETOOLONG); filetype_t ftype = path_is_dir(f); if (ftype == NOT_EXISTING) RETURN(-ENOENT); else if (ftype == IS_DIR) is_dir = true; int res; if (!uopt.branches[i].rw) { // since original file is on a read-only branch, we copied the from file to a writable branch, // but since we will rename from, we also need to hide the from file on the read-only branch if (is_dir) res = hide_dir(from, i); else res = hide_file(from, i); if (res) RETURN(-errno); } res = rename(f, t); if (res == -1) { int err = errno; // unlink() might overwrite errno // if from was on a read-only branch we copied it, but now rename failed so we need to delete it if (!uopt.branches[i].rw) { if (unlink(f)) USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now " "also unlink() failed\n", __func__, from); if (remove_hidden(from, i)) USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now " "also removing the whiteout failed\n", __func__, from); } RETURN(-err); } if (uopt.branches[i].rw) { // A lower branch still *might* have a file called 'from', we need to delete this. // We only need to do this if we have been on a rw-branch, since we created // a whiteout for read-only branches anyway. if (is_dir) maybe_whiteout(from, i, WHITEOUT_DIR); else maybe_whiteout(from, i, WHITEOUT_FILE); } remove_hidden(to, i); // remove hide file (if any) RETURN(0); }