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; }
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; print_entry_location(); double_lock_dentry(old_dentry, new_dentry); fist_checkinode(old_dir, "unionfs_rename-old_dir"); fist_checkinode(new_dir, "unionfs_rename-new_dir"); fist_print_dentry("IN: unionfs_rename, old_dentry", old_dentry); fist_print_dentry("IN: unionfs_rename, new_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(old_dentry->d_inode->i_mode)) { /* check if this unionfs directory is empty or not */ err = check_empty(new_dentry, NULL); if (err) goto out; /* Handle the case where we are overwriting directories * that are not really empty because of whiteout or * non-whiteout entries. */ } } #ifdef UNIONFS_DELETE_ALL if (IS_SET(old_dir->i_sb, DELETE_ALL)) err = unionfs_rename_all(old_dir, old_dentry, new_dir, new_dentry); else #endif err = unionfs_rename_whiteout(old_dir, old_dentry, new_dir, new_dentry); out: fist_checkinode(new_dir, "post unionfs_rename-new_dir"); fist_print_dentry("OUT: unionfs_rename, old_dentry", old_dentry); 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(&dtopd(old_dentry)->udi_generation); fist_print_dentry("OUT: unionfs_rename, new_dentry", new_dentry); } unlock_dentry(new_dentry); unlock_dentry(old_dentry); print_exit_status(err); return err; }
static int unionfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { int err = 0; struct dentry *hidden_old_dentry = NULL; struct dentry *hidden_new_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; struct dentry *whiteout_dentry; char *name = NULL; print_entry_location(); double_lock_dentry(new_dentry, old_dentry); hidden_new_dentry = dtohd(new_dentry); /* check if whiteout exists in the branch of new dentry, i.e. lookup * .wh.foo first. If present, delete it */ name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_new_dentry->d_parent, new_dentry->d_name.len + WHLEN); if (IS_ERR(whiteout_dentry)) { err = PTR_ERR(whiteout_dentry); goto out; } if (!whiteout_dentry->d_inode) { DPUT(whiteout_dentry); whiteout_dentry = NULL; } else { /* found a .wh.foo entry, unlink it and then call vfs_link() */ hidden_dir_dentry = lock_parent(whiteout_dentry); if (! (err = is_robranch_super(new_dentry->d_sb, dbstart(new_dentry)))) { err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry); } fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); dir->i_nlink = get_nlinks(dir); unlock_dir(hidden_dir_dentry); hidden_dir_dentry = NULL; DPUT(whiteout_dentry); if (err) goto out; } if (dbstart(old_dentry) != dbstart(new_dentry)) { hidden_new_dentry = create_parents(dir, new_dentry, dbstart(old_dentry)); err = PTR_ERR(hidden_new_dentry); if (IS_COPYUP_ERR(err)) goto docopyup; if (!hidden_new_dentry || IS_ERR(hidden_new_dentry)) goto out; } hidden_new_dentry = dtohd(new_dentry); hidden_old_dentry = dtohd(old_dentry); BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); hidden_dir_dentry = lock_parent(hidden_new_dentry); if (!(err = is_robranch(old_dentry))) err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry); unlock_dir(hidden_dir_dentry); docopyup: if (IS_COPYUP_ERR(err)) { int old_bstart = dbstart(old_dentry); int bindex; for (bindex = old_bstart - 1; bindex >= 0; bindex--) { err = copyup_dentry(old_dentry->d_parent-> d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size); if (!err) { hidden_new_dentry = create_parents(dir, new_dentry, bindex); hidden_old_dentry = dtohd(old_dentry); hidden_dir_dentry = lock_parent(hidden_new_dentry); /* do vfs_link */ err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry); unlock_dir(hidden_dir_dentry); goto check_link; } } goto out; } check_link: if (err || !hidden_new_dentry->d_inode) goto out; /* Its a hard link, so use the same inode */ new_dentry->d_inode = IGRAB(old_dentry->d_inode); d_instantiate(new_dentry, new_dentry->d_inode); fist_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode); /* propagate number of hard-links */ old_dentry->d_inode->i_nlink = get_nlinks(old_dentry->d_inode); out: if (!new_dentry->d_inode) d_drop(new_dentry); KFREE(name); unlock_dentry(new_dentry); unlock_dentry(old_dentry); print_exit_status(err); return err; }