Skip to content

Commit 4b452f1

Browse files
maharmstonekdave
authored andcommitted
btrfs-progs: check: add duplicate filename check
If when doing btrfs check we encounter a DIR_ITEM slot with multiple entries due to a hash collision, loop through them to make sure that they are all distinct names. Pull-request: #1021 Signed-off-by: Mark Harmstone <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 0693834 commit 4b452f1

File tree

6 files changed

+106
-0
lines changed

6 files changed

+106
-0
lines changed

check/main.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec)
653653
rec->nlink);
654654
if (errors & I_ERR_INVALID_XATTR)
655655
fprintf(stderr, ", invalid xattr");
656+
if (errors & I_ERR_DUP_FILENAME)
657+
fprintf(stderr, ", dup filename");
656658
fprintf(stderr, "\n");
657659

658660
/* Print the holes if needed */
@@ -1434,6 +1436,40 @@ static int add_mismatch_dir_hash(struct inode_record *dir_rec,
14341436
return 0;
14351437
}
14361438

1439+
static void check_for_dupe_filenames(struct extent_buffer *eb, int slot,
1440+
int nritems, struct inode_record *rec)
1441+
{
1442+
struct btrfs_dir_item *di, *di2;
1443+
char namebuf[BTRFS_NAME_LEN], namebuf2[BTRFS_NAME_LEN];
1444+
u32 len, len2;
1445+
1446+
di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
1447+
1448+
for (int i = 0; i < nritems - 1; i++) {
1449+
len = btrfs_dir_name_len(eb, di) + btrfs_dir_data_len(eb, di);
1450+
1451+
read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len);
1452+
1453+
di2 = di;
1454+
len2 = len;
1455+
1456+
for (int j = i + 1; j < nritems; j++) {
1457+
di2 = (struct btrfs_dir_item *)((char *)di2 + sizeof(*di2) + len2);
1458+
len2 = btrfs_dir_name_len(eb, di2) + btrfs_dir_data_len(eb, di2);
1459+
1460+
if (len != len2)
1461+
continue;
1462+
1463+
read_extent_buffer(eb, namebuf2, (unsigned long)(di2 + 1), len2);
1464+
1465+
if (memcmp(namebuf, namebuf2, len) == 0)
1466+
rec->errors |= I_ERR_DUP_FILENAME;
1467+
}
1468+
1469+
di = (struct btrfs_dir_item *)((char *)di + sizeof(*di) + len);
1470+
}
1471+
}
1472+
14371473
static int process_dir_item(struct extent_buffer *eb,
14381474
int slot, struct btrfs_key *key,
14391475
struct shared_node *active_node)
@@ -1525,6 +1561,9 @@ static int process_dir_item(struct extent_buffer *eb,
15251561
if (key->type == BTRFS_DIR_INDEX_KEY && nritems > 1)
15261562
rec->errors |= I_ERR_DUP_DIR_INDEX;
15271563

1564+
if (key->type == BTRFS_DIR_ITEM_KEY && nritems > 1)
1565+
check_for_dupe_filenames(eb, slot, nritems, rec);
1566+
15281567
return 0;
15291568
}
15301569

check/mode-lowmem.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,47 @@ static void print_dir_item_err(struct btrfs_root *root, struct btrfs_key *key,
16611661

16621662
}
16631663

1664+
static bool check_for_dupe_filenames_lowmem(struct extent_buffer *eb, int slot,
1665+
int nritems, struct btrfs_root *root,
1666+
struct btrfs_key *di_key)
1667+
{
1668+
struct btrfs_dir_item *di, *di2;
1669+
char namebuf[BTRFS_NAME_LEN], namebuf2[BTRFS_NAME_LEN];
1670+
u32 len, len2;
1671+
1672+
di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item);
1673+
1674+
for (int i = 0; i < nritems - 1; i++) {
1675+
len = btrfs_dir_name_len(eb, di) + btrfs_dir_data_len(eb, di);
1676+
1677+
read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), len);
1678+
1679+
di2 = di;
1680+
len2 = len;
1681+
1682+
for (int j = i + 1; j < nritems; j++) {
1683+
di2 = (struct btrfs_dir_item *)((char *)di2 + sizeof(*di2) + len2);
1684+
len2 = btrfs_dir_name_len(eb, di2) + btrfs_dir_data_len(eb, di2);
1685+
1686+
if (len != len2)
1687+
continue;
1688+
1689+
read_extent_buffer(eb, namebuf2, (unsigned long)(di2 + 1), len2);
1690+
1691+
if (!memcmp(namebuf, namebuf2, len)) {
1692+
error("root %llu inode %llu dup filename %.*s",
1693+
root->objectid, di_key->objectid,
1694+
len, namebuf);
1695+
return true;
1696+
}
1697+
}
1698+
1699+
di = (struct btrfs_dir_item *)((char *)di + sizeof(*di) + len);
1700+
}
1701+
1702+
return false;
1703+
}
1704+
16641705
/*
16651706
* Traverse the given DIR_ITEM/DIR_INDEX and check related INODE_ITEM and
16661707
* call find_inode_ref() to check related INODE_REF/INODE_EXTREF.
@@ -1695,6 +1736,7 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
16951736
int err;
16961737
int tmp_err;
16971738
int need_research = 0;
1739+
int nritems = 1;
16981740

16991741
begin:
17001742
err = 0;
@@ -1835,7 +1877,16 @@ static int check_dir_item(struct btrfs_root *root, struct btrfs_key *di_key,
18351877
di_key->offset);
18361878
break;
18371879
}
1880+
1881+
if (cur < total)
1882+
nritems++;
18381883
}
1884+
1885+
if (nritems > 1) {
1886+
if (check_for_dupe_filenames_lowmem(node, slot, nritems, root, di_key))
1887+
err |= DUP_FILENAME_ERROR;
1888+
}
1889+
18391890
out:
18401891
/* research path */
18411892
btrfs_release_path(path);

check/mode-lowmem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#define INODE_MODE_ERROR (1U << 25) /* Bad inode mode */
4848
#define INVALID_GENERATION (1U << 26) /* Generation is too new */
4949
#define SUPER_BYTES_USED_ERROR (1U << 27) /* Super bytes_used is invalid */
50+
#define DUP_FILENAME_ERROR (1U << 28) /* DIR_ITEM contains duplicate names */
5051

5152
/*
5253
* Error bit for low memory mode check.

check/mode-original.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ struct unaligned_extent_rec_t {
195195
#define I_ERR_INVALID_NLINK (1U << 21)
196196
#define I_ERR_INVALID_XATTR (1U << 22)
197197
#define I_ERR_DEPRECATED_FREE_INO (1U << 23)
198+
#define I_ERR_DUP_FILENAME (1U << 24)
198199

199200
struct inode_record {
200201
struct list_head backrefs;
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
#
3+
# Verify that check can report duplicate filename as an error
4+
5+
source "$TEST_TOP/common" || exit
6+
7+
check_prereq btrfs
8+
9+
check_image() {
10+
run_mustfail "duplicate filename not reported as error" \
11+
"$TOP/btrfs" check "$1"
12+
}
13+
14+
check_all_images

0 commit comments

Comments
 (0)