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 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_rmdir_all(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bstart, bend, bindex; int err = 0; int global_err = 0; print_entry_location(); fist_print_dentry("IN unionfs_rmdir_all: ", dentry); bstart = dbstart(dentry); bend = dbend(dentry); for (bindex = bend; bindex >= bstart; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) continue; hidden_dir_dentry = lock_parent(hidden_dentry); if (S_ISDIR(hidden_dentry->d_inode->i_mode)) { delete_whiteouts(dentry, bindex, namelist); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); } } else { err = -EISDIR; } fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); unlock_dir(hidden_dir_dentry); if (err) { int local_err = unionfs_refresh_hidden_dentry(dentry, bindex); if (local_err) { err = local_err; goto out; } if (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY && err != -EISDIR) goto out; global_err = err; } } /* check if encountered error in the above loop */ if (global_err) { /* If we failed in the leftmost branch, then err will be set and we should * move one over to create the whiteout. Otherwise, we should try in the * leftmost branch. */ if (err) { if (dbstart(dentry) == 0) { goto out; } err = create_whiteout(dentry, dbstart(dentry) - 1); } else { err = create_whiteout(dentry, dbstart(dentry)); } } else { err = create_whiteout(dentry, dbstart(dentry)); } out: /* propagate number of hard-links */ dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); fist_print_dentry("OUT unionfs_rmdir_all: ", dentry); print_exit_status(err); return err; }
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *wh_dentry; double_lock_dentry(old_dentry, new_dentry); if (!S_ISDIR(old_dentry->d_inode->i_mode)) err = unionfs_partial_lookup(old_dentry); else err = may_rename_dir(old_dentry); if (err) goto out; err = unionfs_partial_lookup(new_dentry); if (err) goto out; /* * if new_dentry is already hidden because of whiteout, * simply override it even if the whiteouted dir is not empty. */ wh_dentry = lookup_whiteout(new_dentry); if (!IS_ERR(wh_dentry)) dput(wh_dentry); else if (new_dentry->d_inode) { if (S_ISDIR(old_dentry->d_inode->i_mode) != S_ISDIR(new_dentry->d_inode->i_mode)) { err = S_ISDIR(old_dentry->d_inode->i_mode) ? -ENOTDIR : -EISDIR; goto out; } if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct unionfs_dir_state *namelist; /* check if this unionfs directory is empty or not */ err = check_empty(new_dentry, &namelist); if (err) goto out; if (!is_robranch(new_dentry)) err = delete_whiteouts(new_dentry, dbstart(new_dentry), namelist); free_rdstate(namelist); if (err) goto out; } } err = do_unionfs_rename(old_dir, old_dentry, new_dir, new_dentry); out: if (err) /* clear the new_dentry stuff created */ d_drop(new_dentry); else /* force re-lookup since the dir on ro branch is not renamed, and hidden dentries still indicate the un-renamed ones. */ if (S_ISDIR(old_dentry->d_inode->i_mode)) atomic_dec(&UNIONFS_D(old_dentry)->generation); unionfs_unlock_dentry(new_dentry); unionfs_unlock_dentry(old_dentry); return err; }
/* * The locking rules in unionfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *wh_dentry; struct dentry *old_parent, *new_parent; int valid = true; unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); old_parent = dget_parent(old_dentry); new_parent = dget_parent(new_dentry); /* un/lock parent dentries only if they differ from old/new_dentry */ if (old_parent != old_dentry && old_parent != new_dentry) unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT); if (new_parent != old_dentry && new_parent != new_dentry && new_parent != old_parent) unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD); unionfs_double_lock_dentry(old_dentry, new_dentry); valid = __unionfs_d_revalidate(old_dentry, old_parent, false); if (!valid) { err = -ESTALE; goto out; } if (!d_deleted(new_dentry) && new_dentry->d_inode) { valid = __unionfs_d_revalidate(new_dentry, new_parent, false); if (!valid) { err = -ESTALE; goto out; } } if (!S_ISDIR(old_dentry->d_inode->i_mode)) err = unionfs_partial_lookup(old_dentry, old_parent); else err = may_rename_dir(old_dentry, old_parent); if (err) goto out; err = unionfs_partial_lookup(new_dentry, new_parent); if (err) goto out; /* * if new_dentry is already lower because of whiteout, * simply override it even if the whited-out dir is not empty. */ wh_dentry = find_first_whiteout(new_dentry); if (!IS_ERR(wh_dentry)) { dput(wh_dentry); } else if (new_dentry->d_inode) { if (S_ISDIR(old_dentry->d_inode->i_mode) != S_ISDIR(new_dentry->d_inode->i_mode)) { err = S_ISDIR(old_dentry->d_inode->i_mode) ? -ENOTDIR : -EISDIR; goto out; } if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct unionfs_dir_state *namelist = NULL; /* check if this unionfs directory is empty or not */ err = check_empty(new_dentry, new_parent, &namelist); if (err) goto out; if (!is_robranch(new_dentry)) err = delete_whiteouts(new_dentry, dbstart(new_dentry), namelist); free_rdstate(namelist); if (err) goto out; } } err = do_unionfs_rename(old_dir, old_dentry, old_parent, new_dir, new_dentry, new_parent); if (err) goto out; /* * force re-lookup since the dir on ro branch is not renamed, and * lower dentries still indicate the un-renamed ones. */ if (S_ISDIR(old_dentry->d_inode->i_mode)) atomic_dec(&UNIONFS_D(old_dentry)->generation); else unionfs_postcopyup_release(old_dentry); if (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) { unionfs_postcopyup_release(new_dentry); unionfs_postcopyup_setmnt(new_dentry); if (!unionfs_lower_inode(new_dentry->d_inode)) { /* * If we get here, it means that no copyup was * needed, and that a file by the old name already * existing on the destination branch; that file got * renamed earlier in this function, so all we need * to do here is set the lower inode. */ struct inode *inode; inode = unionfs_lower_inode(old_dentry->d_inode); igrab(inode); unionfs_set_lower_inode_idx(new_dentry->d_inode, dbstart(new_dentry), inode); } } /* if all of this renaming succeeded, update our times */ unionfs_copy_attr_times(old_dentry->d_inode); unionfs_copy_attr_times(new_dentry->d_inode); unionfs_check_inode(old_dir); unionfs_check_inode(new_dir); unionfs_check_dentry(old_dentry); unionfs_check_dentry(new_dentry); out: if (err) /* clear the new_dentry stuff created */ d_drop(new_dentry); unionfs_double_unlock_dentry(old_dentry, new_dentry); if (new_parent != old_dentry && new_parent != new_dentry && new_parent != old_parent) unionfs_unlock_dentry(new_parent); if (old_parent != old_dentry && old_parent != new_dentry) unionfs_unlock_dentry(old_parent); dput(new_parent); dput(old_parent); unionfs_read_unlock(old_dentry->d_sb); return err; }