void unionfs_reinterpose(struct dentry *dentry) { struct dentry *hidden_dentry; struct inode *inode; int bindex, bstart, bend; print_entry_location(); verify_locked(dentry); fist_print_dentry("IN: unionfs_reinterpose: ", dentry); /* This is pre-allocated inode */ inode = dentry->d_inode; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; if (!hidden_dentry->d_inode) continue; if (itohi_index(inode, bindex)) continue; set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); fist_print_dentry("OUT: unionfs_reinterpose: ", dentry); fist_print_inode("OUT: unionfs_reinterpose: ", inode); print_exit_location(); }
/* like interpose above, but for an already existing dentry */ void unionfs_reinterpose(struct dentry *dentry) { struct dentry *lower_dentry; struct inode *inode; int bindex, bstart, bend; verify_locked(dentry); /* This is pre-allocated inode */ inode = dentry->d_inode; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; if (!lower_dentry->d_inode) continue; if (unionfs_lower_inode_idx(inode, bindex)) continue; unionfs_set_lower_inode_idx(inode, bindex, igrab(lower_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); }
/* set lower dentry ptr and update bstart & bend if necessary */ static void __set_dentry(struct dentry *upper, struct dentry *lower, int bindex) { unionfs_set_lower_dentry_idx(upper, bindex, lower); if (likely(dbstart(upper) > bindex)) dbstart(upper) = bindex; if (likely(dbend(upper) < bindex)) dbend(upper) = bindex; }
static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bindex; int err = 0; print_entry_location(); if ((err = unionfs_partial_lookup(dentry))) goto out; bindex = dbstart(dentry); hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) goto out; hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ DGET(hidden_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry); DPUT(hidden_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); unlock_dir(hidden_dir_dentry); if (err) { if (!IS_COPYUP_ERR(err)) goto out; } if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else if (dbopaque(dentry) != -1) { /* There is a hidden lower-priority file with the same name. */ err = create_whiteout(dentry, dbopaque(dentry)); } else { err = create_whiteout(dentry, dbstart(dentry)); } out: if (!err) dentry->d_inode->i_nlink--; /* We don't want to leave negative leftover dentries for revalidate. */ if (!err && (dbopaque(dentry) != -1)) update_bstart(dentry); print_exit_status(err); return err; }
/* * return to user-space the branch indices containing the file in question * * We use fd_set and therefore we are limited to the number of the branches * to FD_SETSIZE, which is currently 1024 - plenty for most people */ static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent, unsigned int cmd, unsigned long arg) { int err = 0; fd_set branchlist; int bstart = 0, bend = 0, bindex = 0; int orig_bstart, orig_bend; struct dentry *dentry, *lower_dentry; struct vfsmount *mnt; dentry = file->f_path.dentry; orig_bstart = dbstart(dentry); orig_bend = dbend(dentry); err = unionfs_partial_lookup(dentry, parent); if (err) goto out; bstart = dbstart(dentry); bend = dbend(dentry); FD_ZERO(&branchlist); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; if (likely(lower_dentry->d_inode)) FD_SET(bindex, &branchlist); /* purge any lower objects after partial_lookup */ if (bindex < orig_bstart || bindex > orig_bend) { dput(lower_dentry); unionfs_set_lower_dentry_idx(dentry, bindex, NULL); iput(unionfs_lower_inode_idx(dentry->d_inode, bindex)); unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL); mnt = unionfs_lower_mnt_idx(dentry, bindex); if (!mnt) continue; unionfs_mntput(dentry, bindex); unionfs_set_lower_mnt_idx(dentry, bindex, NULL); } } /* restore original dentry's offsets */ dbstart(dentry) = orig_bstart; dbend(dentry) = orig_bend; ibstart(dentry->d_inode) = orig_bstart; ibend(dentry->d_inode) = orig_bend; err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set)); if (unlikely(err)) err = -EFAULT; out: return err < 0 ? err : bend; }
int unionfs_rmdir(struct inode *dir, struct dentry *dentry) { int err = 0; struct unionfs_dir_state *namelist = NULL; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_rmdir: ", dentry); /* check if this unionfs directory is empty or not */ err = check_empty(dentry, &namelist); if (err) { goto out; } if (IS_SET(dir->i_sb, DELETE_WHITEOUT)) { /* Delete the first directory. */ err = unionfs_rmdir_first(dir, dentry, namelist); /* create whiteout */ if (!err) { err = create_whiteout(dentry, dbstart(dentry)); } else { int new_err; if (dbstart(dentry) == 0) goto out; /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; new_err = create_whiteout(dentry, dbstart(dentry) - 1); if (new_err != -EEXIST) err = new_err; } } else { /* delete all. */ err = unionfs_rmdir_all(dir, dentry, namelist); } out: /* call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); if (namelist) free_rdstate(namelist); unlock_dentry(dentry); print_exit_status(err); return err; }
static void unionfs_fill_inode(struct dentry *dentry, struct inode *inode) { struct inode *lower_inode; struct dentry *lower_dentry; int bindex, bstart, bend; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) { unionfs_set_lower_inode_idx(inode, bindex, NULL); continue; } /* Initialize the lower inode to the new lower inode. */ if (!lower_dentry->d_inode) continue; unionfs_set_lower_inode_idx(inode, bindex, igrab(lower_dentry->d_inode)); } ibstart(inode) = dbstart(dentry); ibend(inode) = dbend(dentry); /* Use attributes from the first branch. */ lower_inode = unionfs_lower_inode(inode); /* Use different set of inode ops for symlinks & directories */ if (S_ISLNK(lower_inode->i_mode)) inode->i_op = &unionfs_symlink_iops; else if (S_ISDIR(lower_inode->i_mode)) inode->i_op = &unionfs_dir_iops; /* Use different set of file ops for directories */ if (S_ISDIR(lower_inode->i_mode)) inode->i_fop = &unionfs_dir_fops; /* properly initialize special inodes */ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) init_special_inode(inode, lower_inode->i_mode, lower_inode->i_rdev); /* all well, copy inode attributes */ unionfs_copy_attr_all(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); }
/* unionfs_open helper function: open a directory */ static int __open_dir(struct inode *inode, struct file *file) { struct dentry *lower_dentry; struct file *lower_file; int bindex, bstart, bend; struct vfsmount *mnt; bstart = fbstart(file) = dbstart(file->f_path.dentry); bend = fbend(file) = dbend(file->f_path.dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(file->f_path.dentry, bindex); if (!lower_dentry) continue; dget(lower_dentry); unionfs_mntget(file->f_path.dentry, bindex); mnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex); lower_file = dentry_open(lower_dentry, mnt, file->f_flags, current_cred()); if (IS_ERR(lower_file)) return PTR_ERR(lower_file); unionfs_set_lower_file_idx(file, bindex, lower_file); /* * The branchget goes after the open, because otherwise * we would miss the reference on release. */ branchget(inode->i_sb, bindex); } return 0; }
/* open all lower files for a given file */ static int open_all_files(struct file *file) { int bindex, bstart, bend, err = 0; struct file *lower_file; struct dentry *lower_dentry; struct dentry *dentry = file->f_path.dentry; struct super_block *sb = dentry->d_sb; bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; dget(lower_dentry); unionfs_mntget(dentry, bindex); branchget(sb, bindex); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(dentry, bindex), file->f_flags, current_cred()); if (IS_ERR(lower_file)) { branchput(sb, bindex); err = PTR_ERR(lower_file); goto out; } else { unionfs_set_lower_file_idx(file, bindex, lower_file); } } out: return err; }
static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist) { int err; struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry = NULL; /* Here we need to remove whiteout entries. */ err = delete_whiteouts(dentry, dbstart(dentry), namelist); if (err) goto out; hidden_dentry = unionfs_lower_dentry(dentry); hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ dget(hidden_dentry); if (!(err = is_robranch(dentry))) err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); dput(hidden_dentry); fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* propagate number of hard-links */ dentry->d_inode->i_nlink = unionfs_get_nlinks(dentry->d_inode); out: if (hidden_dir_dentry) unlock_dir(hidden_dir_dentry); return err; }
static struct dentry *lookup_whiteout(struct dentry *dentry) { char *whname; int bindex = -1, bstart = -1, bend = -1; struct dentry *parent, *hidden_parent, *wh_dentry; whname = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(whname)) return (void *)whname; parent = dget_parent(dentry); unionfs_lock_dentry(parent); bstart = dbstart(parent); bend = dbend(parent); wh_dentry = ERR_PTR(-ENOENT); for (bindex = bstart; bindex <= bend; bindex++) { hidden_parent = unionfs_lower_dentry_idx(parent, bindex); if (!hidden_parent) continue; wh_dentry = lookup_one_len(whname, hidden_parent, dentry->d_name.len + UNIONFS_WHLEN); if (IS_ERR(wh_dentry)) continue; if (wh_dentry->d_inode) break; dput(wh_dentry); wh_dentry = ERR_PTR(-ENOENT); } unionfs_unlock_dentry(parent); dput(parent); kfree(whname); return wh_dentry; }
static struct dentry *lookup_whiteout(struct dentry *dentry) { char *whname; int bindex = -1, bstart = -1, bend = -1; struct dentry *parent, *hidden_parent, *wh_dentry; whname = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(whname)) return (void *)whname; parent = GET_PARENT(dentry); lock_dentry(parent); bstart = dbstart(parent); bend = dbend(parent); wh_dentry = ERR_PTR(-ENOENT); for (bindex = bstart; bindex <= bend; bindex++) { hidden_parent = dtohd_index(parent, bindex); if (!hidden_parent) continue; wh_dentry = LOOKUP_ONE_LEN(whname, hidden_parent, dentry->d_name.len + WHLEN); if (IS_ERR(wh_dentry)) continue; if (wh_dentry->d_inode) break; DPUT(wh_dentry); wh_dentry = ERR_PTR(-ENOENT); } unlock_dentry(parent); DPUT(parent); KFREE(whname); return wh_dentry; }
void unionfs_d_release(struct dentry *dentry) { struct dentry *hidden_dentry; int bindex, bstart, bend; print_entry_location(); /* There is no reason to lock the dentry, because we have the only * reference, but the printing functions verify that we have a lock * on the dentry before calling dbstart, etc. */ lock_dentry(dentry); __fist_print_dentry("unionfs_d_release IN dentry", dentry, 0); /* this could be a negative dentry, so check first */ if (!dtopd(dentry)) { fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name); goto out; } else if (dbstart(dentry) < 0) { /* this is due to a failed lookup */ /* the failed lookup has a dtohd_ptr set to null, but this is a better check */ fist_dprint(6, "dentry without hidden dentries : %*s", dentry->d_name.len, dentry->d_name.name); goto out_free; } /* Release all the hidden dentries */ bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); DPUT(hidden_dentry); set_dtohd_index(dentry, bindex, NULL); } /* free private data (unionfs_dentry_info) here */ KFREE(dtohd_ptr(dentry)); dtohd_ptr(dentry) = NULL; out_free: /* No need to unlock it, because it is disappeared. */ #ifdef TRACKLOCK printk("DESTROYLOCK:%p\n", dentry); #endif free_dentry_private_data(dtopd(dentry)); dtopd_lhs(dentry) = NULL; /* just to be safe */ out: print_exit_location(); }
static void unionfs_d_release(struct dentry *dentry) { int bindex, bstart, bend; /* There is no reason to lock the dentry, because we have the only * reference, but the printing functions verify that we have a lock * on the dentry before calling dbstart, etc. */ unionfs_lock_dentry(dentry); /* this could be a negative dentry, so check first */ if (!UNIONFS_D(dentry)) { printk(KERN_DEBUG "dentry without private data: %.*s", dentry->d_name.len, dentry->d_name.name); goto out; } else if (dbstart(dentry) < 0) { /* this is due to a failed lookup */ printk(KERN_DEBUG "dentry without hidden dentries : %.*s", dentry->d_name.len, dentry->d_name.name); goto out_free; } /* Release all the hidden dentries */ bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bstart; bindex <= bend; bindex++) { dput(unionfs_lower_dentry_idx(dentry, bindex)); mntput(unionfs_lower_mnt_idx(dentry, bindex)); unionfs_set_lower_dentry_idx(dentry, bindex, NULL); unionfs_set_lower_mnt_idx(dentry, bindex, NULL); } /* free private data (unionfs_dentry_info) here */ kfree(UNIONFS_D(dentry)->lower_paths); UNIONFS_D(dentry)->lower_paths = NULL; out_free: /* No need to unlock it, because it is disappeared. */ free_dentry_private_data(UNIONFS_D(dentry)); dentry->d_fsdata = NULL; /* just to be safe */ out: return; }
/* * scan through the lower dentry objects, and set bstart to reflect the * starting branch */ void update_bstart(struct dentry *dentry) { int bindex; int bstart = dbstart(dentry); int bend = dbend(dentry); struct dentry *lower_dentry; for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) continue; if (lower_dentry->d_inode) { dbstart(dentry) = bindex; break; } dput(lower_dentry); unionfs_set_lower_dentry_idx(dentry, bindex, NULL); } }
int unionfs_rmdir(struct inode *dir, struct dentry *dentry) { int err = 0; struct unionfs_dir_state *namelist = NULL; unionfs_lock_dentry(dentry); /* check if this unionfs directory is empty or not */ err = check_empty(dentry, &namelist); if (err) goto out; err = unionfs_rmdir_first(dir, dentry, namelist); /* create whiteout */ if (!err) err = create_whiteout(dentry, dbstart(dentry)); else { int new_err; if (dbstart(dentry) == 0) goto out; /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; new_err = create_whiteout(dentry, dbstart(dentry) - 1); if (new_err != -EEXIST) err = new_err; } out: /* call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); if (namelist) free_rdstate(namelist); unionfs_unlock_dentry(dentry); return err; }
/* * dput the lower references for old and new dentry & clear a lower dentry * pointer */ static void __clear(struct dentry *dentry, struct dentry *old_lower_dentry, int old_bstart, int old_bend, struct dentry *new_lower_dentry, int new_bindex) { /* get rid of the lower dentry and all its traces */ unionfs_set_lower_dentry_idx(dentry, new_bindex, NULL); dbstart(dentry) = old_bstart; dbend(dentry) = old_bend; dput(new_lower_dentry); dput(old_lower_dentry); }
static void epilog(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex) { d_drop(dentry); dentry->d_inode->i_ctime = dir->i_ctime; if (atomic_read(&dentry->d_count) == 1) { set_h_dptr(dentry, dbstart(dentry), NULL); au_update_dbstart(dentry); } if (ibstart(dir) == bindex) au_cpup_attr_timesizes(dir); dir->i_version++; }
/* * Post-copyup helper to release all non-directory source objects of a * copied-up file. Regular files should have only one lower object. */ void unionfs_postcopyup_release(struct dentry *dentry) { int bstart, bend; BUG_ON(S_ISDIR(dentry->d_inode->i_mode)); bstart = dbstart(dentry); bend = dbend(dentry); path_put_lowers(dentry, bstart + 1, bend, false); iput_lowers(dentry->d_inode, bstart + 1, bend, false); dbend(dentry) = bstart; ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bstart; }
/* * We can't copyup a directory, because it may involve huge numbers of * children, etc. Doing that in the kernel would be bad, so instead we * return EXDEV to the user-space utility that caused this, and let the * user-space recurse and ask us to copy up each file separately. */ static int may_rename_dir(struct dentry *dentry, struct dentry *parent) { int err, bstart; err = check_empty(dentry, parent, NULL); if (err == -ENOTEMPTY) { if (is_robranch(dentry)) return -EXDEV; } else if (err) { return err; } bstart = dbstart(dentry); if (dbend(dentry) == bstart || dbopaque(dentry) == bstart) return 0; dbstart(dentry) = bstart + 1; err = check_empty(dentry, parent, NULL); dbstart(dentry) = bstart; if (err == -ENOTEMPTY) err = -EXDEV; return err; }
/* * unionfs_lookup is the only special function which takes a dentry, yet we * do NOT want to call __unionfs_d_revalidate_chain because by definition, * we don't have a valid dentry here yet. */ static struct dentry *unionfs_lookup(struct inode *dir, struct dentry *dentry, /* XXX: pass flags to lower? */ unsigned int flags_unused) { struct dentry *ret, *parent; int err = 0; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); /* * As long as we lock/dget the parent, then can skip validating the * parent now; we may have to rebuild this dentry on the next * ->d_revalidate, however. */ /* allocate dentry private data. We free it in ->d_release */ err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD); if (unlikely(err)) { ret = ERR_PTR(err); goto out; } ret = unionfs_lookup_full(dentry, parent, INTERPOSE_LOOKUP); if (!IS_ERR(ret)) { if (ret) dentry = ret; /* lookup_full can return multiple positive dentries */ if (dentry->d_inode && !S_ISDIR(dentry->d_inode->i_mode)) { BUG_ON(dbstart(dentry) < 0); unionfs_postcopyup_release(dentry); } unionfs_copy_attr_times(dentry->d_inode); } unionfs_check_inode(dir); if (!IS_ERR(ret)) unionfs_check_dentry(dentry); unionfs_check_dentry(parent); unionfs_unlock_dentry(dentry); /* locked in new_dentry_private data */ out: unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return ret; }
/* perform a delayed copyup of a read-write file on a read-only branch */ static int do_delayed_copyup(struct file *file, struct dentry *parent) { int bindex, bstart, bend, err = 0; struct dentry *dentry = file->f_path.dentry; struct inode *parent_inode = parent->d_inode; bstart = fbstart(file); bend = fbend(file); BUG_ON(!S_ISREG(dentry->d_inode->i_mode)); unionfs_check_file(file); for (bindex = bstart - 1; bindex >= 0; bindex--) { if (!d_deleted(dentry)) err = copyup_file(parent_inode, file, bstart, bindex, i_size_read(dentry->d_inode)); else err = copyup_deleted_file(file, dentry, parent, bstart, bindex); /* if succeeded, set lower open-file flags and break */ if (!err) { struct file *lower_file; lower_file = unionfs_lower_file_idx(file, bindex); lower_file->f_flags = file->f_flags; break; } } if (err || (bstart <= fbstart(file))) goto out; bend = fbend(file); for (bindex = bstart; bindex <= bend; bindex++) { if (unionfs_lower_file_idx(file, bindex)) { branchput(dentry->d_sb, bindex); fput(unionfs_lower_file_idx(file, bindex)); unionfs_set_lower_file_idx(file, bindex, NULL); } } path_put_lowers(dentry, bstart, bend, false); iput_lowers(dentry->d_inode, bstart, bend, false); /* for reg file, we only open it "once" */ fbend(file) = fbstart(file); dbend(dentry) = dbstart(dentry); ibend(dentry->d_inode) = ibstart(dentry->d_inode); out: unionfs_check_file(file); return err; }
/* purge a dentry's lower-branch states (dput/mntput, etc.) */ static void __cleanup_dentry(struct dentry *dentry, int bindex, int old_bstart, int old_bend) { int loop_start; int loop_end; int new_bstart = -1; int new_bend = -1; int i; loop_start = min(old_bstart, bindex); loop_end = max(old_bend, bindex); /* * This loop sets the bstart and bend for the new dentry by * traversing from left to right. It also dputs all negative * dentries except bindex */ for (i = loop_start; i <= loop_end; i++) { if (!unionfs_lower_dentry_idx(dentry, i)) continue; if (i == bindex) { new_bend = i; if (new_bstart < 0) new_bstart = i; continue; } if (!unionfs_lower_dentry_idx(dentry, i)->d_inode) { dput(unionfs_lower_dentry_idx(dentry, i)); unionfs_set_lower_dentry_idx(dentry, i, NULL); unionfs_mntput(dentry, i); unionfs_set_lower_mnt_idx(dentry, i, NULL); } else { if (new_bstart < 0) new_bstart = i; new_bend = i; } } if (new_bstart < 0) new_bstart = bindex; if (new_bend < 0) new_bend = bindex; dbstart(dentry) = new_bstart; dbend(dentry) = new_bend; }
int au_reopen_nondir(struct file *file) { int err; struct dentry *dentry; aufs_bindex_t bstart, bindex, bend; struct file *hidden_file, *h_file_tmp; dentry = file->f_dentry; LKTRTrace("%.*s\n", DLNPair(dentry)); DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode) || !au_h_dptr(dentry)->d_inode); bstart = dbstart(dentry); h_file_tmp = NULL; if (fbstart(file) == bstart) { hidden_file = au_h_fptr(file); if (file->f_mode == hidden_file->f_mode) return 0; /* success */ h_file_tmp = hidden_file; get_file(h_file_tmp); set_h_fptr(file, bstart, NULL); } DEBUG_ON(fbstart(file) < bstart || ftofi(file)->fi_hfile[0 + bstart].hf_file); hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC); //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart)); //hidden_file = ERR_PTR(-1);} err = PTR_ERR(hidden_file); if (IS_ERR(hidden_file)) goto out; // close all? err = 0; //cpup_file_flags(hidden_file, file); set_fbstart(file, bstart); set_h_fptr(file, bstart, hidden_file); memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //?? /* close lower files */ bend = fbend(file); for (bindex = bstart + 1; bindex <= bend; bindex++) set_h_fptr(file, bindex, NULL); set_fbend(file, bstart); out: if (h_file_tmp) fput(h_file_tmp); TraceErr(err); return err; }
/* open the highest priority file for a given upper file */ static int open_highest_file(struct file *file, bool willwrite) { int bindex, bstart, bend, err = 0; struct file *lower_file; struct dentry *lower_dentry; struct dentry *dentry = file->f_path.dentry; struct dentry *parent = dget_parent(dentry); struct inode *parent_inode = parent->d_inode; struct super_block *sb = dentry->d_sb; bstart = dbstart(dentry); bend = dbend(dentry); lower_dentry = unionfs_lower_dentry(dentry); if (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) { for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_file(parent_inode, file, bstart, bindex, i_size_read(dentry->d_inode)); if (!err) break; } atomic_set(&UNIONFS_F(file)->generation, atomic_read(&UNIONFS_I(dentry->d_inode)-> generation)); goto out; } dget(lower_dentry); unionfs_mntget(dentry, bstart); lower_file = dentry_open(lower_dentry, unionfs_lower_mnt_idx(dentry, bstart), file->f_flags, current_cred()); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); goto out; } branchget(sb, bstart); unionfs_set_lower_file(file, lower_file); /* Fix up the position. */ lower_file->f_pos = file->f_pos; memcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state)); out: dput(parent); return err; }
/* * Post-copyup helper to ensure we have valid mnts: set lower mnt of * dentry+parents to the first parent node that has an mnt. */ void unionfs_postcopyup_setmnt(struct dentry *dentry) { struct dentry *parent, *hasone; int bindex = dbstart(dentry); if (unionfs_lower_mnt_idx(dentry, bindex)) return; hasone = dentry->d_parent; /* this loop should stop at root dentry */ while (!unionfs_lower_mnt_idx(hasone, bindex)) hasone = hasone->d_parent; parent = dentry; while (!unionfs_lower_mnt_idx(parent, bindex)) { unionfs_set_lower_mnt_idx(parent, bindex, unionfs_mntget(hasone, bindex)); parent = parent->d_parent; } }
void update_bstart(struct dentry *dentry) { int bindex; int bstart = dbstart(dentry); int bend = dbend(dentry); struct dentry *hidden_dentry; for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; if (hidden_dentry->d_inode) { set_dbstart(dentry, bindex); break; } DPUT(hidden_dentry); set_dtohd_index(dentry, bindex, NULL); } }
int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; fd_set branchlist; int bstart = 0, bend = 0, bindex = 0; struct dentry *dentry, *hidden_dentry; print_entry_location(); dentry = file->f_dentry; lock_dentry(dentry); if ((err = unionfs_partial_lookup(dentry))) goto out; bstart = dbstart(dentry); bend = dbend(dentry); FD_ZERO(&branchlist); for (bindex = bstart; bindex <= bend; bindex++) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; if (hidden_dentry->d_inode) FD_SET(bindex, &branchlist); } err = copy_to_user((void *)arg, &branchlist, sizeof(fd_set)); if (err) { err = -EFAULT; goto out; } out: unlock_dentry(dentry); err = err < 0 ? err : bend; print_exit_status(err); return (err); }
static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist) { int err; struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry = NULL; print_entry_location(); fist_print_dentry("IN unionfs_rmdir_first: ", dentry); /* Here we need to remove whiteout entries. */ err = delete_whiteouts(dentry, dbstart(dentry), namelist); if (err) { goto out; } hidden_dentry = dtohd(dentry); PASSERT(hidden_dentry); hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ DGET(hidden_dentry); if (!(err = is_robranch(dentry))) { err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); } DPUT(hidden_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* propagate number of hard-links */ dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); out: if (hidden_dir_dentry) { unlock_dir(hidden_dir_dentry); } fist_print_dentry("OUT unionfs_rmdir_first: ", dentry); print_exit_status(err); return err; }
static int unionfs_setattr(struct dentry *dentry, struct iattr *ia) { int err = 0; struct dentry *lower_dentry; struct dentry *parent; struct inode *inode; struct inode *lower_inode; int bstart, bend, bindex; loff_t size; struct iattr lower_ia; /* check if user has permission to change inode */ err = inode_change_ok(dentry->d_inode, ia); if (err) goto out_err; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); if (unlikely(!__unionfs_d_revalidate(dentry, parent, false, 0))) { err = -ESTALE; goto out; } bstart = dbstart(dentry); bend = dbend(dentry); inode = dentry->d_inode; /* * mode change is for clearing setuid/setgid. Allow lower filesystem * to reinterpret it in its own way. */ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ia->ia_valid &= ~ATTR_MODE; lower_dentry = unionfs_lower_dentry(dentry); if (!lower_dentry) { /* should never happen after above revalidate */ err = -EINVAL; goto out; } /* * Get the lower inode directly from lower dentry, in case ibstart * is -1 (which happens when the file is open but unlinked. */ lower_inode = lower_dentry->d_inode; /* check if user has permission to change lower inode */ err = inode_change_ok(lower_inode, ia); if (err) goto out; /* copyup if the file is on a read only branch */ if (is_robranch_super(dentry->d_sb, bstart) || __is_rdonly(lower_inode)) { /* check if we have a branch to copy up to */ if (bstart <= 0) { err = -EACCES; goto out; } if (ia->ia_valid & ATTR_SIZE) size = ia->ia_size; else size = i_size_read(inode); /* copyup to next available branch */ for (bindex = bstart - 1; bindex >= 0; bindex--) { err = copyup_dentry(parent->d_inode, dentry, bstart, bindex, dentry->d_name.name, dentry->d_name.len, NULL, size); if (!err) break; } if (err) goto out; /* get updated lower_dentry/inode after copyup */ lower_dentry = unionfs_lower_dentry(dentry); lower_inode = unionfs_lower_inode(inode); /* * check for whiteouts in writeable branch, and remove them * if necessary. */ if (lower_dentry) { err = check_unlink_whiteout(dentry, lower_dentry, bindex); if (err > 0) /* ignore if whiteout found and removed */ err = 0; } } /* * If shrinking, first truncate upper level to cancel writing dirty * pages beyond the new eof; and also if its' maxbytes is more * limiting (fail with -EFBIG before making any change to the lower * level). There is no need to vmtruncate the upper level * afterwards in the other cases: we fsstack_copy_inode_size from * the lower level. */ if (ia->ia_valid & ATTR_SIZE) { err = inode_newsize_ok(inode, ia->ia_size); if (err) goto out; truncate_setsize(inode, ia->ia_size); } /* notify the (possibly copied-up) lower inode */ /* * Note: we use lower_dentry->d_inode, because lower_inode may be * unlinked (no inode->i_sb and i_ino==0. This happens if someone * tries to open(), unlink(), then ftruncate() a file. */ /* prepare our own lower struct iattr (with our own lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) { lower_ia.ia_file = unionfs_lower_file(ia->ia_file); BUG_ON(!lower_ia.ia_file); // XXX? } mutex_lock(&lower_dentry->d_inode->i_mutex); err = notify_change(lower_dentry, &lower_ia); mutex_unlock(&lower_dentry->d_inode->i_mutex); if (err) goto out; /* get attributes from the first lower inode */ if (ibstart(inode) >= 0) unionfs_copy_attr_all(inode, lower_inode); /* * unionfs_copy_attr_all will copy the lower times to our inode if * the lower ones are newer (useful for cache coherency). However, * ->setattr is the only place in which we may have to copy the * lower inode times absolutely, to support utimes(2). */ if (ia->ia_valid & ATTR_MTIME_SET) inode->i_mtime = lower_inode->i_mtime; if (ia->ia_valid & ATTR_CTIME) inode->i_ctime = lower_inode->i_ctime; if (ia->ia_valid & ATTR_ATIME_SET) inode->i_atime = lower_inode->i_atime; fsstack_copy_inode_size(inode, lower_inode); out: if (!err) unionfs_check_dentry(dentry); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); out_err: return err; }