cbd/cbd/cbd.c

413 lines
11 KiB
C

/*
*/
#include <libcbd.h>
#include <cbdutil.h>
#include <getopt.h>
/*
* Parse argument into an unsigned 64-bit value.
*
* The following suffixes are recognized (case insensitive and
* always base two, as in LVM):
* b bytes
* s sectors
* k kilo (1024)
* m mega (1024^2)
* g giga (1024^3)
* t tera (1024^4)
* p peta (1024^5)
*
* If no suffix is given, bytes are assumed.
*/
static bool
parse_numeric_arg(const char* arg, uint64_t* val)
{
unsigned long ulval;
const char* end;
ulval = strtoul(arg, (char**)&end, 0);
if (ulval == ULONG_MAX || end == arg) {
return false;
}
*val = ulval;
switch (*end) {
case '\0':
case 'B':
case 'b':
/* No conversion */
break;
case 'S':
case 's':
*val <<= 9;
break;
case 'K':
case 'k':
*val <<= 10;
break;
case 'M':
case 'm':
*val <<= 20;
break;
case 'G':
case 'g':
*val <<= 30;
break;
case 'T':
case 't':
*val <<= 40;
break;
case 'P':
case 'p':
*val <<= 50;
break;
default:
return false;
}
return true;
}
static const char* progname;
static void __attribute__((noreturn))
usage(void)
{
fprintf(stderr, "Usage: %s [-h] [-v] <command> [options] <device>\n"
"\n", progname);
fprintf(stderr, "Global arguments:\n"
" -h --help Show this help message and exit\n"
" -v --verbose Increase verbose level\n"
"\n");
fprintf(stderr, "Commands:\n"
" format [opts] <device> Create (format) a compressed device\n"
" -S --pysical-size Physical size [device size]\n"
" -L --logical-blksize Logical block size\n"
" -P --pbat-len Physical block allocation table length [1]\n"
" -c --compress-factor Compression factor [2.0]\n"
" -l --logical-shift Logical block shift [4]\n"
" -s --size Logical size\n"
" -z --compress-alg Compression algorithm [lz4]\n"
" -Z --compress-level Compression level [?]\n"
" Note:\n"
" -c and -s are different ways of specifying the compressed device size.\n"
" Only one may be used, not both.\n"
" -l and -L are different ways of specifying the logical block size.\n"
" Only one may be used, not both.\n"
"\n"
" open [opts] <device> <name> Open an existing compressed device\n"
" create [opts] <device> <name> Alias for open\n"
" close [opts] <name> Close an opened compressed device\n"
" check [opts] <device> Check and repair a compressed device\n"
" -f --force Force check even if device seems clean\n"
" -n --assume-no Assume \"no\" no all questions\n"
" -y --assume-yes Assume \"yes\" to all questions\n"
" resize [opts] <device> Resize an existing compressed device\n"
" -s --size New logical size [use all]\n"
" stats [opts] <name> Show device statistics\n"
"\n");
exit(1);
}
static int
do_format(int argc, char** argv)
{
static const char short_opts[] = "S:L:P:c:l:s:z:Z:";
static const struct option long_opts[] = {
{ "physical-size", required_argument, NULL, 'S' },
{ "logical-blksize", required_argument, NULL, 'L' },
{ "pbat-len", required_argument, NULL, 'P' },
{ "compress-factor", required_argument, NULL, 'c' },
{ "logical-shift", required_argument, NULL, 'l' },
{ "size", required_argument, NULL, 's' },
{ "compress-alg", required_argument, NULL, 'z' },
{ "compress-level", required_argument, NULL, 'Z' },
{ NULL, no_argument, NULL, 0 }
};
char opt;
uint64_t optval;
uint64_t psize = 0;
uint16_t pbatlen = 1;
uint16_t lshift = 0;
uint64_t lsize = 0;
enum cbd_alg alg = CBD_ALG_LZ4;
uint level = 1;
const char* dev;
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 'S':
if (!parse_numeric_arg(optarg, &optval)) {
error("Failed to parse \"%s\"\n", optarg);
}
psize = optval;
break;
case 'L':
if (!parse_numeric_arg(optarg, &optval)) {
error("Failed to parse \"%s\"\n", optarg);
}
if ((optval & (optval-1)) || optval < PBLK_SIZE) {
error("Size \"%s\" is not a valid logical block size\n", optarg);
}
lshift = (optval >> PBLK_SHIFT);
break;
case 'P':
if (!parse_numeric_arg(optarg, &optval)) {
error("Failed to parse \"%s\"\n", optarg);
}
pbatlen = optval;
break;
case 'c':
error("Implement me!\n");
break;
case 'l':
if (!parse_numeric_arg(optarg, &optval)) {
error("Failed to parse \"%s\"\n", optarg);
}
lshift = optval;
break;
case 's':
if (!parse_numeric_arg(optarg, &optval)) {
error("Failed to parse \"%s\"\n", optarg);
}
lsize = optval;
break;
case 'z':
alg = CBD_ALG_NONE;
if (!strcmp(optarg, "lz4")) {
alg = CBD_ALG_LZ4;
}
if (!strcmp(optarg, "zlib")) {
alg = CBD_ALG_ZLIB;
}
if (alg == CBD_ALG_NONE) {
error("Invalid compression algorithm \"%s\"\n", optarg);
}
break;
case 'Z':
if (!parse_numeric_arg(optarg, &optval)) {
error("Failed to parse \"%s\"\n", optarg);
}
if (optval < 1 || optval > 9) {
error("Compression level \"%s\" out of bounds\n", optarg);
}
level = optval;
break;
default:
usage();
}
}
if (argc - optind != 1) {
usage();
}
dev = argv[optind++];
cbd_format(dev, psize, pbatlen, lshift, lsize, alg, level);
return 0;
}
static int
do_open(int argc, char** argv)
{
char dev[PATH_MAX];
const char* name;
if (argc != 3) {
usage();
}
strcpy(dev, argv[1]);
if (dev[0] != '/') {
sprintf(dev, "/dev/mapper/%s", argv[1]);
}
name = argv[2];
cbd_open(dev, name);
return 0;
}
static int
do_close(int argc, char** argv)
{
const char* dev;
if (argc != 2) {
usage();
}
dev = argv[1];
cbd_close(dev);
return 0;
}
static int
do_stats(int argc, char** argv)
{
const char* name;
if (argc != 2) {
usage();
}
name = argv[1];
cbd_stats(name);
return 0;
}
static int
do_check(int argc, char** argv)
{
static const char short_opts[] = "fny";
static const struct option long_opts[] = {
{ "force", no_argument, NULL, 'f' },
{ "assume-no", no_argument, NULL, 'n' },
{ "assume-yes", no_argument, NULL, 'y' },
{ NULL, no_argument, NULL, 0 }
};
char opt;
bool force = false;
tristate_t auto_response = t_none;
const char* dev;
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 'f':
force = true;
break;
case 'n':
if (auto_response == t_true) {
fprintf(stderr, "Cannot specify both -y and -n\n");
usage();
}
auto_response = t_false;
break;
case 'y':
if (auto_response == t_false) {
fprintf(stderr, "Cannot specify both -y and -n\n");
usage();
}
auto_response = t_true;
break;
default:
usage();
}
}
if (argc - optind != 1) {
usage();
}
dev = argv[optind++];
cbd_check(dev, force, auto_response);
return 0;
}
static int
do_resize(int argc, char** argv)
{
static const char short_opts[] = "s:";
static const struct option long_opts[] = {
{ "size", required_argument, NULL, 's' },
{ NULL, no_argument, NULL, 0 }
};
char opt;
uint64_t optval;
uint64_t lsize = 0;
char dev[PATH_MAX];
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 's':
if (!parse_numeric_arg(optarg, &optval)) {
fprintf(stderr, "Failed to parse \"%s\"\n", optarg);
usage();
}
lsize = optval;
break;
default:
usage();
}
}
if (argc - optind != 1) {
usage();
}
strcpy(dev, argv[optind]);
if (dev[0] != '/') {
sprintf(dev, "/dev/mapper/%s", argv[optind]);
}
++optind;
cbd_resize(dev, lsize);
return 0;
}
struct cmd_dispatch
{
const char* cmd;
int (*func)(int, char**);
};
static struct cmd_dispatch dispatch[] =
{
{ "format", do_format },
{ "open", do_open },
{ "create", do_open },
{ "close", do_close },
{ "stats", do_stats },
{ "check", do_check },
{ "resize", do_resize },
};
int
main(int argc, char** argv)
{
static const char short_opts[] = "+v";
static const struct option long_opts[] = {
{ "verbose", 0, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
char opt;
int verbose_level = 0; /* XXX: make global or ...? */
const char* cmd;
uint n;
progname = argv[0];
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 'v':
++verbose_level;
break;
default:
usage();
}
}
if (argc - optind < 1) {
usage();
}
cmd = argv[optind];
argc -= optind;
argv += optind;
optind = 0;
for (n = 0; n < ARRAY_SIZE(dispatch); ++n) {
if (!strcmp(cmd, dispatch[n].cmd)) {
return dispatch[n].func(argc, argv);
}
}
fprintf(stderr, "Unrecognized command \"%s\"\n", cmd);
usage();
/* NOTREACHED */
}