Skip to content

Commit 2f2ff0e

Browse files
fdmananamasoncl
authored andcommitted
Btrfs: fix metadata inconsistencies after directory fsync
We can get into inconsistency between inodes and directory entries after fsyncing a directory. The issue is that while a directory gets the new dentries persisted in the fsync log and replayed at mount time, the link count of the inode that directory entries point to doesn't get updated, staying with an incorrect link count (smaller then the correct value). This later leads to stale file handle errors when accessing (including attempt to delete) some of the links if all the other ones are removed, which also implies impossibility to delete the parent directories, since the dentries can not be removed. Another issue is that (unlike ext3/4, xfs, f2fs, reiserfs, nilfs2), when fsyncing a directory, new files aren't logged (their metadata and dentries) nor any child directories. So this patch fixes this issue too, since it has the same resolution as the incorrect inode link count issue mentioned before. This is very easy to reproduce, and the following excerpt from my test case for xfstests shows how: _scratch_mkfs >> $seqres.full 2>&1 _init_flakey _mount_flakey # Create our main test file and directory. $XFS_IO_PROG -f -c "pwrite -S 0xaa 0 8K" $SCRATCH_MNT/foo | _filter_xfs_io mkdir $SCRATCH_MNT/mydir # Make sure all metadata and data are durably persisted. sync # Add a hard link to 'foo' inside our test directory and fsync only the # directory. The btrfs fsync implementation had a bug that caused the new # directory entry to be visible after the fsync log replay but, the inode # of our file remained with a link count of 1. ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/foo_2 # Add a few more links and new files. # This is just to verify nothing breaks or gives incorrect results after the # fsync log is replayed. ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/foo_3 $XFS_IO_PROG -f -c "pwrite -S 0xff 0 64K" $SCRATCH_MNT/hello | _filter_xfs_io ln $SCRATCH_MNT/hello $SCRATCH_MNT/mydir/hello_2 # Add some subdirectories and new files and links to them. This is to verify # that after fsyncing our top level directory 'mydir', all the subdirectories # and their files/links are registered in the fsync log and exist after the # fsync log is replayed. mkdir -p $SCRATCH_MNT/mydir/x/y/z ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/x/y/foo_y_link ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/x/y/z/foo_z_link touch $SCRATCH_MNT/mydir/x/y/z/qwerty # Now fsync only our top directory. $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/mydir # And fsync now our new file named 'hello', just to verify later that it has # the expected content and that the previous fsync on the directory 'mydir' had # no bad influence on this fsync. $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/hello # Simulate a crash/power loss. _load_flakey_table $FLAKEY_DROP_WRITES _unmount_flakey _load_flakey_table $FLAKEY_ALLOW_WRITES _mount_flakey # Verify the content of our file 'foo' remains the same as before, 8192 bytes, # all with the value 0xaa. echo "File 'foo' content after log replay:" od -t x1 $SCRATCH_MNT/foo # Remove the first name of our inode. Because of the directory fsync bug, the # inode's link count was 1 instead of 5, so removing the 'foo' name ended up # deleting the inode and the other names became stale directory entries (still # visible to applications). Attempting to remove or access the remaining # dentries pointing to that inode resulted in stale file handle errors and # made it impossible to remove the parent directories since it was impossible # for them to become empty. echo "file 'foo' link count after log replay: $(stat -c %h $SCRATCH_MNT/foo)" rm -f $SCRATCH_MNT/foo # Now verify that all files, links and directories created before fsyncing our # directory exist after the fsync log was replayed. [ -f $SCRATCH_MNT/mydir/foo_2 ] || echo "Link mydir/foo_2 is missing" [ -f $SCRATCH_MNT/mydir/foo_3 ] || echo "Link mydir/foo_3 is missing" [ -f $SCRATCH_MNT/hello ] || echo "File hello is missing" [ -f $SCRATCH_MNT/mydir/hello_2 ] || echo "Link mydir/hello_2 is missing" [ -f $SCRATCH_MNT/mydir/x/y/foo_y_link ] || \ echo "Link mydir/x/y/foo_y_link is missing" [ -f $SCRATCH_MNT/mydir/x/y/z/foo_z_link ] || \ echo "Link mydir/x/y/z/foo_z_link is missing" [ -f $SCRATCH_MNT/mydir/x/y/z/qwerty ] || \ echo "File mydir/x/y/z/qwerty is missing" # We expect our file here to have a size of 64Kb and all the bytes having the # value 0xff. echo "file 'hello' content after log replay:" od -t x1 $SCRATCH_MNT/hello # Now remove all files/links, under our test directory 'mydir', and verify we # can remove all the directories. rm -f $SCRATCH_MNT/mydir/x/y/z/* rmdir $SCRATCH_MNT/mydir/x/y/z rm -f $SCRATCH_MNT/mydir/x/y/* rmdir $SCRATCH_MNT/mydir/x/y rmdir $SCRATCH_MNT/mydir/x rm -f $SCRATCH_MNT/mydir/* rmdir $SCRATCH_MNT/mydir # An fsck, run by the fstests framework everytime a test finishes, also detected # the inconsistency and printed the following error message: # # root 5 inode 257 errors 2001, no inode item, link count wrong # unresolved ref dir 258 index 2 namelen 5 name foo_2 filetype 1 errors 4, no inode ref # unresolved ref dir 258 index 3 namelen 5 name foo_3 filetype 1 errors 4, no inode ref status=0 exit The expected golden output for the test is: wrote 8192/8192 bytes at offset 0 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 0 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) File 'foo' content after log replay: 0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa * 0020000 file 'foo' link count after log replay: 5 file 'hello' content after log replay: 0000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff * 0200000 Which is the output after this patch and when running the test against ext3/4, xfs, f2fs, reiserfs or nilfs2. Without this patch, the test's output is: wrote 8192/8192 bytes at offset 0 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 0 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) File 'foo' content after log replay: 0000000 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa * 0020000 file 'foo' link count after log replay: 1 Link mydir/foo_2 is missing Link mydir/foo_3 is missing Link mydir/x/y/foo_y_link is missing Link mydir/x/y/z/foo_z_link is missing File mydir/x/y/z/qwerty is missing file 'hello' content after log replay: 0000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff * 0200000 rmdir: failed to remove '/home/fdmanana/btrfs-tests/scratch_1/mydir/x/y/z': No such file or directory rmdir: failed to remove '/home/fdmanana/btrfs-tests/scratch_1/mydir/x/y': No such file or directory rmdir: failed to remove '/home/fdmanana/btrfs-tests/scratch_1/mydir/x': No such file or directory rm: cannot remove '/home/fdmanana/btrfs-tests/scratch_1/mydir/foo_2': Stale file handle rm: cannot remove '/home/fdmanana/btrfs-tests/scratch_1/mydir/foo_3': Stale file handle rmdir: failed to remove '/home/fdmanana/btrfs-tests/scratch_1/mydir': Directory not empty Fsck, without this fix, also complains about the wrong link count: root 5 inode 257 errors 2001, no inode item, link count wrong unresolved ref dir 258 index 2 namelen 5 name foo_2 filetype 1 errors 4, no inode ref unresolved ref dir 258 index 3 namelen 5 name foo_3 filetype 1 errors 4, no inode ref So fix this by logging the inodes that the dentries point to when fsyncing a directory. A test case for xfstests follows. Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent bf69196 commit 2f2ff0e

5 files changed

Lines changed: 253 additions & 10 deletions

File tree

fs/btrfs/btrfs_inode.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,11 @@ struct btrfs_inode {
6666
*/
6767
struct btrfs_key location;
6868

69-
/* Lock for counters */
69+
/*
70+
* Lock for counters and all fields used to determine if the inode is in
71+
* the log or not (last_trans, last_sub_trans, last_log_commit,
72+
* logged_trans).
73+
*/
7074
spinlock_t lock;
7175

7276
/* the extent_tree has caches of all the extent mappings to disk */
@@ -250,6 +254,9 @@ static inline bool btrfs_is_free_space_inode(struct inode *inode)
250254

251255
static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
252256
{
257+
int ret = 0;
258+
259+
spin_lock(&BTRFS_I(inode)->lock);
253260
if (BTRFS_I(inode)->logged_trans == generation &&
254261
BTRFS_I(inode)->last_sub_trans <=
255262
BTRFS_I(inode)->last_log_commit &&
@@ -263,9 +270,10 @@ static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
263270
*/
264271
smp_mb();
265272
if (list_empty(&BTRFS_I(inode)->extent_tree.modified_extents))
266-
return 1;
273+
ret = 1;
267274
}
268-
return 0;
275+
spin_unlock(&BTRFS_I(inode)->lock);
276+
return ret;
269277
}
270278

271279
#define BTRFS_DIO_ORIG_BIO_SUBMITTED 0x1

fs/btrfs/file.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,7 +1811,9 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
18111811
* otherwise subsequent syncs to a file that's been synced in this
18121812
* transaction will appear to have already occured.
18131813
*/
1814+
spin_lock(&BTRFS_I(inode)->lock);
18141815
BTRFS_I(inode)->last_sub_trans = root->log_transid;
1816+
spin_unlock(&BTRFS_I(inode)->lock);
18151817
if (num_written > 0) {
18161818
err = generic_write_sync(file, pos, num_written);
18171819
if (err < 0)

fs/btrfs/transaction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,11 @@ struct btrfs_pending_snapshot {
136136
static inline void btrfs_set_inode_last_trans(struct btrfs_trans_handle *trans,
137137
struct inode *inode)
138138
{
139+
spin_lock(&BTRFS_I(inode)->lock);
139140
BTRFS_I(inode)->last_trans = trans->transaction->transid;
140141
BTRFS_I(inode)->last_sub_trans = BTRFS_I(inode)->root->log_transid;
141142
BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->root->last_log_commit;
143+
spin_unlock(&BTRFS_I(inode)->lock);
142144
}
143145

144146
int btrfs_end_transaction(struct btrfs_trans_handle *trans,

fs/btrfs/tree-log.c

Lines changed: 236 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -492,11 +492,19 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,
492492

493493
if (btrfs_inode_generation(eb, src_item) == 0) {
494494
struct extent_buffer *dst_eb = path->nodes[0];
495+
const u64 ino_size = btrfs_inode_size(eb, src_item);
495496

497+
/*
498+
* For regular files an ino_size == 0 is used only when
499+
* logging that an inode exists, as part of a directory
500+
* fsync, and the inode wasn't fsynced before. In this
501+
* case don't set the size of the inode in the fs/subvol
502+
* tree, otherwise we would be throwing valid data away.
503+
*/
496504
if (S_ISREG(btrfs_inode_mode(eb, src_item)) &&
497-
S_ISREG(btrfs_inode_mode(dst_eb, dst_item))) {
505+
S_ISREG(btrfs_inode_mode(dst_eb, dst_item)) &&
506+
ino_size != 0) {
498507
struct btrfs_map_token token;
499-
u64 ino_size = btrfs_inode_size(eb, src_item);
500508

501509
btrfs_init_map_token(&token);
502510
btrfs_set_token_inode_size(dst_eb, dst_item,
@@ -3124,6 +3132,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
31243132
struct btrfs_root *root, struct inode *inode,
31253133
struct btrfs_path *path,
31263134
struct btrfs_path *dst_path, int key_type,
3135+
struct btrfs_log_ctx *ctx,
31273136
u64 min_offset, u64 *last_offset_ret)
31283137
{
31293138
struct btrfs_key min_key;
@@ -3208,6 +3217,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
32083217
src = path->nodes[0];
32093218
nritems = btrfs_header_nritems(src);
32103219
for (i = path->slots[0]; i < nritems; i++) {
3220+
struct btrfs_dir_item *di;
3221+
32113222
btrfs_item_key_to_cpu(src, &min_key, i);
32123223

32133224
if (min_key.objectid != ino || min_key.type != key_type)
@@ -3218,6 +3229,37 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
32183229
err = ret;
32193230
goto done;
32203231
}
3232+
3233+
/*
3234+
* We must make sure that when we log a directory entry,
3235+
* the corresponding inode, after log replay, has a
3236+
* matching link count. For example:
3237+
*
3238+
* touch foo
3239+
* mkdir mydir
3240+
* sync
3241+
* ln foo mydir/bar
3242+
* xfs_io -c "fsync" mydir
3243+
* <crash>
3244+
* <mount fs and log replay>
3245+
*
3246+
* Would result in a fsync log that when replayed, our
3247+
* file inode would have a link count of 1, but we get
3248+
* two directory entries pointing to the same inode.
3249+
* After removing one of the names, it would not be
3250+
* possible to remove the other name, which resulted
3251+
* always in stale file handle errors, and would not
3252+
* be possible to rmdir the parent directory, since
3253+
* its i_size could never decrement to the value
3254+
* BTRFS_EMPTY_DIR_SIZE, resulting in -ENOTEMPTY errors.
3255+
*/
3256+
di = btrfs_item_ptr(src, i, struct btrfs_dir_item);
3257+
btrfs_dir_item_key_to_cpu(src, di, &tmp);
3258+
if (ctx &&
3259+
(btrfs_dir_transid(src, di) == trans->transid ||
3260+
btrfs_dir_type(src, di) == BTRFS_FT_DIR) &&
3261+
tmp.type != BTRFS_ROOT_ITEM_KEY)
3262+
ctx->log_new_dentries = true;
32213263
}
32223264
path->slots[0] = nritems;
32233265

@@ -3279,7 +3321,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
32793321
static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
32803322
struct btrfs_root *root, struct inode *inode,
32813323
struct btrfs_path *path,
3282-
struct btrfs_path *dst_path)
3324+
struct btrfs_path *dst_path,
3325+
struct btrfs_log_ctx *ctx)
32833326
{
32843327
u64 min_key;
32853328
u64 max_key;
@@ -3291,7 +3334,7 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
32913334
max_key = 0;
32923335
while (1) {
32933336
ret = log_dir_items(trans, root, inode, path,
3294-
dst_path, key_type, min_key,
3337+
dst_path, key_type, ctx, min_key,
32953338
&max_key);
32963339
if (ret)
32973340
return ret;
@@ -4067,7 +4110,7 @@ static int logged_inode_size(struct btrfs_root *log, struct inode *inode,
40674110
if (ret < 0) {
40684111
return ret;
40694112
} else if (ret > 0) {
4070-
*size_ret = i_size_read(inode);
4113+
*size_ret = 0;
40714114
} else {
40724115
struct btrfs_inode_item *item;
40734116

@@ -4374,15 +4417,18 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
43744417
}
43754418

43764419
if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode)) {
4377-
ret = log_directory_changes(trans, root, inode, path, dst_path);
4420+
ret = log_directory_changes(trans, root, inode, path, dst_path,
4421+
ctx);
43784422
if (ret) {
43794423
err = ret;
43804424
goto out_unlock;
43814425
}
43824426
}
43834427

4428+
spin_lock(&BTRFS_I(inode)->lock);
43844429
BTRFS_I(inode)->logged_trans = trans->transid;
43854430
BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->last_sub_trans;
4431+
spin_unlock(&BTRFS_I(inode)->lock);
43864432
out_unlock:
43874433
if (unlikely(err))
43884434
btrfs_put_logged_extents(&logged_list);
@@ -4469,6 +4515,181 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
44694515
return ret;
44704516
}
44714517

4518+
struct btrfs_dir_list {
4519+
u64 ino;
4520+
struct list_head list;
4521+
};
4522+
4523+
/*
4524+
* Log the inodes of the new dentries of a directory. See log_dir_items() for
4525+
* details about the why it is needed.
4526+
* This is a recursive operation - if an existing dentry corresponds to a
4527+
* directory, that directory's new entries are logged too (same behaviour as
4528+
* ext3/4, xfs, f2fs, reiserfs, nilfs2). Note that when logging the inodes
4529+
* the dentries point to we do not lock their i_mutex, otherwise lockdep
4530+
* complains about the following circular lock dependency / possible deadlock:
4531+
*
4532+
* CPU0 CPU1
4533+
* ---- ----
4534+
* lock(&type->i_mutex_dir_key#3/2);
4535+
* lock(sb_internal#2);
4536+
* lock(&type->i_mutex_dir_key#3/2);
4537+
* lock(&sb->s_type->i_mutex_key#14);
4538+
*
4539+
* Where sb_internal is the lock (a counter that works as a lock) acquired by
4540+
* sb_start_intwrite() in btrfs_start_transaction().
4541+
* Not locking i_mutex of the inodes is still safe because:
4542+
*
4543+
* 1) For regular files we log with a mode of LOG_INODE_EXISTS. It's possible
4544+
* that while logging the inode new references (names) are added or removed
4545+
* from the inode, leaving the logged inode item with a link count that does
4546+
* not match the number of logged inode reference items. This is fine because
4547+
* at log replay time we compute the real number of links and correct the
4548+
* link count in the inode item (see replay_one_buffer() and
4549+
* link_to_fixup_dir());
4550+
*
4551+
* 2) For directories we log with a mode of LOG_INODE_ALL. It's possible that
4552+
* while logging the inode's items new items with keys BTRFS_DIR_ITEM_KEY and
4553+
* BTRFS_DIR_INDEX_KEY are added to fs/subvol tree and the logged inode item
4554+
* has a size that doesn't match the sum of the lengths of all the logged
4555+
* names. This does not result in a problem because if a dir_item key is
4556+
* logged but its matching dir_index key is not logged, at log replay time we
4557+
* don't use it to replay the respective name (see replay_one_name()). On the
4558+
* other hand if only the dir_index key ends up being logged, the respective
4559+
* name is added to the fs/subvol tree with both the dir_item and dir_index
4560+
* keys created (see replay_one_name()).
4561+
* The directory's inode item with a wrong i_size is not a problem as well,
4562+
* since we don't use it at log replay time to set the i_size in the inode
4563+
* item of the fs/subvol tree (see overwrite_item()).
4564+
*/
4565+
static int log_new_dir_dentries(struct btrfs_trans_handle *trans,
4566+
struct btrfs_root *root,
4567+
struct inode *start_inode,
4568+
struct btrfs_log_ctx *ctx)
4569+
{
4570+
struct btrfs_root *log = root->log_root;
4571+
struct btrfs_path *path;
4572+
LIST_HEAD(dir_list);
4573+
struct btrfs_dir_list *dir_elem;
4574+
int ret = 0;
4575+
4576+
path = btrfs_alloc_path();
4577+
if (!path)
4578+
return -ENOMEM;
4579+
4580+
dir_elem = kmalloc(sizeof(*dir_elem), GFP_NOFS);
4581+
if (!dir_elem) {
4582+
btrfs_free_path(path);
4583+
return -ENOMEM;
4584+
}
4585+
dir_elem->ino = btrfs_ino(start_inode);
4586+
list_add_tail(&dir_elem->list, &dir_list);
4587+
4588+
while (!list_empty(&dir_list)) {
4589+
struct extent_buffer *leaf;
4590+
struct btrfs_key min_key;
4591+
int nritems;
4592+
int i;
4593+
4594+
dir_elem = list_first_entry(&dir_list, struct btrfs_dir_list,
4595+
list);
4596+
if (ret)
4597+
goto next_dir_inode;
4598+
4599+
min_key.objectid = dir_elem->ino;
4600+
min_key.type = BTRFS_DIR_ITEM_KEY;
4601+
min_key.offset = 0;
4602+
again:
4603+
btrfs_release_path(path);
4604+
ret = btrfs_search_forward(log, &min_key, path, trans->transid);
4605+
if (ret < 0) {
4606+
goto next_dir_inode;
4607+
} else if (ret > 0) {
4608+
ret = 0;
4609+
goto next_dir_inode;
4610+
}
4611+
4612+
process_leaf:
4613+
leaf = path->nodes[0];
4614+
nritems = btrfs_header_nritems(leaf);
4615+
for (i = path->slots[0]; i < nritems; i++) {
4616+
struct btrfs_dir_item *di;
4617+
struct btrfs_key di_key;
4618+
struct inode *di_inode;
4619+
struct btrfs_dir_list *new_dir_elem;
4620+
int log_mode = LOG_INODE_EXISTS;
4621+
int type;
4622+
4623+
btrfs_item_key_to_cpu(leaf, &min_key, i);
4624+
if (min_key.objectid != dir_elem->ino ||
4625+
min_key.type != BTRFS_DIR_ITEM_KEY)
4626+
goto next_dir_inode;
4627+
4628+
di = btrfs_item_ptr(leaf, i, struct btrfs_dir_item);
4629+
type = btrfs_dir_type(leaf, di);
4630+
if (btrfs_dir_transid(leaf, di) < trans->transid &&
4631+
type != BTRFS_FT_DIR)
4632+
continue;
4633+
btrfs_dir_item_key_to_cpu(leaf, di, &di_key);
4634+
if (di_key.type == BTRFS_ROOT_ITEM_KEY)
4635+
continue;
4636+
4637+
di_inode = btrfs_iget(root->fs_info->sb, &di_key,
4638+
root, NULL);
4639+
if (IS_ERR(di_inode)) {
4640+
ret = PTR_ERR(di_inode);
4641+
goto next_dir_inode;
4642+
}
4643+
4644+
if (btrfs_inode_in_log(di_inode, trans->transid)) {
4645+
iput(di_inode);
4646+
continue;
4647+
}
4648+
4649+
ctx->log_new_dentries = false;
4650+
if (type == BTRFS_FT_DIR)
4651+
log_mode = LOG_INODE_ALL;
4652+
btrfs_release_path(path);
4653+
ret = btrfs_log_inode(trans, root, di_inode,
4654+
log_mode, 0, LLONG_MAX, ctx);
4655+
iput(di_inode);
4656+
if (ret)
4657+
goto next_dir_inode;
4658+
if (ctx->log_new_dentries) {
4659+
new_dir_elem = kmalloc(sizeof(*new_dir_elem),
4660+
GFP_NOFS);
4661+
if (!new_dir_elem) {
4662+
ret = -ENOMEM;
4663+
goto next_dir_inode;
4664+
}
4665+
new_dir_elem->ino = di_key.objectid;
4666+
list_add_tail(&new_dir_elem->list, &dir_list);
4667+
}
4668+
break;
4669+
}
4670+
if (i == nritems) {
4671+
ret = btrfs_next_leaf(log, path);
4672+
if (ret < 0) {
4673+
goto next_dir_inode;
4674+
} else if (ret > 0) {
4675+
ret = 0;
4676+
goto next_dir_inode;
4677+
}
4678+
goto process_leaf;
4679+
}
4680+
if (min_key.offset < (u64)-1) {
4681+
min_key.offset++;
4682+
goto again;
4683+
}
4684+
next_dir_inode:
4685+
list_del(&dir_elem->list);
4686+
kfree(dir_elem);
4687+
}
4688+
4689+
btrfs_free_path(path);
4690+
return ret;
4691+
}
4692+
44724693
/*
44734694
* helper function around btrfs_log_inode to make sure newly created
44744695
* parent directories also end up in the log. A minimal inode and backref
@@ -4491,6 +4712,8 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
44914712
const struct dentry * const first_parent = parent;
44924713
const bool did_unlink = (BTRFS_I(inode)->last_unlink_trans >
44934714
last_committed);
4715+
bool log_dentries = false;
4716+
struct inode *orig_inode = inode;
44944717

44954718
sb = inode->i_sb;
44964719

@@ -4546,6 +4769,9 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
45464769
goto end_trans;
45474770
}
45484771

4772+
if (S_ISDIR(inode->i_mode) && ctx && ctx->log_new_dentries)
4773+
log_dentries = true;
4774+
45494775
while (1) {
45504776
if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb)
45514777
break;
@@ -4582,7 +4808,10 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
45824808
dput(old_parent);
45834809
old_parent = parent;
45844810
}
4585-
ret = 0;
4811+
if (log_dentries)
4812+
ret = log_new_dir_dentries(trans, root, orig_inode, ctx);
4813+
else
4814+
ret = 0;
45864815
end_trans:
45874816
dput(old_parent);
45884817
if (ret < 0) {

0 commit comments

Comments
 (0)