From 7845ca8dc28c93939d99a1d0564f97eddcba1079 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 28 Jan 2013 16:14:21 +0100 Subject: [PATCH] libfdisk: add "ask" interface Signed-off-by: Karel Zak --- libfdisk/src/Makemodule.am | 10 ++ libfdisk/src/ask.c | 223 +++++++++++++++++++++++++++++++++++++ libfdisk/src/context.c | 49 +++++--- libfdisk/src/fdiskP.h | 35 +++++- libfdisk/src/label.c | 22 +++- libfdisk/src/libfdisk.h | 18 ++- 6 files changed, 338 insertions(+), 19 deletions(-) create mode 100644 libfdisk/src/ask.c diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am index eb40a3f1af..fbfb1b4f21 100644 --- a/libfdisk/src/Makemodule.am +++ b/libfdisk/src/Makemodule.am @@ -11,6 +11,7 @@ libfdisk_la_SOURCES = \ \ libfdisk/src/init.c \ libfdisk/src/test.c \ + libfdisk/src/ask.c \ libfdisk/src/alignment.c \ libfdisk/src/label.c \ libfdisk/src/utils.c \ @@ -39,6 +40,10 @@ endif libfdisk_la_DEPENDENCIES = $(libfdisk_la_LIBADD) + +check_PROGRAMS += \ + test_fdisk_ask + libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS) libfdisk_tests_ldflags = -static libfdisk_tests_ldadd = libfdisk.la @@ -50,3 +55,8 @@ endif if BUILD_LIBUUID libfdisk_tests_ldflags += libuuid.la endif + +test_fdisk_ask_SOURCES = libfdisk/src/ask.c +test_fdisk_ask_CFLAGS = $(libfdisk_tests_cflags) +test_fdisk_ask_LDFLAGS = $(libfdisk_tests_ldflags) +test_fdisk_ask_LDADD = $(libfdisk_tests_ldadd) diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c new file mode 100644 index 0000000000..a5ee2f5591 --- /dev/null +++ b/libfdisk/src/ask.c @@ -0,0 +1,223 @@ + +#include "fdiskP.h" + + +const char *fdisk_ask_get_question(struct fdisk_ask *ask) +{ + assert(ask); + return ask->query; +} + +int fdisk_ask_get_type(struct fdisk_ask *ask) +{ + assert(ask); + return ask->type; +} + +const char *fdisk_ask_number_get_range(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.range; +} +uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.dfl; +} + +uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.low; +} + +uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.hig; +} + +int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result) +{ + assert(ask); + ask->data.num.result = result; + return 0; +} + +/* + * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur' + */ +static char *mk_string_list(char *ptr, size_t *len, size_t *begin, + size_t *run, ssize_t cur) +{ + int rlen; + + if (cur != -1) { + if (!*begin) { /* begin of the list */ + *begin = cur + 1; + return ptr; + } + + if (*begin + *run == cur) { /* no gap, continue */ + (*run)++; + return ptr; + } + } else if (!*begin) { + *ptr = '\0'; + return ptr; /* end of empty list */ + } + + /* add to the list */ + if (!*run) + rlen = snprintf(ptr, *len, "%zd,", *begin); + else if (*run == 1) + rlen = snprintf(ptr, *len, "%zd,%zd,", *begin, *begin + 1); + else + rlen = snprintf(ptr, *len, "%zd-%zd,", *begin, *begin + *run); + + if (rlen < 0 || (size_t) rlen + 1 > *len) + return NULL; + + ptr += rlen; + + if (rlen > 0 && *len > (size_t) rlen) + *len -= rlen; + else + *len = 0; + + if (cur == -1 && *begin) { + /* end of the list */ + *(ptr - 1) = '\0'; /* remove tailing ',' from the list */ + return ptr; + } + + *begin = cur + 1; + *run = 0; + + return ptr; +} + +int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) +{ + int rc; + char range[BUFSIZ], *ptr = range; + size_t i, len = sizeof(range), begin = 0, run = 0; + struct fdisk_ask ask = { .name = "partnum" }; + __typeof__(ask.data.num) *num = &ask.data.num; + + assert(cxt); + assert(cxt->label); + assert(partnum); + + if (!cxt->ask_cb) { + DBG(ASK, dbgprint("no ask callback specified!")); + return -EINVAL; /* ask callback undefined */ + } + + DBG(ASK, dbgprint("%s: asking for %s partition number (max: %zd)", + cxt->label->name, + wantnew ? "new" : "used", + cxt->label->nparts_max)); + + for (i = 0; i < cxt->label->nparts_max; i++) { + int status = 0; + + rc = fdisk_partition_get_status(cxt, i, &status); + if (rc) + return rc; + + if (wantnew && !(status & FDISK_PARTSTAT_USED)) { + ptr = mk_string_list(ptr, &len, &begin, &run, i); + if (!ptr) + return -EINVAL; + if (!num->low) + num->dfl = num->low = i + 1; + num->hig = i + 1; + } else if (!wantnew && (status & FDISK_PARTSTAT_USED)) { + ptr = mk_string_list(ptr, &len, &begin, &run, i); + if (!num->low) + num->low = i + 1; + num->dfl = num->hig = i + 1; + } + } + + if (wantnew && num->low == 0 && num->hig == 0) { + DBG(ASK, dbgprint("no free partition")); + return 1; + } + + mk_string_list(ptr, &len, &begin, &run, -1); /* terminate the list */ + num->range = range; + + ask.query = _("Partition number"); + ask.type = FDISK_ASKTYPE_NUMBER; + + rc = cxt->ask_cb(cxt, &ask, cxt->ask_data); + *partnum = num->result ? num->result - 1 : 0; + DBG(ASK, dbgprint("result: %zd [rc=%d]\n", num->result, rc)); + return rc; +} + + +#ifdef TEST_PROGRAM +struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_aix_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_mac_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt) { return NULL; } + +unsigned int read_int_with_suffix(struct fdisk_context *cxt, + unsigned int low, unsigned int dflt, unsigned int high, + unsigned int base, char *mesg, int *is_suffix_used) +{ + return 0; +} + +unsigned int read_int(struct fdisk_context *cxt, + unsigned int low, unsigned int dflt, + unsigned int high, unsigned int base, char *mesg) +{ + return 0; +} + +int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) +{ + /* 1 - 3, 6, 8, 9, 11 13 */ + size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 }; + size_t numx[] = { 0, 0, 0 }; + char range[BUFSIZ], *ptr = range; + size_t i, len = sizeof(range), begin = 0, run = 0; + + for (i = 0; i < ARRAY_SIZE(nums); i++) { + if (!nums[i]) + continue; + ptr = mk_string_list(ptr, &len, &begin, &run, i); + } + mk_string_list(ptr, &len, &begin, &run, -1); + printf("list: '%s'\n", range); + + ptr = range; + len = sizeof(range), begin = 0, run = 0; + for (i = 0; i < ARRAY_SIZE(numx); i++) { + if (!numx[i]) + continue; + ptr = mk_string_list(ptr, &len, &begin, &run, i); + } + mk_string_list(ptr, &len, &begin, &run, -1); + printf("empty list: '%s'\n", range); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct fdisk_test tss[] = { + { "--ranges", test_ranges, "generates ranges" }, + { NULL } + }; + + return fdisk_run_test(tss, argc, argv); +} + +#endif diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 908889b4f1..4a2faf97d6 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -68,8 +68,7 @@ int fdisk_context_switch_label(struct fdisk_context *cxt, const char *name) static void reset_context(struct fdisk_context *cxt) { - size_t nlbs, i; - struct fdisk_label *lbs[ ARRAY_SIZE(cxt->labels) ]; + size_t i; DBG(CONTEXT, dbgprint("\n-----\nresetting context %p", cxt)); @@ -77,25 +76,30 @@ static void reset_context(struct fdisk_context *cxt) for (i = 0; i < cxt->nlabels; i++) fdisk_deinit_label(cxt->labels[i]); - /* remember permanent setting */ - memcpy(lbs, cxt->labels, sizeof(lbs)); - nlbs = cxt->nlabels; - /* free device specific stuff */ if (cxt->dev_fd > -1) close(cxt->dev_fd); free(cxt->dev_path); free(cxt->firstsector); - /* the reset */ - memset(cxt, 0, sizeof(*cxt)); - /* initialize */ cxt->dev_fd = -1; - - /* set permanent setting */ - memcpy(cxt->labels, lbs, sizeof(lbs)); - cxt->nlabels = nlbs; + cxt->dev_path = NULL; + cxt->firstsector = NULL; + + cxt->io_size = 0; + cxt->optimal_io_size = 0; + cxt->min_io_size = 0; + cxt->phy_sector_size = 0; + cxt->sector_size = 0; + cxt->alignment_offset = 0; + cxt->grain = 0; + cxt->first_lba = 0; + cxt->total_sectors = 0; + + memset(&cxt->geom, 0, sizeof(struct fdisk_geometry)); + + cxt->label = NULL; } /** @@ -178,3 +182,22 @@ void fdisk_free_context(struct fdisk_context *cxt) free(cxt); } + +/** + * fdisk_context_set_ask: + * @cxt: context + * @ask_cb: callback + * @data: callback data + * + * Returns: 0 on sucess, < 0 on error. + */ +int fdisk_context_set_ask(struct fdisk_context *cxt, + int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *), + void *data) +{ + assert(cxt); + + cxt->ask_cb = ask_cb; + cxt->ask_data = data; + return 0; +} diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index 3a40bb449a..58c8393cc6 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -47,6 +47,7 @@ #define FDISK_DEBUG_TOPOLOGY (1 << 3) #define FDISK_DEBUG_GEOMETRY (1 << 4) #define FDISK_DEBUG_LABEL (1 << 5) +#define FDISK_DEBUG_ASK (1 << 6) #define FDISK_DEBUG_ALL 0xFFFF # define ON_DBG(m, x) do { \ @@ -206,6 +207,33 @@ extern struct fdisk_label *fdisk_new_mac_label(struct fdisk_context *cxt); extern struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt); extern struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt); + +/* fdisk dialog -- note that nothing from this stuff will be directly exported, + * we will have get/set() function for everything. + */ +struct fdisk_ask { + const char *name; + int type; /* FDISK_ASKTYPE_* */ + + char *query; + char *hint; + + union { + struct ask_number { + uint64_t hig; /* high limit */ + uint64_t low; /* low limit */ + uint64_t dfl; /* default */ + uint64_t result; + const char *range; /* by library generated list */ + } num; + } data; +}; + +enum { + FDISK_ASKTYPE_NONE, + FDISK_ASKTYPE_NUMBER +}; + struct fdisk_context { int dev_fd; /* device descriptor */ char *dev_path; /* device path */ @@ -232,8 +260,10 @@ struct fdisk_context { size_t nlabels; /* number of initialized label drivers */ struct fdisk_label *labels[8]; /* all supported labels, * FIXME: use any enum rather than hardcoded number */ -}; + int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *); /* fdisk dialogs callback */ + void *ask_data; /* ask_cb() data */ +}; /* context.c */ extern int __fdisk_context_switch_label(struct fdisk_context *cxt, @@ -276,4 +306,7 @@ extern void fdisk_deinit_label(struct fdisk_label *lb); /* gpt.c -- temporary bypass library API... */ extern void gpt_list_table(struct fdisk_context *cxt, int xtra); +/* ask.c */ +extern int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew); + #endif /* _LIBFDISK_PRIVATE_H */ diff --git a/libfdisk/src/label.c b/libfdisk/src/label.c index e631154817..f80bf66b12 100644 --- a/libfdisk/src/label.c +++ b/libfdisk/src/label.c @@ -34,6 +34,7 @@ int fdisk_probe_labels(struct fdisk_context *cxt) return 0; } + DBG(LABEL, dbgprint("no label found")); return 1; /* not found */ } @@ -100,21 +101,31 @@ int fdisk_verify_disklabel(struct fdisk_context *cxt) /** * fdisk_add_partition: * @cxt: fdisk context - * @partnum: partition number to create * @t: partition type to create or NULL for label-specific default * * Creates a new partition, with number @partnum and type @parttype. * * Returns 0. */ -int fdisk_add_partition(struct fdisk_context *cxt, size_t partnum, +int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_parttype *t) { + size_t partnum = 0; + + assert(cxt); + assert(cxt->label); + if (!cxt || !cxt->label) return -EINVAL; if (!cxt->label->op->part_add) return -ENOSYS; + if (!(cxt->label->flags & FDISK_LABEL_FL_ADDPART_NOPARTNO)) { + int rc = fdisk_ask_partnum(cxt, &partnum, 1); + if (rc) + return rc; + } + DBG(LABEL, dbgprint("adding new partition number %zd", partnum)); cxt->label->op->part_add(cxt, partnum, t); return 0; @@ -243,12 +254,17 @@ int fdisk_partition_get_status(struct fdisk_context *cxt, size_t partnum, int *status) { + int rc; + if (!cxt || !cxt->label) return -EINVAL; if (!cxt->label->op->part_get_status) return -ENOSYS; - return cxt->label->op->part_get_status(cxt, partnum, status); + rc = cxt->label->op->part_get_status(cxt, partnum, status); + + /* DBG(LABEL, dbgprint("partition: %zd: status: 0x%04x [rc=%d]", partnum, *status, rc)); */ + return rc; } diff --git a/libfdisk/src/libfdisk.h b/libfdisk/src/libfdisk.h index 18b14163ec..a19849dc4d 100644 --- a/libfdisk/src/libfdisk.h +++ b/libfdisk/src/libfdisk.h @@ -28,6 +28,7 @@ extern "C" { struct fdisk_context; struct fdisk_label; struct fdisk_parttype; +struct fdisk_ask; /* * Supported partition table types (labels) @@ -44,7 +45,7 @@ enum fdisk_labeltype { }; enum { - FDISK_PARTSTAT_NONE, + FDISK_PARTSTAT_NONE = 0, FDISK_PARTSTAT_USED /* partition used */ }; @@ -55,6 +56,10 @@ extern void fdisk_init_debug(int mask); extern struct fdisk_context *fdisk_new_context(void); extern void fdisk_free_context(struct fdisk_context *cxt); +extern int fdisk_context_set_ask(struct fdisk_context *cxt, + int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *), + void *data); + extern int fdisk_context_assign_device(struct fdisk_context *cxt, const char *fname, int readonly); @@ -85,7 +90,7 @@ extern int fdisk_write_disklabel(struct fdisk_context *cxt); extern int fdisk_verify_disklabel(struct fdisk_context *cxt); extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name); -extern int fdisk_add_partition(struct fdisk_context *cxt, size_t partnum, struct fdisk_parttype *t); +extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_parttype *t); extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partnum); extern struct fdisk_parttype *fdisk_get_partition_type(struct fdisk_context *cxt, size_t partnum); @@ -105,6 +110,15 @@ extern int fdisk_reset_alignment(struct fdisk_context *cxt); extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable); extern int fdisk_dos_is_compatible(struct fdisk_label *lb); +/* ask.c */ +extern const char *fdisk_ask_get_question(struct fdisk_ask *ask); +extern int fdisk_ask_get_type(struct fdisk_ask *ask); +extern const char *fdisk_ask_number_get_range(struct fdisk_ask *ask); +extern uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask); +extern uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask); +extern uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result); + #ifdef __cplusplus } #endif -- 2.47.2