diff --git a/bcachefs.c b/bcachefs.c index 79533411..8408c297 100644 --- a/bcachefs.c +++ b/bcachefs.c @@ -51,6 +51,7 @@ static void usage(void) " device offline Take a device offline, without removing it\n" " device evacuate Migrate data off of a specific device\n" " device set-state Mark a device as failed\n" + " device resize Resize filesystem on a device\n" "\n" "Encryption:\n" " unlock Unlock an encrypted filesystem prior to running/mounting\n" @@ -112,6 +113,8 @@ static int device_cmds(int argc, char *argv[]) return cmd_device_offline(argc, argv); if (!strcmp(cmd, "set-state")) return cmd_device_set_state(argc, argv); + if (!strcmp(cmd, "resize")) + return cmd_device_resize(argc, argv); usage(); return 0; diff --git a/cmd_assemble.c b/cmd_assemble.c index 5fbabdd9..b2a832e6 100644 --- a/cmd_assemble.c +++ b/cmd_assemble.c @@ -9,6 +9,7 @@ #include "libbcachefs/bcachefs_ioctl.h" #include "cmds.h" +#include "libbcachefs.h" int cmd_assemble(int argc, char *argv[]) { diff --git a/cmd_device.c b/cmd_device.c index f2d751c5..f8e0cfbf 100644 --- a/cmd_device.c +++ b/cmd_device.c @@ -13,6 +13,7 @@ #include #include "libbcachefs/bcachefs_ioctl.h" +#include "libbcachefs/super-io.h" #include "cmds.h" #include "libbcachefs.h" #include "libbcachefs/opts.h" @@ -99,7 +100,7 @@ int cmd_device_show(int argc, char *argv[]) if (argc != 2) die("Please supply a single device"); - struct bcache_handle fs = bcache_fs_open(argv[1]); + struct bchfs_handle fs = bcache_fs_open(argv[1]); struct dirent *entry; struct bcache_dev devices[256]; @@ -240,7 +241,7 @@ int cmd_device_add(int argc, char *argv[]) if (argc - optind != 2) die("Please supply a filesystem and a device to add"); - struct bcache_handle fs = bcache_fs_open(argv[optind]); + struct bchfs_handle fs = bcache_fs_open(argv[optind]); dev_opts.path = argv[optind + 1]; dev_opts.fd = open_for_format(dev_opts.path, force); @@ -255,9 +256,7 @@ int cmd_device_add(int argc, char *argv[]) fsync(dev_opts.fd); close(dev_opts.fd); - struct bch_ioctl_disk i = { .dev = (__u64) dev_opts.path, }; - - xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ADD, &i); + bchu_disk_add(fs, dev_opts.path); return 0; } @@ -436,20 +435,113 @@ int cmd_device_set_state(int argc, char *argv[]) if (argc - optind != 3) die("Please supply a filesystem, device and state"); - struct bcache_handle fs = bcache_fs_open(argv[optind]); + struct bchfs_handle fs = bcache_fs_open(argv[optind]); - struct bch_ioctl_disk_set_state i = { - .flags = flags, - .new_state = read_string_list_or_die(argv[optind + 2], - bch2_dev_state, "device state"), - }; - - const char *dev = argv[optind + 1]; - if (!kstrtoull(dev, 10, &i.dev)) - i.flags |= BCH_BY_INDEX; - else - i.dev = (u64) dev; - - xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i); + bchu_disk_set_state(fs, argv[optind + 1], + read_string_list_or_die(argv[optind + 2], + bch2_dev_state, "device state"), + flags); + return 0; +} + +static void device_resize_usage(void) +{ + puts("bcachefs device resize \n" + "Usage: bcachefs device resize device [ size ]\n" + "\n" + "Options:\n" + " -h, --help display this help and exit\n" + "Report bugs to "); + exit(EXIT_SUCCESS); +} + +int cmd_device_resize(int argc, char *argv[]) +{ + static const struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL } + }; + u64 size; + int opt; + + while ((opt = getopt_long(argc, argv, "h", longopts, NULL)) != -1) + switch (opt) { + case 'h': + device_resize_usage(); + } + + if (argc < optind + 1) + die("Please supply a device to resize"); + + char *dev = argv[optind]; + int dev_fd = xopen(dev, O_RDONLY); + + if (argc == optind + 1) + size = get_size(dev, dev_fd); + else if (bch2_strtoull_h(argv[optind + 1], &size)) + die("invalid size"); + + size >>= 9; + + if (argc > optind + 2) + die("Too many arguments"); + + struct stat dev_stat = xfstat(dev_fd); + + char *mount = dev_to_mount(dev); + if (mount) { + if (!S_ISBLK(dev_stat.st_mode)) + die("%s is mounted but isn't a block device?!", dev); + + printf("Doing online resize of %s\n", dev); + + struct bchfs_handle fs = bcache_fs_open(mount); + + unsigned idx = bchu_disk_get_idx(fs, dev_stat.st_rdev); + + struct bch_sb *sb = bchu_read_super(fs, -1); + if (idx >= sb->nr_devices) + die("error reading superblock: dev idx >= sb->nr_devices"); + + struct bch_sb_field_members *mi = bch2_sb_get_members(sb); + if (!mi) + die("error reading superblock: no member info"); + + /* could also just read this out of sysfs... meh */ + struct bch_member *m = mi->members + idx; + + u64 nbuckets = size / le16_to_cpu(m->bucket_size); + + printf("resizing %s to %llu buckets\n", dev, nbuckets); + bchu_disk_resize(fs, idx, nbuckets); + } else { + printf("Doing offline resize of %s\n", dev); + + struct bch_fs *c = NULL; + struct bch_opts opts = bch2_opts_empty(); + const char *err = bch2_fs_open(&dev, 1, opts, &c); + if (err) + die("error opening %s: %s", argv[optind], err); + + struct bch_dev *ca, *resize = NULL; + unsigned i; + + for_each_online_member(ca, c, i) { + if (resize) + die("confused: more than one online device?"); + resize = ca; + percpu_ref_get(&resize->io_ref); + } + + u64 nbuckets = size / le16_to_cpu(resize->mi.bucket_size); + + printf("resizing %s to %llu buckets\n", dev, nbuckets); + int ret = bch2_dev_resize(c, resize, nbuckets); + if (ret) + fprintf(stderr, "resize error: %s\n", strerror(-ret)); + + percpu_ref_put(&resize->io_ref); + bch2_fs_stop(c); + } return 0; } diff --git a/cmd_fs.c b/cmd_fs.c index 65ee539e..3f64161f 100644 --- a/cmd_fs.c +++ b/cmd_fs.c @@ -8,6 +8,7 @@ #include "libbcachefs/opts.h" #include "cmds.h" +#include "libbcachefs.h" static inline int printf_pad(unsigned pad, const char * fmt, ...) { @@ -26,21 +27,11 @@ static inline int printf_pad(unsigned pad, const char * fmt, ...) static void print_fs_usage(const char *path, enum units units) { - unsigned i, j, nr_devices = 4; - struct bcache_handle fs = bcache_fs_open(path); - struct bch_ioctl_usage *u = NULL; + unsigned i, j; char uuid[40]; - while (1) { - u = xrealloc(u, sizeof(*u) + sizeof(u->devs[0]) * nr_devices); - u->nr_devices = nr_devices; - - if (!ioctl(fs.ioctl_fd, BCH_IOCTL_USAGE, u)) - break; - if (errno != ENOSPC) - die("BCH_IOCTL_USAGE error: %m"); - nr_devices *= 2; - } + struct bchfs_handle fs = bcache_fs_open(path); + struct bch_ioctl_usage *u = bchu_usage(fs); uuid_unparse(fs.uuid.b, uuid); printf("Filesystem %s:\n", uuid); diff --git a/cmd_run.c b/cmd_run.c index bbb37e5d..673d519a 100644 --- a/cmd_run.c +++ b/cmd_run.c @@ -13,6 +13,7 @@ #include "libbcachefs/bcachefs_ioctl.h" #include "cmds.h" +#include "libbcachefs.h" int cmd_run(int argc, char *argv[]) { @@ -24,7 +25,7 @@ int cmd_stop(int argc, char *argv[]) if (argc != 2) die("Please supply a filesystem"); - struct bcache_handle fs = bcache_fs_open(argv[1]); + struct bchfs_handle fs = bcache_fs_open(argv[1]); xioctl(fs.ioctl_fd, BCH_IOCTL_STOP); return 0; } diff --git a/cmds.h b/cmds.h index 1c07e072..e28a5045 100644 --- a/cmds.h +++ b/cmds.h @@ -25,6 +25,7 @@ int cmd_device_online(int argc, char *argv[]); int cmd_device_offline(int argc, char *argv[]); int cmd_device_evacuate(int argc, char *argv[]); int cmd_device_set_state(int argc, char *argv[]); +int cmd_device_resize(int argc, char *argv[]); int cmd_unlock(int argc, char *argv[]); int cmd_set_passphrase(int argc, char *argv[]); diff --git a/libbcachefs.c b/libbcachefs.c index 351eb31e..afbc8d7a 100644 --- a/libbcachefs.c +++ b/libbcachefs.c @@ -455,3 +455,57 @@ void bch2_super_print(struct bch_sb *sb, int units) BCH_MEMBER_DISCARD(m)); } } + +/* ioctl interface: */ + +/* Global control device: */ +int bcachectl_open(void) +{ + return xopen("/dev/bcachefs-ctl", O_RDWR); +} + +/* Filesystem handles (ioctl, sysfs dir): */ + +#define SYSFS_BASE "/sys/fs/bcachefs/" + +void bcache_fs_close(struct bchfs_handle fs) +{ + close(fs.ioctl_fd); + close(fs.sysfs_fd); +} + +struct bchfs_handle bcache_fs_open(const char *path) +{ + struct bchfs_handle ret; + + if (!uuid_parse(path, ret.uuid.b)) { + /* It's a UUID, look it up in sysfs: */ + char *sysfs = mprintf("%s%s", SYSFS_BASE, path); + ret.sysfs_fd = xopen(sysfs, O_RDONLY); + + char *minor = read_file_str(ret.sysfs_fd, "minor"); + char *ctl = mprintf("/dev/bcachefs%s-ctl", minor); + ret.ioctl_fd = xopen(ctl, O_RDWR); + + free(sysfs); + free(minor); + free(ctl); + } else { + /* It's a path: */ + ret.ioctl_fd = xopen(path, O_RDONLY); + + struct bch_ioctl_query_uuid uuid; + xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid); + + ret.uuid = uuid.uuid; + + char uuid_str[40]; + uuid_unparse(uuid.uuid.b, uuid_str); + + char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str); + ret.sysfs_fd = xopen(sysfs, O_RDONLY); + free(sysfs); + } + + return ret; +} diff --git a/libbcachefs.h b/libbcachefs.h index 836cb7a9..97f4b2d8 100644 --- a/libbcachefs.h +++ b/libbcachefs.h @@ -5,6 +5,7 @@ #include #include "libbcachefs/bcachefs_format.h" +#include "libbcachefs/bcachefs_ioctl.h" #include "tools-util.h" #include "libbcachefs/vstructs.h" @@ -76,4 +77,103 @@ struct bch_sb *__bch2_super_read(int, u64); void bch2_super_print(struct bch_sb *, int); +/* ioctl interface: */ + +int bcachectl_open(void); + +struct bchfs_handle { + uuid_le uuid; + int ioctl_fd; + int sysfs_fd; +}; + +void bcache_fs_close(struct bchfs_handle); +struct bchfs_handle bcache_fs_open(const char *); + +static inline void bchu_disk_add(struct bchfs_handle fs, char *dev) +{ + struct bch_ioctl_disk i = { .dev = (__u64) dev, }; + + xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_ADD, &i); +} + +static inline void bchu_disk_set_state(struct bchfs_handle fs, const char *dev, + unsigned new_state, unsigned flags) +{ + struct bch_ioctl_disk_set_state i = { + .flags = flags, + .new_state = new_state, + }; + + if (!kstrtoull(dev, 10, &i.dev)) + i.flags |= BCH_BY_INDEX; + else + i.dev = (u64) dev; + + xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i); +} + +static inline struct bch_ioctl_usage *bchu_usage(struct bchfs_handle fs) +{ + struct bch_ioctl_usage *u = NULL; + unsigned nr_devices = 4; + + while (1) { + u = xrealloc(u, sizeof(*u) + sizeof(u->devs[0]) * nr_devices); + u->nr_devices = nr_devices; + + if (!ioctl(fs.ioctl_fd, BCH_IOCTL_USAGE, u)) + return u; + + if (errno != ENOSPC) + die("BCH_IOCTL_USAGE error: %m"); + nr_devices *= 2; + } +} + +static inline struct bch_sb *bchu_read_super(struct bchfs_handle fs, unsigned idx) +{ + size_t size = 4096; + struct bch_sb *sb = NULL; + + while (1) { + sb = xrealloc(sb, size); + struct bch_ioctl_read_super i = { + .size = size, + .sb = (u64) sb, + }; + + if (idx != -1) { + i.flags |= BCH_READ_DEV|BCH_BY_INDEX; + i.dev = idx; + } + + if (!ioctl(fs.ioctl_fd, BCH_IOCTL_READ_SUPER, &i)) + return sb; + if (errno != ERANGE) + die("BCH_IOCTL_READ_SUPER error: %m"); + size *= 2; + } +} + +static inline unsigned bchu_disk_get_idx(struct bchfs_handle fs, dev_t dev) +{ + struct bch_ioctl_disk_get_idx i = { .dev = dev }; + + return xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_GET_IDX, &i); +} + +static inline void bchu_disk_resize(struct bchfs_handle fs, + unsigned idx, + u64 nbuckets) +{ + struct bch_ioctl_disk_resize i = { + .flags = BCH_BY_INDEX, + .dev = idx, + .nbuckets = nbuckets, + }; + + xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_RESIZE, &i); +} + #endif /* _LIBBCACHE_H */ diff --git a/tools-util.c b/tools-util.c index 70f5558b..d5450495 100644 --- a/tools-util.c +++ b/tools-util.c @@ -262,58 +262,6 @@ int open_for_format(const char *dev, bool force) return fd; } -/* Global control device: */ -int bcachectl_open(void) -{ - return xopen("/dev/bcachefs-ctl", O_RDWR); -} - -/* Filesystem handles (ioctl, sysfs dir): */ - -#define SYSFS_BASE "/sys/fs/bcachefs/" - -void bcache_fs_close(struct bcache_handle fs) -{ - close(fs.ioctl_fd); - close(fs.sysfs_fd); -} - -struct bcache_handle bcache_fs_open(const char *path) -{ - struct bcache_handle ret; - - if (!uuid_parse(path, ret.uuid.b)) { - /* It's a UUID, look it up in sysfs: */ - char *sysfs = mprintf("%s%s", SYSFS_BASE, path); - ret.sysfs_fd = xopen(sysfs, O_RDONLY); - - char *minor = read_file_str(ret.sysfs_fd, "minor"); - char *ctl = mprintf("/dev/bcachefs%s-ctl", minor); - ret.ioctl_fd = xopen(ctl, O_RDWR); - - free(sysfs); - free(minor); - free(ctl); - } else { - /* It's a path: */ - ret.ioctl_fd = xopen(path, O_RDONLY); - - struct bch_ioctl_query_uuid uuid; - xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid); - - ret.uuid = uuid.uuid; - - char uuid_str[40]; - uuid_unparse(uuid.uuid.b, uuid_str); - - char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str); - ret.sysfs_fd = xopen(sysfs, O_RDONLY); - free(sysfs); - } - - return ret; -} - bool ask_yn(void) { const char *short_yes = "yY"; @@ -586,9 +534,9 @@ u32 crc32c(u32 crc, const void *buf, size_t size) #endif /* HAVE_WORKING_IFUNC */ -char *dev_to_path(dev_t dev) +char *dev_to_name(dev_t dev) { - char *line = NULL, *name = NULL, *path = NULL; + char *line = NULL, *name = NULL; size_t n = 0; FILE *f = fopen("/proc/partitions", "r"); @@ -602,17 +550,58 @@ char *dev_to_path(dev_t dev) name = realloc(name, n + 1); if (sscanf(line, " %u %u %llu %s", &ma, &mi, §ors, name) == 4 && - ma == major(dev) && mi == minor(dev)) { - path = mprintf("/dev/%s", name); - break; - } - + ma == major(dev) && mi == minor(dev)) + goto found; } + free(name); + name = NULL; +found: fclose(f); free(line); + return name; +} + +char *dev_to_path(dev_t dev) +{ + char *name = dev_to_name(dev); + if (!name) + return NULL; + + char *path = mprintf("/dev/%s", name); + free(name); return path; } +char *dev_to_mount(char *dev) +{ + char *line = NULL, *ret = NULL; + size_t n = 0; + + FILE *f = fopen("/proc/mounts", "r"); + if (!f) + die("error opening /proc/mounts: %m"); + + while (getline(&line, &n, f) != -1) { + char *d, *p = line; + char *devs = strsep(&p, " "); + char *mount = strsep(&p, " "); + + if (!devs || !mount) + continue; + + p = devs; + while ((d = strsep(&p, ":"))) + if (!strcmp(d, dev)) { + ret = strdup(mount); + goto found; + } + } +found: + fclose(f); + free(line); + return ret; +} + #endif diff --git a/tools-util.h b/tools-util.h index 649ea9bb..dcca376b 100644 --- a/tools-util.h +++ b/tools-util.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -39,10 +40,12 @@ struct stat xfstat(int); #define xopen(...) xopenat(AT_FDCWD, __VA_ARGS__) #define xioctl(_fd, _nr, ...) \ -do { \ - if (ioctl((_fd), (_nr), ##__VA_ARGS__)) \ +({ \ + int _ret = ioctl((_fd), (_nr), ##__VA_ARGS__); \ + if (_ret < 0) \ die(#_nr " ioctl error: %m"); \ -} while (0) + _ret; \ +}) enum units { BYTES, @@ -68,17 +71,6 @@ u64 get_size(const char *, int); unsigned get_blocksize(const char *, int); int open_for_format(const char *, bool); -int bcachectl_open(void); - -struct bcache_handle { - uuid_le uuid; - int ioctl_fd; - int sysfs_fd; -}; - -void bcache_fs_close(struct bcache_handle); -struct bcache_handle bcache_fs_open(const char *); - bool ask_yn(void); struct range { @@ -155,6 +147,8 @@ unsigned hatoi_validate(const char *, const char *); u32 crc32c(u32, const void *, size_t); +char *dev_to_name(dev_t); char *dev_to_path(dev_t); +char *dev_to_mount(char *); #endif /* _TOOLS_UTIL_H */