Goffredo Baroncelli
2010-Feb-17 20:02 UTC
[PATCH 1/2 V2] btrfs, a new tool to manage a btrfs filesystem; source code
This is the source code of btrfs.c. The main changes from my previous patch are: - Rearranged the short command in order to avoid conflicts - Renamed the ''create'' verb is ''subvolume'' - Rearranged the parsing code, which now is integrated in the help in order to avoid mismatch BR G.Baroncelli diff --git a/Makefile b/Makefile index 02f881e..888ef8d 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ bindir = $(prefix)/bin LIBS=-luuid progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \ - btrfs-map-logical + btrfs-map-logical btrfs # make C=1 to enable sparse ifdef C @@ -36,6 +36,9 @@ all: version $(progs) manpages version: bash version.sh +btrfs: $(objects) btrfs.o + gcc $(CFLAGS) -o btrfs btrfs.o $(objects) $(LDFLAGS) $(LIBS) + btrfsctl: $(objects) btrfsctl.o gcc $(CFLAGS) -o btrfsctl btrfsctl.o $(objects) $(LDFLAGS) $(LIBS) diff --git a/btrfs.c b/btrfs.c new file mode 100644 index 0000000..cc55599 --- /dev/null +++ b/btrfs.c @@ -0,0 +1,775 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgen.h> +#include <limits.h> +#include <uuid/uuid.h> + +#undef ULONG_MAX + +#include "kerncompat.h" +#include "ctree.h" +#include "transaction.h" +#include "utils.h" +#include "version.h" +#include "ioctl.h" +#include "volumes.h" + +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +#define BTRFS_IOC_SNAP_CREATE 0 +#define BTRFS_VOL_NAME_MAX 255 +struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; }; +static inline int ioctl(int fd, int define, void *arg) { return 0; } +#endif + +/* + * test if path is a subvolume: + * this function return + * 0-> path exists but it is not a subvolume + * 1-> path exists and it is a subvolume + * -1 -> path is unaccessible + */ +static int test_issubvolume(char *path) +{ + + struct stat st; + int res; + + res = stat(path, &st); + if(res < 0 ) + return -1; + + return (st.st_ino == 256) && S_ISDIR(st.st_mode); + +} + +/* + * test if path is a directory + * this function return + * 0-> path exists but it is not a directory + * 1-> path exists and it is a directory + * -1 -> path is unaccessible + */ +static int test_isdir(char *path) +{ + struct stat st; + int res; + + res = stat(path, &st); + if(res < 0 ) + return -1; + + return S_ISDIR(st.st_mode); + +} + +static int open_file_or_dir(const char *fname) +{ + int ret; + struct stat st; + DIR *dirstream; + int fd; + + ret = stat(fname, &st); + if (ret < 0) { + return -1; + } + if (S_ISDIR(st.st_mode)) { + dirstream = opendir(fname); + if (!dirstream) { + return -2; + } + fd = dirfd(dirstream); + } else { + fd = open(fname, O_RDWR); + } + if (fd < 0) { + return -3; + } + return fd; +} + +static int do_clone(char *subvol, char *dst) +{ + int res, fd, fddst; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args args; + + res = test_issubvolume(subvol); + if(res<0){ + fprintf(stderr, "ERROR: error accessing ''%s''\n", subvol); + return 12; + } + if(!res){ + fprintf(stderr, "ERROR: ''%s'' is not a subvolume\n", subvol); + return 13; + } + + res = test_isdir(dst); + if(res == 0 ){ + fprintf(stderr, "ERROR: ''%s'' exists and it is not a directory\n", dst); + return 12; + } + + if(res>0){ + newname = strdup(subvol); + newname = basename(newname); + dstdir = dst; + }else{ + newname = strdup(dst); + newname = basename(newname); + dstdir = strdup(dst); + dstdir = dirname(dstdir); + } + + if( !strcmp(newname,".") || !strcmp(newname,"..") || + strchr(newname, ''/'') ){ + fprintf(stderr, "ERROR: uncorrect snapshot name (''%s'')\n", + newname); + return 14; + } + + fddst = open_file_or_dir(dstdir); + if (fddst < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", dstdir); + return 12; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + close(fddst); + fprintf(stderr, "ERROR: can''t access to ''%s''\n", dstdir); + return 12; + } + + printf("Create a snapshot of ''%s'' in ''%s/%s''\n", + subvol, dstdir, newname); + args.fd = fd; + strcpy(args.name, newname); + res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args); + + close(fd); + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot snapshot ''%s''\n",subvol); + return 11; + } + + return 0; + +} + +static int do_delete_subvolume(char *path) +{ + int res, fd; + struct btrfs_ioctl_vol_args args; + char *dname, *vname, *cpath; + + res = test_issubvolume(path); + if(res<0){ + fprintf(stderr, "ERROR: error accessing ''%s''\n", path); + return 12; + } + if(!res){ + fprintf(stderr, "ERROR: ''%s'' is not a subvolume\n", path); + return 13; + } + + cpath = realpath(path, 0); + dname = strdup(cpath); + dname = dirname(dname); + vname = strdup(cpath); + vname = basename(vname); + free(cpath); + + if( !strcmp(vname,".") || !strcmp(vname,"..") || + strchr(vname, ''/'') ){ + fprintf(stderr, "ERROR: uncorrect subvolume name (''%s'')\n", + vname); + return 14; + } + + fd = open_file_or_dir(dname); + if (fd < 0) { + close(fd); + fprintf(stderr, "ERROR: can''t access to ''%s''\n", dname); + return 12; + } + + printf("Delete subvolume ''%s/%s''\n", dname, vname); + strcpy(args.name, vname); + res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + + close(fd); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot delete ''%s/%s''\n",dname, vname); + return 11; + } + + return 0; + +} + +static int do_create_subvol(char *dst) +{ + int res, fddst; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args args; + + res = test_isdir(dst); + if(res >= 0 ){ + fprintf(stderr, "ERROR: ''%s'' exists\n", dst); + return 12; + } + + newname = strdup(dst); + newname = basename(newname); + dstdir = strdup(dst); + dstdir = dirname(dstdir); + + if( !strcmp(newname,".") || !strcmp(newname,"..") || + strchr(newname, ''/'') ){ + fprintf(stderr, "ERROR: uncorrect subvolume name (''%s'')\n", + newname); + return 14; + } + + fddst = open_file_or_dir(dstdir); + if (fddst < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", dstdir); + return 12; + } + + printf("Create subvolume ''%s/%s''\n", dstdir, newname); + strcpy(args.name, newname); + res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); + + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot create subvolume\n"); + return 11; + } + + return 0; + +} + +static int do_fssync(char *path) +{ + int fd, res; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", path); + return 12; + } + + printf("FSSync ''%s''\n", path); + res = ioctl(fd, BTRFS_IOC_SYNC); + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to fs-syncing ''%s''\n", path); + return 16; + } + + return 0; +} + +static int do_scan(int nargs, char **argv) +{ + int i, fd; + if(!nargs){ + int ret; + + printf("Scanning for Btrfs filesystems\n"); + ret = btrfs_scan_one_dir("/dev", 1); + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + return 0; + } + + fd = open("/dev/btrfs-control", O_RDWR); + if (fd < 0) { + perror("failed to open /dev/btrfs-control"); + return 10; + } + + for( i = 0 ; i < nargs ; i++ ){ + struct btrfs_ioctl_vol_args args; + int ret; + + printf("Scanning for Btrfs filesystems in ''%s''\n", argv[i]); + + strcpy(args.name, argv[i]); + /* + * FIXME: which are the error code returned by this ioctl ? + * it seems that is impossible to undetand if there no is + * a btrfs filesystem from an I/O error !!! + */ + ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); + + if( ret < 0 ){ + close(fd); + fprintf(stderr, "ERROR: unable to scan the device ''%s''\n", argv[i]); + return 11; + } + } + + close(fd); + return 0; + +} + +static int do_defrag(int argc, char **argv) +{ + + int i, ret=0; + + for(i=0 ; i <argc ; i++){ + int fd, res; + char *path = argv[i]; + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", path); + ret++; + continue; + } + + printf("Defrag ''%s''\n", path); + res = ioctl(fd, BTRFS_IOC_DEFRAG); + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to defrag ''%s''\n", argv[i]); + ret++; + continue; + } + } + + /* + * the return code is 0 (success) or the number of the failure + 20 + */ + if(ret) + ret+=20; + return ret; +} + +static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) +{ + struct list_head *cur; + struct btrfs_device *device; + + list_for_each(cur, &fs_devices->devices) { + device = list_entry(cur, struct btrfs_device, dev_list); + if ((device->label && strcmp(device->label, search) == 0) || + strcmp(device->name, search) == 0) + return 1; + } + return 0; +} + +static void print_one_uuid(struct btrfs_fs_devices *fs_devices) +{ + char uuidbuf[37]; + struct list_head *cur; + struct btrfs_device *device; + char *super_bytes_used; + u64 devs_found = 0; + u64 total; + + uuid_unparse(fs_devices->fsid, uuidbuf); + device = list_entry(fs_devices->devices.next, struct btrfs_device, + dev_list); + if (device->label && device->label[0]) + printf("Label: ''%s'' ", device->label); + else + printf("Label: none "); + + super_bytes_used = pretty_sizes(device->super_bytes_used); + + total = device->total_devs; + printf(" uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf, + (unsigned long long)total, super_bytes_used); + + free(super_bytes_used); + + list_for_each(cur, &fs_devices->devices) { + char *total_bytes; + char *bytes_used; + device = list_entry(cur, struct btrfs_device, dev_list); + total_bytes = pretty_sizes(device->total_bytes); + bytes_used = pretty_sizes(device->bytes_used); + printf("\tdevid %4llu size %s used %s path %s\n", + (unsigned long long)device->devid, + total_bytes, bytes_used, device->name); + free(total_bytes); + free(bytes_used); + devs_found++; + } + if (devs_found < total) { + printf("\t*** Some devices missing\n"); + } + printf("\n"); +} + +static int do_show_volume(char *search) +{ + struct list_head *all_uuids; + struct btrfs_fs_devices *fs_devices; + struct list_head *cur_uuid; + int ret; + + ret = btrfs_scan_one_dir("/dev", 0); + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + + all_uuids = btrfs_scanned_uuids(); + list_for_each(cur_uuid, all_uuids) { + fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices, + list); + if (search && uuid_search(fs_devices, search) == 0) + continue; + print_one_uuid(fs_devices); + } + printf("%s\n", BTRFS_BUILD_VERSION); + return 0; +} + +static int do_add_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0; + + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", mntpnt); + return 12; + } + + for(i=0 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args ioctl_args; + int devfd, res; + u64 dev_block_count = 0; + struct stat st; + + devfd = open(args[i], O_RDWR); + if (!devfd) { + fprintf(stderr, "ERROR: Unable to open device ''%s''\n", args[i]); + close(devfd); + ret++; + continue; + } + ret = fstat(devfd, &st); + if (ret) { + fprintf(stderr, "ERROR: Unable to stat ''%s''\n", args[i]); + close(devfd); + ret++; + continue; + } + if (!S_ISBLK(st.st_mode)) { + fprintf(stderr, "ERROR: ''%s'' is not a block device\n", args[i]); + close(devfd); + ret++; + continue; + } + + res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count); + if (ret) { + fprintf(stderr, "ERROR: Unable to init ''%s''\n", args[i]); + close(devfd); + ret++; + continue; + } + close(devfd); + + strcpy(ioctl_args.name, args[i]); + res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); + if(res<0){ + fprintf(stderr, "ERROR: error adding the device ''%s''\n", args[i]); + ret++; + } + + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; + +} + +static int do_balance(char *path) +{ + + int fdmnt, ret=0; + + fdmnt = open_file_or_dir(path); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", path); + return 12; + } + + ret = ioctl(fdmnt, BTRFS_IOC_BALANCE); + close(fdmnt); + if(ret<0){ + fprintf(stderr, "ERROR: balancing ''%s''\n", path); + + return 19; + } + return 0; +} +static int do_remove_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0; + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", mntpnt); + return 12; + } + + for(i=0 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args arg; + int res; + + strcpy(arg.name, args[i]); + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); + if(res<0){ + fprintf(stderr, "ERROR: error removing the device ''%s''\n", args[i]); + ret++; + } + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; +} + +struct Command { + int nargs; /* if == 999, any number of arguments + if >= 0, number of arguments, + if < 0, _minimum_ number of arguments */ + char *verb, *alias; /* verb and alias */ + char *help; /* help lines; form the 2nd onward they are + indented */ +}; + +static struct Command commands[] = { + + /* + avoid short commands different for the case only + */ + { 2, "snapshot", "-s", "[<dest>/]<name>\n" + "Create a writeble snapshot of the subvolume <source> with\n" + "the name <name> in the <dest> directory." + }, + { 1, "delete", "-D","<subvolume>\n" + "Delete the subvolume <subvolume>." + }, + { 1, "subvolume", "-c", "[<dest>/]<name>\n" + "Create a subvolume in <dest> (or the current directory if\n" + "not passed)." + }, + {-1, "defrag", "-f", "<file>|<dir> [<file>|<dir>...]\n" + "Defragment a file or a directory." + }, + { 999, "scan", "-n", "[<device> [<device>..]\n" + "Scan all device for or the passed device for a btrfs\n" + "filesystem." + }, + { 1, "fssync", "-y", "<path>\n" + "Force a fs sync on the filesystem <path>" + }, + { 2, "resize", "-z", "[+/-]<newsize>[gkm]|max <filesystem>\n" + "Resize the file system. If ''max'' is passed, the filesystem\n" + "will occupe all available space on the device." + }, + { 999, "show", "-l", "[<dev>|<label>...]\n" + "Show the btrfs devices" + }, + { 1, "balance", "-b", "<path>\n" + "Balance the chunk across the device" + }, + { -1, "add-dev", "-A", "<dev> [<dev>..] <path>\n" + "Add a device to a filesystem" + }, + { -1, "rm-dev", "-R", "<dev> [<dev>..] <path>\n" + "Remove a device to a filesystem" + }, + /* coming soon + { 2, "label", "-m", "<label> <path>\n" + "Set the label of a filesystem" + } + { 2, "list", "-t", "<path>\n" + "List the snapshot/subvolume of a filesystem" + } + */ + + { 0, 0 , 0 } +}; + +static void help(char *programname) +{ + char *np, *pc; + struct Command *cp; + + np = strrchr(programname,''/''); + if(!np) + np = programname; + else + np++; + + printf("Usage:\n"); + + for( cp = commands; cp->alias || cp->verb; cp++ ){ + /* this syntax allows a command with a verb (or alias) only */ + printf("\t%s %s%s%s ", np, + cp->verb ? cp->verb : "", + cp->verb && cp->alias ? "|": "", + cp->alias ? cp->alias : ""); + + for(pc = cp->help; *pc; pc++){ + putchar(*pc); + if(*pc == ''\n'') + printf("\t\t"); + } + putchar(''\n''); + } + + printf("\n\t%s help|--help|-h\n\t\tShow the help.\n",np); + printf("\n%s\n", BTRFS_BUILD_VERSION); + +} + +static int parse_args(int argc, char **argv, + int *nargs_, char **cmd_, char ***args_ ) +{ + struct Command *cp; + char *cmd=0, **args; + int nargs = argc -2; + + if( argc < 2 || !strcmp(argv[1], "help") || + !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + help(argv[0]); + return 0; + } + + for( cp = commands; cp->alias || cp->verb; cp++ ){ + if (strcmp(argv[1], cp->alias) && strcmp(argv[1], cp->verb)) + continue; + if (cp->nargs < 0 && cp->nargs < -nargs ){ + fprintf(stderr, "ERROR: ''%s'' requires minimum %d arg(s)\n", + argv[1], -cp->nargs); + exit(2); + } + if(cp->nargs >= 0 && cp->nargs != nargs && cp->nargs != 999 ){ + fprintf(stderr, "ERROR: ''%s'' requires %d arg(s)\n", + argv[1], cp->nargs); + exit(2); + } + + cmd = cp->verb ? cp->verb : cp->alias; + args = argv+2; + break; + } + + if(!cmd){ + fprintf( stderr, "ERROR: unknow command ''%s''\n",argv[1]); + help(argv[0]); + return -1; + } + + *cmd_ = cmd; + *args_ = args; + *nargs_ = nargs; + return 1; +} + +int main(int ac, char **av ) +{ + + char *cmd, **args; + int nargs, r; + + r = parse_args(ac, av, &nargs, &cmd, &args); + if( r <= 0 ){ + /* error or no command to parse*/ + exit(-r); + } + + if( !strcmp(cmd, "scan") ){ + exit(do_scan(nargs, args)); + + }else if( !strcmp(cmd, "defrag") ){ + exit(do_defrag(nargs, args)); + + }else if( !strcmp(cmd, "snapshot")){ + exit(do_clone(args[0], args[1])); + + }else if( !strcmp(cmd, "subvolume")){ + exit(do_create_subvol(args[0])); + + }else if( !strcmp(cmd, "fssync") ){ + exit(do_fssync(args[0])); + + }else if( !strcmp(cmd, "delete")){ + exit(do_delete_subvolume(args[0])); + + }else if( !strcmp(cmd, "show")){ + exit(do_show_volume(nargs ? args[0]:0)); + + }else if( !strcmp(cmd, "add-dev")){ + exit(do_add_volume(nargs, args)); + + }else if( !strcmp(cmd, "rm-dev")){ + exit(do_remove_volume(nargs, args)); + + }else if( !strcmp(cmd, "balance")){ + exit(do_balance(args[0])); + + } + + fprintf(stderr, "ERROR: internal parsing error\n"); + exit(111); + +} + -- gpg key@ keyserver.linux.it: Goffredo Baroncelli (ghigo) <kreijackATinwind.it> Key fingerprint = 4769 7E51 5293 D36C 814E C054 BF04 F161 3DC5 0512
TARUISI Hiroaki
2010-Feb-18 00:54 UTC
Re: [PATCH 1/2 V2] btrfs, a new tool to manage a btrfs filesystem; source code
Hi, It''s a cool utility with tidy source code. At my first glance, there''s typo, a question and an impression. - There''s no "<source>" in usage of utility. - will occupe -> will occupy (usage of utility : "btrfs resize") - Is specifying a target device of resizing isn''t public even in your utility? - I felt somehow confusing with the short commands. It''s not easy to associate it from its command. Regards, taruisi (2010/02/18 5:02), Goffredo Baroncelli wrote:> This is the source code of btrfs.c. > The main changes from my previous patch are: > - Rearranged the short command in order to avoid conflicts > - Renamed the ''create'' verb is ''subvolume'' > - Rearranged the parsing code, which now is integrated in the help in order to avoid mismatch > > BR > G.Baroncelli >- snip - -- 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