Skip to content

Commit b971424

Browse files
committed
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
2 parents 47c9f2b + e30458d commit b971424

File tree

5 files changed

+189
-74
lines changed

5 files changed

+189
-74
lines changed

fs/afs/cell.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,20 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
146146
return ERR_PTR(-ENOMEM);
147147
}
148148

149-
cell->name = kmalloc(namelen + 1, GFP_KERNEL);
149+
cell->name = kmalloc(1 + namelen + 1, GFP_KERNEL);
150150
if (!cell->name) {
151151
kfree(cell);
152152
return ERR_PTR(-ENOMEM);
153153
}
154154

155-
cell->net = net;
155+
cell->name[0] = '.';
156+
cell->name++;
156157
cell->name_len = namelen;
157158
for (i = 0; i < namelen; i++)
158159
cell->name[i] = tolower(name[i]);
159160
cell->name[i] = 0;
160161

162+
cell->net = net;
161163
refcount_set(&cell->ref, 1);
162164
atomic_set(&cell->active, 0);
163165
INIT_WORK(&cell->manager, afs_manage_cell_work);
@@ -211,7 +213,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
211213
if (ret == -EINVAL)
212214
printk(KERN_ERR "kAFS: bad VL server IP address\n");
213215
error:
214-
kfree(cell->name);
216+
kfree(cell->name - 1);
215217
kfree(cell);
216218
_leave(" = %d", ret);
217219
return ERR_PTR(ret);
@@ -365,6 +367,14 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
365367
len = cp - rootcell;
366368
}
367369

370+
if (len == 0 || !rootcell[0] || rootcell[0] == '.' || rootcell[len - 1] == '.')
371+
return -EINVAL;
372+
if (memchr(rootcell, '/', len))
373+
return -EINVAL;
374+
cp = strstr(rootcell, "..");
375+
if (cp && cp < rootcell + len)
376+
return -EINVAL;
377+
368378
/* allocate a cell record for the root cell */
369379
new_root = afs_lookup_cell(net, rootcell, len, vllist, false);
370380
if (IS_ERR(new_root)) {
@@ -502,7 +512,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
502512
afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers));
503513
afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias);
504514
key_put(cell->anonymous_key);
505-
kfree(cell->name);
515+
kfree(cell->name - 1);
506516
kfree(cell);
507517

508518
afs_dec_cells_outstanding(net);
@@ -710,7 +720,8 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
710720
afs_proc_cell_remove(cell);
711721

712722
mutex_lock(&net->proc_cells_lock);
713-
hlist_del_rcu(&cell->proc_link);
723+
if (!hlist_unhashed(&cell->proc_link))
724+
hlist_del_rcu(&cell->proc_link);
714725
afs_dynroot_rmdir(net, cell);
715726
mutex_unlock(&net->proc_cells_lock);
716727

fs/afs/dynroot.c

Lines changed: 161 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -185,50 +185,6 @@ struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
185185
return ret == -ENOENT ? NULL : ERR_PTR(ret);
186186
}
187187

188-
/*
189-
* Look up @cell in a dynroot directory. This is a substitution for the
190-
* local cell name for the net namespace.
191-
*/
192-
static struct dentry *afs_lookup_atcell(struct dentry *dentry)
193-
{
194-
struct afs_cell *cell;
195-
struct afs_net *net = afs_d2net(dentry);
196-
struct dentry *ret;
197-
char *name;
198-
int len;
199-
200-
if (!net->ws_cell)
201-
return ERR_PTR(-ENOENT);
202-
203-
ret = ERR_PTR(-ENOMEM);
204-
name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
205-
if (!name)
206-
goto out_p;
207-
208-
down_read(&net->cells_lock);
209-
cell = net->ws_cell;
210-
if (cell) {
211-
len = cell->name_len;
212-
memcpy(name, cell->name, len + 1);
213-
}
214-
up_read(&net->cells_lock);
215-
216-
ret = ERR_PTR(-ENOENT);
217-
if (!cell)
218-
goto out_n;
219-
220-
ret = lookup_one_len(name, dentry->d_parent, len);
221-
222-
/* We don't want to d_add() the @cell dentry here as we don't want to
223-
* the cached dentry to hide changes to the local cell name.
224-
*/
225-
226-
out_n:
227-
kfree(name);
228-
out_p:
229-
return ret;
230-
}
231-
232188
/*
233189
* Look up an entry in a dynroot directory.
234190
*/
@@ -247,10 +203,6 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
247203
return ERR_PTR(-ENAMETOOLONG);
248204
}
249205

250-
if (dentry->d_name.len == 5 &&
251-
memcmp(dentry->d_name.name, "@cell", 5) == 0)
252-
return afs_lookup_atcell(dentry);
253-
254206
return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
255207
}
256208

@@ -271,7 +223,8 @@ const struct dentry_operations afs_dynroot_dentry_operations = {
271223
int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
272224
{
273225
struct super_block *sb = net->dynroot_sb;
274-
struct dentry *root, *subdir;
226+
struct dentry *root, *subdir, *dsubdir;
227+
char *dotname = cell->name - 1;
275228
int ret;
276229

277230
if (!sb || atomic_read(&sb->s_active) == 0)
@@ -286,47 +239,185 @@ int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
286239
goto unlock;
287240
}
288241

289-
/* Note that we're retaining an extra ref on the dentry */
242+
dsubdir = lookup_one_len(dotname, root, cell->name_len + 1);
243+
if (IS_ERR(dsubdir)) {
244+
ret = PTR_ERR(dsubdir);
245+
dput(subdir);
246+
goto unlock;
247+
}
248+
249+
/* Note that we're retaining extra refs on the dentries. */
290250
subdir->d_fsdata = (void *)1UL;
251+
dsubdir->d_fsdata = (void *)1UL;
291252
ret = 0;
292253
unlock:
293254
inode_unlock(root->d_inode);
294255
return ret;
295256
}
296257

258+
static void afs_dynroot_rm_one_dir(struct dentry *root, const char *name, size_t name_len)
259+
{
260+
struct dentry *subdir;
261+
262+
/* Don't want to trigger a lookup call, which will re-add the cell */
263+
subdir = try_lookup_one_len(name, root, name_len);
264+
if (IS_ERR_OR_NULL(subdir)) {
265+
_debug("lookup %ld", PTR_ERR(subdir));
266+
return;
267+
}
268+
269+
_debug("rmdir %pd %u", subdir, d_count(subdir));
270+
271+
if (subdir->d_fsdata) {
272+
_debug("unpin %u", d_count(subdir));
273+
subdir->d_fsdata = NULL;
274+
dput(subdir);
275+
}
276+
dput(subdir);
277+
}
278+
297279
/*
298280
* Remove a manually added cell mount directory.
299281
* - The caller must hold net->proc_cells_lock
300282
*/
301283
void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
302284
{
303285
struct super_block *sb = net->dynroot_sb;
304-
struct dentry *root, *subdir;
286+
char *dotname = cell->name - 1;
305287

306288
if (!sb || atomic_read(&sb->s_active) == 0)
307289
return;
308290

309-
root = sb->s_root;
310-
inode_lock(root->d_inode);
291+
inode_lock(sb->s_root->d_inode);
292+
afs_dynroot_rm_one_dir(sb->s_root, cell->name, cell->name_len);
293+
afs_dynroot_rm_one_dir(sb->s_root, dotname, cell->name_len + 1);
294+
inode_unlock(sb->s_root->d_inode);
295+
_leave("");
296+
}
311297

312-
/* Don't want to trigger a lookup call, which will re-add the cell */
313-
subdir = try_lookup_one_len(cell->name, root, cell->name_len);
314-
if (IS_ERR_OR_NULL(subdir)) {
315-
_debug("lookup %ld", PTR_ERR(subdir));
316-
goto no_dentry;
298+
static void afs_atcell_delayed_put_cell(void *arg)
299+
{
300+
struct afs_cell *cell = arg;
301+
302+
afs_put_cell(cell, afs_cell_trace_put_atcell);
303+
}
304+
305+
/*
306+
* Read @cell or .@cell symlinks.
307+
*/
308+
static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode,
309+
struct delayed_call *done)
310+
{
311+
struct afs_vnode *vnode = AFS_FS_I(inode);
312+
struct afs_cell *cell;
313+
struct afs_net *net = afs_i2net(inode);
314+
const char *name;
315+
bool dotted = vnode->fid.vnode == 3;
316+
317+
if (!net->ws_cell)
318+
return ERR_PTR(-ENOENT);
319+
320+
down_read(&net->cells_lock);
321+
322+
cell = net->ws_cell;
323+
if (dotted)
324+
name = cell->name - 1;
325+
else
326+
name = cell->name;
327+
afs_get_cell(cell, afs_cell_trace_get_atcell);
328+
set_delayed_call(done, afs_atcell_delayed_put_cell, cell);
329+
330+
up_read(&net->cells_lock);
331+
return name;
332+
}
333+
334+
static const struct inode_operations afs_atcell_inode_operations = {
335+
.get_link = afs_atcell_get_link,
336+
};
337+
338+
/*
339+
* Look up @cell or .@cell in a dynroot directory. This is a substitution for
340+
* the local cell name for the net namespace.
341+
*/
342+
static struct dentry *afs_dynroot_create_symlink(struct dentry *root, const char *name)
343+
{
344+
struct afs_vnode *vnode;
345+
struct afs_fid fid = { .vnode = 2, .unique = 1, };
346+
struct dentry *dentry;
347+
struct inode *inode;
348+
349+
if (name[0] == '.')
350+
fid.vnode = 3;
351+
352+
dentry = d_alloc_name(root, name);
353+
if (!dentry)
354+
return ERR_PTR(-ENOMEM);
355+
356+
inode = iget5_locked(dentry->d_sb, fid.vnode,
357+
afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
358+
if (!inode) {
359+
dput(dentry);
360+
return ERR_PTR(-ENOMEM);
317361
}
318362

319-
_debug("rmdir %pd %u", subdir, d_count(subdir));
363+
vnode = AFS_FS_I(inode);
320364

321-
if (subdir->d_fsdata) {
322-
_debug("unpin %u", d_count(subdir));
323-
subdir->d_fsdata = NULL;
324-
dput(subdir);
365+
/* there shouldn't be an existing inode */
366+
if (WARN_ON_ONCE(!(inode->i_state & I_NEW))) {
367+
iput(inode);
368+
dput(dentry);
369+
return ERR_PTR(-EIO);
325370
}
326-
dput(subdir);
327-
no_dentry:
371+
372+
netfs_inode_init(&vnode->netfs, NULL, false);
373+
simple_inode_init_ts(inode);
374+
set_nlink(inode, 1);
375+
inode->i_size = 0;
376+
inode->i_mode = S_IFLNK | 0555;
377+
inode->i_op = &afs_atcell_inode_operations;
378+
inode->i_uid = GLOBAL_ROOT_UID;
379+
inode->i_gid = GLOBAL_ROOT_GID;
380+
inode->i_blocks = 0;
381+
inode->i_generation = 0;
382+
inode->i_flags |= S_NOATIME;
383+
384+
unlock_new_inode(inode);
385+
d_splice_alias(inode, dentry);
386+
return dentry;
387+
}
388+
389+
/*
390+
* Create @cell and .@cell symlinks.
391+
*/
392+
static int afs_dynroot_symlink(struct afs_net *net)
393+
{
394+
struct super_block *sb = net->dynroot_sb;
395+
struct dentry *root, *symlink, *dsymlink;
396+
int ret;
397+
398+
/* Let the ->lookup op do the creation */
399+
root = sb->s_root;
400+
inode_lock(root->d_inode);
401+
symlink = afs_dynroot_create_symlink(root, "@cell");
402+
if (IS_ERR(symlink)) {
403+
ret = PTR_ERR(symlink);
404+
goto unlock;
405+
}
406+
407+
dsymlink = afs_dynroot_create_symlink(root, ".@cell");
408+
if (IS_ERR(dsymlink)) {
409+
ret = PTR_ERR(dsymlink);
410+
dput(symlink);
411+
goto unlock;
412+
}
413+
414+
/* Note that we're retaining extra refs on the dentries. */
415+
symlink->d_fsdata = (void *)1UL;
416+
dsymlink->d_fsdata = (void *)1UL;
417+
ret = 0;
418+
unlock:
328419
inode_unlock(root->d_inode);
329-
_leave("");
420+
return ret;
330421
}
331422

332423
/*
@@ -341,6 +432,10 @@ int afs_dynroot_populate(struct super_block *sb)
341432
mutex_lock(&net->proc_cells_lock);
342433

343434
net->dynroot_sb = sb;
435+
ret = afs_dynroot_symlink(net);
436+
if (ret < 0)
437+
goto error;
438+
344439
hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
345440
ret = afs_dynroot_mkdir(net, cell);
346441
if (ret < 0)

fs/afs/proc.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
240240
/* determine command to perform */
241241
_debug("rootcell=%s", buf);
242242

243-
ret = afs_cell_init(net, buf);
243+
ret = -EEXIST;
244+
inode_lock(file_inode(file));
245+
if (!net->ws_cell)
246+
ret = afs_cell_init(net, buf);
247+
else
248+
printk("busy\n");
249+
inode_unlock(file_inode(file));
244250

245251
out:
246252
_leave(" = %d", ret);

fs/afs/yfsclient.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,9 @@ static int yfs_deliver_fs_remove_file2(struct afs_call *call)
667667
static void yfs_done_fs_remove_file2(struct afs_call *call)
668668
{
669669
if (call->error == -ECONNABORTED &&
670-
call->abort_code == RX_INVALID_OPERATION) {
671-
set_bit(AFS_SERVER_FL_NO_RM2, &call->server->flags);
670+
(call->abort_code == RX_INVALID_OPERATION ||
671+
call->abort_code == RXGEN_OPCODE)) {
672+
set_bit(AFS_SERVER_FL_NO_RM2, &call->op->server->flags);
672673
call->op->flags |= AFS_OPERATION_DOWNGRADE;
673674
}
674675
}

include/trace/events/afs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,14 @@ enum yfs_cm_operation {
170170
#define afs_cell_traces \
171171
EM(afs_cell_trace_alloc, "ALLOC ") \
172172
EM(afs_cell_trace_free, "FREE ") \
173+
EM(afs_cell_trace_get_atcell, "GET atcell") \
173174
EM(afs_cell_trace_get_queue_dns, "GET q-dns ") \
174175
EM(afs_cell_trace_get_queue_manage, "GET q-mng ") \
175176
EM(afs_cell_trace_get_queue_new, "GET q-new ") \
176177
EM(afs_cell_trace_get_vol, "GET vol ") \
177178
EM(afs_cell_trace_insert, "INSERT ") \
178179
EM(afs_cell_trace_manage, "MANAGE ") \
180+
EM(afs_cell_trace_put_atcell, "PUT atcell") \
179181
EM(afs_cell_trace_put_candidate, "PUT candid") \
180182
EM(afs_cell_trace_put_destroy, "PUT destry") \
181183
EM(afs_cell_trace_put_queue_work, "PUT q-work") \

0 commit comments

Comments
 (0)