From 8d93d71f6aa521e0a3e078d2c4ab2c406ebd884c Mon Sep 17 00:00:00 2001 From: "Misono, Tomohiro" Date: Wed, 27 Sep 2017 11:03:48 +0900 Subject: btrfs-progs: subvol: fix subvol del --commit-after Fix 'subvolume delete --commit-after' to work properly: - SYNC ioctl will be issued even when last delete fails - SYNC ioctl will be issued on each file system only once in the end To achieve this, get_fsid() and add_seen_fsid() are called after each delete to keep only one fd for each fs. In the end, seen_fsid_hash will be traversed and SYNC is issued on each fs. Signed-off-by: Tomohiro Misono Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- cmds-subvolume.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/cmds-subvolume.c b/cmds-subvolume.c index 8b24de37..69c50387 100644 --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -263,6 +263,9 @@ static int cmd_subvol_delete(int argc, char **argv) DIR *dirstream = NULL; int verbose = 0; int commit_mode = 0; + u8 fsid[BTRFS_FSID_SIZE]; + char uuidbuf[BTRFS_UUID_UNPARSED_SIZE]; + struct seen_fsid *seen_fsid_hash[SEEN_FSID_HASH_SIZE] = { NULL, }; enum { COMMIT_AFTER = 1, COMMIT_EACH = 2 }; while (1) { @@ -358,31 +361,74 @@ again: path, strerror(errno)); ret = 1; } + } else if (commit_mode == COMMIT_AFTER) { + res = get_fsid(dname, fsid, 0); + if (res < 0) { + error("unable to get fsid for '%s': %s", + path, strerror(-res)); + error( + "delete suceeded but commit may not be done in the end"); + ret = 1; + goto out; + } + + if (add_seen_fsid(fsid, seen_fsid_hash, fd, dirstream) == 0) { + if (verbose > 0) { + uuid_unparse(fsid, uuidbuf); + printf(" new fs is found for '%s', fsid: %s\n", + path, uuidbuf); + } + /* + * This is the first time a subvolume on this + * filesystem is deleted, keep fd in order to issue + * SYNC ioctl in the end + */ + goto keep_fd; + } } out: + close_file_or_dir(fd, dirstream); +keep_fd: + fd = -1; + dirstream = NULL; free(dupdname); free(dupvname); dupdname = NULL; dupvname = NULL; cnt++; - if (cnt < argc) { - close_file_or_dir(fd, dirstream); - /* avoid double free */ - fd = -1; - dirstream = NULL; + if (cnt < argc) goto again; - } - if (commit_mode == COMMIT_AFTER && fd != -1) { - res = wait_for_commit(fd); - if (res < 0) { - error("unable to do final sync after deletion: %s", - strerror(errno)); - ret = 1; + if (commit_mode == COMMIT_AFTER) { + int slot; + + /* + * Traverse seen_fsid_hash and issue SYNC ioctl on each + * filesystem + */ + for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) { + struct seen_fsid *seen = seen_fsid_hash[slot]; + + while (seen) { + res = wait_for_commit(seen->fd); + if (res < 0) { + uuid_unparse(seen->fsid, uuidbuf); + error( + "unable to do final sync after deletion: %s, fsid: %s", + strerror(errno), uuidbuf); + ret = 1; + } else if (verbose > 0) { + uuid_unparse(seen->fsid, uuidbuf); + printf("final sync is done for fsid: %s\n", + uuidbuf); + } + seen = seen->next; + } } + /* fd will also be closed in free_seen_fsid */ + free_seen_fsid(seen_fsid_hash); } - close_file_or_dir(fd, dirstream); return ret; } -- cgit v1.2.3