#include #include #include #include #include "version.h" #include "libutils.h" /* Argp's global variables. */ const char *argp_program_version = "F3 Fix " F3_STR_VERSION; /* 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", arg); 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", arg); 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"); } 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)) { printf("%s\t", fs_type->name); i++; if (i == 5) { printf("\n"); i = 0; } } if (i > 0) printf("\n"); printf("\n"); } static PedSector map_sector_to_logical_sector(PedSector sector, int logical_sector_size) { assert(logical_sector_size >= 512); assert(logical_sector_size % 512 == 0); return sector / (logical_sector_size / 512); } /* 0 on failure, 1 otherwise. */ static int fix_disk(PedDevice *dev, PedDiskType *type, PedFileSystemType *fs_type, int boot, PedSector start, PedSector end) { PedDisk *disk; PedPartition *part; PedGeometry *geom; PedConstraint *constraint; int ret = 0; disk = ped_disk_new_fresh(dev, type); if (!disk) goto out; start = map_sector_to_logical_sector(start, dev->sector_size); end = map_sector_to_logical_sector(end, dev->sector_size); part = ped_partition_new(disk, PED_PARTITION_NORMAL, fs_type, start, end); if (!part) goto disk; if (boot && !ped_partition_set_flag(part, PED_PARTITION_BOOT, 1)) goto part; geom = ped_geometry_new(dev, start, end - start + 1); if (!geom) goto part; constraint = ped_constraint_exact(geom); ped_geometry_destroy(geom); if (!constraint) goto part; ret = ped_disk_add_partition(disk, part, constraint); ped_constraint_destroy(constraint); if (!ret) goto part; /* ped_disk_print(disk); */ ret = ped_disk_commit(disk); goto disk; part: ped_partition_destroy(part); disk: ped_disk_destroy(disk); out: return ret; } 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; int ret; /* Read parameters. */ argp_parse(&argp, argc, argv, 0, NULL, &args); print_header(stdout, "fix"); 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, * 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) return 1; ret = !fix_disk(dev, args.disk_type, args.fs_type, args.boot, args.first_sec, args.last_sec); printf("Drive `%s' was successfully fixed\n", args.dev_filename); ped_device_destroy(dev); return ret; }