diff --git a/cmds-filesystem.c b/cmds-filesystem.c index 25c4174c..780baad8 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -14,6 +14,7 @@ * Boston, MA 021110-1307, USA. */ +#define _XOPEN_SOURCE 500 #include #include #include @@ -22,6 +23,8 @@ #include #include #include +#include +#include #include "kerncompat.h" #include "ctree.h" @@ -348,6 +351,7 @@ static const char * const cmd_defrag_usage[] = { "Defragment a file or a directory", "", "-v be verbose", + "-r defragment files recursively", "-c[zlib,lzo] compress the file while defragmenting", "-f flush data to disk immediately after defragmenting", "-s start defragment only from byte onward", @@ -356,6 +360,58 @@ static const char * const cmd_defrag_usage[] = { NULL }; +static int do_defrag(int fd, int fancy_ioctl, + struct btrfs_ioctl_defrag_range_args *range) +{ + int ret; + + if (!fancy_ioctl) + ret = ioctl(fd, BTRFS_IOC_DEFRAG, NULL); + else + ret = ioctl(fd, BTRFS_IOC_DEFRAG_RANGE, range); + + return ret; +} + +static int defrag_global_fancy_ioctl; +static struct btrfs_ioctl_defrag_range_args defrag_global_range; +static int defrag_global_verbose; +static int defrag_global_errors; +static int defrag_callback(const char *fpath, const struct stat *sb, + int typeflag, struct FTW *ftwbuf) +{ + int ret = 0; + int e = 0; + int fd = 0; + + if (typeflag == FTW_F) { + if (defrag_global_verbose) + printf("%s\n", fpath); + fd = open(fpath, O_RDWR); + e = errno; + if (fd < 0) + goto error; + ret = do_defrag(fd, defrag_global_fancy_ioctl, &defrag_global_range); + e = errno; + close(fd); + if (ret && e == ENOTTY) { + fprintf(stderr, "ERROR: defrag range ioctl not " + "supported in this kernel, please try " + "without any options.\n"); + defrag_global_errors++; + return ENOTTY; + } + if (ret) + goto error; + } + return 0; + +error: + fprintf(stderr, "ERROR: defrag failed on %s - %s\n", fpath, strerror(e)); + defrag_global_errors++; + return 0; +} + static int cmd_defrag(int argc, char **argv) { int fd; @@ -364,18 +420,20 @@ static int cmd_defrag(int argc, char **argv) u64 len = (u64)-1; u32 thresh = 0; int i; - int errors = 0; + int recursive = 0; int ret = 0; - int verbose = 0; - int fancy_ioctl = 0; struct btrfs_ioctl_defrag_range_args range; - int e=0; + int e = 0; int compress_type = BTRFS_COMPRESS_NONE; - DIR *dirstream = NULL; + DIR *dirstream; + defrag_global_errors = 0; + defrag_global_verbose = 0; + defrag_global_errors = 0; + defrag_global_fancy_ioctl = 0; optind = 1; while(1) { - int c = getopt(argc, argv, "vc::fs:l:t:"); + int c = getopt(argc, argv, "vrc::fs:l:t:"); if (c < 0) break; @@ -384,26 +442,29 @@ static int cmd_defrag(int argc, char **argv) compress_type = BTRFS_COMPRESS_ZLIB; if (optarg) compress_type = parse_compress_type(optarg); - fancy_ioctl = 1; + defrag_global_fancy_ioctl = 1; break; case 'f': flush = 1; - fancy_ioctl = 1; + defrag_global_fancy_ioctl = 1; break; case 'v': - verbose = 1; + defrag_global_verbose = 1; break; case 's': start = parse_size(optarg); - fancy_ioctl = 1; + defrag_global_fancy_ioctl = 1; break; case 'l': len = parse_size(optarg); - fancy_ioctl = 1; + defrag_global_fancy_ioctl = 1; break; case 't': thresh = parse_size(optarg); - fancy_ioctl = 1; + defrag_global_fancy_ioctl = 1; + break; + case 'r': + recursive = 1; break; default: usage(cmd_defrag_usage); @@ -413,55 +474,72 @@ static int cmd_defrag(int argc, char **argv) if (check_argc_min(argc - optind, 1)) usage(cmd_defrag_usage); - memset(&range, 0, sizeof(range)); - range.start = start; - range.len = len; - range.extent_thresh = thresh; + memset(&defrag_global_range, 0, sizeof(range)); + defrag_global_range.start = start; + defrag_global_range.len = len; + defrag_global_range.extent_thresh = thresh; if (compress_type) { - range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; - range.compress_type = compress_type; + defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_COMPRESS; + defrag_global_range.compress_type = compress_type; } if (flush) - range.flags |= BTRFS_DEFRAG_RANGE_START_IO; + defrag_global_range.flags |= BTRFS_DEFRAG_RANGE_START_IO; for (i = optind; i < argc; i++) { - if (verbose) - printf("%s\n", argv[i]); + dirstream = NULL; fd = open_file_or_dir(argv[i], &dirstream); if (fd < 0) { - fprintf(stderr, "failed to open %s\n", argv[i]); - perror("open:"); - errors++; + fprintf(stderr, "ERROR: failed to open %s - %s\n", argv[i], + strerror(errno)); + defrag_global_errors++; + close_file_or_dir(fd, dirstream); 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 (recursive) { + struct stat st; + + fstat(fd, &st); + if (S_ISDIR(st.st_mode)) { + ret = nftw(argv[i], defrag_callback, 10, + FTW_MOUNT | FTW_PHYS); + if (ret == ENOTTY) + exit(1); + /* errors are handled in the callback */ + ret = 0; + } else { + if (defrag_global_verbose) + printf("%s\n", argv[i]); + ret = do_defrag(fd, defrag_global_fancy_ioctl, + &defrag_global_range); + e = errno; } + } else { + if (defrag_global_verbose) + printf("%s\n", argv[i]); + ret = do_defrag(fd, defrag_global_fancy_ioctl, + &defrag_global_range); e = errno; } + close_file_or_dir(fd, dirstream); + if (ret && e == ENOTTY) { + fprintf(stderr, "ERROR: defrag range ioctl not " + "supported in this kernel, please try " + "without any options.\n"); + defrag_global_errors++; + break; + } if (ret) { fprintf(stderr, "ERROR: defrag failed on %s - %s\n", argv[i], strerror(e)); - errors++; + defrag_global_errors++; } - close_file_or_dir(fd, dirstream); } - if (verbose) + if (defrag_global_verbose) printf("%s\n", BTRFS_BUILD_VERSION); - if (errors) - fprintf(stderr, "total %d failures\n", errors); + if (defrag_global_errors) + fprintf(stderr, "total %d failures\n", defrag_global_errors); - return !!errors; + return !!defrag_global_errors; } static const char * const cmd_resize_usage[] = {