Modify the subvol and snapshot creation ioctls to work anywhere in the tree
authorChris Mason <chris.mason@oracle.com>
Tue, 18 Nov 2008 16:26:35 +0000 (11:26 -0500)
committerChris Mason <chris.mason@oracle.com>
Tue, 18 Nov 2008 16:26:35 +0000 (11:26 -0500)
This changes the snapshot and subvol ioctl API and command lines so
that new snapshots and subvols can be created anywhere.

Subvolume creation hasn't changed much:

btrfsctl -S subvol_name directory

This creates a new subvolume under 'directory'

Snapshot creation looks the same, but is actually different:

btrfsctl -s full_path_to_new_snapshot file_or_dir

For example: btrfsctl -s /mnt/new_snap /mnt/subvol

Will create a new snapshot named new_snap under /mnt of the root
found in /mnt/subvol.  It always snapshots the entire root regardless of
which file or directory inside the root you give it.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
btrfsctl.c
ioctl.h

index 82c5f30..e049799 100644 (file)
@@ -28,6 +28,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <dirent.h>
+#include <libgen.h>
 #include "kerncompat.h"
 #include "ctree.h"
 #include "transaction.h"
@@ -48,8 +49,8 @@ void print_usage(void)
        printf("                [-r size] [-A device] [-a] [-c]\n");
        printf("\t-d filename: defragments one file\n");
        printf("\t-d directory: defragments the entire Btree\n");
-       printf("\t-s snap_name: existing_subvol creates a new snapshot\n");
-       printf("\t-s snap_name: tree_root creates a new subvolume\n");
+       printf("\t-s snap_name dir: creates a new snapshot of dir\n");
+       printf("\t-S subvol_name dir: creates a new subvolume\n");
        printf("\t-r [+-]size[gkm]: resize the FS by size amount\n");
        printf("\t-A device: scans the device file for a Btrfs filesystem\n");
        printf("\t-a: scans all devices for Btrfs filesystems\n");
@@ -58,18 +59,47 @@ void print_usage(void)
        exit(1);
 }
 
+int open_file_or_dir(char *fname)
+{
+       int ret;
+       struct stat st;
+       DIR *dirstream;
+       int fd;
+
+       ret = stat(fname, &st);
+       if (ret < 0) {
+               perror("stat:");
+               exit(1);
+       }
+       if (S_ISDIR(st.st_mode)) {
+               dirstream = opendir(fname);
+               if (!dirstream) {
+                       perror("opendir");
+                       exit(1);
+               }
+               fd = dirfd(dirstream);
+       } else {
+               fd = open(fname, O_RDWR);
+       }
+       if (fd < 0) {
+               perror("open");
+               exit(1);
+       }
+       return fd;
+}
 int main(int ac, char **av)
 {
-       char *fname;
+       char *fname = NULL;
+       char *snap_location = NULL;
+       int snap_fd = 0;
        int fd;
        int ret;
        struct btrfs_ioctl_vol_args args;
        char *name = NULL;
        int i;
-       struct stat st;
-       DIR *dirstream;
        unsigned long command = 0;
        int len;
+       char *fullpath;
 
        if (ac == 2 && strcmp(av[1], "-a") == 0) {
                fprintf(stderr, "Scanning for Btrfs filesystems\n");
@@ -82,8 +112,17 @@ int main(int ac, char **av)
                                fprintf(stderr, "-s requires an arg");
                                print_usage();
                        }
-                       name = av[i + 1];
+                       fullpath = av[i + 1];
+
+                       snap_location = strdup(fullpath);
+                       snap_location = dirname(snap_location);
+
+                       snap_fd = open_file_or_dir(snap_location);
+
+                       name = strdup(fullpath);
+                       name = basename(name);
                        len = strlen(name);
+
                        if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
                                fprintf(stderr,
                                     "snapshot name zero length or too long\n");
@@ -95,6 +134,24 @@ int main(int ac, char **av)
                                exit(1);
                        }
                        command = BTRFS_IOC_SNAP_CREATE;
+               } else if (strcmp(av[i], "-S") == 0) {
+                       if (i + 1 >= ac - 1) {
+                               fprintf(stderr, "-S requires an arg");
+                               print_usage();
+                       }
+                       name = av[i + 1];
+                       len = strlen(name);
+                       if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
+                               fprintf(stderr,
+                                    "snapshot name zero length or too long\n");
+                               exit(1);
+                       }
+                       if (strchr(name, '/')) {
+                               fprintf(stderr,
+                                       "error: / not allowed in names\n");
+                               exit(1);
+                       }
+                       command = BTRFS_IOC_SUBVOL_CREATE;
                } else if (strcmp(av[i], "-d") == 0) {
                        if (i >= ac - 1) {
                                fprintf(stderr, "-d requires an arg\n");
@@ -129,33 +186,24 @@ int main(int ac, char **av)
                exit(1);
        }
        fname = av[ac - 1];
-       ret = stat(fname, &st);
-       if (ret < 0) {
-               perror("stat:");
-               exit(1);
-       }
-       if (S_ISDIR(st.st_mode)) {
-               dirstream = opendir(fname);
-               if (!dirstream) {
-                       perror("opendir");
-                       exit(1);
-               }
-               fd = dirfd(dirstream);
-       } else if (command == BTRFS_IOC_SCAN_DEV) {
+
+       if (command == BTRFS_IOC_SCAN_DEV) {
                fd = open("/dev/btrfs-control", O_RDWR);
                name = fname;
-       } else {
-               fd = open(fname, O_RDWR);
-       }
-       if (fd < 0) {
-               perror("open");
-               exit(1);
-       }
+        } else {
+               fd = open_file_or_dir(fname);
+        }
+
        if (name)
                strcpy(args.name, name);
        else
                args.name[0] = '\0';
-       ret = ioctl(fd, command, &args);
+
+       if (command == BTRFS_IOC_SNAP_CREATE) {
+               args.fd = fd;
+               ret = ioctl(snap_fd, command, &args);
+       } else
+               ret = ioctl(fd, command, &args);
        if (ret < 0) {
                perror("ioctl:");
                exit(1);
diff --git a/ioctl.h b/ioctl.h
index 85ed35a..5fb2ad1 100644 (file)
--- a/ioctl.h
+++ b/ioctl.h
 
 #ifndef __IOCTL_
 #define __IOCTL_
+#include <asm/types.h>
 #include <linux/ioctl.h>
 
 #define BTRFS_IOCTL_MAGIC 0x94
 #define BTRFS_VOL_NAME_MAX 255
-#define BTRFS_PATH_NAME_MAX 4095
+#define BTRFS_PATH_NAME_MAX 3072
 
 struct btrfs_ioctl_vol_args {
+       __s64 fd;
        char name[BTRFS_PATH_NAME_MAX + 1];
 };
 
@@ -51,5 +53,7 @@ struct btrfs_ioctl_vol_args {
                                   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_BALANCE _IOW(BTRFS_IOCTL_MAGIC, 12, \
                                   struct btrfs_ioctl_vol_args)
-
+/* 13 is for CLONE_RANGE */
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
+                                  struct btrfs_ioctl_vol_args)
 #endif