Add a '-R' switch to btrfs subvolume snapshot to allow a recursively
subvolume snapshotting.

Signed-off-by: Goffredo Baroncelli <kreij...@inwind.it>
---
 cmds-subvolume.c | 213 +++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 167 insertions(+), 46 deletions(-)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 422e1fc..eb14ecd 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -567,14 +567,162 @@ out:
        return !!ret;
 }
 
+static int do_snapshot(char *subvol, char *dstdir, char *newname,
+                       int readonly,
+                       struct btrfs_qgroup_inherit *inherit)
+{
+
+       int fd = -1, fddst = -1;
+       int res, retval = -1;
+       struct btrfs_ioctl_vol_args_v2  args = {0};
+       DIR *dirstream1 = NULL, *dirstream2 = NULL;
+
+       fddst = open_file_or_dir(dstdir, &dirstream1);
+       if (fddst < 0) {
+               fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
+               goto out;
+       }
+
+       fd = open_file_or_dir(subvol, &dirstream2);
+       if (fd < 0) {
+               fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
+               goto out;
+       }
+
+       if (readonly) {
+               args.flags |= BTRFS_SUBVOL_RDONLY;
+               printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
+                      subvol, dstdir, newname);
+       } else {
+               printf("Create a snapshot of '%s' in '%s/%s'\n",
+                      subvol, dstdir, newname);
+       }
+
+       args.fd = fd;
+       if (inherit) {
+               args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+               args.size = qgroup_inherit_size(inherit);
+               args.qgroup_inherit = inherit;
+       }
+       strncpy_null(args.name, newname);
+
+       res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
+
+       if (res < 0) {
+               fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
+                       subvol, strerror(errno));
+               goto out;
+       }
+
+       retval = 0;     /* success */
+
+out:
+       close_file_or_dir(fddst, dirstream1);
+       close_file_or_dir(fd, dirstream2);
+
+       return retval;
+}
+
+
+static int cleanup_subvol_dir(char *sv_dst, char *sv_newname)
+{
+       char *path = pathjoin(sv_dst, sv_newname, NULL);
+       int ret;
+
+       if (!path)
+               return -1;
+
+       ret = rmdir(path);
+       free(path);
+
+       return ret;
+}
+
+struct rec_snapshot {
+       char *dstdir;
+       char *src;
+       char *newname;
+       int readonly:1;
+       int first:1;
+       struct btrfs_qgroup_inherit *inherit;
+};
+
+static int recursively_snapshot_func(char *real_root, char *relative_root,
+                                    char *path, void *data)
+{
+
+       struct rec_snapshot     *rs = (struct rec_snapshot*)data;
+       char *sv_src = NULL;
+       char *sv_dst = NULL;
+       char *sv_newname = NULL;
+       int  ret=-1;
+
+       sv_src = pathjoin(rs->src, path, NULL);
+       sv_dst = pathjoin(rs->dstdir, rs->newname, path, NULL);
+
+       if (!sv_src || !sv_dst) {
+               free(sv_src);
+               free(sv_dst);
+               fprintf(stderr, "ERROR: not enough memory\n");
+               goto out;
+       }
+
+       sv_newname = strrchr(sv_dst, '/');
+       *sv_newname++ = 0;
+
+       if (!rs->first) {
+               ret = cleanup_subvol_dir(sv_dst, sv_newname);
+               if (ret) {
+                       fprintf(stderr, "ERROR: cannot delete %s/%s\n",
+                               sv_dst, sv_newname);
+                       goto out;
+               }
+       }
+       rs->first = 0;
+
+       ret = do_snapshot(sv_src, sv_dst, sv_newname,
+                               rs->readonly, rs->inherit);
+
+out:
+       free(sv_src);
+       free(sv_dst);
+
+       return ret;
+}
+
+static int recursively_snapshot(char *src, char *dstdir, char *newname,
+                               int readonly,
+                               struct btrfs_qgroup_inherit *inherit)
+{
+
+       struct rec_snapshot rs = {
+               .dstdir         = dstdir,
+               .src            = src,
+               .newname        = newname,
+               .readonly       = readonly,
+               .inherit        = inherit,
+               .first          = 1
+       };
+
+       int ret;
+
+       ret = traverse_list_subvol_rec(src, 0, 0, recursively_snapshot_func,
+                                      (void *)&rs);
+
+       return ret;
+
+}
+
 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>] <source> 
<dest>|[<dest>/]<name>",
+       "btrfs subvolume snapshot [-R][-i <qgroupid>] <source> 
<dest>|[<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,",
        "the subvolume will be named the basename of <source>.",
        "",
+       "-R             create snapshot recursively",
        "-r             create a readonly snapshot",
        "-i <qgroupid>  add the newly created snapshot to a qgroup. This",
        "               option can be given multiple times.",
@@ -585,20 +733,18 @@ static int cmd_snapshot(int argc, char **argv)
 {
        char    *subvol, *dst;
        int     res, retval;
-       int     fd = -1, fddst = -1;
-       int     len, readonly = 0;
+       int     len, readonly = 0, rec=0;
        char    *dupname = NULL;
        char    *dupdir = NULL;
        char    *newname;
        char    *dstdir;
        struct btrfs_ioctl_vol_args_v2  args;
        struct btrfs_qgroup_inherit *inherit = NULL;
-       DIR *dirstream1 = NULL, *dirstream2 = NULL;
 
        optind = 1;
        memset(&args, 0, sizeof(args));
        while (1) {
-               int c = getopt(argc, argv, "c:i:r");
+               int c = getopt(argc, argv, "c:i:rR");
                if (c < 0)
                        break;
 
@@ -620,6 +766,9 @@ static int cmd_snapshot(int argc, char **argv)
                case 'r':
                        readonly = 1;
                        break;
+               case 'R':
+                       rec = 1;
+                       break;
                case 'x':
                        res = qgroup_inherit_add_copy(&inherit, optarg, 1);
                        if (res) {
@@ -635,10 +784,17 @@ static int cmd_snapshot(int argc, char **argv)
        if (check_argc_exact(argc - optind, 2))
                usage(cmd_snapshot_usage);
 
+       retval = 1;     /* failure */
+
+       if (rec && readonly) {
+               fprintf(stderr, "ERROR: impossible to make a recursively "
+                               "readonly snapshot\n");
+               goto out;
+       }
+
        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);
@@ -680,48 +836,13 @@ static int cmd_snapshot(int argc, char **argv)
                goto out;
        }
 
-       fddst = open_file_or_dir(dstdir, &dirstream1);
-       if (fddst < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-               goto out;
-       }
-
-       fd = open_file_or_dir(subvol, &dirstream2);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
-               goto out;
-       }
-
-       if (readonly) {
-               args.flags |= BTRFS_SUBVOL_RDONLY;
-               printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
-                      subvol, dstdir, newname);
-       } else {
-               printf("Create a snapshot of '%s' in '%s/%s'\n",
-                      subvol, dstdir, newname);
-       }
-
-       args.fd = fd;
-       if (inherit) {
-               args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
-               args.size = qgroup_inherit_size(inherit);
-               args.qgroup_inherit = inherit;
-       }
-       strncpy_null(args.name, newname);
-
-       res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
-
-       if (res < 0) {
-               fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n",
-                       subvol, strerror(errno));
-               goto out;
-       }
-
-       retval = 0;     /* success */
+       if (rec)
+               retval = recursively_snapshot(subvol, dstdir, newname,
+                                          readonly, inherit);
+       else
+               retval = do_snapshot(subvol, dstdir, newname, readonly,inherit);
 
 out:
-       close_file_or_dir(fddst, dirstream1);
-       close_file_or_dir(fd, dirstream2);
        free(inherit);
        free(dupname);
        free(dupdir);
-- 
1.8.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to