diff --git a/fs/sharefs/authentication.c b/fs/sharefs/authentication.c index bc6e825092fa5e029cd6990562bc635e8ba8f1d1..8986fcbcc894dd48f5312a6b77d2d6327b077089 100644 --- a/fs/sharefs/authentication.c +++ b/fs/sharefs/authentication.c @@ -75,8 +75,7 @@ void sharefs_root_inode_perm_init(struct inode *root_inode) hii->perm = SHAREFS_PERM_FIX; } -#ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE -const struct cred *sharefs_override_file_fsids(struct inode *dir, __u16 *_perm) +const struct cred *sharefs_override_file_fsids(struct inode *dir) { struct cred *cred = NULL; cred = prepare_creds(); @@ -95,4 +94,3 @@ void sharefs_revert_fsids(const struct cred *old_cred) revert_creds(old_cred); put_cred(cur_cred); } -#endif diff --git a/fs/sharefs/authentication.h b/fs/sharefs/authentication.h index e598fa883c0a47f52b4264314864ccad4d2884fb..0a096d1ee710413580719544ec81cf731bdb86de 100644 --- a/fs/sharefs/authentication.h +++ b/fs/sharefs/authentication.h @@ -40,11 +40,8 @@ extern void sharefs_exit_configfs(void); void sharefs_root_inode_perm_init(struct inode *root_inode); void fixup_perm_from_level(struct inode *dir, struct dentry *dentry); -#ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE -const struct cred *sharefs_override_file_fsids(struct inode *dir, - __u16 *_perm); +const struct cred *sharefs_override_file_fsids(struct inode *dir); void sharefs_revert_fsids(const struct cred *old_cred); -#endif static inline bool is_read_only_auth(__u16 perm) { diff --git a/fs/sharefs/dentry.c b/fs/sharefs/dentry.c index dee29cace6b751a5cd29b648bb5024bc53e96714..c1a4c30f30fe70596621bca66c882090b2c4c914 100644 --- a/fs/sharefs/dentry.c +++ b/fs/sharefs/dentry.c @@ -10,6 +10,7 @@ */ #include "sharefs.h" +#include "authentication.h" /* * returns: 0: tell VFS to invalidate dentry in share directory @@ -29,7 +30,7 @@ static void sharefs_d_release(struct dentry *dentry) */ if (SHAREFS_D(dentry)) { /* release and reset the lower paths */ - sharefs_put_reset_lower_path(dentry); + sharefs_reset_lower_path(dentry); free_dentry_private_data(dentry); } return; @@ -39,3 +40,106 @@ const struct dentry_operations sharefs_dops = { .d_revalidate = sharefs_d_revalidate, .d_release = sharefs_d_release, }; + +static int try_to_create_lower_path(struct dentry *d, struct path *lower_path) +{ + int err; + struct path lower_dir_path; + struct qstr this; + struct dentry *parent; + struct dentry *lower_dentry; + struct inode *lower_dir_inode; + + parent = dget_parent(d); + err = sharefs_get_lower_path(parent, &lower_dir_path, 0); + dput(parent); + if (err) + return err; + /* instantiate a new negative dentry */ + this.name = d->d_name.name; + this.len = strlen(d->d_name.name); + this.hash = full_name_hash(lower_dir_path.dentry, this.name, this.len); + lower_dentry = d_alloc(lower_dir_path.dentry, &this); + if (!lower_dentry) { + err = -ENOMEM; + goto out; + } + lower_dir_inode = d_inode(lower_dir_path.dentry); + if (lower_dir_inode->i_op && lower_dir_inode->i_op->lookup) + lower_dir_inode->i_op->lookup(lower_dir_inode, lower_dentry, 0); + + spin_lock(&SHAREFS_D(d)->lock); + lower_path->dentry = lower_dentry; + lower_path->mnt = lower_dir_path.mnt; + spin_unlock(&SHAREFS_D(d)->lock); + +out: + sharefs_put_lower_path(parent, &lower_dir_path); + return err; +} + +/* Returns struct path. Caller must path_put it. */ +int sharefs_get_lower_path(struct dentry *d, struct path *lower_path, + bool try_to_create) +{ + int err = -ENOMEM; + char *path_buf; + char *path_name = NULL; + struct path lower_root_path; + const struct cred *saved_cred = NULL; + + if (unlikely(!d)) + goto out; + + path_buf = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!path_buf)) + goto out; + path_name = dentry_path_raw(d, path_buf, PATH_MAX); + if (IS_ERR(path_name)) { + err = PTR_ERR(path_name); + goto out_free; + } + + sharefs_get_lower_root_path(d, &lower_root_path); + saved_cred = sharefs_override_file_fsids(d_inode(lower_root_path.dentry)); + if (!saved_cred) { + sharefs_put_lower_path(d, &lower_root_path); + goto out_free; + } + spin_lock(&SHAREFS_D(d)->lock); + err = vfs_path_lookup(lower_root_path.dentry, lower_root_path.mnt, + path_name, LOOKUP_CREATE, lower_path); + spin_unlock(&SHAREFS_D(d)->lock); + sharefs_revert_fsids(saved_cred); + sharefs_put_lower_path(d, &lower_root_path); + + if (err != -ENOENT || !try_to_create) + goto out_free; + err = try_to_create_lower_path(d, lower_path); + +out_free: + kfree(path_buf); +out: + return err; +} + +int sharefs_get_lower_inode(struct dentry *d, struct inode **lower_inode) +{ + int err = 0; + struct path lower_path; + + err = sharefs_get_lower_path(d, &lower_path, 0); + if (err) + goto out; + + *lower_inode = d_inode(lower_path.dentry); + if ((*lower_inode)->i_flags & S_DEAD) { + err = -ENOENT; + } else if (!igrab(*lower_inode)) { + err = -ESTALE; + } + + sharefs_put_lower_path(d, &lower_path); +out: + return err; +} \ No newline at end of file diff --git a/fs/sharefs/file.c b/fs/sharefs/file.c index f04963d57a4a6b8896393ade9d80d59cae885ff9..1aa44baf67e5d0e1a848e24c04c739130c173a5b 100644 --- a/fs/sharefs/file.c +++ b/fs/sharefs/file.c @@ -31,6 +31,7 @@ static int sharefs_open(struct inode *inode, struct file *file) int err = 0; struct file *lower_file = NULL; struct path lower_path; + struct inode *lower_inode = NULL; /* don't open unhashed/deleted files */ if (d_unhashed(file->f_path.dentry)) { @@ -46,7 +47,9 @@ static int sharefs_open(struct inode *inode, struct file *file) } /* open lower object and link sharefs's file struct to lower's */ - sharefs_get_lower_path(file->f_path.dentry, &lower_path); + err = sharefs_get_lower_path(file->f_path.dentry, &lower_path, 0); + if (err) + goto out_free; lower_file = dentry_open(&lower_path, file->f_flags, current_cred()); path_put(&lower_path); if (IS_ERR(lower_file)) { @@ -60,16 +63,21 @@ static int sharefs_open(struct inode *inode, struct file *file) sharefs_set_lower_file(file, lower_file); } +out_free: if (err) { kfree(SHAREFS_F(file)); } else { kuid_t uid = inode->i_uid; kgid_t gid = inode->i_gid; mode_t mode = inode->i_mode; - fsstack_copy_attr_all(inode, sharefs_lower_inode(inode)); + err = sharefs_get_lower_inode(file->f_path.dentry, &lower_inode); + if (err) + goto out_err; + fsstack_copy_attr_all(inode, lower_inode); inode->i_uid = uid; inode->i_gid = gid; inode->i_mode = mode; + iput(lower_inode); } out_err: return err; @@ -116,7 +124,9 @@ static int sharefs_fsync(struct file *file, loff_t start, loff_t end, if (err) goto out; lower_file = sharefs_lower_file(file); - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 0); + if (err) + goto out; err = vfs_fsync_range(lower_file, start, end, datasync); sharefs_put_lower_path(dentry, &lower_path); out: diff --git a/fs/sharefs/inode.c b/fs/sharefs/inode.c index 7bd08abe79ea30ad6ec60298f812f00837176702..e5cb396263e219dab9262df74c1dc380a2e720f5 100644 --- a/fs/sharefs/inode.c +++ b/fs/sharefs/inode.c @@ -20,7 +20,9 @@ static int sharefs_getattr(const struct path *path, struct kstat *stat, struct path lower_path; int ret; - sharefs_get_lower_path(path->dentry, &lower_path); + ret = sharefs_get_lower_path(path->dentry, &lower_path, 0); + if (ret) + return ret; ret = vfs_getattr(&lower_path, stat, request_mask, flags); stat->ino = d_inode(path->dentry)->i_ino; stat->uid = d_inode(path->dentry)->i_uid; @@ -39,8 +41,14 @@ static ssize_t sharefs_listxattr(struct dentry *dentry, char *buffer, size_t buf struct dentry *lower_dentry; struct path lower_path; - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 0); + if (err) + return err; lower_dentry = lower_path.dentry; + if (d_inode(lower_dentry)->i_flags & S_DEAD) { + err = -ENOENT; + goto out; + } if (!(d_inode(lower_dentry)->i_opflags & IOP_XATTR)) { err = -EOPNOTSUPP; goto out; @@ -85,17 +93,23 @@ static int sharefs_create(struct inode *dir, struct dentry *dentry, struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; - __u16 child_perm; + struct inode *lower_inode = NULL; - saved_cred = sharefs_override_file_fsids(dir, &child_perm); + saved_cred = sharefs_override_file_fsids(dir); if (!saved_cred) { err = -ENOMEM; return err; } - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 1); + if (err) + goto out_revert; lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); + if (unlikely(!lower_parent_dentry)) { + err = -ENOENT; + goto out; + } err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) @@ -103,12 +117,17 @@ static int sharefs_create(struct inode *dir, struct dentry *dentry, err = sharefs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; - fsstack_copy_attr_times(dir, sharefs_lower_inode(dir)); + err = sharefs_get_lower_inode(dentry, &lower_inode); + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_inode); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); + iput(lower_inode); out: unlock_dir(lower_parent_dentry); sharefs_put_lower_path(dentry, &lower_path); +out_revert: sharefs_revert_fsids(saved_cred); return err; } @@ -120,17 +139,23 @@ static int sharefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; - __u16 child_perm; + struct inode *lower_inode = NULL; - saved_cred = sharefs_override_file_fsids(dir, &child_perm); + saved_cred = sharefs_override_file_fsids(dir); if (!saved_cred) { err = -ENOMEM; return err; } - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 1); + if (err) + goto out_revert; lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); + if (unlikely(!lower_parent_dentry)) { + err = -ENOENT; + goto out; + } err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); if (err) goto out; @@ -139,14 +164,19 @@ static int sharefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) if (err) goto out; - fsstack_copy_attr_times(dir, sharefs_lower_inode(dir)); + err = sharefs_get_lower_inode(dentry, &lower_inode); + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_inode); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ - set_nlink(dir, sharefs_lower_inode(dir)->i_nlink); + set_nlink(dir, lower_inode->i_nlink); + iput(lower_inode); out: unlock_dir(lower_parent_dentry); sharefs_put_lower_path(dentry, &lower_path); +out_revert: sharefs_revert_fsids(saved_cred); return err; } @@ -155,11 +185,20 @@ static int sharefs_unlink(struct inode *dir, struct dentry *dentry) { int err; struct dentry *lower_dentry = NULL; - struct inode *lower_dir_inode = sharefs_lower_inode(dir); + struct inode *lower_dir_inode = NULL; + struct inode *lower_inode = NULL; struct dentry *lower_dir_dentry = NULL; struct path lower_path; - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_inode(dentry->d_parent, &lower_dir_inode); + if (err) + return err; + err = sharefs_get_lower_inode(dentry, &lower_inode); + if (err) + goto put; + err = sharefs_get_lower_path(dentry, &lower_path, 0); + if (err) + goto put_dir; lower_dentry = lower_path.dentry; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); @@ -168,8 +207,7 @@ static int sharefs_unlink(struct inode *dir, struct dentry *dentry) goto out; fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_inode_size(dir, lower_dir_inode); - set_nlink(dentry->d_inode, - sharefs_lower_inode(dentry->d_inode)->i_nlink); + set_nlink(dentry->d_inode, lower_inode->i_nlink); dentry->d_inode->i_ctime = dir->i_ctime; d_drop(dentry); @@ -177,6 +215,10 @@ static int sharefs_unlink(struct inode *dir, struct dentry *dentry) unlock_dir(lower_dir_dentry); dput(lower_dentry); sharefs_put_lower_path(dentry, &lower_path); +put_dir: + iput(lower_inode); +put: + iput(lower_dir_inode); return err; } @@ -187,7 +229,9 @@ static int sharefs_rmdir(struct inode *dir, struct dentry *dentry) struct dentry *lower_dir_dentry; struct path lower_path; - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 0); + if (err) + return err; lower_dentry = lower_path.dentry; lower_dir_dentry = lock_parent(lower_dentry); err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); @@ -222,8 +266,14 @@ static int sharefs_rename(struct inode *old_dir, struct dentry *old_dentry, if (flags) return -EINVAL; - sharefs_get_lower_path(old_dentry, &lower_old_path); - sharefs_get_lower_path(new_dentry, &lower_new_path); + err = sharefs_get_lower_path(old_dentry, &lower_old_path, 0); + if (err) + return err; + err = sharefs_get_lower_path(new_dentry, &lower_new_path, 1); + if (err) { + sharefs_put_lower_path(old_dentry, &lower_old_path); + return err; + } lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_old_dir_dentry = dget_parent(lower_old_dentry); @@ -282,11 +332,15 @@ static int sharefs_setattr(struct dentry *dentry, struct iattr *ia) err = setattr_prepare(dentry, ia); if (err) - goto out_err; + return err; - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 0); + if (err) + return err; lower_dentry = lower_path.dentry; - lower_inode = sharefs_lower_inode(inode); + err = sharefs_get_lower_inode(dentry, &lower_inode); + if (err) + goto out_err; /* prepare our own lower struct iattr (with the lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); @@ -339,8 +393,9 @@ static int sharefs_setattr(struct dentry *dentry, struct iattr *ia) */ out: - sharefs_put_lower_path(dentry, &lower_path); + iput(lower_inode); out_err: + sharefs_put_lower_path(dentry, &lower_path); return err; } #endif diff --git a/fs/sharefs/lookup.c b/fs/sharefs/lookup.c index 0b0c30ef46f12aa9e22c49ebe7cee8ca5efd9875..9d5536c83161854d9e8a79ed337b5a5e6a74d977 100644 --- a/fs/sharefs/lookup.c +++ b/fs/sharefs/lookup.c @@ -141,6 +141,7 @@ struct inode *sharefs_iget(struct super_block *sb, struct inode *lower_inode) fsstack_copy_inode_size(inode, lower_inode); unlock_new_inode(inode); + iput(lower_inode); return inode; } @@ -199,14 +200,14 @@ int sharefs_interpose(struct dentry *dentry, struct super_block *sb, */ static struct dentry *__sharefs_lookup(struct dentry *dentry, unsigned int flags, - struct path *lower_parent_path) + struct path *lower_parent_path, + struct path *lower_path) { int err = 0; struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; const char *name; - struct path lower_path; struct qstr this; struct dentry *ret_dentry = NULL; @@ -214,7 +215,7 @@ static struct dentry *__sharefs_lookup(struct dentry *dentry, d_set_d_op(dentry, &sharefs_dops); if (IS_ROOT(dentry)) - goto out; + return NULL; name = dentry->d_name.name; @@ -224,12 +225,12 @@ static struct dentry *__sharefs_lookup(struct dentry *dentry, /* Use vfs_path_lookup to check if the dentry exists or not */ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, - &lower_path); + lower_path); /* no error: handle positive dentries */ if (!err) { - sharefs_set_lower_path(dentry, &lower_path); + sharefs_set_lower_path(dentry, lower_path); ret_dentry = - __sharefs_interpose(dentry, dentry->d_sb, &lower_path); + __sharefs_interpose(dentry, dentry->d_sb, lower_path); if (IS_ERR(ret_dentry)) { err = PTR_ERR(ret_dentry); /* path_put underlying path on error */ @@ -267,9 +268,9 @@ static struct dentry *__sharefs_lookup(struct dentry *dentry, lower_dentry, flags); setup_lower: - lower_path.dentry = lower_dentry; - lower_path.mnt = mntget(lower_dir_mnt); - sharefs_set_lower_path(dentry, &lower_path); + lower_path->dentry = lower_dentry; + lower_path->mnt = mntget(lower_dir_mnt); + sharefs_set_lower_path(dentry, lower_path); /* * If the intent is to create a file, then don't return an error, so @@ -290,29 +291,31 @@ struct dentry *sharefs_lookup(struct inode *dir, struct dentry *dentry, { int err; struct dentry *ret, *parent; + struct path lower_path; struct path lower_parent_path; #ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE const struct cred *saved_cred = NULL; - __u16 permission; #endif - parent = dget_parent(dentry); - sharefs_get_lower_path(parent, &lower_parent_path); + err = sharefs_get_lower_path(parent, &lower_parent_path, 0); + if (err) { + dput(parent); + return ERR_PTR(err); + } #ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE - saved_cred = sharefs_override_file_fsids(dir, &permission); + saved_cred = sharefs_override_file_fsids(dir); if (!saved_cred) { ret = ERR_PTR(-ENOMEM); goto out_err; } #endif - /* allocate dentry private data. We free it in ->d_release */ err = new_dentry_private_data(dentry); if (err) { ret = ERR_PTR(err); goto out; } - ret = __sharefs_lookup(dentry, flags, &lower_parent_path); + ret = __sharefs_lookup(dentry, flags, &lower_parent_path, &lower_path); if (IS_ERR(ret)) { sharefs_err("sharefs_lookup error!"); goto out; @@ -327,6 +330,7 @@ struct dentry *sharefs_lookup(struct inode *dir, struct dentry *dentry, fsstack_copy_attr_atime(d_inode(parent), sharefs_lower_inode(d_inode(parent))); fixup_perm_from_level(d_inode(parent), dentry); + sharefs_put_lower_path(dentry, &lower_path); out: #ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE sharefs_revert_fsids(saved_cred); diff --git a/fs/sharefs/sharefs.h b/fs/sharefs/sharefs.h index 18aa5e6d2066297c9952df70c1c797fd41853b88..bf0c8ebce50032e90321bc33d0cc1912182c7ae2 100644 --- a/fs/sharefs/sharefs.h +++ b/fs/sharefs/sharefs.h @@ -93,6 +93,9 @@ extern int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, struct path *path); extern int sharefs_parse_options(struct sharefs_sb_info *sbi, const char *data); +extern int sharefs_get_lower_inode(struct dentry *d, struct inode **lower_path); +extern int sharefs_get_lower_path(struct dentry *d, struct path *lower_path, + bool try_to_create); /* * inode to private data @@ -156,16 +159,18 @@ static inline void pathcpy(struct path *dst, const struct path *src) dst->dentry = src->dentry; dst->mnt = src->mnt; } -/* Returns struct path. Caller must path_put it. */ -static inline void sharefs_get_lower_path(const struct dentry *dent, - struct path *lower_path) + +static inline void sharefs_get_lower_root_path(const struct dentry *dent, + struct path *lower_root_path) { - spin_lock(&SHAREFS_D(dent)->lock); - pathcpy(lower_path, &SHAREFS_D(dent)->lower_path); - path_get(lower_path); - spin_unlock(&SHAREFS_D(dent)->lock); - return; + struct super_block *sb = dent->d_sb; + + spin_lock(&SHAREFS_D(sb->s_root)->lock); + pathcpy(lower_root_path, &SHAREFS_D(sb->s_root)->lower_path); + path_get(lower_root_path); + spin_unlock(&SHAREFS_D(sb->s_root)->lock); } + static inline void sharefs_put_lower_path(const struct dentry *dent, struct path *lower_path) { diff --git a/fs/sharefs/super.c b/fs/sharefs/super.c index 4db8f91542354d5bf57f64e6b02546ca9fe9c860..9a1bfde6d27eff6d363acb219da6ddd8aef0ac8a 100644 --- a/fs/sharefs/super.c +++ b/fs/sharefs/super.c @@ -109,7 +109,9 @@ static int sharefs_statfs(struct dentry *dentry, struct kstatfs *buf) int err; struct path lower_path; - sharefs_get_lower_path(dentry, &lower_path); + err = sharefs_get_lower_path(dentry, &lower_path, 0); + if (err) + return err; err = vfs_statfs(&lower_path, buf); sharefs_put_lower_path(dentry, &lower_path); @@ -127,17 +129,13 @@ static int sharefs_statfs(struct dentry *dentry, struct kstatfs *buf) */ static void sharefs_evict_inode(struct inode *inode) { - struct inode *lower_inode; - truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); /* * Decrement a reference to a lower_inode, which was incremented * by our read_inode when it was created initially. */ - lower_inode = sharefs_lower_inode(inode); sharefs_set_lower_inode(inode, NULL); - iput(lower_inode); } void __sharefs_log(const char *level, const bool ratelimited,