diff options
author | 2025-01-20 11:40:48 -0800 | |
---|---|---|
committer | 2025-01-20 11:40:48 -0800 | |
commit | b971424b6e3cbea5c017061fedda6a5f74e142cd (patch) | |
tree | 6edae033a7cbcf6e66ec4aeca7ef9acf522ba288 /fs/afs | |
parent | Merge tag 'vfs-6.14-rc1.statx.dio' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs (diff) | |
parent | afs: Fix the fallback handling for the YFS.RemoveFile2 RPC call (diff) | |
download | wireguard-linux-b971424b6e3cbea5c017061fedda6a5f74e142cd.tar.xz wireguard-linux-b971424b6e3cbea5c017061fedda6a5f74e142cd.zip |
Merge tag 'vfs-6.14-rc1.afs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull afs updates from Christian Brauner:
"Dynamic root improvements:
- Create an /afs/.<cell> mountpoint to match the /afs/<cell>
mountpoint when a cell is created
- Add some more checks on cell names proposed by the user to prevent
dodgy symlink bodies from being created. Also prevent rootcell from
being altered once set to simplify the locking
- Change the handling of /afs/@cell from being a dentry name
substitution at lookup time to making it a symlink to the current
cell name and also provide a /afs/.@cell symlink to point to the
dotted cell mountpoint
Fixes:
- Fix the abort code check in the fallback handling for the
YFS.RemoveFile2 RPC call
- Use call->op->server() for oridnary filesystem RPC calls that have
an operation descriptor instead of call->server()"
* tag 'vfs-6.14-rc1.afs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
afs: Fix the fallback handling for the YFS.RemoveFile2 RPC call
afs: Make /afs/@cell and /afs/.@cell symlinks
afs: Add rootcell checks
afs: Make /afs/.<cell> as well as /afs/<cell> mountpoints
Diffstat (limited to 'fs/afs')
-rw-r--r-- | fs/afs/cell.c | 21 | ||||
-rw-r--r-- | fs/afs/dynroot.c | 227 | ||||
-rw-r--r-- | fs/afs/proc.c | 8 | ||||
-rw-r--r-- | fs/afs/yfsclient.c | 5 |
4 files changed, 187 insertions, 74 deletions
diff --git a/fs/afs/cell.c b/fs/afs/cell.c index caa09875f520..cee42646736c 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -146,18 +146,20 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, return ERR_PTR(-ENOMEM); } - cell->name = kmalloc(namelen + 1, GFP_KERNEL); + cell->name = kmalloc(1 + namelen + 1, GFP_KERNEL); if (!cell->name) { kfree(cell); return ERR_PTR(-ENOMEM); } - cell->net = net; + cell->name[0] = '.'; + cell->name++; cell->name_len = namelen; for (i = 0; i < namelen; i++) cell->name[i] = tolower(name[i]); cell->name[i] = 0; + cell->net = net; refcount_set(&cell->ref, 1); atomic_set(&cell->active, 0); INIT_WORK(&cell->manager, afs_manage_cell_work); @@ -211,7 +213,7 @@ parse_failed: if (ret == -EINVAL) printk(KERN_ERR "kAFS: bad VL server IP address\n"); error: - kfree(cell->name); + kfree(cell->name - 1); kfree(cell); _leave(" = %d", ret); return ERR_PTR(ret); @@ -365,6 +367,14 @@ int afs_cell_init(struct afs_net *net, const char *rootcell) len = cp - rootcell; } + if (len == 0 || !rootcell[0] || rootcell[0] == '.' || rootcell[len - 1] == '.') + return -EINVAL; + if (memchr(rootcell, '/', len)) + return -EINVAL; + cp = strstr(rootcell, ".."); + if (cp && cp < rootcell + len) + return -EINVAL; + /* allocate a cell record for the root cell */ new_root = afs_lookup_cell(net, rootcell, len, vllist, false); if (IS_ERR(new_root)) { @@ -502,7 +512,7 @@ static void afs_cell_destroy(struct rcu_head *rcu) afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers)); afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias); key_put(cell->anonymous_key); - kfree(cell->name); + kfree(cell->name - 1); kfree(cell); afs_dec_cells_outstanding(net); @@ -710,7 +720,8 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell) afs_proc_cell_remove(cell); mutex_lock(&net->proc_cells_lock); - hlist_del_rcu(&cell->proc_link); + if (!hlist_unhashed(&cell->proc_link)) + hlist_del_rcu(&cell->proc_link); afs_dynroot_rmdir(net, cell); mutex_unlock(&net->proc_cells_lock); diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index c4d2711e20ad..d8bf52f77d93 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -186,50 +186,6 @@ out: } /* - * Look up @cell in a dynroot directory. This is a substitution for the - * local cell name for the net namespace. - */ -static struct dentry *afs_lookup_atcell(struct dentry *dentry) -{ - struct afs_cell *cell; - struct afs_net *net = afs_d2net(dentry); - struct dentry *ret; - char *name; - int len; - - if (!net->ws_cell) - return ERR_PTR(-ENOENT); - - ret = ERR_PTR(-ENOMEM); - name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL); - if (!name) - goto out_p; - - down_read(&net->cells_lock); - cell = net->ws_cell; - if (cell) { - len = cell->name_len; - memcpy(name, cell->name, len + 1); - } - up_read(&net->cells_lock); - - ret = ERR_PTR(-ENOENT); - if (!cell) - goto out_n; - - ret = lookup_one_len(name, dentry->d_parent, len); - - /* We don't want to d_add() the @cell dentry here as we don't want to - * the cached dentry to hide changes to the local cell name. - */ - -out_n: - kfree(name); -out_p: - return ret; -} - -/* * Look up an entry in a dynroot directory. */ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, @@ -247,10 +203,6 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr return ERR_PTR(-ENAMETOOLONG); } - if (dentry->d_name.len == 5 && - memcmp(dentry->d_name.name, "@cell", 5) == 0) - return afs_lookup_atcell(dentry); - return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry); } @@ -271,7 +223,8 @@ const struct dentry_operations afs_dynroot_dentry_operations = { int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell) { struct super_block *sb = net->dynroot_sb; - struct dentry *root, *subdir; + struct dentry *root, *subdir, *dsubdir; + char *dotname = cell->name - 1; int ret; if (!sb || atomic_read(&sb->s_active) == 0) @@ -286,14 +239,43 @@ int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell) goto unlock; } - /* Note that we're retaining an extra ref on the dentry */ + dsubdir = lookup_one_len(dotname, root, cell->name_len + 1); + if (IS_ERR(dsubdir)) { + ret = PTR_ERR(dsubdir); + dput(subdir); + goto unlock; + } + + /* Note that we're retaining extra refs on the dentries. */ subdir->d_fsdata = (void *)1UL; + dsubdir->d_fsdata = (void *)1UL; ret = 0; unlock: inode_unlock(root->d_inode); return ret; } +static void afs_dynroot_rm_one_dir(struct dentry *root, const char *name, size_t name_len) +{ + struct dentry *subdir; + + /* Don't want to trigger a lookup call, which will re-add the cell */ + subdir = try_lookup_one_len(name, root, name_len); + if (IS_ERR_OR_NULL(subdir)) { + _debug("lookup %ld", PTR_ERR(subdir)); + return; + } + + _debug("rmdir %pd %u", subdir, d_count(subdir)); + + if (subdir->d_fsdata) { + _debug("unpin %u", d_count(subdir)); + subdir->d_fsdata = NULL; + dput(subdir); + } + dput(subdir); +} + /* * Remove a manually added cell mount directory. * - The caller must hold net->proc_cells_lock @@ -301,32 +283,141 @@ unlock: void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell) { struct super_block *sb = net->dynroot_sb; - struct dentry *root, *subdir; + char *dotname = cell->name - 1; if (!sb || atomic_read(&sb->s_active) == 0) return; - root = sb->s_root; - inode_lock(root->d_inode); + inode_lock(sb->s_root->d_inode); + afs_dynroot_rm_one_dir(sb->s_root, cell->name, cell->name_len); + afs_dynroot_rm_one_dir(sb->s_root, dotname, cell->name_len + 1); + inode_unlock(sb->s_root->d_inode); + _leave(""); +} - /* Don't want to trigger a lookup call, which will re-add the cell */ - subdir = try_lookup_one_len(cell->name, root, cell->name_len); - if (IS_ERR_OR_NULL(subdir)) { - _debug("lookup %ld", PTR_ERR(subdir)); - goto no_dentry; +static void afs_atcell_delayed_put_cell(void *arg) +{ + struct afs_cell *cell = arg; + + afs_put_cell(cell, afs_cell_trace_put_atcell); +} + +/* + * Read @cell or .@cell symlinks. + */ +static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + struct afs_vnode *vnode = AFS_FS_I(inode); + struct afs_cell *cell; + struct afs_net *net = afs_i2net(inode); + const char *name; + bool dotted = vnode->fid.vnode == 3; + + if (!net->ws_cell) + return ERR_PTR(-ENOENT); + + down_read(&net->cells_lock); + + cell = net->ws_cell; + if (dotted) + name = cell->name - 1; + else + name = cell->name; + afs_get_cell(cell, afs_cell_trace_get_atcell); + set_delayed_call(done, afs_atcell_delayed_put_cell, cell); + + up_read(&net->cells_lock); + return name; +} + +static const struct inode_operations afs_atcell_inode_operations = { + .get_link = afs_atcell_get_link, +}; + +/* + * Look up @cell or .@cell in a dynroot directory. This is a substitution for + * the local cell name for the net namespace. + */ +static struct dentry *afs_dynroot_create_symlink(struct dentry *root, const char *name) +{ + struct afs_vnode *vnode; + struct afs_fid fid = { .vnode = 2, .unique = 1, }; + struct dentry *dentry; + struct inode *inode; + + if (name[0] == '.') + fid.vnode = 3; + + dentry = d_alloc_name(root, name); + if (!dentry) + return ERR_PTR(-ENOMEM); + + inode = iget5_locked(dentry->d_sb, fid.vnode, + afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid); + if (!inode) { + dput(dentry); + return ERR_PTR(-ENOMEM); } - _debug("rmdir %pd %u", subdir, d_count(subdir)); + vnode = AFS_FS_I(inode); - if (subdir->d_fsdata) { - _debug("unpin %u", d_count(subdir)); - subdir->d_fsdata = NULL; - dput(subdir); + /* there shouldn't be an existing inode */ + if (WARN_ON_ONCE(!(inode->i_state & I_NEW))) { + iput(inode); + dput(dentry); + return ERR_PTR(-EIO); } - dput(subdir); -no_dentry: + + netfs_inode_init(&vnode->netfs, NULL, false); + simple_inode_init_ts(inode); + set_nlink(inode, 1); + inode->i_size = 0; + inode->i_mode = S_IFLNK | 0555; + inode->i_op = &afs_atcell_inode_operations; + inode->i_uid = GLOBAL_ROOT_UID; + inode->i_gid = GLOBAL_ROOT_GID; + inode->i_blocks = 0; + inode->i_generation = 0; + inode->i_flags |= S_NOATIME; + + unlock_new_inode(inode); + d_splice_alias(inode, dentry); + return dentry; +} + +/* + * Create @cell and .@cell symlinks. + */ +static int afs_dynroot_symlink(struct afs_net *net) +{ + struct super_block *sb = net->dynroot_sb; + struct dentry *root, *symlink, *dsymlink; + int ret; + + /* Let the ->lookup op do the creation */ + root = sb->s_root; + inode_lock(root->d_inode); + symlink = afs_dynroot_create_symlink(root, "@cell"); + if (IS_ERR(symlink)) { + ret = PTR_ERR(symlink); + goto unlock; + } + + dsymlink = afs_dynroot_create_symlink(root, ".@cell"); + if (IS_ERR(dsymlink)) { + ret = PTR_ERR(dsymlink); + dput(symlink); + goto unlock; + } + + /* Note that we're retaining extra refs on the dentries. */ + symlink->d_fsdata = (void *)1UL; + dsymlink->d_fsdata = (void *)1UL; + ret = 0; +unlock: inode_unlock(root->d_inode); - _leave(""); + return ret; } /* @@ -341,6 +432,10 @@ int afs_dynroot_populate(struct super_block *sb) mutex_lock(&net->proc_cells_lock); net->dynroot_sb = sb; + ret = afs_dynroot_symlink(net); + if (ret < 0) + goto error; + hlist_for_each_entry(cell, &net->proc_cells, proc_link) { ret = afs_dynroot_mkdir(net, cell); if (ret < 0) diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 15eab053af6d..e7614f4f30c2 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -240,7 +240,13 @@ static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size) /* determine command to perform */ _debug("rootcell=%s", buf); - ret = afs_cell_init(net, buf); + ret = -EEXIST; + inode_lock(file_inode(file)); + if (!net->ws_cell) + ret = afs_cell_init(net, buf); + else + printk("busy\n"); + inode_unlock(file_inode(file)); out: _leave(" = %d", ret); diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index f57c089f26ee..257af259c04a 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -667,8 +667,9 @@ static int yfs_deliver_fs_remove_file2(struct afs_call *call) static void yfs_done_fs_remove_file2(struct afs_call *call) { if (call->error == -ECONNABORTED && - call->abort_code == RX_INVALID_OPERATION) { - set_bit(AFS_SERVER_FL_NO_RM2, &call->server->flags); + (call->abort_code == RX_INVALID_OPERATION || + call->abort_code == RXGEN_OPCODE)) { + set_bit(AFS_SERVER_FL_NO_RM2, &call->op->server->flags); call->op->flags |= AFS_OPERATION_DOWNGRADE; } } |