" 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"
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;
#include "libbcachefs/bcachefs_ioctl.h"
#include "cmds.h"
+#include "libbcachefs.h"
int cmd_assemble(int argc, char *argv[])
{
#include <unistd.h>
#include "libbcachefs/bcachefs_ioctl.h"
+#include "libbcachefs/super-io.h"
#include "cmds.h"
#include "libbcachefs.h"
#include "libbcachefs/opts.h"
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];
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);
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;
}
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]);
+
+ 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 <linux-bcache@vger.kernel.org>");
+ exit(EXIT_SUCCESS);
+}
- struct bch_ioctl_disk_set_state i = {
- .flags = flags,
- .new_state = read_string_list_or_die(argv[optind + 2],
- bch2_dev_state, "device state"),
+int cmd_device_resize(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL }
};
+ u64 size;
+ int opt;
- const char *dev = argv[optind + 1];
- if (!kstrtoull(dev, 10, &i.dev))
- i.flags |= BCH_BY_INDEX;
- else
- i.dev = (u64) dev;
+ 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");
- xioctl(fs.ioctl_fd, BCH_IOCTL_DISK_SET_STATE, &i);
+ 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;
}
#include "libbcachefs/opts.h"
#include "cmds.h"
+#include "libbcachefs.h"
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);
#include "libbcachefs/bcachefs_ioctl.h"
#include "cmds.h"
+#include "libbcachefs.h"
int cmd_run(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;
}
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[]);
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;
+}
#include <stdbool.h>
#include "libbcachefs/bcachefs_format.h"
+#include "libbcachefs/bcachefs_ioctl.h"
#include "tools-util.h"
#include "libbcachefs/vstructs.h"
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 */
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";
#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");
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
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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,
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 {
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 */