diff options
Diffstat (limited to 'tools-util.c')
-rw-r--r-- | tools-util.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/tools-util.c b/tools-util.c new file mode 100644 index 0000000..68a42a9 --- /dev/null +++ b/tools-util.c @@ -0,0 +1,314 @@ +#include <alloca.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <linux/fs.h> +#include <math.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <uuid/uuid.h> + +#include "ccan/crc/crc.h" + +#include "tools-util.h" + +/* Integer stuff: */ + +struct units_buf pr_units(u64 v, enum units units) +{ + struct units_buf ret; + + switch (units) { + case BYTES: + snprintf(ret.b, sizeof(ret.b), "%llu", v << 9); + break; + case SECTORS: + snprintf(ret.b, sizeof(ret.b), "%llu", v); + break; + case HUMAN_READABLE: + v <<= 9; + + if (v >= 1024) { + int exp = log(v) / log(1024); + snprintf(ret.b, sizeof(ret.b), "%.1f%c", + v / pow(1024, exp), + "KMGTPE"[exp-1]); + } else { + snprintf(ret.b, sizeof(ret.b), "%llu", v); + } + + break; + } + + return ret; +} + +/* Argument parsing stuff: */ + +long strtoul_or_die(const char *p, size_t max, const char *msg) +{ + errno = 0; + long v = strtol(p, NULL, 10); + if (errno || v < 0 || v >= max) + die("Invalid %s %zi", msg, v); + + return v; +} + +u64 hatoi(const char *s) +{ + char *e; + long long i = strtoll(s, &e, 10); + switch (*e) { + case 't': + case 'T': + i *= 1024; + case 'g': + case 'G': + i *= 1024; + case 'm': + case 'M': + i *= 1024; + case 'k': + case 'K': + i *= 1024; + } + return i; +} + +unsigned hatoi_validate(const char *s, const char *msg) +{ + u64 v = hatoi(s); + + if (v & (v - 1)) + die("%s must be a power of two", msg); + + v /= 512; + + if (v > USHRT_MAX) + die("%s too large\n", msg); + + if (!v) + die("%s too small\n", msg); + + return v; +} + +unsigned nr_args(char * const *args) +{ + unsigned i; + + for (i = 0; args[i]; i++) + ; + + return i; +} + +/* File parsing (i.e. sysfs) */ + +char *read_file_str(int dirfd, const char *path) +{ + int fd = openat(dirfd, path, O_RDONLY); + + if (fd < 0) + die("Unable to open %s\n", path); + + struct stat statbuf; + if (fstat(fd, &statbuf) < 0) + die("fstat error\n"); + + char *buf = malloc(statbuf.st_size + 1); + + int len = read(fd, buf, statbuf.st_size); + if (len < 0) + die("read error while reading from file %s\n", path); + + buf[len] = '\0'; + if (len && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + close(fd); + + return buf; +} + +u64 read_file_u64(int dirfd, const char *path) +{ + char *buf = read_file_str(dirfd, path); + u64 ret = strtoll(buf, NULL, 10); + + free(buf); + return ret; +} + +/* String list options: */ + +ssize_t read_string_list(const char *buf, const char * const list[]) +{ + size_t i; + char *s, *d = strdup(buf); + if (!d) + return -ENOMEM; + + s = strim(d); + + for (i = 0; list[i]; i++) + if (!strcmp(list[i], s)) + break; + + free(d); + + if (!list[i]) + return -EINVAL; + + return i; +} + +ssize_t read_string_list_or_die(const char *opt, const char * const list[], + const char *msg) +{ + ssize_t v = read_string_list(opt, list); + if (v < 0) + die("Bad %s %s", msg, opt); + + return v; +} + +void print_string_list(const char * const list[], size_t selected) +{ + size_t i; + + for (i = 0; list[i]; i++) { + if (i) + putchar(' '); + printf(i == selected ? "[%s] ": "%s", list[i]); + } +} + +/* Returns size of file or block device, in units of 512 byte sectors: */ +u64 get_size(const char *path, int fd) +{ + struct stat statbuf; + if (fstat(fd, &statbuf)) + die("Error statting %s: %s", path, strerror(errno)); + + if (!S_ISBLK(statbuf.st_mode)) + return statbuf.st_size >> 9; + + u64 ret; + if (ioctl(fd, BLKGETSIZE64, &ret)) + die("Error getting block device size on %s: %s\n", + path, strerror(errno)); + + return ret >> 9; +} + +/* Returns blocksize in units of 512 byte sectors: */ +unsigned get_blocksize(const char *path, int fd) +{ + struct stat statbuf; + if (fstat(fd, &statbuf)) + die("Error statting %s: %s", path, strerror(errno)); + + if (!S_ISBLK(statbuf.st_mode)) + return statbuf.st_blksize >> 9; + + unsigned ret; + if (ioctl(fd, BLKPBSZGET, &ret)) + die("Error getting blocksize on %s: %s\n", + path, strerror(errno)); + + return ret >> 9; +} + +/* Global control device: */ +int bcachectl_open(void) +{ + int fd = open("/dev/bcache-ctl", O_RDWR); + if (fd < 0) + die("Can't open bcache device: %s", strerror(errno)); + + return fd; +} + +/* Filesystem handles (ioctl, sysfs dir): */ + +#define SYSFS_BASE "/sys/fs/bcache/" + +struct bcache_handle bcache_fs_open(const char *path) +{ + struct bcache_handle ret; + uuid_t tmp; + + if (!uuid_parse(path, tmp)) { + /* It's a UUID, look it up in sysfs: */ + + char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(path) + 1); + sprintf(sysfs, "%s%s", SYSFS_BASE, path); + + ret.sysfs_fd = open(sysfs, O_RDONLY); + if (!ret.sysfs_fd) + die("Unable to open %s\n", path); + + char *minor = read_file_str(ret.sysfs_fd, "minor"); + char *ctl = alloca(20 + strlen(minor)); + + sprintf(ctl, "/dev/bcache%s-ctl", minor); + free(minor); + + ret.ioctl_fd = open(ctl, O_RDWR); + if (ret.ioctl_fd < 0) + die("Error opening control device: %s\n", + strerror(errno)); + } else { + /* It's a path: */ + + ret.ioctl_fd = open(path, O_RDONLY); + if (ret.ioctl_fd < 0) + die("Error opening %s: %s\n", + path, strerror(errno)); + + struct bch_ioctl_query_uuid uuid; + if (ioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid)) + die("ioctl error (not a bcache fs?): %s\n", + strerror(errno)); + + char uuid_str[40]; + uuid_unparse(uuid.uuid.b, uuid_str); + + char *sysfs = alloca(strlen(SYSFS_BASE) + strlen(uuid_str) + 1); + sprintf(sysfs, "%s%s", SYSFS_BASE, uuid_str); + + ret.sysfs_fd = open(sysfs, O_RDONLY); + if (ret.sysfs_fd < 0) + die("Unable to open sysfs dir %s: %s\n", + sysfs, strerror(errno)); + } + + return ret; +} + +bool ask_proceed(void) +{ + const char *short_yes = "yY"; + char *buf = NULL; + size_t buflen = 0; + bool ret; + + fputs("Proceed anyway? (y,n) ", stdout); + + if (getline(&buf, &buflen, stdin) < 0) + die("error reading from standard input"); + + ret = strchr(short_yes, buf[0]); + free(buf); + return ret; +} |