Jeff Mahoney
2014-Mar-24 23:58 UTC
[PATCH] btrfs-progs: allow use of subvolume id to create snapshots
This patch uses the new BTRFS_SUBVOL_CREATE_SUBVOLID flag to create snapshots by subvolume ID. usage: btrfs subvolume snapshot [-r] [-q <qgroupid>] -s <subvolid> <dest>/<name> Since we don't have a name for the source snapshot, the complete path to the destination must be specified. Signed-off-by: Jeff Mahoney <jeffm@suse.com> --- cmds-subvolume.c | 101 +++++++++++++++++++++++++++++++++++++++++-------------- ioctl.h | 6 ++- man/btrfs.8.in | 31 +++++++++++++++- 3 files changed, 110 insertions(+), 28 deletions(-) --- a/cmds-subvolume.c +++ b/cmds-subvolume.c @@ -14,6 +14,7 @@ * Boston, MA 021110-1307, USA. */ +#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -576,6 +577,7 @@ out: static const char * const cmd_snapshot_usage[] = { "btrfs subvolume snapshot [-r] <source> <dest>|[<dest>/]<name>", "btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>", + "btrfs subvolume snapshot [-r] [-i <qgroupid>] -s <subvolid> <dest>/<name>", "Create a snapshot of the subvolume", "Create a writable/readonly snapshot of the subvolume <source> with", "the name <name> in the <dest> directory. If only <dest> is given,", @@ -584,12 +586,27 @@ static const char * const cmd_snapshot_u "-r create a readonly snapshot", "-i <qgroupid> add the newly created snapshot to a qgroup. This", " option can be given multiple times.", + "-s <subvolid> create a snapshot using the subvolume id. This", + " is useful for snapshotting subvolumes outside", + " of the mounted namespace.", NULL }; +static int get_subvolid(const char *str, u64 *subvolid) +{ + char *p; + errno = 0; + *subvolid = strtoull(optarg, &p, 10); + if (errno || *p != '\0') { + fprintf(stderr, "ERROR: invalid subvolume id '%s'\n", optarg); + return -EINVAL; + } + return 0; +} + static int cmd_snapshot(int argc, char **argv) { - char *subvol, *dst; + char *subvol = NULL, *dst; int res, retval; int fd = -1, fddst = -1; int len, readonly = 0; @@ -597,6 +614,9 @@ static int cmd_snapshot(int argc, char * char *dupdir = NULL; char *newname; char *dstdir; + u64 subvolid = 0; + char *subvol_descr = NULL; + int nargs = 2; struct btrfs_ioctl_vol_args_v2 args; struct btrfs_qgroup_inherit *inherit = NULL; DIR *dirstream1 = NULL, *dirstream2 = NULL; @@ -604,7 +624,7 @@ static int cmd_snapshot(int argc, char * optind = 1; memset(&args, 0, sizeof(args)); while (1) { - int c = getopt(argc, argv, "c:i:r"); + int c = getopt(argc, argv, "c:i:rs:"); if (c < 0) break; @@ -633,27 +653,39 @@ static int cmd_snapshot(int argc, char * goto out; } break; + case 's': + res = get_subvolid(optarg, &subvolid); + if (res) { + retval = res; + goto out; + } + nargs = 1; + break; default: usage(cmd_snapshot_usage); } } - if (check_argc_exact(argc - optind, 2)) + if (check_argc_exact(argc - optind, nargs)) usage(cmd_snapshot_usage); - subvol = argv[optind]; - dst = argv[optind + 1]; + if (nargs == 2) { + subvol = argv[optind]; + dst = argv[optind + 1]; - retval = 1; /* failure */ - res = test_issubvolume(subvol); - if (res < 0) { - fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); - goto out; - } - if (!res) { - fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); - goto out; - } + retval = 1; /* failure */ + res = test_issubvolume(subvol); + if (res < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + goto out; + } + if (!res) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", + subvol); + goto out; + } + } else + dst = argv[optind]; res = test_isdir(dst); if (res == 0) { @@ -662,6 +694,13 @@ static int cmd_snapshot(int argc, char * } if (res > 0) { + if (!subvol) { + retval = 1; + fprintf(stderr, + "ERROR: '%s' exists and must not when snapshotting by specifying subvolid.\n", + dst); + goto out; + } dupname = strdup(subvol); newname = basename(dupname); dstdir = dst; @@ -692,22 +731,34 @@ static int cmd_snapshot(int argc, char * goto out; } - fd = open_file_or_dir(subvol, &dirstream2); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", dstdir); + if (subvol) { + fd = open_file_or_dir(subvol, &dirstream2); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", dstdir); + goto out; + } + args.fd = fd; + res = asprintf(&subvol_descr, "'%s'", subvol); + } else { + args.subvolid = subvolid; + args.flags |= BTRFS_SUBVOL_CREATE_SUBVOLID; + res = asprintf(&subvol_descr, "subvolume id %llu", subvolid); + } + if (res < 0) { + fprintf(stderr, "ERROR: can't allocate memory\n"); + retval = 1; goto out; } if (readonly) { args.flags |= BTRFS_SUBVOL_RDONLY; - printf("Create a readonly snapshot of '%s' in '%s/%s'\n", - subvol, dstdir, newname); + printf("Create a readonly snapshot of %s in '%s/%s'\n", + subvol_descr, dstdir, newname); } else { - printf("Create a snapshot of '%s' in '%s/%s'\n", - subvol, dstdir, newname); + printf("Create a snapshot of %s in '%s/%s'\n", + subvol_descr, dstdir, newname); } - args.fd = fd; if (inherit) { args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT; args.size = qgroup_inherit_size(inherit); @@ -727,7 +778,9 @@ static int cmd_snapshot(int argc, char * out: close_file_or_dir(fddst, dirstream1); - close_file_or_dir(fd, dirstream2); + if (subvol) + close_file_or_dir(fd, dirstream2); + free(subvol_descr); free(inherit); free(dupname); free(dupdir); --- a/ioctl.h +++ b/ioctl.h @@ -41,6 +41,7 @@ struct btrfs_ioctl_vol_args { #define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0) #define BTRFS_SUBVOL_RDONLY (1ULL << 1) #define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2) +#define BTRFS_SUBVOL_CREATE_SUBVOLID (1ULL << 3) #define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) @@ -69,7 +70,10 @@ struct btrfs_ioctl_qgroup_limit_args { #define BTRFS_SUBVOL_NAME_MAX 4039 struct btrfs_ioctl_vol_args_v2 { - __s64 fd; + union { + __s64 fd; + __u64 subvolid; + }; __u64 transid; __u64 flags; union { --- a/man/btrfs.8.in +++ b/man/btrfs.8.in @@ -12,7 +12,9 @@ btrfs \- control a btrfs filesystem .PP \fBbtrfs\fP \fBsubvolume list\fP [\fIoptions\fP] [-G [+|-]\fIvalue\fP] [-C [+|-]\fIvalue\fP] [--sort=rootid,gen,ogen,path] \fI<path>\fP .PP -\fBbtrfs\fP \fBsubvolume snapshot\fP [-r] \fI<source>\fP \fI<dest>\fP|[\fI<dest>\fP/]\fI<name>\fP +\fBbtrfs\fP \fBsubvolume snapshot\fP [-r] [-i qgroupid] \fI<source>\fP \fI<dest>\fP|[\fI<dest>\fP/]\fI<name>\fP +.PP +\fBbtrfs\fP \fBsubvolume snapshot\fP [-r] [-i qgroupid] \fI-s <subvolid>\fP \fI<dest>/<name>\fP .PP \fBbtrfs\fP \fBsubvolume get-default\fP\fI <path>\fP .PP @@ -242,12 +244,35 @@ for \fB--sort\fP you can combine some it .RE .TP -\fBsubvolume snapshot\fP [-r] \fI<source>\fP \fI<dest>\fP|[\fI<dest>\fP/]\fI<name>\fP +\fBsubvolume snapshot\fP [-r] [-i qgroupid] \fI<source>\fP \fI<dest>\fP|[\fI<dest>\fP/]\fI<name>\fP Create a writable/readonly snapshot of the subvolume \fI<source>\fR with the name \fI<name>\fR in the \fI<dest>\fR directory. If only \fI<dest>\fR is given, the subvolume will be named the basename of \fI<source>\fR. If \fI<source>\fR is not a subvolume, \fBbtrfs\fR returns an error. -If \fI-r\fR is given, the snapshot will be readonly. +.RS + +\fIOptions\fP +.IP \fB-r\fP 5 +The newly created snapshot will be readonly. +.IP "\fB-i\fP \fI<qgroupid>\fR" 5 +Add the newly created subvolume to a qgroup. This option can be given multiple +times. +.RE +.TP + +\fBsubvolume snapshot\fP [-r] [-i qgroupid] \fI-s <subvolid>\fP \fI<dest>\fP/\fI<name>\fP +Create a writable/readonly snapshot of the subvolume \fI<subvolid>\fR with the +name \fI<name>\fR in the \fI<dest>\fR directory. +If \fI<subvolid>\fR does not refer to a subvolume, \fBbtrfs\fR returns an error. +.RS + +\fIOptions\fP +.IP \fB-r\fP 5 +The newly created snapshot will be readonly. +.IP "\fB-i\fP \fI<qgroupid>\fR" 5 +Add the newly created subvolume to a qgroup. This option can be given multiple +times. +.RE .TP \fBsubvolume get-default\fR\fI <path>\fR -- Jeff Mahoney SUSE Labs -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html