/* * returns: -ERRNO if error (returned to user) * 0: tell VFS to invalidate dentry * 1: dentry is valid */ static int esdfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { struct path lower_path; struct path lower_parent_path; struct dentry *parent_dentry = NULL; struct dentry *lower_dentry = NULL; struct dentry *lower_parent_dentry = NULL; int err = 1; if (nd && (nd->flags & LOOKUP_RCU)) return -ECHILD; /* short-circuit if it's root */ spin_lock(&dentry->d_lock); if (IS_ROOT(dentry)) { spin_unlock(&dentry->d_lock); return 1; } spin_unlock(&dentry->d_lock); esdfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; esdfs_get_lower_parent(dentry, lower_dentry, &lower_parent_dentry); parent_dentry = dget_parent(dentry); esdfs_get_lower_path(parent_dentry, &lower_parent_path); if (lower_parent_path.dentry != lower_parent_dentry) goto drop; /* can't do strcmp if lower is hashed */ spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); goto drop; } spin_lock(&dentry->d_lock); if (lower_dentry->d_name.len != dentry->d_name.len || strncasecmp(lower_dentry->d_name.name, dentry->d_name.name, dentry->d_name.len) != 0) { err = 0; __d_drop(dentry); /* already holding spin lock */ } spin_unlock(&dentry->d_lock); spin_unlock(&lower_dentry->d_lock); esdfs_revalidate_perms(dentry); goto out; drop: d_drop(dentry); err = 0; out: esdfs_put_lower_path(parent_dentry, &lower_parent_path); dput(parent_dentry); esdfs_put_lower_parent(dentry, &lower_parent_dentry); esdfs_put_lower_path(dentry, &lower_path); return err; }
/* * The locking rules in esdfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. */ static int esdfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; struct dentry *trap = NULL; struct path lower_old_path, lower_new_path; int mask; const struct cred *creds = esdfs_override_creds(ESDFS_SB(old_dir->i_sb), &mask); if (!creds) return -ENOMEM; /* Never rename to or from a pseudo hard link target. */ if (ESDFS_DENTRY_HAS_STUB(old_dentry)) esdfs_get_lower_stub_path(old_dentry, &lower_old_path); else esdfs_get_lower_path(old_dentry, &lower_old_path); if (ESDFS_DENTRY_HAS_STUB(new_dentry)) esdfs_get_lower_stub_path(new_dentry, &lower_new_path); else esdfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; esdfs_get_lower_parent(old_dentry, lower_old_dentry, &lower_old_dir_dentry); esdfs_get_lower_parent(new_dentry, lower_new_dentry, &lower_new_dir_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); /* source should not be ancestor of target */ if (trap == lower_old_dentry) { err = -EINVAL; goto out; } /* target should not be ancestor of source */ if (trap == lower_new_dentry) { err = -ENOTEMPTY; goto out; } err = mnt_want_write(lower_old_path.mnt); if (err) goto out; err = mnt_want_write(lower_new_path.mnt); if (err) goto out_drop_old_write; err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (err) goto out_err; esdfs_copy_attr(new_dir, lower_new_dir_dentry->d_inode); fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); if (new_dir != old_dir) { esdfs_copy_attr(old_dir, lower_old_dir_dentry->d_inode); fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); } /* Drop any old links */ if (ESDFS_DENTRY_HAS_STUB(old_dentry)) d_drop(old_dentry); if (ESDFS_DENTRY_HAS_STUB(new_dentry)) d_drop(new_dentry); out_err: mnt_drop_write(lower_new_path.mnt); out_drop_old_write: mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); esdfs_put_lower_parent(old_dentry, &lower_old_dir_dentry); esdfs_put_lower_parent(new_dentry, &lower_new_dir_dentry); esdfs_put_lower_path(old_dentry, &lower_old_path); esdfs_put_lower_path(new_dentry, &lower_new_path); esdfs_revert_creds(creds, &mask); return err; }