public inbox for linux-btrfs@vger.kernel.org
 help / color / mirror / Atom feed
From: Josef Bacik <josef@toxicpanda.com>
To: Mark Harmstone <maharmstone@fb.com>
Cc: linux-btrfs@vger.kernel.org
Subject: Re: [PATCH v2] btrfs-progs: add --subvol option to mkfs.btrfs
Date: Wed, 31 Jul 2024 11:02:05 -0400	[thread overview]
Message-ID: <20240731150205.GB3908975@perftesting> (raw)
In-Reply-To: <20240730093833.1169945-1-maharmstone@fb.com>

On Tue, Jul 30, 2024 at 10:38:26AM +0100, Mark Harmstone wrote:
> This patch adds a --subvol option, which tells mkfs.btrfs to create the
> specified directories as subvolumes.
> 
> Given a populated directory img, the command
> 
> $ mkfs.btrfs --rootdir img --subvol img/usr --subvol img/home --subvol img/home/username /dev/loop0
> 
> will create subvolumes usr and home within the FS root, and subvolume
> username within the home subvolume. It will fail if any of the
> directories do not yet exist.
> 
> Signed-off-by: Mark Harmstone <maharmstone@fb.com>
> ---
> Changes from first patch:
> * Rebased against upstream changes
> * Rewrote so that directory sizes are correct within transactions
> * Changed --subvol so that it is relative to cwd rather than rootdir, so
> that in future we might allow out-of-tree subvols
> 
>  mkfs/main.c                                 | 316 +++++++++++++++++++-
>  mkfs/rootdir.c                              | 125 +++++++-
>  mkfs/rootdir.h                              |  21 +-
>  tests/mkfs-tests/034-rootdir-subvol/test.sh |  33 ++
>  4 files changed, 479 insertions(+), 16 deletions(-)
>  create mode 100755 tests/mkfs-tests/034-rootdir-subvol/test.sh
> 
> diff --git a/mkfs/main.c b/mkfs/main.c
> index e000aeff..6c98b954 100644
> --- a/mkfs/main.c
> +++ b/mkfs/main.c
> @@ -442,6 +442,7 @@ static const char * const mkfs_usage[] = {
>  	"Creation:",
>  	OPTLINE("-b|--byte-count SIZE", "set size of each device to SIZE (filesystem size is sum of all device sizes)"),
>  	OPTLINE("-r|--rootdir DIR", "copy files from DIR to the image root directory"),
> +	OPTLINE("-u|--subvol SUBDIR", "create SUBDIR as subvolume rather than normal directory"),
>  	OPTLINE("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"),
>  	OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"),
>  	OPTLINE("-f|--force", "force overwrite of existing filesystem"),
> @@ -1016,6 +1017,19 @@ static void *prepare_one_device(void *ctx)
>  	return NULL;
>  }
>  
> +static int subvol_compar(const void *p1, const void *p2)
> +{
> +	const struct rootdir_subvol *s1 = *(const struct rootdir_subvol**)p1;
> +	const struct rootdir_subvol *s2 = *(const struct rootdir_subvol**)p2;
> +
> +	if (s1->depth < s2->depth)
> +		return 1;
> +	else if (s1->depth > s2->depth)
> +		return -1;
> +	else
> +		return 0;
> +}
> +
>  int BOX_MAIN(mkfs)(int argc, char **argv)
>  {
>  	char *file;
> @@ -1057,6 +1071,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
>  	char *label = NULL;
>  	int nr_global_roots = sysconf(_SC_NPROCESSORS_ONLN);
>  	char *source_dir = NULL;
> +	LIST_HEAD(subvols);
>  
>  	cpu_detect_flags();
>  	hash_init_accel();
> @@ -1087,6 +1102,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
>  			{ "data", required_argument, NULL, 'd' },
>  			{ "version", no_argument, NULL, 'V' },
>  			{ "rootdir", required_argument, NULL, 'r' },
> +			{ "subvol", required_argument, NULL, 'u' },
>  			{ "nodiscard", no_argument, NULL, 'K' },
>  			{ "features", required_argument, NULL, 'O' },
>  			{ "runtime-features", required_argument, NULL, 'R' },
> @@ -1104,7 +1120,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
>  			{ NULL, 0, NULL, 0}
>  		};
>  
> -		c = getopt_long(argc, argv, "A:b:fl:n:s:m:d:L:R:O:r:U:VvMKq",
> +		c = getopt_long(argc, argv, "A:b:fl:n:s:m:d:L:R:O:r:U:VvMKqu:",
>  				long_options, NULL);
>  		if (c < 0)
>  			break;
> @@ -1210,6 +1226,27 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
>  				free(source_dir);
>  				source_dir = strdup(optarg);
>  				break;
> +			case 'u': {
> +				struct rootdir_subvol *s;
> +
> +				s = malloc(sizeof(struct rootdir_subvol));
> +				if (!s) {
> +					error("out of memory");
> +					ret = 1;
> +					goto error;
> +				}
> +
> +				s->dir = strdup(optarg);
> +				s->srcpath = NULL;
> +				s->destpath = NULL;
> +				s->parent = NULL;
> +				s->parent_inum = 0;
> +				INIT_LIST_HEAD(&s->children);
> +				s->root = NULL;
> +
> +				list_add_tail(&s->list, &subvols);
> +				break;
> +				}
>  			case 'U':
>  				strncpy_null(fs_uuid, optarg, BTRFS_UUID_UNPARSED_SIZE);
>  				break;
> @@ -1274,6 +1311,154 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
>  		ret = 1;
>  		goto error;
>  	}
> +	if (!list_empty(&subvols) && source_dir == NULL) {
> +		error("the option --subvol must be used with --rootdir");
> +		ret = 1;
> +		goto error;
> +	}
> +
> +	if (source_dir) {
> +		char *canonical = realpath(source_dir, NULL);
> +
> +		if (!canonical) {
> +			error("could not get canonical path to %s", source_dir);
> +			ret = 1;
> +			goto error;
> +		}
> +
> +		free(source_dir);
> +		source_dir = canonical;
> +	}
> +
> +	if (!list_empty(&subvols)) {
> +		unsigned int num_subvols = 0;
> +		size_t source_dir_len = strlen(source_dir);
> +		struct rootdir_subvol **arr, **ptr, *s;
> +
> +		list_for_each_entry(s, &subvols, list) {
> +			char *path;
> +			struct rootdir_subvol *s2;
> +
> +			if (!path_exists(s->dir)) {
> +				error("subvol %s does not exist",
> +				      s->dir);
> +				ret = 1;
> +				goto error;
> +			}
> +
> +			if (!path_is_dir(s->dir)) {
> +				error("subvol %s is not a directory", s->dir);
> +				ret = 1;
> +				goto error;
> +			}
> +
> +			path = realpath(s->dir, NULL);
> +
> +			if (!path) {
> +				error("could not get canonical path to %s",
> +				      s->dir);
> +				ret = 1;
> +				goto error;
> +			}
> +
> +			s->srcpath = path;
> +			s->srcpath_len = strlen(path);
> +
> +			if (s->srcpath_len >= source_dir_len + 1 &&
> +			    !memcmp(path, source_dir, source_dir_len) &&
> +			    path[source_dir_len] == '/') {
> +				s->destpath_len = s->srcpath_len - source_dir_len - 1;
> +
> +				s->destpath = malloc(s->destpath_len + 1);
> +				if (!s->destpath) {
> +					error("out of memory");
> +					ret = 1;
> +					goto error;
> +				}
> +
> +				memcpy(s->destpath, s->srcpath + source_dir_len + 1,
> +				       s->destpath_len);
> +				s->destpath[s->destpath_len] = 0;
> +			} else {
> +				error("subvol %s is not a child of %s",
> +				      s->dir, source_dir);
> +				ret = 1;
> +				goto error;
> +			}
> +
> +			for (s2 = list_first_entry(&subvols, struct rootdir_subvol, list);
> +			     s2 != s; s2 = list_next_entry(s2, list)) {
> +				if (!strcmp(s2->srcpath, path)) {
> +					error("subvol %s specified more than once",
> +					      s->dir);
> +					ret = 1;
> +					goto error;
> +				}
> +			}
> +
> +			s->depth = 0;
> +			for (i = 0; i < s->destpath_len; i++) {
> +				if (s->destpath[i] == '/')
> +					s->depth++;
> +			}
> +
> +			num_subvols++;
> +		}
> +
> +		/* Reorder subvol list by depth. */
> +
> +		arr = malloc(sizeof(struct rootdir_subvol*) * num_subvols);
> +		if (!arr) {
> +			error("out of memory");
> +			ret = 1;
> +			goto error;
> +		}
> +
> +		ptr = arr;
> +
> +		list_for_each_entry(s, &subvols, list) {
> +			*ptr = s;
> +			ptr++;
> +		}
> +
> +		qsort(arr, num_subvols, sizeof(struct rootdir_subvol*),
> +		      subvol_compar);
> +
> +		INIT_LIST_HEAD(&subvols);
> +		for (i = 0; i < num_subvols; i++) {
> +			list_add_tail(&arr[i]->list, &subvols);
> +		}
> +
> +		free(arr);

This whole bit is unnecessary, we have list_sort() available to us.

> +
> +		/* Assign subvols to parents. */
> +
> +		list_for_each_entry(s, &subvols, list) {
> +			if (s->depth == 0)
> +				break;
> +
> +			for (struct rootdir_subvol *s2 = list_next_entry(s, list);
> +			     !list_entry_is_head(s2, &subvols, list);
> +			     s2 = list_next_entry(s2, list)) {
> +				if (s2->depth == s->depth)
> +					continue;
> +
> +				if (s->destpath_len <= s2->destpath_len + 1)
> +					continue;
> +
> +				if (s->destpath[s2->destpath_len] != '/')
> +					continue;
> +
> +				if (memcmp(s->destpath, s2->destpath, s2->destpath_len))
> +					continue;
> +
> +				s->parent = s2;
> +				list_add_tail(&s->child_list, &s2->children);
> +
> +				break;
> +			}
> +		}
> +	}

This whole subvol list processing bit is large enough to be its own helper, and
could go into mkfs/rootdir.c

>  
>  	if (*fs_uuid) {
>  		uuid_t dummy_uuid;
> @@ -1823,6 +2008,51 @@ raid_groups:
>  		error_msg(ERROR_MSG_START_TRANS, "%m");
>  		goto out;
>  	}
> +
> +	if (!list_empty(&subvols)) {
> +		struct rootdir_subvol *s;
> +		u64 objectid = BTRFS_FIRST_FREE_OBJECTID;
> +
> +		list_for_each_entry_reverse(s, &subvols, list) {
> +			struct btrfs_key key;
> +
> +			s->objectid = objectid;
> +
> +			trans = btrfs_start_transaction(root, 1);
> +			if (IS_ERR(trans)) {
> +				errno = -PTR_ERR(trans);
> +				error_msg(ERROR_MSG_START_TRANS, "%m");
> +				ret = 1;
> +				goto error;
> +			}
> +
> +			ret = btrfs_make_subvolume(trans, objectid);
> +			if (ret < 0) {
> +				error("failed to create subvolume: %d", ret);
> +				goto out;
> +			}
> +
> +			ret = btrfs_commit_transaction(trans, root);
> +			if (ret) {
> +				errno = -ret;
> +				error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
> +				goto out;
> +			}
> +
> +			key.objectid = objectid;
> +			key.type = BTRFS_ROOT_ITEM_KEY;
> +			key.offset = (u64)-1;
> +
> +			s->root = btrfs_read_fs_root(fs_info, &key);
> +			if (IS_ERR(s->root)) {
> +				error("unable to fs read root: %lu", PTR_ERR(s->root));
> +				goto out;
> +			}
> +
> +			objectid++;
> +		}
> +	}
> +

I would also put this in its own helper;

>  	ret = btrfs_rebuild_uuid_tree(fs_info);
>  	if (ret < 0)
>  		goto out;
> @@ -1835,8 +2065,24 @@ raid_groups:
>  	}
>  
>  	if (source_dir) {
> +		LIST_HEAD(subvol_children);
> +
>  		pr_verbose(LOG_DEFAULT, "Rootdir from:       %s\n", source_dir);
> -		ret = btrfs_mkfs_fill_dir(source_dir, root);
> +
> +		if (!list_empty(&subvols)) {
> +			struct rootdir_subvol *s;
> +
> +			list_for_each_entry(s, &subvols, list) {
> +				if (s->parent)
> +					continue;
> +
> +				list_add_tail(&s->child_list,
> +					      &subvol_children);
> +			}
> +		}
> +
> +		ret = btrfs_mkfs_fill_dir(source_dir, root, &subvol_children,
> +					  "");
>  		if (ret) {
>  			error("error while filling filesystem: %d", ret);
>  			goto out;
> @@ -1853,6 +2099,59 @@ raid_groups:
>  		} else {
>  			pr_verbose(LOG_DEFAULT, "  Shrink:           no\n");
>  		}
> +
> +		if (!list_empty(&subvols)) {
> +			struct rootdir_subvol *s;
> +
> +			list_for_each_entry_reverse(s, &subvols, list) {
> +				pr_verbose(LOG_DEFAULT,
> +					   "  Subvol from:      %s -> %s\n",
> +					   s->srcpath, s->destpath);
> +			}
> +		}
> +	}
> +
> +	if (!list_empty(&subvols)) {
> +		struct rootdir_subvol *s;
> +
> +		list_for_each_entry(s, &subvols, list) {
> +			ret = btrfs_mkfs_fill_dir(s->srcpath, s->root,
> +						  &s->children, s->destpath);
> +			if (ret) {
> +				error("error while filling filesystem: %d",
> +				      ret);
> +				goto out;
> +			}
> +		}
> +
> +		list_for_each_entry_reverse(s, &subvols, list) {
> +			struct btrfs_root *parent = s->parent ? s->parent->root : root;
> +
> +			trans = btrfs_start_transaction(parent, 1);
> +			if (IS_ERR(trans)) {
> +				errno = -PTR_ERR(trans);
> +				error_msg(ERROR_MSG_START_TRANS, "%m");
> +				goto out;
> +			}
> +
> +			ret = btrfs_link_subvolume(trans, parent,
> +						 s->parent_inum,
> +						 path_basename(s->destpath),
> +						 strlen(path_basename(s->destpath)),
> +						 s->root);
> +			if (ret) {
> +				error("unable to link subvolume %s",
> +					path_basename(s->destpath));
> +				goto out;
> +			}
> +
> +			ret = btrfs_commit_transaction(trans, parent);
> +			if (ret) {
> +				errno = -ret;
> +				error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
> +				goto out;
> +			}
> +		}
>  	}
>  
>  	if (features.runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA ||
> @@ -1948,6 +2247,19 @@ error:
>  	free(label);
>  	free(source_dir);
>  
> +	while (!list_empty(&subvols)) {
> +		struct rootdir_subvol *head = list_entry(subvols.next,
> +					      struct rootdir_subvol,
> +					      list);
> +
> +		free(head->dir);
> +		free(head->srcpath);
> +		free(head->destpath);
> +
> +		list_del(&head->list);
> +		free(head);
> +	}
> +
>  	return !!ret;
>  
>  success:
> diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
> index 2b39f6bb..e3e576b2 100644
> --- a/mkfs/rootdir.c
> +++ b/mkfs/rootdir.c
> @@ -184,17 +184,58 @@ static void free_namelist(struct dirent **files, int count)
>  	free(files);
>  }
>  
> -static u64 calculate_dir_inode_size(const char *dirname)
> +static u64 calculate_dir_inode_size(const char *src_dir,
> +				    struct list_head *subvol_children,
> +				    const char *dest_dir)
>  {
>  	int count, i;
>  	struct dirent **files, *cur_file;
>  	u64 dir_inode_size = 0;
> +	struct rootdir_subvol *s;
>  
> -	count = scandir(dirname, &files, directory_select, NULL);
> +	count = scandir(src_dir, &files, directory_select, NULL);
>  
>  	for (i = 0; i < count; i++) {
> +		size_t name_len;
> +
>  		cur_file = files[i];
> -		dir_inode_size += strlen(cur_file->d_name);
> +		name_len = strlen(cur_file->d_name);
> +
> +		/* Skip entry if it's going to be a subvol (it will be accounted
> +		 * for in btrfs_link_subvolume). */
> +		if (cur_file->d_type == DT_DIR && !list_empty(subvol_children)) {
> +			bool skip = false;
> +
> +			list_for_each_entry(s, subvol_children, child_list) {
> +				if (!strcmp(dest_dir, "")) {
> +					if (!strcmp(cur_file->d_name, s->destpath)) {
> +						skip = true;
> +						break;
> +					}
> +				} else {
> +					if (s->destpath_len != strlen(dest_dir) + 1 +
> +					    name_len)
> +						continue;
> +
> +					if (memcmp(s->destpath, dest_dir, strlen(dest_dir)))
> +						continue;
> +
> +					if (s->destpath[strlen(dest_dir)] != '/')
> +						continue;
> +
> +					if (!memcmp(s->destpath + strlen(dest_dir) + 1,
> +						    cur_file->d_name, name_len)) {

This comparison code is exists twice, pull it out into a helper and use it here
and when you're building the children relationship.  Thanks,

Josef

> +						skip = true;
> +						break;
> +					}
> +				}
> +			}
> +
> +			if (skip)
> +				continue;
> +		}
> +
> +		dir_inode_size += name_len;
>  	}
>  
>  	free_namelist(files, count);
> @@ -207,7 +248,9 @@ static int add_inode_items(struct btrfs_trans_handle *trans,
>  			   struct btrfs_root *root,
>  			   struct stat *st, const char *name,
>  			   u64 self_objectid,
> -			   struct btrfs_inode_item *inode_ret)
> +			   struct btrfs_inode_item *inode_ret,
> +			   struct list_head *subvol_children,
> +			   const char *dest_dir)
>  {
>  	int ret;
>  	struct btrfs_inode_item btrfs_inode;
> @@ -218,7 +261,9 @@ static int add_inode_items(struct btrfs_trans_handle *trans,
>  	objectid = self_objectid;
>  
>  	if (S_ISDIR(st->st_mode)) {
> -		inode_size = calculate_dir_inode_size(name);
> +		inode_size = calculate_dir_inode_size(name,
> +						      subvol_children,
> +						      dest_dir);
>  		btrfs_set_stack_inode_size(&btrfs_inode, inode_size);
>  	}
>  
> @@ -430,7 +475,9 @@ end:
>  }
>  
>  static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
> -			      struct btrfs_root *root, const char *dir_name)
> +			      struct btrfs_root *root, const char *dir_name,
> +			      struct list_head *subvol_children,
> +			      const char *dest_dir)
>  {
>  	u64 root_dir_inode_size;
>  	struct btrfs_inode_item *inode_item;
> @@ -468,7 +515,8 @@ static int copy_rootdir_inode(struct btrfs_trans_handle *trans,
>  	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);
> +	root_dir_inode_size = calculate_dir_inode_size(dir_name, subvol_children,
> +						       dest_dir);
>  	btrfs_set_inode_size(leaf, inode_item, root_dir_inode_size);
>  
>  	/* Unlike fill_inode_item, we only need to copy part of the attributes. */
> @@ -493,7 +541,9 @@ error:
>  
>  static int traverse_directory(struct btrfs_trans_handle *trans,
>  			      struct btrfs_root *root, const char *dir_name,
> -			      struct directory_name_entry *dir_head)
> +			      struct directory_name_entry *dir_head,
> +			      struct list_head *subvol_children,
> +			      const char *dest_dir)
>  {
>  	int ret = 0;
>  
> @@ -519,12 +569,21 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
>  		goto fail_no_dir;
>  	}
>  
> +	dir_entry->dest_dir = strdup(dest_dir);
> +	if (!dir_entry->dest_dir) {
> +		error_msg(ERROR_MSG_MEMORY, NULL);
> +		ret = -ENOMEM;
> +		goto fail_no_dir;
> +	}
> +
>  	parent_inum = highest_inum + BTRFS_FIRST_FREE_OBJECTID;
>  	dir_entry->inum = parent_inum;
>  	list_add_tail(&dir_entry->list, &dir_head->list);
>  
> -	ret = copy_rootdir_inode(trans, root, dir_name);
> +	ret = copy_rootdir_inode(trans, root, dir_name, subvol_children,
> +				 dest_dir);
>  	if (ret < 0) {
> +		free(dir_entry->dest_dir);
>  		errno = -ret;
>  		error("failed to copy rootdir inode: %m");
>  		goto fail_no_dir;
> @@ -555,7 +614,7 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
>  		}
>  
>  		for (i = 0; i < count; i++) {
> -			char tmp[PATH_MAX];
> +			char tmp[PATH_MAX], cur_dest[PATH_MAX];
>  
>  			cur_file = files[i];
>  
> @@ -570,6 +629,33 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
>  				pr_verbose(LOG_INFO, "ADD: %s\n", tmp);
>  			}
>  
> +			if (S_ISDIR(st.st_mode)) {
> +				if (!strcmp(parent_dir_entry->dest_dir, "")) {
> +					strcpy(cur_dest, cur_file->d_name);
> +				} else {
> +					strcpy(cur_dest, parent_dir_entry->dest_dir);
> +					strcat(cur_dest, "/");
> +					strcat(cur_dest, cur_file->d_name);
> +				}
> +			}
> +
> +			/* Omit child if it is going to be a subvolume. */
> +			if (!list_empty(subvol_children) && S_ISDIR(st.st_mode)) {
> +				struct rootdir_subvol *s;
> +				bool skip = false;
> +
> +				list_for_each_entry(s, subvol_children, child_list) {
> +					if (!strcmp(cur_dest, s->destpath)) {
> +						s->parent_inum = parent_inum;
> +						skip = true;
> +						break;
> +					}
> +				}
> +
> +				if (skip)
> +					continue;
> +			}
> +
>  			/*
>  			 * We can not directly use the source ino number,
>  			 * as there is a chance that the ino is smaller than
> @@ -589,7 +675,8 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
>  
>  			ret = add_inode_items(trans, root, &st,
>  					      cur_file->d_name, cur_inum,
> -					      &cur_inode);
> +					      &cur_inode, subvol_children,
> +					      cur_dest);
>  			if (ret == -EEXIST) {
>  				if (st.st_nlink <= 1) {
>  					error(
> @@ -638,6 +725,15 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
>  					goto fail;
>  				}
>  				dir_entry->inum = cur_inum;
> +
> +				dir_entry->dest_dir = strdup(cur_dest);
> +				if (!dir_entry->dest_dir) {
> +					free(dir_entry->path);
> +					error_msg(ERROR_MSG_MEMORY, NULL);
> +					ret = -ENOMEM;
> +					goto fail;
> +				}
> +
>  				list_add_tail(&dir_entry->list,
>  					      &dir_head->list);
>  			} else if (S_ISREG(st.st_mode)) {
> @@ -661,6 +757,7 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
>  		}
>  
>  		free_namelist(files, count);
> +		free(parent_dir_entry->dest_dir);
>  		free(parent_dir_entry->path);
>  		free(parent_dir_entry);
>  
> @@ -680,7 +777,8 @@ fail_no_dir:
>  	goto out;
>  }
>  
> -int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root)
> +int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root,
> +			struct list_head *subvol_children, const char* dest_dir)
>  {
>  	int ret;
>  	struct btrfs_trans_handle *trans;
> @@ -705,7 +803,8 @@ int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root)
>  		goto fail;
>  }
>  
> -	ret = traverse_directory(trans, root, source_dir, &dir_head);
> +	ret = traverse_directory(trans, root, source_dir, &dir_head,
> +				 subvol_children, dest_dir);
>  	if (ret) {
>  		error("unable to traverse directory %s: %d", source_dir, ret);
>  		goto fail;
> diff --git a/mkfs/rootdir.h b/mkfs/rootdir.h
> index 8d5f6896..64402dbe 100644
> --- a/mkfs/rootdir.h
> +++ b/mkfs/rootdir.h
> @@ -33,10 +33,29 @@ struct directory_name_entry {
>  	const char *dir_name;
>  	char *path;
>  	ino_t inum;
> +	char *dest_dir;
>  	struct list_head list;
>  };
>  
> -int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root);
> +struct rootdir_subvol {
> +	struct list_head list;
> +	struct list_head child_list;
> +	char *dir;
> +	char *srcpath;
> +	size_t srcpath_len;
> +	char *destpath;
> +	size_t destpath_len;
> +	struct rootdir_subvol *parent;
> +	u64 parent_inum;
> +	struct list_head children;
> +	unsigned int depth;
> +	u64 objectid;
> +	struct btrfs_root *root;
> +};
> +
> +int btrfs_mkfs_fill_dir(const char *source_dir, struct btrfs_root *root,
> +			struct list_head *subvol_children,
> +			const char* dest_dir);
>  u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,
>  			u64 meta_profile, u64 data_profile);
>  int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,
> diff --git a/tests/mkfs-tests/034-rootdir-subvol/test.sh b/tests/mkfs-tests/034-rootdir-subvol/test.sh
> new file mode 100755
> index 00000000..ccd6893f
> --- /dev/null
> +++ b/tests/mkfs-tests/034-rootdir-subvol/test.sh
> @@ -0,0 +1,33 @@
> +#!/bin/bash
> +# smoke test for mkfs.btrfs --subvol option
> +
> +source "$TEST_TOP/common" || exit
> +
> +check_prereq mkfs.btrfs
> +check_prereq btrfs
> +
> +setup_root_helper
> +prepare_test_dev
> +
> +tmp=$(_mktemp_dir mkfs-rootdir)
> +
> +touch $tmp/foo
> +mkdir $tmp/dir
> +mkdir $tmp/dir/subvol
> +touch $tmp/dir/subvol/bar
> +
> +run_check_mkfs_test_dev --rootdir "$tmp" --subvol "$tmp/dir/subvol"
> +run_check $SUDO_HELPER "$TOP/btrfs" check "$TEST_DEV"
> +
> +run_check_mount_test_dev
> +run_check_stdout $SUDO_HELPER "$TOP/btrfs" subvolume list "$TEST_MNT" | \
> +	cut -d\  -f9 > "$tmp/output"
> +run_check_umount_test_dev
> +
> +result=$(cat "$tmp/output")
> +
> +if [ "$result" != "dir/subvol" ]; then
> +	_fail "dir/subvol not in subvolume list"
> +fi
> +
> +rm -rf -- "$tmp"
> -- 
> 2.44.2
> 

      parent reply	other threads:[~2024-07-31 15:02 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-30  9:38 [PATCH v2] btrfs-progs: add --subvol option to mkfs.btrfs Mark Harmstone
2024-07-30  9:55 ` Qu Wenruo
2024-07-30 13:50   ` Mark Harmstone
2024-07-30 21:04     ` Qu Wenruo
2024-07-30 23:44       ` Qu Wenruo
2024-07-31 13:45   ` Josef Bacik
2024-07-31 22:30     ` Qu Wenruo
2024-07-31 15:02 ` Josef Bacik [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240731150205.GB3908975@perftesting \
    --to=josef@toxicpanda.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=maharmstone@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox