A btrfs directory entry can refer to two different sorts of objects BTRFS_INODE_ITEM_KEY - a regular fs object (file, dir, etc) BTRFS_ROOT_ITEM_KEY - a reference to a subvol. The 'objectid' numbers for these two are independent, so it is possible (and common) for an INODE and a ROOT to have the same objectid. As readdir reports the objectid as the inode number, if two such are in the same directory, a tool which examines the inode numbers in getdents results could think they are links. As the BTRFS_ROOT_ITEM_KEY objectid is not visible via stat() (only getdents), this is rarely if ever a problem. However a future patch will expose this number as the i_ino of an automount point. This will cause problems if the objectid is used as-is. So: create a simple mapping function to reduce (or eliminate?) the possibility of conflict. The objectid of BTRFS_ROOT_ITEM_KEY is subtracted from ULONG_MAX to make an inode number. Signed-off-by: NeilBrown <neilb@xxxxxxx> --- fs/btrfs/btrfs_inode.h | 10 ++++++++++ fs/btrfs/inode.c | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index c652e19ad74e..a4b5f38196e6 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -328,6 +328,16 @@ static inline bool btrfs_inode_in_log(struct btrfs_inode *inode, u64 generation) return ret; } +static inline unsigned long btrfs_location_to_ino(struct btrfs_key *location) +{ + if (location->type == BTRFS_INODE_ITEM_KEY) + return location->objectid; + /* Probably BTRFS_ROOT_ITEM_KEY, try to keep the inode + * numbers separate. + */ + return ULONG_MAX - location->objectid; +} + struct btrfs_dio_private { struct inode *inode; u64 logical_offset; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8f60314c36c5..02537c1a9763 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6136,7 +6136,8 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) put_unaligned(fs_ftype_to_dtype(btrfs_dir_type(leaf, di)), &entry->type); btrfs_dir_item_key_to_cpu(leaf, di, &location); - put_unaligned(location.objectid, &entry->ino); + put_unaligned(btrfs_location_to_ino(&location), + &entry->ino); put_unaligned(found_key.offset, &entry->offset); entries++; addr += sizeof(struct dir_entry) + name_len;