diff --git a/Makefile b/Makefile index 834be475..0d47bd51 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ volumes.o utils.o btrfs-list.o btrfslabel.o +cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ + cmds-inspect.o CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ -Wuninitialized -Wshadow -Wundef @@ -36,8 +38,8 @@ all: version $(progs) manpages version: bash version.sh -btrfs: $(objects) btrfs.o btrfs_cmds.o scrub.o - $(CC) $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o scrub.o \ +btrfs: $(objects) btrfs.o common.o $(cmds_objects) + $(CC) $(CFLAGS) -o btrfs btrfs.o common.o $(cmds_objects) \ $(objects) $(LDFLAGS) $(LIBS) -lpthread calc-size: $(objects) calc-size.o diff --git a/btrfs_cmds.c b/btrfs_cmds.c deleted file mode 100644 index b59e9cbf..00000000 --- a/btrfs_cmds.c +++ /dev/null @@ -1,1307 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef ULONG_MAX - -#include "kerncompat.h" -#include "ctree.h" -#include "transaction.h" -#include "utils.h" -#include "version.h" -#include "ioctl.h" -#include "volumes.h" - -#include "btrfs_cmds.h" -#include "btrfslabel.h" - -#ifdef __CHECKER__ -#define BLKGETSIZE64 0 -#define BTRFS_IOC_SNAP_CREATE_V2 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); - -} - -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 u64 parse_size(char *s) -{ - int len = strlen(s); - char c; - u64 mult = 1; - - if (!isdigit(s[len - 1])) { - c = tolower(s[len - 1]); - switch (c) { - case 'g': - mult *= 1024; - case 'm': - mult *= 1024; - case 'k': - mult *= 1024; - case 'b': - break; - default: - fprintf(stderr, "Unknown size descriptor %c\n", c); - exit(1); - } - s[len - 1] = '\0'; - } - return atoll(s) * mult; -} - -static int parse_compress_type(char *s) -{ - if (strcmp(optarg, "zlib") == 0) - return BTRFS_COMPRESS_ZLIB; - else if (strcmp(optarg, "lzo") == 0) - return BTRFS_COMPRESS_LZO; - else { - fprintf(stderr, "Unknown compress type %s\n", s); - exit(1); - }; -} - -int do_defrag(int ac, char **av) -{ - int fd; - int flush = 0; - u64 start = 0; - u64 len = (u64)-1; - u32 thresh = 0; - int i; - int errors = 0; - int ret = 0; - int verbose = 0; - int fancy_ioctl = 0; - struct btrfs_ioctl_defrag_range_args range; - int e=0; - int compress_type = BTRFS_COMPRESS_NONE; - - optind = 1; - while(1) { - int c = getopt(ac, av, "vc::fs:l:t:"); - if (c < 0) - break; - switch(c) { - case 'c': - compress_type = BTRFS_COMPRESS_ZLIB; - if (optarg) - compress_type = parse_compress_type(optarg); - fancy_ioctl = 1; - break; - case 'f': - flush = 1; - fancy_ioctl = 1; - break; - case 'v': - verbose = 1; - break; - case 's': - start = parse_size(optarg); - fancy_ioctl = 1; - break; - case 'l': - len = parse_size(optarg); - fancy_ioctl = 1; - break; - case 't': - thresh = parse_size(optarg); - fancy_ioctl = 1; - break; - default: - fprintf(stderr, "Invalid arguments for defragment\n"); - free(av); - return 1; - } - } - if (ac - optind == 0) { - fprintf(stderr, "Invalid arguments for defragment\n"); - free(av); - return 1; - } - - memset(&range, 0, sizeof(range)); - range.start = start; - range.len = len; - range.extent_thresh = thresh; - if (compress_type) { - range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; - range.compress_type = compress_type; - } - if (flush) - range.flags |= BTRFS_DEFRAG_RANGE_START_IO; - - for (i = optind; i < ac; i++) { - if (verbose) - printf("%s\n", av[i]); - fd = open_file_or_dir(av[i]); - if (fd < 0) { - fprintf(stderr, "failed to open %s\n", av[i]); - perror("open:"); - errors++; - continue; - } - if (!fancy_ioctl) { - ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL); - e=errno; - } else { - ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range); - if (ret && errno == ENOTTY) { - fprintf(stderr, "ERROR: defrag range ioctl not " - "supported in this kernel, please try " - "without any options.\n"); - errors++; - close(fd); - break; - } - } - if (ret) { - fprintf(stderr, "ERROR: defrag failed on %s - %s\n", - av[i], strerror(e)); - errors++; - } - close(fd); - } - if (verbose) - printf("%s\n", BTRFS_BUILD_VERSION); - if (errors) { - fprintf(stderr, "total %d failures\n", errors); - exit(1); - } - - free(av); - return errors + 20; -} - -int do_find_newer(int argc, char **argv) -{ - int fd; - int ret; - char *subvol; - u64 last_gen; - - subvol = argv[1]; - last_gen = atoll(argv[2]); - - ret = test_issubvolume(subvol); - if (ret < 0) { - fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); - return 12; - } - if (!ret) { - fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); - return 13; - } - - fd = open_file_or_dir(subvol); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", subvol); - return 12; - } - ret = find_updated_files(fd, 0, last_gen); - if (ret) - return 19; - return 0; -} - -int do_subvol_list(int argc, char **argv) -{ - int fd; - int ret; - int print_parent = 0; - char *subvol; - int optind = 1; - - while(1) { - int c = getopt(argc, argv, "p"); - if (c < 0) break; - switch(c) { - case 'p': - print_parent = 1; - optind++; - break; - } - } - - if (argc - optind != 1) { - fprintf(stderr, "ERROR: invalid arguments for subvolume list\n"); - return 1; - } - - subvol = argv[optind]; - - ret = test_issubvolume(subvol); - if (ret < 0) { - fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); - return 12; - } - if (!ret) { - fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); - return 13; - } - - fd = open_file_or_dir(subvol); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", subvol); - return 12; - } - ret = list_subvols(fd, print_parent, 0); - if (ret) - return 19; - return 0; -} - -int do_clone(int argc, char **argv) -{ - char *subvol, *dst; - int res, fd, fddst, len, e, optind = 0, readonly = 0; - char *newname; - char *dstdir; - struct btrfs_ioctl_vol_args_v2 args; - - memset(&args, 0, sizeof(args)); - - while (1) { - int c = getopt(argc, argv, "r"); - - if (c < 0) - break; - switch (c) { - case 'r': - optind++; - readonly = 1; - break; - default: - fprintf(stderr, - "Invalid arguments for subvolume snapshot\n"); - free(argv); - return 1; - } - } - if (argc - optind != 3) { - fprintf(stderr, "Invalid arguments for subvolume snapshot\n"); - free(argv); - return 1; - } - - subvol = argv[optind+1]; - dst = argv[optind+2]; - - 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: incorrect snapshot name ('%s')\n", - newname); - return 14; - } - - len = strlen(newname); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, "ERROR: snapshot name too long ('%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; - } - - 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; - strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX); - res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); - e = errno; - - close(fd); - close(fddst); - - if(res < 0 ){ - fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n", - subvol, strerror(e)); - return 11; - } - - return 0; - -} - -int do_delete_subvolume(int argc, char **argv) -{ - int res, fd, len, e; - struct btrfs_ioctl_vol_args args; - char *dname, *vname, *cpath; - char *path = argv[1]; - - 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: incorrect subvolume name ('%s')\n", - vname); - return 14; - } - - len = strlen(vname); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, "ERROR: snapshot name too long ('%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); - strncpy(args.name, vname, BTRFS_PATH_NAME_MAX); - res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); - e = errno; - - close(fd); - - if(res < 0 ){ - fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n", - dname, vname, strerror(e)); - return 11; - } - - return 0; - -} - -int do_create_subvol(int argc, char **argv) -{ - int res, fddst, len, e; - char *newname; - char *dstdir; - struct btrfs_ioctl_vol_args args; - char *dst = argv[1]; - - 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; - } - - len = strlen(newname); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, "ERROR: subvolume name too long ('%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); - strncpy(args.name, newname, BTRFS_PATH_NAME_MAX); - res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); - e = errno; - - close(fddst); - - if(res < 0 ){ - fprintf( stderr, "ERROR: cannot create subvolume - %s\n", - strerror(e)); - return 11; - } - - return 0; - -} - -int do_fssync(int argc, char **argv) -{ - int fd, res, e; - char *path = argv[1]; - - 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); - e = errno; - close(fd); - if( res < 0 ){ - fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n", - path, strerror(e)); - return 16; - } - - return 0; -} - -int do_scan(int argc, char **argv) -{ - int i, fd, e; - int checklist = 1; - int devstart = 1; - - if( argc >= 2 && !strcmp(argv[1],"--all-devices")){ - - if( argc >2 ){ - fprintf(stderr, "ERROR: too may arguments\n"); - return 22; - } - - checklist = 0; - devstart += 1; - } - - if(argc<=devstart){ - - int ret; - - printf("Scanning for Btrfs filesystems\n"); - if(checklist) - ret = btrfs_scan_block_devices(1); - else - 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 = devstart ; i < argc ; i++ ){ - struct btrfs_ioctl_vol_args args; - int ret; - - printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]); - - strncpy(args.name, argv[i], BTRFS_PATH_NAME_MAX); - /* - * FIXME: which are the error code returned by this ioctl ? - * it seems that is impossible to understand if there no is - * a btrfs filesystem from an I/O error !!! - */ - ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); - e = errno; - - if( ret < 0 ){ - close(fd); - fprintf(stderr, "ERROR: unable to scan the device '%s' - %s\n", - argv[i], strerror(e)); - return 11; - } - } - - close(fd); - return 0; - -} - -int do_resize(int argc, char **argv) -{ - - struct btrfs_ioctl_vol_args args; - int fd, res, len, e; - char *amount=argv[1], *path=argv[2]; - - fd = open_file_or_dir(path); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); - return 12; - } - len = strlen(amount); - if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { - fprintf(stderr, "ERROR: size value too long ('%s)\n", - amount); - return 14; - } - - printf("Resize '%s' of '%s'\n", path, amount); - strncpy(args.name, amount, BTRFS_PATH_NAME_MAX); - res = ioctl(fd, BTRFS_IOC_RESIZE, &args); - e = errno; - close(fd); - if( res < 0 ){ - fprintf(stderr, "ERROR: unable to resize '%s' - %s\n", - path, strerror(e)); - return 30; - } - return 0; -} - -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"); -} - -int do_show_filesystem(int argc, char **argv) -{ - struct list_head *all_uuids; - struct btrfs_fs_devices *fs_devices; - struct list_head *cur_uuid; - char *search = 0; - int ret; - int checklist = 1; - int searchstart = 1; - - if( argc >= 2 && !strcmp(argv[1],"--all-devices")){ - checklist = 0; - searchstart += 1; - } - - if( argc > searchstart+1 ){ - fprintf(stderr, "ERROR: too many arguments\n"); - return 22; - } - - if(checklist) - ret = btrfs_scan_block_devices(0); - else - ret = btrfs_scan_one_dir("/dev", 0); - - if (ret){ - fprintf(stderr, "ERROR: error %d while scanning\n", ret); - return 18; - } - - if(searchstart < argc) - search = argv[searchstart]; - - 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; -} - -int do_add_volume(int nargs, char **args) -{ - - char *mntpnt = args[nargs-1]; - int i, fdmnt, ret=0, e; - - - fdmnt = open_file_or_dir(mntpnt); - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); - return 12; - } - - for (i = 1; i < (nargs-1); i++ ){ - struct btrfs_ioctl_vol_args ioctl_args; - int devfd, res; - u64 dev_block_count = 0; - struct stat st; - int mixed = 0; - - res = check_mounted(args[i]); - if (res < 0) { - fprintf(stderr, "error checking %s mount status\n", - args[i]); - ret++; - continue; - } - if (res == 1) { - fprintf(stderr, "%s is mounted\n", args[i]); - ret++; - continue; - } - - devfd = open(args[i], O_RDWR); - if (!devfd) { - fprintf(stderr, "ERROR: Unable to open device '%s'\n", args[i]); - close(devfd); - ret++; - continue; - } - res = fstat(devfd, &st); - if (res) { - 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, &mixed); - if (res) { - fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]); - close(devfd); - ret++; - continue; - } - close(devfd); - - strncpy(ioctl_args.name, args[i], BTRFS_PATH_NAME_MAX); - res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); - e = errno; - if(res<0){ - fprintf(stderr, "ERROR: error adding the device '%s' - %s\n", - args[i], strerror(e)); - ret++; - } - - } - - close(fdmnt); - if (ret) - return ret+20; - else - return 0; - -} - -int do_balance(int argc, char **argv) -{ - - int fdmnt, ret=0, e; - struct btrfs_ioctl_vol_args args; - char *path = argv[1]; - - fdmnt = open_file_or_dir(path); - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); - return 12; - } - - memset(&args, 0, sizeof(args)); - ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args); - e = errno; - close(fdmnt); - if(ret<0){ - fprintf(stderr, "ERROR: error during balancing '%s' - %s\n", - path, strerror(e)); - - return 19; - } - return 0; -} -int do_remove_volume(int nargs, char **args) -{ - - char *mntpnt = args[nargs-1]; - int i, fdmnt, ret=0, e; - - fdmnt = open_file_or_dir(mntpnt); - if (fdmnt < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); - return 12; - } - - for(i=1 ; i < (nargs-1) ; i++ ){ - struct btrfs_ioctl_vol_args arg; - int res; - - strncpy(arg.name, args[i], BTRFS_PATH_NAME_MAX); - res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); - e = errno; - if(res<0){ - fprintf(stderr, "ERROR: error removing the device '%s' - %s\n", - args[i], strerror(e)); - ret++; - } - } - - close(fdmnt); - if( ret) - return ret+20; - else - return 0; -} - -int do_set_default_subvol(int nargs, char **argv) -{ - int ret=0, fd, e; - u64 objectid; - char *path = argv[2]; - char *subvolid = argv[1]; - - fd = open_file_or_dir(path); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); - return 12; - } - - objectid = (unsigned long long)strtoll(subvolid, NULL, 0); - if (errno == ERANGE) { - fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid); - return 30; - } - ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid); - e = errno; - close(fd); - if( ret < 0 ){ - fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n", - strerror(e)); - return 30; - } - return 0; -} - -int do_change_label(int nargs, char **argv) -{ - /* check the number of argument */ - if ( nargs > 3 ){ - fprintf(stderr, "ERROR: '%s' requires maximum 2 args\n", - argv[0]); - return -2; - }else if (nargs == 2){ - return get_label(argv[1]); - } else { /* nargs == 0 */ - return set_label(argv[1], argv[2]); - } -} - - -int do_get_default_subvol(int nargs, char **argv) -{ - int fd; - int ret; - char *subvol; - - subvol = argv[1]; - - ret = test_issubvolume(subvol); - if (ret < 0) { - fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); - return 12; - } - if (!ret) { - fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); - return 13; - } - - fd = open_file_or_dir(subvol); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", subvol); - return 12; - } - ret = list_subvols(fd, 0, 1); - if (ret) - return 19; - return 0; -} - -int do_df_filesystem(int nargs, char **argv) -{ - struct btrfs_ioctl_space_args *sargs; - u64 count = 0, i; - int ret; - int fd; - int e; - char *path = argv[1]; - - fd = open_file_or_dir(path); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access to '%s'\n", path); - return 12; - } - - sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); - if (!sargs) - return -ENOMEM; - - sargs->space_slots = 0; - sargs->total_spaces = 0; - - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n", - path, strerror(e)); - free(sargs); - return ret; - } - if (!sargs->total_spaces) - return 0; - - count = sargs->total_spaces; - - sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + - (count * sizeof(struct btrfs_ioctl_space_info))); - if (!sargs) - return -ENOMEM; - - sargs->space_slots = count; - sargs->total_spaces = 0; - - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n", - path, strerror(e)); - close(fd); - free(sargs); - return ret; - } - - for (i = 0; i < sargs->total_spaces; i++) { - char description[80]; - char *total_bytes; - char *used_bytes; - int written = 0; - u64 flags = sargs->spaces[i].flags; - - memset(description, 0, 80); - - if (flags & BTRFS_BLOCK_GROUP_DATA) { - if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 14, "%s", - "Data+Metadata"); - written += 13; - } else { - snprintf(description, 5, "%s", "Data"); - written += 4; - } - } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { - snprintf(description, 7, "%s", "System"); - written += 6; - } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 9, "%s", "Metadata"); - written += 8; - } - - if (flags & BTRFS_BLOCK_GROUP_RAID0) { - snprintf(description+written, 8, "%s", ", RAID0"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { - snprintf(description+written, 8, "%s", ", RAID1"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_DUP) { - snprintf(description+written, 6, "%s", ", DUP"); - written += 5; - } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { - snprintf(description+written, 9, "%s", ", RAID10"); - written += 8; - } - - total_bytes = pretty_sizes(sargs->spaces[i].total_bytes); - used_bytes = pretty_sizes(sargs->spaces[i].used_bytes); - printf("%s: total=%s, used=%s\n", description, total_bytes, - used_bytes); - } - free(sargs); - - return 0; -} - -static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend) -{ - int ret; - int i; - struct btrfs_ioctl_ino_path_args ipa; - struct btrfs_data_container *fspath; - - fspath = malloc(4096); - if (!fspath) - return 1; - - ipa.inum = inum; - ipa.size = 4096; - ipa.fspath = (u64)fspath; - - ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa); - if (ret) { - printf("ioctl ret=%d, error: %s\n", ret, strerror(errno)); - goto out; - } - - if (verbose) - printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, " - "cnt=%d, missed=%d\n", ret, - (unsigned long)fspath->bytes_left, - (unsigned long)fspath->bytes_missing, - fspath->elem_cnt, fspath->elem_missed); - - for (i = 0; i < fspath->elem_cnt; ++i) { - char **str = (char **)fspath->val; - str[i] += (unsigned long)fspath->val; - if (prepend) - printf("%s/%s\n", prepend, str[i]); - else - printf("%s\n", str[i]); - } - -out: - free(fspath); - return ret; -} - -int do_ino_to_path(int nargs, char **argv) -{ - int fd; - int verbose = 0; - - optind = 1; - while (1) { - int c = getopt(nargs, argv, "v"); - if (c < 0) - break; - switch (c) { - case 'v': - verbose = 1; - break; - default: - fprintf(stderr, "invalid arguments for ipath\n"); - return 1; - } - } - if (nargs - optind != 2) { - fprintf(stderr, "invalid arguments for ipath\n"); - return 1; - } - - fd = open_file_or_dir(argv[optind+1]); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]); - return 12; - } - - return __ino_to_path_fd(atoll(argv[optind]), fd, verbose, - argv[optind+1]); -} - -int do_logical_to_ino(int nargs, char **argv) -{ - int ret; - int fd; - int i; - int verbose = 0; - int getpath = 1; - int bytes_left; - struct btrfs_ioctl_logical_ino_args loi; - struct btrfs_data_container *inodes; - char full_path[4096]; - char *path_ptr; - - optind = 1; - while (1) { - int c = getopt(nargs, argv, "Pv"); - if (c < 0) - break; - switch (c) { - case 'P': - getpath = 0; - break; - case 'v': - verbose = 1; - break; - default: - fprintf(stderr, "invalid arguments for ipath\n"); - return 1; - } - } - if (nargs - optind != 2) { - fprintf(stderr, "invalid arguments for ipath\n"); - return 1; - } - - inodes = malloc(4096); - if (!inodes) - return 1; - - loi.logical = atoll(argv[optind]); - loi.size = 4096; - loi.inodes = (u64)inodes; - - fd = open_file_or_dir(argv[optind+1]); - if (fd < 0) { - fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]); - ret = 12; - goto out; - } - - ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi); - if (ret) { - printf("ioctl ret=%d, error: %s\n", ret, strerror(errno)); - goto out; - } - - if (verbose) - printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, " - "cnt=%d, missed=%d\n", ret, - (unsigned long)inodes->bytes_left, - (unsigned long)inodes->bytes_missing, - inodes->elem_cnt, inodes->elem_missed); - - bytes_left = sizeof(full_path); - ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]); - path_ptr = full_path + ret; - bytes_left -= ret + 1; - BUG_ON(bytes_left < 0); - - for (i = 0; i < inodes->elem_cnt; i += 3) { - u64 inum = inodes->val[i]; - u64 offset = inodes->val[i+1]; - u64 root = inodes->val[i+2]; - int path_fd; - char *name; - - if (getpath) { - name = path_for_root(fd, root); - if (IS_ERR(name)) - return PTR_ERR(name); - if (!name) { - path_ptr[-1] = '\0'; - path_fd = fd; - } else { - path_ptr[-1] = '/'; - ret = snprintf(path_ptr, bytes_left, "%s", - name); - BUG_ON(ret >= bytes_left); - free(name); - path_fd = open_file_or_dir(full_path); - if (path_fd < 0) { - fprintf(stderr, "ERROR: can't access " - "'%s'\n", full_path); - goto out; - } - } - __ino_to_path_fd(inum, path_fd, verbose, full_path); - } else { - printf("inode %llu offset %llu root %llu\n", inum, - offset, root); - } - } - -out: - free(inodes); - return ret; -} diff --git a/btrfs_cmds.h b/btrfs_cmds.h index 81182b16..efca7c51 100644 --- a/btrfs_cmds.h +++ b/btrfs_cmds.h @@ -33,12 +33,9 @@ int do_resize(int nargs, char **argv); int do_subvol_list(int nargs, char **argv); int do_set_default_subvol(int nargs, char **argv); int do_get_default_subvol(int nargs, char **argv); -int list_subvols(int fd, int print_parent, int get_default); int do_df_filesystem(int nargs, char **argv); -int find_updated_files(int fd, u64 root_id, u64 oldest_gen); int do_find_newer(int argc, char **argv); int do_change_label(int argc, char **argv); int open_file_or_dir(const char *fname); int do_ino_to_path(int nargs, char **argv); int do_logical_to_ino(int nargs, char **argv); -char *path_for_root(int fd, u64 root); diff --git a/cmds-device.c b/cmds-device.c new file mode 100644 index 00000000..0fc4c7fa --- /dev/null +++ b/cmds-device.c @@ -0,0 +1,223 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "kerncompat.h" +#include "ctree.h" +#include "ioctl.h" +#include "utils.h" + +#include "btrfs_cmds.h" + +/* FIXME - imported cruft, fix sparse errors and warnings */ +#ifdef __CHECKER__ +#define BLKGETSIZE64 0 +#define BTRFS_IOC_SNAP_CREATE_V2 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 + +int do_add_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0, e; + + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + return 12; + } + + for (i = 1; i < (nargs-1); i++ ){ + struct btrfs_ioctl_vol_args ioctl_args; + int devfd, res; + u64 dev_block_count = 0; + struct stat st; + int mixed = 0; + + res = check_mounted(args[i]); + if (res < 0) { + fprintf(stderr, "error checking %s mount status\n", + args[i]); + ret++; + continue; + } + if (res == 1) { + fprintf(stderr, "%s is mounted\n", args[i]); + ret++; + continue; + } + + devfd = open(args[i], O_RDWR); + if (!devfd) { + fprintf(stderr, "ERROR: Unable to open device '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + res = fstat(devfd, &st); + if (res) { + 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, &mixed); + if (res) { + fprintf(stderr, "ERROR: Unable to init '%s'\n", args[i]); + close(devfd); + ret++; + continue; + } + close(devfd); + + strncpy(ioctl_args.name, args[i], BTRFS_PATH_NAME_MAX); + res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args); + e = errno; + if(res<0){ + fprintf(stderr, "ERROR: error adding the device '%s' - %s\n", + args[i], strerror(e)); + ret++; + } + + } + + close(fdmnt); + if (ret) + return ret+20; + else + return 0; + +} + +int do_remove_volume(int nargs, char **args) +{ + + char *mntpnt = args[nargs-1]; + int i, fdmnt, ret=0, e; + + fdmnt = open_file_or_dir(mntpnt); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", mntpnt); + return 12; + } + + for(i=1 ; i < (nargs-1) ; i++ ){ + struct btrfs_ioctl_vol_args arg; + int res; + + strncpy(arg.name, args[i], BTRFS_PATH_NAME_MAX); + res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg); + e = errno; + if(res<0){ + fprintf(stderr, "ERROR: error removing the device '%s' - %s\n", + args[i], strerror(e)); + ret++; + } + } + + close(fdmnt); + if( ret) + return ret+20; + else + return 0; +} + +int do_scan(int argc, char **argv) +{ + int i, fd, e; + int checklist = 1; + int devstart = 1; + + if( argc >= 2 && !strcmp(argv[1],"--all-devices")){ + + if( argc >2 ){ + fprintf(stderr, "ERROR: too may arguments\n"); + return 22; + } + + checklist = 0; + devstart += 1; + } + + if(argc<=devstart){ + + int ret; + + printf("Scanning for Btrfs filesystems\n"); + if(checklist) + ret = btrfs_scan_block_devices(1); + else + 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 = devstart ; i < argc ; i++ ){ + struct btrfs_ioctl_vol_args args; + int ret; + + printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]); + + strncpy(args.name, argv[i], BTRFS_PATH_NAME_MAX); + /* + * FIXME: which are the error code returned by this ioctl ? + * it seems that is impossible to understand if there no is + * a btrfs filesystem from an I/O error !!! + */ + ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); + e = errno; + + if( ret < 0 ){ + close(fd); + fprintf(stderr, "ERROR: unable to scan the device '%s' - %s\n", + argv[i], strerror(e)); + return 11; + } + } + + close(fd); + return 0; + +} + diff --git a/cmds-filesystem.c b/cmds-filesystem.c new file mode 100644 index 00000000..2e607d2c --- /dev/null +++ b/cmds-filesystem.c @@ -0,0 +1,488 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "kerncompat.h" +#include "ctree.h" +#include "ioctl.h" +#include "utils.h" +#include "volumes.h" + +#include "version.h" + +#include "btrfs_cmds.h" +#include "btrfslabel.h" + +int do_df_filesystem(int nargs, char **argv) +{ + struct btrfs_ioctl_space_args *sargs; + u64 count = 0, i; + int ret; + int fd; + int e; + char *path = argv[1]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); + if (!sargs) + return -ENOMEM; + + sargs->space_slots = 0; + sargs->total_spaces = 0; + + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); + e = errno; + if (ret) { + fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n", + path, strerror(e)); + free(sargs); + return ret; + } + if (!sargs->total_spaces) + return 0; + + count = sargs->total_spaces; + + sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + + (count * sizeof(struct btrfs_ioctl_space_info))); + if (!sargs) + return -ENOMEM; + + sargs->space_slots = count; + sargs->total_spaces = 0; + + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); + e = errno; + if (ret) { + fprintf(stderr, "ERROR: couldn't get space info on '%s' - %s\n", + path, strerror(e)); + close(fd); + free(sargs); + return ret; + } + + for (i = 0; i < sargs->total_spaces; i++) { + char description[80]; + char *total_bytes; + char *used_bytes; + int written = 0; + u64 flags = sargs->spaces[i].flags; + + memset(description, 0, 80); + + if (flags & BTRFS_BLOCK_GROUP_DATA) { + if (flags & BTRFS_BLOCK_GROUP_METADATA) { + snprintf(description, 14, "%s", + "Data+Metadata"); + written += 13; + } else { + snprintf(description, 5, "%s", "Data"); + written += 4; + } + } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { + snprintf(description, 7, "%s", "System"); + written += 6; + } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { + snprintf(description, 9, "%s", "Metadata"); + written += 8; + } + + if (flags & BTRFS_BLOCK_GROUP_RAID0) { + snprintf(description+written, 8, "%s", ", RAID0"); + written += 7; + } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { + snprintf(description+written, 8, "%s", ", RAID1"); + written += 7; + } else if (flags & BTRFS_BLOCK_GROUP_DUP) { + snprintf(description+written, 6, "%s", ", DUP"); + written += 5; + } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { + snprintf(description+written, 9, "%s", ", RAID10"); + written += 8; + } + + total_bytes = pretty_sizes(sargs->spaces[i].total_bytes); + used_bytes = pretty_sizes(sargs->spaces[i].used_bytes); + printf("%s: total=%s, used=%s\n", description, total_bytes, + used_bytes); + } + free(sargs); + + return 0; +} + +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"); +} + +int do_show_filesystem(int argc, char **argv) +{ + struct list_head *all_uuids; + struct btrfs_fs_devices *fs_devices; + struct list_head *cur_uuid; + char *search = 0; + int ret; + int checklist = 1; + int searchstart = 1; + + if( argc >= 2 && !strcmp(argv[1],"--all-devices")){ + checklist = 0; + searchstart += 1; + } + + if( argc > searchstart+1 ){ + fprintf(stderr, "ERROR: too many arguments\n"); + return 22; + } + + if(checklist) + ret = btrfs_scan_block_devices(0); + else + ret = btrfs_scan_one_dir("/dev", 0); + + if (ret){ + fprintf(stderr, "ERROR: error %d while scanning\n", ret); + return 18; + } + + if(searchstart < argc) + search = argv[searchstart]; + + 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; +} + +int do_fssync(int argc, char **argv) +{ + int fd, res, e; + char *path = argv[1]; + + 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); + e = errno; + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to fs-syncing '%s' - %s\n", + path, strerror(e)); + return 16; + } + + return 0; +} + +static u64 parse_size(char *s) +{ + int len = strlen(s); + char c; + u64 mult = 1; + + if (!isdigit(s[len - 1])) { + c = tolower(s[len - 1]); + switch (c) { + case 'g': + mult *= 1024; + case 'm': + mult *= 1024; + case 'k': + mult *= 1024; + case 'b': + break; + default: + fprintf(stderr, "Unknown size descriptor %c\n", c); + exit(1); + } + s[len - 1] = '\0'; + } + return atoll(s) * mult; +} + +static int parse_compress_type(char *s) +{ + if (strcmp(optarg, "zlib") == 0) + return BTRFS_COMPRESS_ZLIB; + else if (strcmp(optarg, "lzo") == 0) + return BTRFS_COMPRESS_LZO; + else { + fprintf(stderr, "Unknown compress type %s\n", s); + exit(1); + }; +} + +int do_defrag(int ac, char **av) +{ + int fd; + int flush = 0; + u64 start = 0; + u64 len = (u64)-1; + u32 thresh = 0; + int i; + int errors = 0; + int ret = 0; + int verbose = 0; + int fancy_ioctl = 0; + struct btrfs_ioctl_defrag_range_args range; + int e=0; + int compress_type = BTRFS_COMPRESS_NONE; + + optind = 1; + while(1) { + int c = getopt(ac, av, "vc::fs:l:t:"); + if (c < 0) + break; + switch(c) { + case 'c': + compress_type = BTRFS_COMPRESS_ZLIB; + if (optarg) + compress_type = parse_compress_type(optarg); + fancy_ioctl = 1; + break; + case 'f': + flush = 1; + fancy_ioctl = 1; + break; + case 'v': + verbose = 1; + break; + case 's': + start = parse_size(optarg); + fancy_ioctl = 1; + break; + case 'l': + len = parse_size(optarg); + fancy_ioctl = 1; + break; + case 't': + thresh = parse_size(optarg); + fancy_ioctl = 1; + break; + default: + fprintf(stderr, "Invalid arguments for defragment\n"); + free(av); + return 1; + } + } + if (ac - optind == 0) { + fprintf(stderr, "Invalid arguments for defragment\n"); + free(av); + return 1; + } + + memset(&range, 0, sizeof(range)); + range.start = start; + range.len = len; + range.extent_thresh = thresh; + if (compress_type) { + range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; + range.compress_type = compress_type; + } + if (flush) + range.flags |= BTRFS_DEFRAG_RANGE_START_IO; + + for (i = optind; i < ac; i++) { + if (verbose) + printf("%s\n", av[i]); + fd = open_file_or_dir(av[i]); + if (fd < 0) { + fprintf(stderr, "failed to open %s\n", av[i]); + perror("open:"); + errors++; + continue; + } + if (!fancy_ioctl) { + ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL); + e=errno; + } else { + ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, &range); + if (ret && errno == ENOTTY) { + fprintf(stderr, "ERROR: defrag range ioctl not " + "supported in this kernel, please try " + "without any options.\n"); + errors++; + close(fd); + break; + } + } + if (ret) { + fprintf(stderr, "ERROR: defrag failed on %s - %s\n", + av[i], strerror(e)); + errors++; + } + close(fd); + } + if (verbose) + printf("%s\n", BTRFS_BUILD_VERSION); + if (errors) { + fprintf(stderr, "total %d failures\n", errors); + exit(1); + } + + free(av); + return errors + 20; +} + +int do_balance(int argc, char **argv) +{ + + int fdmnt, ret=0, e; + struct btrfs_ioctl_vol_args args; + char *path = argv[1]; + + fdmnt = open_file_or_dir(path); + if (fdmnt < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + memset(&args, 0, sizeof(args)); + ret = ioctl(fdmnt, BTRFS_IOC_BALANCE, &args); + e = errno; + close(fdmnt); + if(ret<0){ + fprintf(stderr, "ERROR: error during balancing '%s' - %s\n", + path, strerror(e)); + + return 19; + } + return 0; +} + +int do_resize(int argc, char **argv) +{ + + struct btrfs_ioctl_vol_args args; + int fd, res, len, e; + char *amount=argv[1], *path=argv[2]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + len = strlen(amount); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: size value too long ('%s)\n", + amount); + return 14; + } + + printf("Resize '%s' of '%s'\n", path, amount); + strncpy(args.name, amount, BTRFS_PATH_NAME_MAX); + res = ioctl(fd, BTRFS_IOC_RESIZE, &args); + e = errno; + close(fd); + if( res < 0 ){ + fprintf(stderr, "ERROR: unable to resize '%s' - %s\n", + path, strerror(e)); + return 30; + } + return 0; +} + +int do_change_label(int nargs, char **argv) +{ + /* check the number of argument */ + if ( nargs > 3 ){ + fprintf(stderr, "ERROR: '%s' requires maximum 2 args\n", + argv[0]); + return -2; + }else if (nargs == 2){ + return get_label(argv[1]); + } else { /* nargs == 0 */ + return set_label(argv[1], argv[2]); + } +} + diff --git a/cmds-inspect.c b/cmds-inspect.c new file mode 100644 index 00000000..ca42d7f5 --- /dev/null +++ b/cmds-inspect.c @@ -0,0 +1,214 @@ +/* + * 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 +#include +#include +#include +#include + +#include "kerncompat.h" +#include "ioctl.h" + +#include "btrfs_cmds.h" + +/* btrfs-list.c */ +char *path_for_root(int fd, u64 root); + +static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend) +{ + int ret; + int i; + struct btrfs_ioctl_ino_path_args ipa; + struct btrfs_data_container *fspath; + + fspath = malloc(4096); + if (!fspath) + return 1; + + ipa.inum = inum; + ipa.size = 4096; + ipa.fspath = (u64)fspath; + + ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa); + if (ret) { + printf("ioctl ret=%d, error: %s\n", ret, strerror(errno)); + goto out; + } + + if (verbose) + printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, " + "cnt=%d, missed=%d\n", ret, + (unsigned long)fspath->bytes_left, + (unsigned long)fspath->bytes_missing, + fspath->elem_cnt, fspath->elem_missed); + + for (i = 0; i < fspath->elem_cnt; ++i) { + char **str = (char **)fspath->val; + str[i] += (unsigned long)fspath->val; + if (prepend) + printf("%s/%s\n", prepend, str[i]); + else + printf("%s\n", str[i]); + } + +out: + free(fspath); + return ret; +} + +int do_ino_to_path(int nargs, char **argv) +{ + int fd; + int verbose = 0; + + optind = 1; + while (1) { + int c = getopt(nargs, argv, "v"); + if (c < 0) + break; + switch (c) { + case 'v': + verbose = 1; + break; + default: + fprintf(stderr, "invalid arguments for ipath\n"); + return 1; + } + } + if (nargs - optind != 2) { + fprintf(stderr, "invalid arguments for ipath\n"); + return 1; + } + + fd = open_file_or_dir(argv[optind+1]); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]); + return 12; + } + + return __ino_to_path_fd(atoll(argv[optind]), fd, verbose, + argv[optind+1]); +} + +int do_logical_to_ino(int nargs, char **argv) +{ + int ret; + int fd; + int i; + int verbose = 0; + int getpath = 1; + int bytes_left; + struct btrfs_ioctl_logical_ino_args loi; + struct btrfs_data_container *inodes; + char full_path[4096]; + char *path_ptr; + + optind = 1; + while (1) { + int c = getopt(nargs, argv, "Pv"); + if (c < 0) + break; + switch (c) { + case 'P': + getpath = 0; + break; + case 'v': + verbose = 1; + break; + default: + fprintf(stderr, "invalid arguments for ipath\n"); + return 1; + } + } + if (nargs - optind != 2) { + fprintf(stderr, "invalid arguments for ipath\n"); + return 1; + } + + inodes = malloc(4096); + if (!inodes) + return 1; + + loi.logical = atoll(argv[optind]); + loi.size = 4096; + loi.inodes = (u64)inodes; + + fd = open_file_or_dir(argv[optind+1]); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]); + ret = 12; + goto out; + } + + ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi); + if (ret) { + printf("ioctl ret=%d, error: %s\n", ret, strerror(errno)); + goto out; + } + + if (verbose) + printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, " + "cnt=%d, missed=%d\n", ret, + (unsigned long)inodes->bytes_left, + (unsigned long)inodes->bytes_missing, + inodes->elem_cnt, inodes->elem_missed); + + bytes_left = sizeof(full_path); + ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]); + path_ptr = full_path + ret; + bytes_left -= ret + 1; + BUG_ON(bytes_left < 0); + + for (i = 0; i < inodes->elem_cnt; i += 3) { + u64 inum = inodes->val[i]; + u64 offset = inodes->val[i+1]; + u64 root = inodes->val[i+2]; + int path_fd; + char *name; + + if (getpath) { + name = path_for_root(fd, root); + if (IS_ERR(name)) + return PTR_ERR(name); + if (!name) { + path_ptr[-1] = '\0'; + path_fd = fd; + } else { + path_ptr[-1] = '/'; + ret = snprintf(path_ptr, bytes_left, "%s", + name); + BUG_ON(ret >= bytes_left); + free(name); + path_fd = open_file_or_dir(full_path); + if (path_fd < 0) { + fprintf(stderr, "ERROR: can't access " + "'%s'\n", full_path); + goto out; + } + } + __ino_to_path_fd(inum, path_fd, verbose, full_path); + } else { + printf("inode %llu offset %llu root %llu\n", inum, + offset, root); + } + } + +out: + free(inodes); + return ret; +} + diff --git a/scrub.c b/cmds-scrub.c similarity index 100% rename from scrub.c rename to cmds-scrub.c diff --git a/cmds-subvolume.c b/cmds-subvolume.c new file mode 100644 index 00000000..931506c8 --- /dev/null +++ b/cmds-subvolume.c @@ -0,0 +1,444 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kerncompat.h" +#include "ioctl.h" + +#include "btrfs_cmds.h" + +/* btrfs-list.c */ +int list_subvols(int fd, int print_parent, int get_default); +int find_updated_files(int fd, u64 root_id, u64 oldest_gen); + +/* + * 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); +} + +int do_create_subvol(int argc, char **argv) +{ + int res, fddst, len, e; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args args; + char *dst = argv[1]; + + 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; + } + + len = strlen(newname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: subvolume name too long ('%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); + strncpy(args.name, newname, BTRFS_PATH_NAME_MAX); + res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); + e = errno; + + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot create subvolume - %s\n", + strerror(e)); + return 11; + } + + return 0; + +} + +/* + * 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); +} + +int do_delete_subvolume(int argc, char **argv) +{ + int res, fd, len, e; + struct btrfs_ioctl_vol_args args; + char *dname, *vname, *cpath; + char *path = argv[1]; + + 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: incorrect subvolume name ('%s')\n", + vname); + return 14; + } + + len = strlen(vname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: snapshot name too long ('%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); + strncpy(args.name, vname, BTRFS_PATH_NAME_MAX); + res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); + e = errno; + + close(fd); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot delete '%s/%s' - %s\n", + dname, vname, strerror(e)); + return 11; + } + + return 0; + +} + +int do_subvol_list(int argc, char **argv) +{ + int fd; + int ret; + int print_parent = 0; + char *subvol; + int optind = 1; + + while(1) { + int c = getopt(argc, argv, "p"); + if (c < 0) break; + switch(c) { + case 'p': + print_parent = 1; + optind++; + break; + } + } + + if (argc - optind != 1) { + fprintf(stderr, "ERROR: invalid arguments for subvolume list\n"); + return 1; + } + + subvol = argv[optind]; + + ret = test_issubvolume(subvol); + if (ret < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if (!ret) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); + return 12; + } + ret = list_subvols(fd, print_parent, 0); + if (ret) + return 19; + return 0; +} + +int do_clone(int argc, char **argv) +{ + char *subvol, *dst; + int res, fd, fddst, len, e, optind = 0, readonly = 0; + char *newname; + char *dstdir; + struct btrfs_ioctl_vol_args_v2 args; + + memset(&args, 0, sizeof(args)); + + while (1) { + int c = getopt(argc, argv, "r"); + + if (c < 0) + break; + switch (c) { + case 'r': + optind++; + readonly = 1; + break; + default: + fprintf(stderr, + "Invalid arguments for subvolume snapshot\n"); + free(argv); + return 1; + } + } + if (argc - optind != 3) { + fprintf(stderr, "Invalid arguments for subvolume snapshot\n"); + free(argv); + return 1; + } + + subvol = argv[optind+1]; + dst = argv[optind+2]; + + 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: incorrect snapshot name ('%s')\n", + newname); + return 14; + } + + len = strlen(newname); + if (len == 0 || len >= BTRFS_VOL_NAME_MAX) { + fprintf(stderr, "ERROR: snapshot name too long ('%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; + } + + 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; + strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX); + res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); + e = errno; + + close(fd); + close(fddst); + + if(res < 0 ){ + fprintf( stderr, "ERROR: cannot snapshot '%s' - %s\n", + subvol, strerror(e)); + return 11; + } + + return 0; + +} + +int do_get_default_subvol(int nargs, char **argv) +{ + int fd; + int ret; + char *subvol; + + subvol = argv[1]; + + ret = test_issubvolume(subvol); + if (ret < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if (!ret) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); + return 12; + } + ret = list_subvols(fd, 0, 1); + if (ret) + return 19; + return 0; +} + +int do_set_default_subvol(int nargs, char **argv) +{ + int ret=0, fd, e; + u64 objectid; + char *path = argv[2]; + char *subvolid = argv[1]; + + fd = open_file_or_dir(path); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access to '%s'\n", path); + return 12; + } + + objectid = (unsigned long long)strtoll(subvolid, NULL, 0); + if (errno == ERANGE) { + fprintf(stderr, "ERROR: invalid tree id (%s)\n",subvolid); + return 30; + } + ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid); + e = errno; + close(fd); + if( ret < 0 ){ + fprintf(stderr, "ERROR: unable to set a new default subvolume - %s\n", + strerror(e)); + return 30; + } + return 0; +} + +int do_find_newer(int argc, char **argv) +{ + int fd; + int ret; + char *subvol; + u64 last_gen; + + subvol = argv[1]; + last_gen = atoll(argv[2]); + + ret = test_issubvolume(subvol); + if (ret < 0) { + fprintf(stderr, "ERROR: error accessing '%s'\n", subvol); + return 12; + } + if (!ret) { + fprintf(stderr, "ERROR: '%s' is not a subvolume\n", subvol); + return 13; + } + + fd = open_file_or_dir(subvol); + if (fd < 0) { + fprintf(stderr, "ERROR: can't access '%s'\n", subvol); + return 12; + } + ret = find_updated_files(fd, 0, last_gen); + if (ret) + return 19; + return 0; +} + diff --git a/common.c b/common.c new file mode 100644 index 00000000..03f65703 --- /dev/null +++ b/common.c @@ -0,0 +1,46 @@ +/* + * 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 +#include +#include +#include + +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; +}