+
+ if (by_uuid) {
+ err = btrfs_util_create_subvolume_iterator_fd(fd,
+ BTRFS_FS_TREE_OBJECTID,
+ 0, &iter);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
+
+ for (;;) {
+ err = btrfs_util_subvolume_iterator_next_info(iter,
+ &subvol_path,
+ &subvol);
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+ uuid_unparse(uuid_arg, uuidparse);
+ error("can't find uuid '%s' on '%s'", uuidparse,
+ fullpath);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ goto out;
+ } else if (err) {
+ error_btrfs_util(err);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ goto out;
+ }
+
+ if (uuid_compare(subvol.uuid, uuid_arg) == 0)
+ break;
+
+ free(subvol_path);
+ }
+ btrfs_util_destroy_subvolume_iterator(iter);
+ } else {
+ /*
+ * If !by_rootid, rootid_arg = 0, which means find the
+ * subvolume ID of the fd and use that.
+ */
+ err = btrfs_util_subvolume_info_fd(fd, rootid_arg, &subvol);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
+
+ err = btrfs_util_subvolume_path_fd(fd, subvol.id, &subvol_path);
+ if (err) {
+ error_btrfs_util(err);
+ goto out;
+ }
+
+ }
+
+ /* print the info */
+ printf("%s\n", subvol.id == BTRFS_FS_TREE_OBJECTID ? "/" : subvol_path);
+ printf("\tName: \t\t\t%s\n",
+ (subvol.id == BTRFS_FS_TREE_OBJECTID ? "<FS_TREE>" :
+ basename(subvol_path)));
+
+ if (uuid_is_null(subvol.uuid))
+ strcpy(uuidparse, "-");
+ else
+ uuid_unparse(subvol.uuid, uuidparse);
+ printf("\tUUID: \t\t\t%s\n", uuidparse);
+
+ if (uuid_is_null(subvol.parent_uuid))
+ strcpy(uuidparse, "-");
+ else
+ uuid_unparse(subvol.parent_uuid, uuidparse);
+ printf("\tParent UUID: \t\t%s\n", uuidparse);
+
+ if (uuid_is_null(subvol.received_uuid))
+ strcpy(uuidparse, "-");
+ else
+ uuid_unparse(subvol.received_uuid, uuidparse);
+ printf("\tReceived UUID: \t\t%s\n", uuidparse);
+
+ if (subvol.otime.tv_sec) {
+ struct tm tm;
+
+ localtime_r(&subvol.otime.tv_sec, &tm);
+ strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
+ } else
+ strcpy(tstr, "-");
+ printf("\tCreation time: \t\t%s\n", tstr);
+
+ printf("\tSubvolume ID: \t\t%" PRIu64 "\n", subvol.id);
+ printf("\tGeneration: \t\t%" PRIu64 "\n", subvol.generation);
+ printf("\tGen at creation: \t%" PRIu64 "\n", subvol.otransid);
+ printf("\tParent ID: \t\t%" PRIu64 "\n", subvol.parent_id);
+ printf("\tTop level ID: \t\t%" PRIu64 "\n", subvol.parent_id);
+
+ if (subvol.flags & BTRFS_ROOT_SUBVOL_RDONLY)
+ printf("\tFlags: \t\t\treadonly\n");
+ else
+ printf("\tFlags: \t\t\t-\n");
+
+ /* print the snapshots of the given subvol if any*/
+ printf("\tSnapshot(s):\n");
+
+ err = btrfs_util_create_subvolume_iterator_fd(fd,
+ BTRFS_FS_TREE_OBJECTID, 0,
+ &iter);
+
+ for (;;) {
+ struct btrfs_util_subvolume_info subvol2;
+ char *path;
+
+ err = btrfs_util_subvolume_iterator_next_info(iter, &path, &subvol2);
+ if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) {
+ break;
+ } else if (err) {
+ error_btrfs_util(err);
+ btrfs_util_destroy_subvolume_iterator(iter);
+ goto out;
+ }
+
+ if (uuid_compare(subvol2.parent_uuid, subvol.uuid) == 0)
+ printf("\t\t\t\t%s\n", path);
+
+ free(path);
+ }
+ btrfs_util_destroy_subvolume_iterator(iter);
+
+ ret = 0;
+out:
+ free(subvol_path);
+ close_file_or_dir(fd, dirstream1);
+ free(fullpath);
+ return !!ret;
+}
+
+static const char * const cmd_subvol_sync_usage[] = {
+ "btrfs subvolume sync <path> [<subvol-id>...]",
+ "Wait until given subvolume(s) are completely removed from the filesystem.",
+ "Wait until given subvolume(s) are completely removed from the filesystem",
+ "after deletion.",
+ "If no subvolume id is given, wait until all current deletion requests",
+ "are completed, but do not wait for subvolumes deleted meanwhile.",
+ "The status of subvolume ids is checked periodically.",
+ "",
+ "-s <N> sleep N seconds between checks (default: 1)",
+ NULL
+};
+
+static int cmd_subvol_sync(int argc, char **argv)
+{
+ int fd = -1;
+ int ret = 1;
+ DIR *dirstream = NULL;
+ uint64_t *ids = NULL;
+ size_t id_count, i;
+ int sleep_interval = 1;
+ enum btrfs_util_error err;
+
+ while (1) {
+ int c = getopt(argc, argv, "s:");
+
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case 's':
+ sleep_interval = atoi(optarg);
+ if (sleep_interval < 1) {
+ error("invalid sleep interval %s", optarg);
+ ret = 1;
+ goto out;
+ }
+ break;
+ default:
+ usage(cmd_subvol_sync_usage);
+ }
+ }
+
+ if (check_argc_min(argc - optind, 1))
+ usage(cmd_subvol_sync_usage);
+
+ fd = btrfs_open_dir(argv[optind], &dirstream, 1);
+ if (fd < 0) {
+ ret = 1;
+ goto out;
+ }
+ optind++;
+
+ id_count = argc - optind;
+ if (!id_count) {
+ err = btrfs_util_deleted_subvolumes_fd(fd, &ids, &id_count);
+ if (err) {
+ error_btrfs_util(err);
+ ret = 1;
+ goto out;
+ }
+ if (id_count == 0) {
+ ret = 0;
+ goto out;
+ }
+ } else {
+ ids = malloc(id_count * sizeof(uint64_t));
+ if (!ids) {
+ error("not enough memory");
+ ret = 1;
+ goto out;
+ }
+
+ for (i = 0; i < id_count; i++) {
+ u64 id;
+ const char *arg;
+
+ arg = argv[optind + i];
+ errno = 0;
+ id = strtoull(arg, NULL, 10);
+ if (errno) {
+ error("unrecognized subvolume id %s", arg);
+ ret = 1;
+ goto out;
+ }
+ if (id < BTRFS_FIRST_FREE_OBJECTID ||
+ id > BTRFS_LAST_FREE_OBJECTID) {
+ error("subvolume id %s out of range", arg);
+ ret = 1;
+ goto out;
+ }
+ ids[i] = id;
+ }
+ }
+
+ ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
+
+out:
+ free(ids);
+ close_file_or_dir(fd, dirstream);
+
+ return !!ret;