Skip to content

Commit 526fc58

Browse files
dhowellsAvenger-285714
authored andcommitted
afs: Fix missing subdir edit when renamed between parent dirs
[ Upstream commit 247d65f ] 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]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 1167619 commit 526fc58

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"
@@ -1809,6 +1810,8 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir,
18091810

18101811
static void afs_rename_success(struct afs_operation *op)
18111812
{
1813+
struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
1814+
18121815
_enter("op=%08x", op->debug_id);
18131816

18141817
op->ctime = op->file[0].scb.status.mtime_client;
@@ -1818,6 +1821,22 @@ static void afs_rename_success(struct afs_operation *op)
18181821
op->ctime = op->file[1].scb.status.mtime_client;
18191822
afs_vnode_commit_status(op, &op->file[1]);
18201823
}
1824+
1825+
/* If we're moving a subdir between dirs, we need to update
1826+
* its DV counter too as the ".." will be altered.
1827+
*/
1828+
if (S_ISDIR(vnode->netfs.inode.i_mode) &&
1829+
op->file[0].vnode != op->file[1].vnode) {
1830+
u64 new_dv;
1831+
1832+
write_seqlock(&vnode->cb_lock);
1833+
1834+
new_dv = vnode->status.data_version + 1;
1835+
vnode->status.data_version = new_dv;
1836+
inode_set_iversion_raw(&vnode->netfs.inode, new_dv);
1837+
1838+
write_sequnlock(&vnode->cb_lock);
1839+
}
18211840
}
18221841

18231842
static void afs_rename_edit_dir(struct afs_operation *op)
@@ -1859,6 +1878,12 @@ static void afs_rename_edit_dir(struct afs_operation *op)
18591878
&vnode->fid, afs_edit_dir_for_rename_2);
18601879
}
18611880

1881+
if (S_ISDIR(vnode->netfs.inode.i_mode) &&
1882+
new_dvnode != orig_dvnode &&
1883+
test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
1884+
afs_edit_dir_update_dotdot(vnode, new_dvnode,
1885+
afs_edit_dir_for_rename_sub);
1886+
18621887
new_inode = d_inode(new_dentry);
18631888
if (new_inode) {
18641889
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
@@ -1037,6 +1037,8 @@ extern void afs_check_for_remote_deletion(struct afs_operation *);
10371037
extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *,
10381038
enum afs_edit_dir_reason);
10391039
extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason);
1040+
void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode,
1041+
enum afs_edit_dir_reason why);
10401042

10411043
/*
10421044
* dir_silly.c

include/trace/events/afs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,11 @@ enum yfs_cm_operation {
295295
EM(afs_edit_dir_delete, "delete") \
296296
EM(afs_edit_dir_delete_error, "d_err ") \
297297
EM(afs_edit_dir_delete_inval, "d_invl") \
298-
E_(afs_edit_dir_delete_noent, "d_nent")
298+
EM(afs_edit_dir_delete_noent, "d_nent") \
299+
EM(afs_edit_dir_update_dd, "u_ddot") \
300+
EM(afs_edit_dir_update_error, "u_fail") \
301+
EM(afs_edit_dir_update_inval, "u_invl") \
302+
E_(afs_edit_dir_update_nodd, "u_nodd")
299303

300304
#define afs_edit_dir_reasons \
301305
EM(afs_edit_dir_for_create, "Create") \
@@ -304,6 +308,7 @@ enum yfs_cm_operation {
304308
EM(afs_edit_dir_for_rename_0, "Renam0") \
305309
EM(afs_edit_dir_for_rename_1, "Renam1") \
306310
EM(afs_edit_dir_for_rename_2, "Renam2") \
311+
EM(afs_edit_dir_for_rename_sub, "RnmSub") \
307312
EM(afs_edit_dir_for_rmdir, "RmDir ") \
308313
EM(afs_edit_dir_for_silly_0, "S_Ren0") \
309314
EM(afs_edit_dir_for_silly_1, "S_Ren1") \

0 commit comments

Comments
 (0)