diff --git a/Makefile b/Makefile index b2824c9..c12e00e 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ f3probe: libutils.o libdevs.o libprobe.o utils.o f3probe.o f3brew: libutils.o libdevs.o f3brew.o $(CC) -o $@ $^ -lm -ludev -f3fix: f3fix.o +f3fix: utils.o f3fix.o $(CC) -o $@ $^ -lparted -include *.d diff --git a/f3fix.c b/f3fix.c index 5157ed2..50fe5ee 100644 --- a/f3fix.c +++ b/f3fix.c @@ -1,24 +1,197 @@ -#include -#include +#include +#include #include +#include "version.h" + +/* XXX Refactor utils library since f3probe barely uses it. */ +#include "utils.h" + /* TODO Modify f3probe to suggest using f3fix when the drive is fake. * Wait until f3fix is stable to implement this. */ -/* TODO Add parameters. */ +/* Argp's global variables. */ +const char *argp_program_version = "F3 Fix " F3_STR_VERSION; -/* TODO List types of the partition table. */ +/* Arguments. */ +static char adoc[] = ""; + +static char doc[] = "F3 Fix -- edit the partition table of " + "a fake flash drive to have a single partition that fully covers " + "the real capacity of the drive"; + +static struct argp_option options[] = { + {"disk-type", 'd', "TYPE", 0, + "Disk type of the partition table", 2}, + {"fs-type", 'f', "TYPE", 0, + "Type of the file system of the partition", 0}, + {"boot", 'b', NULL, 0, + "Mark the partition for boot", 0}, + {"no-boot", 'n', NULL, 0, + "Do not mark the partition for boot", 0}, + {"first-sec", 'a', "SEC-NUM", 0, + "Sector where the partition starts", 0}, + {"last-sec", 'l', "SEC-NUM", 0, + "Sector where the partition ends", 0}, + {"list-disk-types", 'k', NULL, 0, + "List all supported disk types", 3}, + {"list-fs-types", 's', NULL, 0, + "List all supported types of file systems", 0}, + { 0 } +}; + +struct args { + bool list_disk_types; + bool list_fs_types; + + bool boot; + + /* 29 free bytes. */ + + const char *dev_filename; + PedDiskType *disk_type; + PedFileSystemType *fs_type; + PedSector first_sec; + PedSector last_sec; +}; + +static long long arg_to_long_long(const struct argp_state *state, + const char *arg) +{ + char *end; + long long ll = strtoll(arg, &end, 0); + if (!arg) + argp_error(state, "An integer must be provided"); + if (!*arg || *end) + argp_error(state, "`%s' is not an integer", arg); + return ll; +} + +static error_t parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + long long ll; + + switch (key) { + case 'd': + args->disk_type = ped_disk_type_get(arg); + if (!args->disk_type) + argp_error(state, + "Disk type `%s' is not supported; use --list-disk-types to see the supported types"); + break; + + case 'f': + args->fs_type = ped_file_system_type_get(arg); + if (!args->fs_type) + argp_error(state, + "File system type `%s' is not supported; use --list-fs-types to see the supported types"); + break; + + case 'b': + args->boot = true; + break; + + case 'n': + args->boot = false; + break; + + case 'a': + ll = arg_to_long_long(state, arg); + if (ll < 0) + argp_error(state, + "First sector must be greater or equal to 0"); + args->first_sec = ll; + break; + + case 'l': + ll = arg_to_long_long(state, arg); + if (ll < 0) + argp_error(state, + "Last sector must be greater or equal to 0"); + args->last_sec = ll; + break; + + case 'k': + args->list_disk_types = true; + break; + + case 's': + args->list_fs_types = true; + break; + + case ARGP_KEY_INIT: + args->dev_filename = NULL; + args->last_sec = -1; + break; + + case ARGP_KEY_ARG: + if (args->dev_filename) + argp_error(state, + "Wrong number of arguments; only one is allowed"); + args->dev_filename = arg; + break; + + case ARGP_KEY_END: + if (args->list_disk_types || args->list_fs_types) + break; + + if (!args->dev_filename) + argp_error(state, + "The disk device was not specified"); + + if (args->last_sec < 0) + argp_error(state, + "Option --last-sec is required"); + if (args->first_sec > args->last_sec) + argp_error(state, + "Option --fist_sec must be less or equal to option --last_sec"); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL}; + +static void list_disk_types(void) +{ + PedDiskType *type; + int i = 0; + printf("Disk types:\n"); + for (type = ped_disk_type_get_next(NULL); type; + type = ped_disk_type_get_next(type)) { + printf("%s\t", type->name); + i++; + if (i == 5) { + printf("\n"); + i = 0; + } + } + if (i > 0) + printf("\n"); + printf("\n"); +} -/* TODO List types of file systems (function below). - * One still needs a calling option to list the types. - */ static void list_fs_types(void) { PedFileSystemType *fs_type; + int i = 0; + printf("File system types:\n"); for (fs_type = ped_file_system_type_get_next(NULL); fs_type; - fs_type = ped_file_system_type_get_next(fs_type)) - puts(fs_type->name); + fs_type = ped_file_system_type_get_next(fs_type)) { + printf("%s\t", fs_type->name); + i++; + if (i == 5) { + printf("\n"); + i = 0; + } + } + if (i > 0) + printf("\n"); + printf("\n"); } /* 0 on failure, 1 otherwise. */ @@ -67,37 +240,49 @@ out: return ret; } -/* Use example: sudo ./f3fix /dev/sdb msdos 2048 15010455 */ - int main (int argc, char *argv[]) { + struct args args = { + /* Defaults. */ + .list_disk_types = false, + .list_fs_types = false, + + .boot = true, + + .disk_type = ped_disk_type_get("msdos"), + .fs_type = ped_file_system_type_get("fat32"), + .first_sec = 2048, /* Skip first 1MB. */ + }; + PedDevice *dev; - PedDiskType *type; - PedFileSystemType *fs_type; - PedSector start = atoi(argv[3]); - PedSector end = atoi(argv[4]); - int ret = 1; + int ret; - if (argc != 5) - error(EXIT_FAILURE, 0, "wrong number of arguments"); + /* Read parameters. */ + argp_parse(&argp, argc, argv, 0, NULL, &args); + print_header(stdout, "fix"); - dev = ped_device_get(argv[1]); + if (args.list_disk_types) + list_disk_types(); + + if (args.list_fs_types) + list_fs_types(); + + if (args.list_disk_types || args.list_fs_types) { + /* If the user has asked for the types, + * so she doesn't want to fix the drive yet. + */ + return 0; + } + + /* XXX If @dev is a partition, refer the user to + * the disk of this partition. + */ + dev = ped_device_get(args.dev_filename); if (!dev) - goto out; + return 1; - type = ped_disk_type_get(argv[2]); - if (!type) - goto device; - - fs_type = ped_file_system_type_get("fat32"); - if (!fs_type) - goto device; - - ret = !fix_disk(dev, type, fs_type, true, start, end); - -device: + ret = !fix_disk(dev, args.disk_type, args.fs_type, args.boot, + args.first_sec, args.last_sec); ped_device_destroy(dev); -out: return ret; - }