summaryrefslogtreecommitdiff
path: root/fs/ksmbd/vfs.c
diff options
context:
space:
mode:
authorHyunchul Lee <hyc.lee@gmail.com>2021-06-23 05:07:43 +0300
committerNamjae Jeon <namjae.jeon@samsung.com>2021-06-28 10:28:37 +0300
commit333111a6dc32a2768f581876bdb5ef4231f5084e (patch)
tree39471453836512e909a8fa131eba841aad2bbbdb /fs/ksmbd/vfs.c
parent49be5aa47b69ad09b0045b662d61bad51e6309e7 (diff)
downloadlinux-333111a6dc32a2768f581876bdb5ef4231f5084e.tar.xz
ksmbd: factor out a ksmbd_vfs_lock_parent helper
Factor out a self-contained helper to get stable parent dentry. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/ksmbd/vfs.c')
-rw-r--r--fs/ksmbd/vfs.c125
1 files changed, 62 insertions, 63 deletions
diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
index fddabb4c7db6..e64eab7a58a8 100644
--- a/fs/ksmbd/vfs.c
+++ b/fs/ksmbd/vfs.c
@@ -60,6 +60,41 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
i_uid_write(inode, i_uid_read(parent_inode));
}
+/**
+ * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
+ *
+ * the parent dentry got by dget_parent or @parent could be
+ * unstable, we try to lock a parent inode and lookup the
+ * child dentry again.
+ *
+ * the reference count of @parent isn't incremented.
+ */
+static int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
+{
+ struct dentry *dentry;
+ int ret = 0;
+
+ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
+ dentry = lookup_one_len(child->d_name.name, parent,
+ child->d_name.len);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto out_err;
+ }
+
+ if (dentry != child) {
+ ret = -ESTALE;
+ dput(dentry);
+ goto out_err;
+ }
+
+ dput(dentry);
+ return 0;
+out_err:
+ inode_unlock(d_inode(parent));
+ return ret;
+}
+
int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
{
int mask, ret = 0;
@@ -78,29 +113,18 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
return -EACCES;
if (delete) {
- struct dentry *child, *parent;
+ struct dentry *parent;
parent = dget_parent(dentry);
- inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
- child = lookup_one_len(dentry->d_name.name, parent,
- dentry->d_name.len);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- goto out_lock;
- }
-
- if (child != dentry) {
- ret = -ESTALE;
- dput(child);
- goto out_lock;
+ ret = ksmbd_vfs_lock_parent(parent, dentry);
+ if (ret) {
+ dput(parent);
+ return ret;
}
- dput(child);
- if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) {
+ if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE))
ret = -EACCES;
- goto out_lock;
- }
-out_lock:
+
inode_unlock(d_inode(parent));
dput(parent);
}
@@ -109,7 +133,7 @@ out_lock:
int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
{
- struct dentry *parent, *child;
+ struct dentry *parent;
int ret = 0;
*daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
@@ -127,25 +151,15 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
*daccess |= FILE_EXECUTE_LE;
parent = dget_parent(dentry);
- inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
- child = lookup_one_len(dentry->d_name.name, parent,
- dentry->d_name.len);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- goto out_lock;
- }
-
- if (child != dentry) {
- ret = -ESTALE;
- dput(child);
- goto out_lock;
+ ret = ksmbd_vfs_lock_parent(parent, dentry);
+ if (ret) {
+ dput(parent);
+ return ret;
}
- dput(child);
if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE))
*daccess |= FILE_DELETE_LE;
-out_lock:
inode_unlock(d_inode(parent));
dput(parent);
return ret;
@@ -573,7 +587,7 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
{
struct path path;
- struct dentry *dentry, *parent;
+ struct dentry *parent;
int err;
int flags = 0;
@@ -592,35 +606,32 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
}
parent = dget_parent(path.dentry);
- inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
- dentry = lookup_one_len(path.dentry->d_name.name, parent,
- strlen(path.dentry->d_name.name));
- if (IS_ERR(dentry)) {
- err = PTR_ERR(dentry);
- ksmbd_debug(VFS, "%s: lookup failed, err %d\n",
- path.dentry->d_name.name, err);
- goto out_err;
+ err = ksmbd_vfs_lock_parent(parent, path.dentry);
+ if (err) {
+ dput(parent);
+ path_put(&path);
+ ksmbd_revert_fsids(work);
+ return err;
}
- if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) {
- dput(dentry);
+ if (!d_inode(path.dentry)->i_nlink) {
err = -ENOENT;
goto out_err;
}
- if (S_ISDIR(d_inode(dentry)->i_mode)) {
- err = vfs_rmdir(&init_user_ns, d_inode(parent), dentry);
+ if (S_ISDIR(d_inode(path.dentry)->i_mode)) {
+ err = vfs_rmdir(&init_user_ns, d_inode(parent), path.dentry);
if (err && err != -ENOTEMPTY)
ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name,
err);
} else {
- err = vfs_unlink(&init_user_ns, d_inode(parent), dentry, NULL);
+ err = vfs_unlink(&init_user_ns, d_inode(parent), path.dentry,
+ NULL);
if (err)
ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name,
err);
}
- dput(dentry);
out_err:
inode_unlock(d_inode(parent));
dput(parent);
@@ -1086,30 +1097,18 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name)
int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry)
{
- struct dentry *child;
int err = 0;
- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
+ err = ksmbd_vfs_lock_parent(dir, dentry);
+ if (err)
+ return err;
dget(dentry);
- child = lookup_one_len(dentry->d_name.name, dir, dentry->d_name.len);
- if (IS_ERR(child)) {
- err = PTR_ERR(child);
- goto out;
- }
-
- if (child != dentry) {
- err = -ESTALE;
- dput(child);
- goto out;
- }
- dput(child);
if (S_ISDIR(d_inode(dentry)->i_mode))
err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry);
else
err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL);
-out:
dput(dentry);
inode_unlock(d_inode(dir));
if (err)