summaryrefslogtreecommitdiff
path: root/cmds-subvolume.c
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.cz>2013-12-12 19:11:26 +0100
committerChris Mason <clm@fb.com>2014-01-31 08:22:20 -0800
commit4b7656f3bf4bb204d567375eb6d43317521543fc (patch)
treeb4e895ab26929e0782d686d2593516ca3ef56730 /cmds-subvolume.c
parent91bc44bd073020c27487b22ccf53a37d593d331e (diff)
downloadbtrfs-progs-4b7656f3bf4bb204d567375eb6d43317521543fc.tar.gz
btrfs-progs: add options to set commit mode after subvol delete
Subvolume deletion does not do a full transaction commit. This can lead to an unexpected result when the system crashes between deletion and commit, the subvolume directory will appear again. Add options to request filesystem sync after each deleted subvolume or after the last one. If the command with --commit option finishes succesfully, the subvolume(s) deletion status is safely stored on the media. Userspace approach is more flexible than in-kernel. Related discussions: http://www.spinics.net/lists/linux-btrfs/msg22088.html http://www.spinics.net/lists/linux-btrfs/msg27240.html CC: Alex Lyakas <alex.btrfs@zadarastorage.com> Signed-off-by: David Sterba <dsterba@suse.cz> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'cmds-subvolume.c')
-rw-r--r--cmds-subvolume.c86
1 files changed, 80 insertions, 6 deletions
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 255f028..38229d0 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -201,25 +201,78 @@ int test_issubvolume(char *path)
return (st.st_ino == 256) && S_ISDIR(st.st_mode);
}
+static int wait_for_commit(int fd)
+{
+ int ret;
+
+ ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
+ if (ret < 0)
+ return ret;
+ return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
+}
+
static const char * const cmd_subvol_delete_usage[] = {
- "btrfs subvolume delete <subvolume> [<subvolume>...]",
+ "btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
"Delete subvolume(s)",
+ "Delete subvolumes from the filesystem. The corresponding directory",
+ "is removed instantly but the data blocks are removed later.",
+ "The deletion does not involve full commit by default due to",
+ "performance reasons (as a consequence, the subvolume may appear again",
+ "after a crash). Use one of the --commit options to wait until the",
+ "operation is safely stored on the media.",
+ "",
+ "-c|--commit-after wait for transaction commit at the end of the operation",
+ "-C|--commit-each wait for transaction commit after deleting each subvolume",
NULL
};
static int cmd_subvol_delete(int argc, char **argv)
{
- int res, fd, len, e, cnt = 1, ret = 0;
+ int res, len, e, ret = 0;
+ int cnt;
+ int fd = -1;
struct btrfs_ioctl_vol_args args;
char *dname, *vname, *cpath;
char *dupdname = NULL;
char *dupvname = NULL;
char *path;
DIR *dirstream = NULL;
+ int sync_mode = 0;
+ struct option long_options[] = {
+ {"commit-after", no_argument, NULL, 'c'}, /* sync mode 1 */
+ {"commit-each", no_argument, NULL, 'C'}, /* sync mode 2 */
+ {NULL, 0, NULL, 0}
+ };
+
+ optind = 1;
+ while (1) {
+ int c;
+
+ c = getopt_long(argc, argv, "cC", long_options, NULL);
+ if (c < 0)
+ break;
+
+ switch(c) {
+ case 'c':
+ sync_mode = 1;
+ break;
+ case 'C':
+ sync_mode = 2;
+ break;
+ default:
+ usage(cmd_subvol_delete_usage);
+ }
+ }
- if (argc < 2)
+ if (check_argc_min(argc - optind, 1))
usage(cmd_subvol_delete_usage);
+ printf("Transaction commit: %s\n",
+ !sync_mode ? "none (default)" :
+ sync_mode == 1 ? "at the end" : "after each");
+
+ cnt = optind;
+
again:
path = argv[cnt];
@@ -276,8 +329,6 @@ again:
res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
e = errno;
- close_file_or_dir(fd, dirstream);
-
if(res < 0 ){
fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n",
dname, vname, strerror(e));
@@ -285,14 +336,37 @@ again:
goto out;
}
+ if (sync_mode == 1) {
+ res = wait_for_commit(fd);
+ if (res < 0) {
+ fprintf(stderr,
+ "ERROR: unable to wait for commit after '%s': %s\n",
+ path, strerror(errno));
+ ret = 1;
+ }
+ }
+
out:
free(dupdname);
free(dupvname);
dupdname = NULL;
dupvname = NULL;
cnt++;
- if (cnt < argc)
+ if (cnt < argc) {
+ close_file_or_dir(fd, dirstream);
goto again;
+ }
+
+ if (sync_mode == 2 && fd != -1) {
+ res = wait_for_commit(fd);
+ if (res < 0) {
+ fprintf(stderr,
+ "ERROR: unable to do final sync: %s\n",
+ strerror(errno));
+ ret = 1;
+ }
+ }
+ close_file_or_dir(fd, dirstream);
return ret;
}