diff mbox series

[v3,1/2] btrfs-progs: mkfs/rootdir: copy missing attributes for the rootdir inode

Message ID d33e5e10e92a0c8c2a82005eba1da47927bf286a.1697057301.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: make sure "mkfs --rootdir" copies all the attributes for the rootdir | expand

Commit Message

Qu Wenruo Oct. 11, 2023, 8:49 p.m. UTC
[BUG]
When using "mkfs.btrfs" with "--rootdir" option, the top level inode
(rootdir) will not get the same xattr from the source dir:

  mkdir -p source_dir/
  touch source_dir/file
  setfattr -n user.rootdir_xattr source_dir/
  setfattr -n user.regular_xattr source_dir/file
  mkfs.btrfs -f --rootdir source_dir $dev
  mount $dev $mnt
  getfattr $mnt
  # Nothing <<<
  getfattr $mnt/file
  # file: $mnt/file
  user.regular_xattr <<<

[CAUSE]
In function traverse_directory(), we only call add_xattr_item() for all
the child inodes, not really for the rootdir inode itself, leading to
the missing xattr items.

Not only xattr, in fact we also miss the uid/gid/timestamps/mode for the
rootdir inode.

[FIX]
Extract a dedicated function, copy_rootdir_inode(), to handle every
needed attributes for the rootdir inode, including:

- xattr
- uid
- gid
- mode
- timestamps

Issue: #688
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 mkfs/rootdir.c | 88 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 67 insertions(+), 21 deletions(-)

Comments

David Sterba Oct. 13, 2023, 3:55 p.m. UTC | #1
On Thu, Oct 12, 2023 at 07:19:25AM +1030, Qu Wenruo wrote:
> [BUG]
> When using "mkfs.btrfs" with "--rootdir" option, the top level inode
> (rootdir) will not get the same xattr from the source dir:
> 
>   mkdir -p source_dir/
>   touch source_dir/file
>   setfattr -n user.rootdir_xattr source_dir/
>   setfattr -n user.regular_xattr source_dir/file
>   mkfs.btrfs -f --rootdir source_dir $dev
>   mount $dev $mnt
>   getfattr $mnt
>   # Nothing <<<
>   getfattr $mnt/file
>   # file: $mnt/file
>   user.regular_xattr <<<
> 
> [CAUSE]
> In function traverse_directory(), we only call add_xattr_item() for all
> the child inodes, not really for the rootdir inode itself, leading to
> the missing xattr items.
> 
> Not only xattr, in fact we also miss the uid/gid/timestamps/mode for the
> rootdir inode.
> 
> [FIX]
> Extract a dedicated function, copy_rootdir_inode(), to handle every
> needed attributes for the rootdir inode, including:
> 
> - xattr
> - uid
> - gid
> - mode
> - timestamps
> 
> Issue: #688
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
>  mkfs/rootdir.c | 88 ++++++++++++++++++++++++++++++++++++++------------
>  1 file changed, 67 insertions(+), 21 deletions(-)
> 
> diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
> index a413a31eb2d6..24e26cdf50e0 100644
> --- a/mkfs/rootdir.c
> +++ b/mkfs/rootdir.c
> @@ -429,6 +429,69 @@ end:
>  	return ret;
>  }
>  
> +static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
> +			      struct btrfs_root *root, const char *dir_name)
> +{
> +	u64 root_dir_inode_size;
> +	struct btrfs_inode_item *inode_item;
> +	struct btrfs_path path = { 0 };
> +	struct btrfs_key key;
> +	struct extent_buffer *leaf;
> +	struct stat st;
> +	int ret;
> +
> +	ret = stat(dir_name, &st);

According to the v3 changelog this should be lstat(), right?

> +	if (ret < 0) {
> +		ret = -errno;
> +		error("lstat failed for direcotry %s: $m", dir_name);
                       ^^^^^

like here.

> +		return ret;
> +	}
David Sterba Oct. 13, 2023, 4:13 p.m. UTC | #2
On Thu, Oct 12, 2023 at 07:19:25AM +1030, Qu Wenruo wrote:
> [BUG]
> When using "mkfs.btrfs" with "--rootdir" option, the top level inode
> (rootdir) will not get the same xattr from the source dir:
> 
>   mkdir -p source_dir/
>   touch source_dir/file
>   setfattr -n user.rootdir_xattr source_dir/
>   setfattr -n user.regular_xattr source_dir/file
>   mkfs.btrfs -f --rootdir source_dir $dev
>   mount $dev $mnt
>   getfattr $mnt
>   # Nothing <<<
>   getfattr $mnt/file
>   # file: $mnt/file
>   user.regular_xattr <<<
> 
> [CAUSE]
> In function traverse_directory(), we only call add_xattr_item() for all
> the child inodes, not really for the rootdir inode itself, leading to
> the missing xattr items.
> 
> Not only xattr, in fact we also miss the uid/gid/timestamps/mode for the
> rootdir inode.
> 
> [FIX]
> Extract a dedicated function, copy_rootdir_inode(), to handle every
> needed attributes for the rootdir inode, including:
> 
> - xattr
> - uid
> - gid
> - mode
> - timestamps
> 
> Issue: #688
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
>  mkfs/rootdir.c | 88 ++++++++++++++++++++++++++++++++++++++------------
>  1 file changed, 67 insertions(+), 21 deletions(-)
> 
> diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
> index a413a31eb2d6..24e26cdf50e0 100644
> --- a/mkfs/rootdir.c
> +++ b/mkfs/rootdir.c
> @@ -429,6 +429,69 @@ end:
>  	return ret;
>  }
>  
> +static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
> +			      struct btrfs_root *root, const char *dir_name)
> +{
> +	u64 root_dir_inode_size;
> +	struct btrfs_inode_item *inode_item;
> +	struct btrfs_path path = { 0 };
> +	struct btrfs_key key;
> +	struct extent_buffer *leaf;
> +	struct stat st;
> +	int ret;
> +
> +	ret = stat(dir_name, &st);
> +	if (ret < 0) {
> +		ret = -errno;
> +		error("lstat failed for direcotry %s: $m", dir_name);
> +		return ret;
> +	}
> +
> +	ret = add_xattr_item(trans, root, btrfs_root_dirid(&root->root_item),
> +			     dir_name);
> +	if (ret < 0) {
> +		errno = -ret;
> +		error("failed to add xattr item for the top level inode: %m");
> +		return ret;
> +	}
> +
> +	key.objectid = btrfs_root_dirid(&root->root_item);
> +	key.offset = 0;
> +	key.type = BTRFS_INODE_ITEM_KEY;
> +	ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
> +	if (ret > 0)
> +		ret = -ENOENT;
> +	if (ret) {
> +		error("failed to lookup root dir: %d", ret);
> +		goto error;
> +	}
> +
> +	leaf = path.nodes[0];
> +	inode_item = btrfs_item_ptr(leaf, path.slots[0],
> +				    struct btrfs_inode_item);
> +
> +	root_dir_inode_size = calculate_dir_inode_size(dir_name);
> +	btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
> +
> +	/* Unlike fill_inode_item, we only need to copy part of the attributes. */
> +	btrfs_set_inode_uid(leaf, inode_item, st.st_uid);
> +	btrfs_set_inode_gid(leaf, inode_item, st.st_gid);
> +	btrfs_set_inode_mode(leaf, inode_item, st.st_mode);
> +	btrfs_set_timespec_sec(leaf, &inode_item->atime, st.st_atime);
> +	btrfs_set_timespec_nsec(leaf, &inode_item->atime, 0);
> +	btrfs_set_timespec_sec(leaf, &inode_item->ctime, st.st_ctime);
> +	btrfs_set_timespec_nsec(leaf, &inode_item->ctime, 0);
> +	btrfs_set_timespec_sec(leaf, &inode_item->mtime, st.st_mtime);
> +	btrfs_set_timespec_nsec(leaf, &inode_item->mtime, 0);
> +	btrfs_set_timespec_sec(leaf, &inode_item->otime, 0);
> +	btrfs_set_timespec_nsec(leaf, &inode_item->otime, 0);

We should put something sensible to the otime, either current time, real
otime or at least mtime/ctime as a fallback.
Qu Wenruo Oct. 13, 2023, 7:55 p.m. UTC | #3
On 2023/10/14 02:25, David Sterba wrote:
> On Thu, Oct 12, 2023 at 07:19:25AM +1030, Qu Wenruo wrote:
>> [BUG]
>> When using "mkfs.btrfs" with "--rootdir" option, the top level inode
>> (rootdir) will not get the same xattr from the source dir:
>>
>>    mkdir -p source_dir/
>>    touch source_dir/file
>>    setfattr -n user.rootdir_xattr source_dir/
>>    setfattr -n user.regular_xattr source_dir/file
>>    mkfs.btrfs -f --rootdir source_dir $dev
>>    mount $dev $mnt
>>    getfattr $mnt
>>    # Nothing <<<
>>    getfattr $mnt/file
>>    # file: $mnt/file
>>    user.regular_xattr <<<
>>
>> [CAUSE]
>> In function traverse_directory(), we only call add_xattr_item() for all
>> the child inodes, not really for the rootdir inode itself, leading to
>> the missing xattr items.
>>
>> Not only xattr, in fact we also miss the uid/gid/timestamps/mode for the
>> rootdir inode.
>>
>> [FIX]
>> Extract a dedicated function, copy_rootdir_inode(), to handle every
>> needed attributes for the rootdir inode, including:
>>
>> - xattr
>> - uid
>> - gid
>> - mode
>> - timestamps
>>
>> Issue: #688
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>> ---
>>   mkfs/rootdir.c | 88 ++++++++++++++++++++++++++++++++++++++------------
>>   1 file changed, 67 insertions(+), 21 deletions(-)
>>
>> diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
>> index a413a31eb2d6..24e26cdf50e0 100644
>> --- a/mkfs/rootdir.c
>> +++ b/mkfs/rootdir.c
>> @@ -429,6 +429,69 @@ end:
>>   	return ret;
>>   }
>>
>> +static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
>> +			      struct btrfs_root *root, const char *dir_name)
>> +{
>> +	u64 root_dir_inode_size;
>> +	struct btrfs_inode_item *inode_item;
>> +	struct btrfs_path path = { 0 };
>> +	struct btrfs_key key;
>> +	struct extent_buffer *leaf;
>> +	struct stat st;
>> +	int ret;
>> +
>> +	ret = stat(dir_name, &st);
>
> According to the v3 changelog this should be lstat(), right?

It should be stat(), not lstat() of v2.

The reason is, the end user may pass a softlink pointing to a directory.
In that case, lstat() would not follow the softlink.

>
>> +	if (ret < 0) {
>> +		ret = -errno;
>> +		error("lstat failed for direcotry %s: $m", dir_name);
>                         ^^^^^
>
> like here.

I'll update it along with other fixes to address the comments.

Thanks,
Qu
>
>> +		return ret;
>> +	}
David Sterba Oct. 17, 2023, 5:23 p.m. UTC | #4
On Sat, Oct 14, 2023 at 06:25:03AM +1030, Qu Wenruo wrote:
> 
> 
> On 2023/10/14 02:25, David Sterba wrote:
> > On Thu, Oct 12, 2023 at 07:19:25AM +1030, Qu Wenruo wrote:
> >> [BUG]
> >> When using "mkfs.btrfs" with "--rootdir" option, the top level inode
> >> (rootdir) will not get the same xattr from the source dir:
> >>
> >>    mkdir -p source_dir/
> >>    touch source_dir/file
> >>    setfattr -n user.rootdir_xattr source_dir/
> >>    setfattr -n user.regular_xattr source_dir/file
> >>    mkfs.btrfs -f --rootdir source_dir $dev
> >>    mount $dev $mnt
> >>    getfattr $mnt
> >>    # Nothing <<<
> >>    getfattr $mnt/file
> >>    # file: $mnt/file
> >>    user.regular_xattr <<<
> >>
> >> [CAUSE]
> >> In function traverse_directory(), we only call add_xattr_item() for all
> >> the child inodes, not really for the rootdir inode itself, leading to
> >> the missing xattr items.
> >>
> >> Not only xattr, in fact we also miss the uid/gid/timestamps/mode for the
> >> rootdir inode.
> >>
> >> [FIX]
> >> Extract a dedicated function, copy_rootdir_inode(), to handle every
> >> needed attributes for the rootdir inode, including:
> >>
> >> - xattr
> >> - uid
> >> - gid
> >> - mode
> >> - timestamps
> >>
> >> Issue: #688
> >> Signed-off-by: Qu Wenruo <wqu@suse.com>
> >> ---
> >>   mkfs/rootdir.c | 88 ++++++++++++++++++++++++++++++++++++++------------
> >>   1 file changed, 67 insertions(+), 21 deletions(-)
> >>
> >> diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
> >> index a413a31eb2d6..24e26cdf50e0 100644
> >> --- a/mkfs/rootdir.c
> >> +++ b/mkfs/rootdir.c
> >> @@ -429,6 +429,69 @@ end:
> >>   	return ret;
> >>   }
> >>
> >> +static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
> >> +			      struct btrfs_root *root, const char *dir_name)
> >> +{
> >> +	u64 root_dir_inode_size;
> >> +	struct btrfs_inode_item *inode_item;
> >> +	struct btrfs_path path = { 0 };
> >> +	struct btrfs_key key;
> >> +	struct extent_buffer *leaf;
> >> +	struct stat st;
> >> +	int ret;
> >> +
> >> +	ret = stat(dir_name, &st);
> >
> > According to the v3 changelog this should be lstat(), right?
> 
> It should be stat(), not lstat() of v2.
> 
> The reason is, the end user may pass a softlink pointing to a directory.
> In that case, lstat() would not follow the softlink.

Ok, I've changed it to stat().

> 
> >
> >> +	if (ret < 0) {
> >> +		ret = -errno;
> >> +		error("lstat failed for direcotry %s: $m", dir_name);
> >                         ^^^^^
> >
> > like here.
> 
> I'll update it along with other fixes to address the comments.

Unless it's just the l/stat references no need to resend, I've updated
the commit.
diff mbox series

Patch

diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
index a413a31eb2d6..24e26cdf50e0 100644
--- a/mkfs/rootdir.c
+++ b/mkfs/rootdir.c
@@ -429,6 +429,69 @@  end:
 	return ret;
 }
 
+static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
+			      struct btrfs_root *root, const char *dir_name)
+{
+	u64 root_dir_inode_size;
+	struct btrfs_inode_item *inode_item;
+	struct btrfs_path path = { 0 };
+	struct btrfs_key key;
+	struct extent_buffer *leaf;
+	struct stat st;
+	int ret;
+
+	ret = stat(dir_name, &st);
+	if (ret < 0) {
+		ret = -errno;
+		error("lstat failed for direcotry %s: $m", dir_name);
+		return ret;
+	}
+
+	ret = add_xattr_item(trans, root, btrfs_root_dirid(&root->root_item),
+			     dir_name);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to add xattr item for the top level inode: %m");
+		return ret;
+	}
+
+	key.objectid = btrfs_root_dirid(&root->root_item);
+	key.offset = 0;
+	key.type = BTRFS_INODE_ITEM_KEY;
+	ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
+	if (ret > 0)
+		ret = -ENOENT;
+	if (ret) {
+		error("failed to lookup root dir: %d", ret);
+		goto error;
+	}
+
+	leaf = path.nodes[0];
+	inode_item = btrfs_item_ptr(leaf, path.slots[0],
+				    struct btrfs_inode_item);
+
+	root_dir_inode_size = calculate_dir_inode_size(dir_name);
+	btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
+
+	/* Unlike fill_inode_item, we only need to copy part of the attributes. */
+	btrfs_set_inode_uid(leaf, inode_item, st.st_uid);
+	btrfs_set_inode_gid(leaf, inode_item, st.st_gid);
+	btrfs_set_inode_mode(leaf, inode_item, st.st_mode);
+	btrfs_set_timespec_sec(leaf, &inode_item->atime, st.st_atime);
+	btrfs_set_timespec_nsec(leaf, &inode_item->atime, 0);
+	btrfs_set_timespec_sec(leaf, &inode_item->ctime, st.st_ctime);
+	btrfs_set_timespec_nsec(leaf, &inode_item->ctime, 0);
+	btrfs_set_timespec_sec(leaf, &inode_item->mtime, st.st_mtime);
+	btrfs_set_timespec_nsec(leaf, &inode_item->mtime, 0);
+	btrfs_set_timespec_sec(leaf, &inode_item->otime, 0);
+	btrfs_set_timespec_nsec(leaf, &inode_item->otime, 0);
+	btrfs_mark_buffer_dirty(leaf);
+
+error:
+	btrfs_release_path(&path);
+	return ret;
+}
+
 static int traverse_directory(struct btrfs_trans_handle *trans,
 			      struct btrfs_root *root, const char *dir_name,
 			      struct directory_name_entry *dir_head)
@@ -436,7 +499,6 @@  static int traverse_directory(struct btrfs_trans_handle *trans,
 	int ret = 0;
 
 	struct btrfs_inode_item cur_inode;
-	struct btrfs_inode_item *inode_item;
 	int count, i, dir_index_cnt;
 	struct dirent **files;
 	struct stat st;
@@ -445,10 +507,6 @@  static int traverse_directory(struct btrfs_trans_handle *trans,
 	ino_t parent_inum, cur_inum;
 	ino_t highest_inum = 0;
 	const char *parent_dir_name;
-	struct btrfs_path path = { 0 };
-	struct extent_buffer *leaf;
-	struct btrfs_key root_dir_key;
-	u64 root_dir_inode_size = 0;
 
 	/* Add list for source directory */
 	dir_entry = malloc(sizeof(struct directory_name_entry));
@@ -466,25 +524,13 @@  static int traverse_directory(struct btrfs_trans_handle *trans,
 	dir_entry->inum = parent_inum;
 	list_add_tail(&dir_entry->list, &dir_head->list);
 
-	root_dir_key.objectid = btrfs_root_dirid(&root->root_item);
-	root_dir_key.offset = 0;
-	root_dir_key.type = BTRFS_INODE_ITEM_KEY;
-	ret = btrfs_lookup_inode(trans, root, &path, &root_dir_key, 1);
-	if (ret) {
-		error("failed to lookup root dir: %d", ret);
+	ret = copy_rootdir_inode(trans, root, dir_name);
+	if (ret < 0) {
+		errno = -ret;
+		error("failed to copy rootdir inode: %m");
 		goto fail_no_dir;
 	}
 
-	leaf = path.nodes[0];
-	inode_item = btrfs_item_ptr(leaf, path.slots[0],
-				    struct btrfs_inode_item);
-
-	root_dir_inode_size = calculate_dir_inode_size(dir_name);
-	btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
-	btrfs_mark_buffer_dirty(leaf);
-
-	btrfs_release_path(&path);
-
 	do {
 		parent_dir_entry = list_entry(dir_head->list.next,
 					      struct directory_name_entry,