Skip to content

Commit 247d65f

Browse files
dhowellsbrauner
authored andcommitted
afs: Fix missing subdir edit when renamed between parent dirs
When rename moves an AFS subdirectory between parent directories, the subdir also needs a bit of editing: the ".." entry needs updating to point to the new parent (though I don't make use of the info) and the DV needs incrementing by 1 to reflect the change of content. The server also sends a callback break notification on the subdirectory if we have one, but we can take care of recovering the promise next time we access the subdir. This can be triggered by something like: mount -t afs %example.com:xfstest.test20 /xfstest.test/ mkdir /xfstest.test/{aaa,bbb,aaa/ccc} touch /xfstest.test/bbb/ccc/d mv /xfstest.test/{aaa/ccc,bbb/ccc} touch /xfstest.test/bbb/ccc/e When the pathwalk for the second touch hits "ccc", kafs spots that the DV is incorrect and downloads it again (so the fix is not critical). Fix this, if the rename target is a directory and the old and new parents are different, by: (1) Incrementing the DV number of the target locally. (2) Editing the ".." entry in the target to refer to its new parent's vnode ID and uniquifier. Link: https://lore.kernel.org/r/[email protected] Fixes: 63a4681 ("afs: Locally edit directory data for mkdir/create/unlink/...") cc: David Howells <[email protected]> cc: Marc Dionne <[email protected]> cc: [email protected] Signed-off-by: David Howells <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 6b51b9f commit 247d65f

File tree

4 files changed

+122
-3
lines changed

4 files changed

+122
-3
lines changed

fs/afs/dir.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/swap.h>
1313
#include <linux/ctype.h>
1414
#include <linux/sched.h>
15+
#include <linux/iversion.h>
1516
#include <linux/task_io_accounting_ops.h>
1617
#include "internal.h"
1718
#include "afs_fs.h"
@@ -1823,6 +1824,8 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
18231824

18241825
static void afs_rename_success(struct afs_operation *op)
18251826
{
1827+
struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
1828+
18261829
_enter("op=%08x", op->debug_id);
18271830

18281831
op->ctime = op->file[0].scb.status.mtime_client;
@@ -1832,6 +1835,22 @@ static void afs_rename_success(struct afs_operation *op)
18321835
op->ctime = op->file[1].scb.status.mtime_client;
18331836
afs_vnode_commit_status(op, &op->file[1]);
18341837
}
1838+
1839+
/* If we're moving a subdir between dirs, we need to update
1840+
* its DV counter too as the ".." will be altered.
1841+
*/
1842+
if (S_ISDIR(vnode->netfs.inode.i_mode) &&
1843+
op->file[0].vnode != op->file[1].vnode) {
1844+
u64 new_dv;
1845+
1846+
write_seqlock(&vnode->cb_lock);
1847+
1848+
new_dv = vnode->status.data_version + 1;
1849+
vnode->status.data_version = new_dv;
1850+
inode_set_iversion_raw(&vnode->netfs.inode, new_dv);
1851+
1852+
write_sequnlock(&vnode->cb_lock);
1853+
}
18351854
}
18361855

18371856
static void afs_rename_edit_dir(struct afs_operation *op)
@@ -1873,6 +1892,12 @@ static void afs_rename_edit_dir(struct afs_operation *op)
18731892
&vnode->fid, afs_edit_dir_for_rename_2);
18741893
}
18751894

1895+
if (S_ISDIR(vnode->netfs.inode.i_mode) &&
1896+
new_dvnode != orig_dvnode &&
1897+
test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
1898+
afs_edit_dir_update_dotdot(vnode, new_dvnode,
1899+
afs_edit_dir_for_rename_sub);
1900+
18761901
new_inode = d_inode(new_dentry);
18771902
if (new_inode) {
18781903
spin_lock(&new_inode->i_lock);

fs/afs/dir_edit.c

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ static struct folio *afs_dir_get_folio(struct afs_vnode *vnode, pgoff_t index)
127127
/*
128128
* Scan a directory block looking for a dirent of the right name.
129129
*/
130-
static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name,
130+
static int afs_dir_scan_block(const union afs_xdr_dir_block *block, const struct qstr *name,
131131
unsigned int blocknum)
132132
{
133-
union afs_xdr_dirent *de;
133+
const union afs_xdr_dirent *de;
134134
u64 bitmap;
135135
int d, len, n;
136136

@@ -492,3 +492,90 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
492492
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
493493
goto out_unmap;
494494
}
495+
496+
/*
497+
* Edit a subdirectory that has been moved between directories to update the
498+
* ".." entry.
499+
*/
500+
void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode,
501+
enum afs_edit_dir_reason why)
502+
{
503+
union afs_xdr_dir_block *block;
504+
union afs_xdr_dirent *de;
505+
struct folio *folio;
506+
unsigned int nr_blocks, b;
507+
pgoff_t index;
508+
loff_t i_size;
509+
int slot;
510+
511+
_enter("");
512+
513+
i_size = i_size_read(&vnode->netfs.inode);
514+
if (i_size < AFS_DIR_BLOCK_SIZE) {
515+
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
516+
return;
517+
}
518+
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
519+
520+
/* Find a block that has sufficient slots available. Each folio
521+
* contains two or more directory blocks.
522+
*/
523+
for (b = 0; b < nr_blocks; b++) {
524+
index = b / AFS_DIR_BLOCKS_PER_PAGE;
525+
folio = afs_dir_get_folio(vnode, index);
526+
if (!folio)
527+
goto error;
528+
529+
block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_pos(folio));
530+
531+
/* Abandon the edit if we got a callback break. */
532+
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
533+
goto invalidated;
534+
535+
slot = afs_dir_scan_block(block, &dotdot_name, b);
536+
if (slot >= 0)
537+
goto found_dirent;
538+
539+
kunmap_local(block);
540+
folio_unlock(folio);
541+
folio_put(folio);
542+
}
543+
544+
/* Didn't find the dirent to clobber. Download the directory again. */
545+
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_nodd,
546+
0, 0, 0, 0, "..");
547+
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
548+
goto out;
549+
550+
found_dirent:
551+
de = &block->dirents[slot];
552+
de->u.vnode = htonl(new_dvnode->fid.vnode);
553+
de->u.unique = htonl(new_dvnode->fid.unique);
554+
555+
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_dd, b, slot,
556+
ntohl(de->u.vnode), ntohl(de->u.unique), "..");
557+
558+
kunmap_local(block);
559+
folio_unlock(folio);
560+
folio_put(folio);
561+
inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version);
562+
563+
out:
564+
_leave("");
565+
return;
566+
567+
invalidated:
568+
kunmap_local(block);
569+
folio_unlock(folio);
570+
folio_put(folio);
571+
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_inval,
572+
0, 0, 0, 0, "..");
573+
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
574+
goto out;
575+
576+
error:
577+
trace_afs_edit_dir(vnode, why, afs_edit_dir_update_error,
578+
0, 0, 0, 0, "..");
579+
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
580+
goto out;
581+
}

fs/afs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,8 @@ extern void afs_check_for_remote_deletion(struct afs_operation *);
10721072
extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *,
10731073
enum afs_edit_dir_reason);
10741074
extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason);
1075+
void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode,
1076+
enum afs_edit_dir_reason why);
10751077

10761078
/*
10771079
* dir_silly.c

include/trace/events/afs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,11 @@ enum yfs_cm_operation {
331331
EM(afs_edit_dir_delete, "delete") \
332332
EM(afs_edit_dir_delete_error, "d_err ") \
333333
EM(afs_edit_dir_delete_inval, "d_invl") \
334-
E_(afs_edit_dir_delete_noent, "d_nent")
334+
EM(afs_edit_dir_delete_noent, "d_nent") \
335+
EM(afs_edit_dir_update_dd, "u_ddot") \
336+
EM(afs_edit_dir_update_error, "u_fail") \
337+
EM(afs_edit_dir_update_inval, "u_invl") \
338+
E_(afs_edit_dir_update_nodd, "u_nodd")
335339

336340
#define afs_edit_dir_reasons \
337341
EM(afs_edit_dir_for_create, "Create") \
@@ -340,6 +344,7 @@ enum yfs_cm_operation {
340344
EM(afs_edit_dir_for_rename_0, "Renam0") \
341345
EM(afs_edit_dir_for_rename_1, "Renam1") \
342346
EM(afs_edit_dir_for_rename_2, "Renam2") \
347+
EM(afs_edit_dir_for_rename_sub, "RnmSub") \
343348
EM(afs_edit_dir_for_rmdir, "RmDir ") \
344349
EM(afs_edit_dir_for_silly_0, "S_Ren0") \
345350
EM(afs_edit_dir_for_silly_1, "S_Ren1") \

0 commit comments

Comments
 (0)