diff mbox series

btrfs: Copy dir permission and time when creating a stub subvolume

Message ID 20230811004657.1661696-1-lee@trager.us (mailing list archive)
State New, archived
Headers show
Series btrfs: Copy dir permission and time when creating a stub subvolume | expand

Commit Message

Lee Trager Aug. 11, 2023, 12:46 a.m. UTC
btrfs supports creating nested subvolumes however snapshots are not recursive.
When a snapshot is taken of a volume which contains a subvolume the subvolume
is replaced with a stub subvolume which has the same name and uses inode
number 2[1]. The stub subvolume kept the directory name but did not set the
time or permissions of the stub subvolume. This resulted in all time information
being the current time and ownership defaulting to root. When subvolumes and
snapshots are created using unshare this results in a snapshot directory the
user created but has no permissions for.

Test case:
[vmuser@archvm ~]# sudo -i
[root@archvm ~]# mkdir -p /mnt/btrfs/test
[root@archvm ~]# chown vmuser:users /mnt/btrfs/test/
[root@archvm ~]# exit
logout
[vmuser@archvm ~]$ cd /mnt/btrfs/test
[vmuser@archvm test]$ unshare --user --keep-caps --map-auto --map-root-user
[root@archvm test]# btrfs subvolume create subvolume
Create subvolume './subvolume'
[root@archvm test]# btrfs subvolume create subvolume/subsubvolume
Create subvolume 'subvolume/subsubvolume'
[root@archvm test]# btrfs subvolume snapshot subvolume snapshot
Create a snapshot of 'subvolume' in './snapshot'
[root@archvm test]# exit
logout
[vmuser@archvm test]$ tree -ug
[vmuser   users   ]  .
├── [vmuser   users   ]  snapshot
│   └── [vmuser   users   ]  subsubvolume  <-- Without patch perm is root:root
└── [vmuser   users   ]  subvolume
    └── [vmuser   users   ]  subsubvolume

5 directories, 0 files

[1] https://btrfs.readthedocs.io/en/latest/btrfs-subvolume.html#nested-subvolumes
Signed-off-by: Lee Trager <lee@trager.us>
---
 fs/btrfs/inode.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

Comments

Lee Trager Aug. 11, 2023, 1:46 a.m. UTC | #1
Apologies, sent this incorrectly formatted. Resubmitted.

On 8/10/23 5:46 PM, Lee Trager wrote:
> btrfs supports creating nested subvolumes however snapshots are not recursive.
> When a snapshot is taken of a volume which contains a subvolume the subvolume
> is replaced with a stub subvolume which has the same name and uses inode
> number 2[1]. The stub subvolume kept the directory name but did not set the
> time or permissions of the stub subvolume. This resulted in all time information
> being the current time and ownership defaulting to root. When subvolumes and
> snapshots are created using unshare this results in a snapshot directory the
> user created but has no permissions for.
>
> Test case:
> [vmuser@archvm ~]# sudo -i
> [root@archvm ~]# mkdir -p /mnt/btrfs/test
> [root@archvm ~]# chown vmuser:users /mnt/btrfs/test/
> [root@archvm ~]# exit
> logout
> [vmuser@archvm ~]$ cd /mnt/btrfs/test
> [vmuser@archvm test]$ unshare --user --keep-caps --map-auto --map-root-user
> [root@archvm test]# btrfs subvolume create subvolume
> Create subvolume './subvolume'
> [root@archvm test]# btrfs subvolume create subvolume/subsubvolume
> Create subvolume 'subvolume/subsubvolume'
> [root@archvm test]# btrfs subvolume snapshot subvolume snapshot
> Create a snapshot of 'subvolume' in './snapshot'
> [root@archvm test]# exit
> logout
> [vmuser@archvm test]$ tree -ug
> [vmuser   users   ]  .
> ├── [vmuser   users   ]  snapshot
> │   └── [vmuser   users   ]  subsubvolume  <-- Without patch perm is root:root
> └── [vmuser   users   ]  subvolume
>      └── [vmuser   users   ]  subsubvolume
>
> 5 directories, 0 files
>
> [1] https://btrfs.readthedocs.io/en/latest/btrfs-subvolume.html#nested-subvolumes
> Signed-off-by: Lee Trager <lee@trager.us>
> ---
>   fs/btrfs/inode.c | 12 +++++++-----
>   1 file changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index 6a68d5a3ed20..37a9b5822808 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -5592,11 +5592,11 @@ struct inode *btrfs_iget(struct super_block *s, u64 ino, struct btrfs_root *root
>   	return btrfs_iget_path(s, ino, root, NULL);
>   }
>   
> -static struct inode *new_simple_dir(struct super_block *s,
> +static struct inode *new_simple_dir(struct inode *dir,
>   				    struct btrfs_key *key,
>   				    struct btrfs_root *root)
>   {
> -	struct inode *inode = new_inode(s);
> +	struct inode *inode = new_inode(dir->i_sb);
>   
>   	if (!inode)
>   		return ERR_PTR(-ENOMEM);
> @@ -5615,9 +5615,11 @@ static struct inode *new_simple_dir(struct super_block *s,
>   	inode->i_fop = &simple_dir_operations;
>   	inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
>   	inode->i_mtime = current_time(inode);
> -	inode->i_atime = inode->i_mtime;
> -	inode->i_ctime = inode->i_mtime;
> +	inode->i_atime = dir->i_atime;
> +	inode->i_ctime = dir->i_ctime;
>   	BTRFS_I(inode)->i_otime = inode->i_mtime;
> +        inode->i_uid = dir->i_uid;
> +        inode->i_gid = dir->i_gid;
>   
>   	return inode;
>   }
> @@ -5676,7 +5678,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
>   		if (ret != -ENOENT)
>   			inode = ERR_PTR(ret);
>   		else
> -			inode = new_simple_dir(dir->i_sb, &location, root);
> +			inode = new_simple_dir(dir, &location, root);
>   	} else {
>   		inode = btrfs_iget(dir->i_sb, location.objectid, sub_root);
>   		btrfs_put_root(sub_root);
diff mbox series

Patch

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 6a68d5a3ed20..37a9b5822808 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5592,11 +5592,11 @@  struct inode *btrfs_iget(struct super_block *s, u64 ino, struct btrfs_root *root
 	return btrfs_iget_path(s, ino, root, NULL);
 }
 
-static struct inode *new_simple_dir(struct super_block *s,
+static struct inode *new_simple_dir(struct inode *dir,
 				    struct btrfs_key *key,
 				    struct btrfs_root *root)
 {
-	struct inode *inode = new_inode(s);
+	struct inode *inode = new_inode(dir->i_sb);
 
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
@@ -5615,9 +5615,11 @@  static struct inode *new_simple_dir(struct super_block *s,
 	inode->i_fop = &simple_dir_operations;
 	inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
 	inode->i_mtime = current_time(inode);
-	inode->i_atime = inode->i_mtime;
-	inode->i_ctime = inode->i_mtime;
+	inode->i_atime = dir->i_atime;
+	inode->i_ctime = dir->i_ctime;
 	BTRFS_I(inode)->i_otime = inode->i_mtime;
+        inode->i_uid = dir->i_uid;
+        inode->i_gid = dir->i_gid;
 
 	return inode;
 }
@@ -5676,7 +5678,7 @@  struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 		if (ret != -ENOENT)
 			inode = ERR_PTR(ret);
 		else
-			inode = new_simple_dir(dir->i_sb, &location, root);
+			inode = new_simple_dir(dir, &location, root);
 	} else {
 		inode = btrfs_iget(dir->i_sb, location.objectid, sub_root);
 		btrfs_put_root(sub_root);