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; }
/* this unionfs_write() does not modify data pages! */ ssize_t unionfs_write(struct file * file, const char *buf, size_t count, loff_t * ppos) { int err = -EINVAL; struct file *hidden_file = NULL; struct inode *inode; struct inode *hidden_inode; loff_t pos = *ppos; int bstart, bend; print_entry_location(); if ((err = unionfs_file_revalidate(file, 1))) goto out; inode = file->f_dentry->d_inode; bstart = fbstart(file); bend = fbend(file); ASSERT(bstart != -1); PASSERT(ftopd(file)); PASSERT(ftohf(file)); hidden_file = ftohf(file); hidden_inode = hidden_file->f_dentry->d_inode; if (!hidden_file->f_op || !hidden_file->f_op->write) goto out; /* adjust for append -- seek to the end of the file */ if (file->f_flags & O_APPEND) pos = inode->i_size; err = hidden_file->f_op->write(hidden_file, buf, count, &pos); /* * copy ctime and mtime from lower layer attributes * atime is unchanged for both layers */ if (err >= 0) fist_copy_attr_times(inode, hidden_inode); *ppos = pos; /* update this inode's size */ if (pos > inode->i_size) inode->i_size = pos; out: print_exit_status(err); return err; }
/* * Unlink all destinations (if they exist) to the left of the left-most * source */ static int __rename_all_unlink(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct rename_info *info) { int bindex; struct dentry *unlink_dentry; struct dentry *unlink_dir_dentry; int err = 0; print_entry_location(); for (bindex = info->old_bstart - 1; bindex > info->new_bstart; bindex--) { unlink_dentry = dtohd_index(new_dentry, bindex); if (!unlink_dentry) continue; /* lock, unlink if possible, copyup times, unlock */ unlink_dir_dentry = lock_parent(unlink_dentry); if (!(err = is_robranch_super(old_dir->i_sb, bindex))) err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry); fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode); new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode); unlock_dir(unlink_dir_dentry); if (!err) { if (bindex != info->new_bstart) { DPUT(unlink_dentry); set_dtohd_index(new_dentry, bindex, NULL); } } else if (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; break; } info->do_copyup = bindex - 1; err = 0; /* reset error */ } else break; /* err is set by is_ro_branch_super or vfs_unlink */ } print_exit_status(err); return err; }
STATIC int base0fs_unlink(inode_t *dir, struct dentry *dentry) { int err = 0; inode_t *lower_dir; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; print_entry_location(); lower_dir = INODE_TO_LOWER(dir); /* CPW: Moved below print_entry_location */ lower_dentry = base0fs_lower_dentry(dentry); BUG_ON(!lower_dentry); fist_checkinode(dir, "base0fs_unlink-dir"); dget(dentry); lower_dir_dentry = base0fs_lock_parent(lower_dentry); /* avoid destroying the lower inode if the file is in use */ dget(lower_dentry); err = VFS_UNLINK(lower_dir, lower_dentry); dput(lower_dentry); if (!err) /* vfs_unlink does that */ d_delete(lower_dentry); out_lock: fist_copy_attr_times(dir, lower_dir); /* propagate number of hard-links */ dentry->d_inode->i_nlink = INODE_TO_LOWER(dentry->d_inode)->i_nlink; fist_copy_attr_ctime(dentry->d_inode, dir); unlock_dir(lower_dir_dentry); /* * call d_drop so the system "forgets" about us */ if (!err) { d_drop(dentry); } dput(dentry); fist_checkinode(dir, "post base0fs_unlink-dir"); 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 base0fs_rmdir(inode_t *dir, struct dentry *dentry) { int err = 0; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "base0fs_rmdir-dir"); dget(dentry); lower_dir_dentry = base0fs_lock_parent(lower_dentry); /* avoid destroying the lower inode if the file is in use */ dget(lower_dentry); err = VFS_RMDIR(lower_dir_dentry->d_inode, lower_dentry); dput(lower_dentry); if (!err) /* vfs_rmdir does that */ d_delete(lower_dentry); out_lock: fist_copy_attr_times(dir, lower_dir_dentry->d_inode); /* copy the nlink count for our dentry and our parent's dentry */ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; unlock_dir(lower_dir_dentry); /* * call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); dput(dentry); print_exit_status(err); return err; }
static int unionfs_unlink_all(struct inode *dir, struct dentry *dentry) { struct dentry *hidden_dentry; struct dentry *hidden_dir_dentry; int bstart, bend, bindex; int err = 0; int global_err = 0; print_entry_location(); if ((err = unionfs_partial_lookup(dentry))) goto out; 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); /* 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) { /* passup the last error we got */ if (!IS_COPYUP_ERR(err)) 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 if (dbopaque(dentry) != -1) { /* There is a hidden lower-priority file with the same name. */ err = create_whiteout(dentry, dbopaque(dentry)); } out: /* propagate number of hard-links */ if (dentry->d_inode->i_nlink != 0) { dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); if (!err && global_err) dentry->d_inode->i_nlink--; } /* We don't want to leave negative leftover dentries for revalidate. */ if (!err && (global_err || dbopaque(dentry) != -1)) update_bstart(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; }
/* * Finish off the rename, by either over writing the last destination or * unlinking the last destination to the left of us */ static int __rename_all_clobber(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct rename_info *info) { int err = 0; print_entry_location(); if (dtohd_index(old_dentry, info->new_bstart)) { /* rename the last source, knowing we're overwriting something */ DPUT(info->wh_old); info->bwh_old = info->new_bstart; err = do_rename(old_dir, old_dentry, new_dir, new_dentry, info->new_bstart, &info->wh_old); if (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; goto out; } if (info->rename_ok > info->new_bstart) { if ((info->do_copyup == -1) || (info->new_bstart - 1 < info->do_copyup)) info->do_copyup = info->new_bstart - 1; } if ((info->do_whiteout == -1) || (info->new_bstart - 1 < info->do_whiteout)) { info->do_whiteout = info->new_bstart - 1; } err = 0; // reset error } } else if (info->new_bstart < info->old_bstart) { /* the newly renamed file would get hidden, let's unlink the * file to the left of it */ struct dentry *unlink_dentry; struct dentry *unlink_dir_dentry; unlink_dentry = dtohd_index(new_dentry, info->new_bstart); unlink_dir_dentry = lock_parent(unlink_dentry); if (!(err = is_robranch_super(old_dir->i_sb, info->new_bstart))) err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry); fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode); new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode); unlock_dir(unlink_dir_dentry); if (IS_COPYUP_ERR(err)) { if (info->isdir) { err = -EXDEV; goto out; } if ((info->do_copyup == -1) || (info->new_bstart - 1 < info->do_copyup)) info->do_copyup = info->new_bstart - 1; err = 0; // reset error } } out: print_exit_status(err); return err; }
static int unionfs_rename_whiteout(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; int bindex, bwh_old; int old_bstart, old_bend; int new_bstart, new_bend; int do_copyup = -1; struct dentry *parent_dentry; int local_err = 0; int eio = 0; int revert = 0; struct dentry *wh_old = NULL; print_entry_location(); old_bstart = dbstart(old_dentry); bwh_old = old_bstart; old_bend = dbend(old_dentry); parent_dentry = old_dentry->d_parent; new_bstart = dbstart(new_dentry); new_bend = dbend(new_dentry); /* Rename source to destination. */ err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart, &wh_old); if (err) { if (!IS_COPYUP_ERR(err)) { goto out; } do_copyup = old_bstart - 1; } else { revert = 1; } /* Unlink all instances of destination that exist to the left of * bstart of source. On error, revert back, goto out. */ for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) { struct dentry *unlink_dentry; struct dentry *unlink_dir_dentry; unlink_dentry = dtohd_index(new_dentry, bindex); if (!unlink_dentry) { continue; } unlink_dir_dentry = lock_parent(unlink_dentry); if (!(err = is_robranch_super(old_dir->i_sb, bindex))) { err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry); } fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode); /* propagate number of hard-links */ new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode); unlock_dir(unlink_dir_dentry); if (!err) { if (bindex != new_bstart) { DPUT(unlink_dentry); set_dtohd_index(new_dentry, bindex, NULL); } } else if (IS_COPYUP_ERR(err)) { do_copyup = bindex - 1; } else if (revert) { DPUT(wh_old); goto revert; } } if (do_copyup != -1) { for (bindex = do_copyup; bindex >= 0; bindex--) { /* copyup the file into some left directory, so that you can rename it */ err = copyup_dentry(old_dentry->d_parent->d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size); if (!err) { DPUT(wh_old); bwh_old = bindex; err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex, &wh_old); break; } } } /* Create whiteout for source, only if: * (1) There is more than one underlying instance of source. * (2) We did a copy_up */ if ((old_bstart != old_bend) || (do_copyup != -1)) { struct dentry *hidden_parent; BUG_ON(!wh_old || IS_ERR(wh_old) || wh_old->d_inode || bwh_old < 0); hidden_parent = lock_parent(wh_old); local_err = vfs_create(hidden_parent->d_inode, wh_old, S_IRUGO, NULL); unlock_dir(hidden_parent); if (!local_err) set_dbopaque(old_dentry, bwh_old); else { /* We can't fix anything now, so we cop-out and use -EIO. */ printk ("<0>We can't create a whiteout for the source in rename!\n"); err = -EIO; } } out: DPUT(wh_old); print_exit_status(err); return err; revert: /* Do revert here. */ local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart); if (local_err) { printk(KERN_WARNING "Revert failed in rename: the new refresh failed.\n"); eio = -EIO; } local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart); if (local_err) { printk(KERN_WARNING "Revert failed in rename: the old refresh failed.\n"); eio = -EIO; goto revert_out; } if (!dtohd_index(new_dentry, bindex) || !dtohd_index(new_dentry, bindex)->d_inode) { printk(KERN_WARNING "Revert failed in rename: the object disappeared from under us!\n"); eio = -EIO; goto revert_out; } if (dtohd_index(old_dentry, bindex) && dtohd_index(old_dentry, bindex)->d_inode) { printk(KERN_WARNING "Revert failed in rename: the object was created underneath us!\n"); eio = -EIO; goto revert_out; } local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart, NULL); /* If we can't fix it, then we cop-out with -EIO. */ if (local_err) { printk(KERN_WARNING "Revert failed in rename!\n"); eio = -EIO; } local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex); if (local_err) eio = -EIO; local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex); if (local_err) eio = -EIO; revert_out: if (eio) err = eio; print_exit_status(err); return err; }
static int unionfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int err = 0; struct dentry *hidden_dentry = NULL; struct dentry *whiteout_dentry = NULL; struct dentry *hidden_dir_dentry = NULL; umode_t mode; int bindex = 0, bstart; char *name = NULL; print_entry_location(); lock_dentry(dentry); fist_print_dentry("IN unionfs_symlink", dentry); /* We start out in the leftmost branch. */ bstart = dbstart(dentry); hidden_dentry = dtohd(dentry); /* check if whiteout exists in this branch, i.e. lookup .wh.foo first. If present, delete it */ name = alloc_whname(dentry->d_name.name, dentry->d_name.len); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } whiteout_dentry = LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, 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_symlink() */ hidden_dir_dentry = lock_parent(whiteout_dentry); fist_print_generic_dentry("HDD", hidden_dir_dentry); fist_print_generic_dentry("WD", whiteout_dentry); if (!(err = is_robranch_super(dentry->d_sb, bstart))) { err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry); } DPUT(whiteout_dentry); fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* propagate number of hard-links */ dir->i_nlink = get_nlinks(dir); unlock_dir(hidden_dir_dentry); if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; /* should now try to create symlink in the another branch */ bstart--; } } /* deleted whiteout if it was present, now do a normal vfs_symlink() with possible recursive directory creation */ for (bindex = bstart; bindex >= 0; bindex--) { hidden_dentry = dtohd_index(dentry, bindex); if (!hidden_dentry) { /* if hidden_dentry is NULL, create the entire * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a * lone negative underlying dentry */ hidden_dentry = create_parents(dir, dentry, bindex); if (!hidden_dentry || IS_ERR(hidden_dentry)) { if (IS_ERR(hidden_dentry)) { err = PTR_ERR(hidden_dentry); } fist_dprint(8, "hidden dentry NULL (or error) for bindex = %d\n", bindex); continue; } } hidden_dir_dentry = lock_parent(hidden_dentry); if (!(err = is_robranch_super(dentry->d_sb, bindex))) { mode = S_IALLUGO; err = vfs_symlink(hidden_dir_dentry->d_inode, hidden_dentry, symname, mode); } unlock_dir(hidden_dir_dentry); if (err || !hidden_dentry->d_inode) { /* break out of for loop if error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) break; } else { err = unionfs_interpose(dentry, dir->i_sb, 0); if (!err) { fist_copy_attr_timesizes(dir, hidden_dir_dentry-> d_inode); /* update number of links on parent directory */ dir->i_nlink = get_nlinks(dir); } break; } } out: if (!dentry->d_inode) d_drop(dentry); KFREE(name); fist_print_dentry("OUT unionfs_symlink :", dentry); unlock_dentry(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; }