diff options
Diffstat (limited to 'libfdisk/src')
| -rw-r--r-- | libfdisk/src/Makemodule.am | 19 | ||||
| -rw-r--r-- | libfdisk/src/alignment.c | 241 | ||||
| -rw-r--r-- | libfdisk/src/ask.c | 230 | ||||
| -rw-r--r-- | libfdisk/src/bsd.c | 943 | ||||
| -rw-r--r-- | libfdisk/src/context.c | 152 | ||||
| -rw-r--r-- | libfdisk/src/dos.c | 2086 | ||||
| -rw-r--r-- | libfdisk/src/fdiskP.h | 69 | ||||
| -rw-r--r-- | libfdisk/src/gpt.c | 498 | ||||
| -rw-r--r-- | libfdisk/src/label.c | 143 | ||||
| -rw-r--r-- | libfdisk/src/libfdisk.h | 118 | ||||
| -rw-r--r-- | libfdisk/src/parttype.c | 15 | ||||
| -rw-r--r-- | libfdisk/src/sgi.c | 1189 | ||||
| -rw-r--r-- | libfdisk/src/sun.c | 1024 | ||||
| -rw-r--r-- | libfdisk/src/utils.c | 74 |
14 files changed, 6556 insertions, 245 deletions
diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am index fbfb1b4f2..b174b784e 100644 --- a/libfdisk/src/Makemodule.am +++ b/libfdisk/src/Makemodule.am @@ -17,12 +17,18 @@ libfdisk_la_SOURCES = \ libfdisk/src/utils.c \ libfdisk/src/context.c \ libfdisk/src/parttype.c \ + \ + libfdisk/src/sun.c \ + libfdisk/src/sgi.c \ + libfdisk/src/dos.c \ + libfdisk/src/bsd.c \ libfdisk/src/gpt.c nodist_libfdisk_la_SOURCES = libfdisk/src/fdiskP.h libfdisk_la_LIBADD = libcommon.la +libfdisk_la_DEPENDENCIES = libcommon.la libfdisk_la_CFLAGS = \ -I$(ul_libfdisk_incdir) \ @@ -30,19 +36,19 @@ libfdisk_la_CFLAGS = \ if BUILD_LIBBLKID libfdisk_la_LIBADD += libblkid.la +libfdisk_la_DEPENDENCIES += libblkid.la libfdisk_la_CFLAGS += -I$(ul_libblkid_incdir) endif if BUILD_LIBUUID libfdisk_la_LIBADD += libuuid.la +libfdisk_la_DEPENDENCIES += libuuid.la libfdisk_la_CFLAGS += -I$(ul_libuuid_incdir) endif -libfdisk_la_DEPENDENCIES = $(libfdisk_la_LIBADD) - - check_PROGRAMS += \ - test_fdisk_ask + test_fdisk_ask \ + test_fdisk_utils libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS) libfdisk_tests_ldflags = -static @@ -60,3 +66,8 @@ 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) + +test_fdisk_utils_SOURCES = libfdisk/src/utils.c +test_fdisk_utils_CFLAGS = $(libfdisk_tests_cflags) +test_fdisk_utils_LDFLAGS = $(libfdisk_tests_ldflags) +test_fdisk_utils_LDADD = $(libfdisk_tests_ldadd) diff --git a/libfdisk/src/alignment.c b/libfdisk/src/alignment.c index ac44e73c1..f661c5cce 100644 --- a/libfdisk/src/alignment.c +++ b/libfdisk/src/alignment.c @@ -74,6 +74,10 @@ sector_t fdisk_align_lba(struct fdisk_context *cxt, sector_t lba, int direction) } } + if (lba != res) + DBG(TOPOLOGY, dbgprint("LBA %ju -aligned-to-> %ju", + (uintmax_t) lba, + (uintmax_t) res)); return res; } @@ -114,32 +118,13 @@ static unsigned long get_sector_size(int fd) return DEFAULT_SECTOR_SIZE; } -/** - * fdisk_override_sector_size: - * @cxt: fdisk context - * @s: required sector size - * - * Overwrites logical and physical sector size. Note that the default sector - * size is discovered by fdisk_new_context_from_device() from device topology. - * - * Don't use this function, rely on the default behavioer is more safe. - * - * Returns: 0 on success, < 0 on error. - */ -int fdisk_override_sector_size(struct fdisk_context *cxt, sector_t s) -{ - if (!cxt) - return -EINVAL; - - cxt->phy_sector_size = cxt->sector_size = s; - cxt->min_io_size = cxt->io_size = s; - - fdisk_reset_alignment(cxt); - return 0; -} - static void recount_geometry(struct fdisk_context *cxt) { + if (!cxt->geom.heads) + cxt->geom.heads = 255; + if (!cxt->geom.sectors) + cxt->geom.sectors = 63; + cxt->geom.cylinders = cxt->total_sectors / (cxt->geom.heads * cxt->geom.sectors); } @@ -151,7 +136,8 @@ static void recount_geometry(struct fdisk_context *cxt) * @heads: user specified heads * @sectors: user specified sectors * - * Overrides autodiscovery and apply user specified geometry. + * Overrides autodiscovery and apply user specified geometry. The function + * fdisk_reset_device_properties() restores the original setting. * * Returns: 0 on success, < 0 on error. */ @@ -173,6 +159,133 @@ int fdisk_override_geometry(struct fdisk_context *cxt, recount_geometry(cxt); fdisk_reset_alignment(cxt); + + DBG(GEOMETRY, dbgprint("override C/H/S: %u/%u/%u", + (unsigned) cxt->geom.cylinders, + (unsigned) cxt->geom.heads, + (unsigned) cxt->geom.sectors)); + + return 0; +} + +int fdisk_save_user_geometry(struct fdisk_context *cxt, + unsigned int cylinders, + unsigned int heads, + unsigned int sectors) +{ + if (!cxt) + return -EINVAL; + + if (heads) + cxt->user_geom.heads = heads > 256 ? 0 : heads; + if (sectors) + cxt->user_geom.sectors = sectors >= 64 ? 0 : sectors; + if (cylinders) + cxt->user_geom.cylinders = cylinders; + + DBG(GEOMETRY, dbgprint("user C/H/S: %u/%u/%u", + (unsigned) cxt->user_geom.cylinders, + (unsigned) cxt->user_geom.heads, + (unsigned) cxt->user_geom.sectors)); + + return 0; +} + +int fdisk_save_user_sector_size(struct fdisk_context *cxt, + unsigned int phy, + unsigned int log) +{ + if (!cxt) + return -EINVAL; + + DBG(TOPOLOGY, dbgprint("user phy/log sector size: %u/%u", phy, log)); + + cxt->user_pyh_sector = phy; + cxt->user_log_sector = log; + + return 0; +} + +int fdisk_has_user_device_properties(struct fdisk_context *cxt) +{ + return (cxt->user_pyh_sector + || cxt->user_log_sector + || cxt->user_geom.heads + || cxt->user_geom.sectors + || cxt->user_geom.cylinders); +} + +int fdisk_apply_user_device_properties(struct fdisk_context *cxt) +{ + if (!cxt) + return -EINVAL; + + DBG(TOPOLOGY, dbgprint("appling user device properties")); + + if (cxt->user_pyh_sector) + cxt->phy_sector_size = cxt->user_pyh_sector; + if (cxt->user_log_sector) + cxt->sector_size = cxt->min_io_size = + cxt->io_size = cxt->user_log_sector; + + if (cxt->user_geom.heads) + cxt->geom.heads = cxt->user_geom.heads; + if (cxt->user_geom.sectors) + cxt->geom.sectors = cxt->user_geom.sectors; + + if (cxt->user_geom.cylinders) + cxt->geom.cylinders = cxt->user_geom.cylinders; + else if (cxt->user_geom.heads || cxt->user_geom.sectors) + recount_geometry(cxt); + + fdisk_reset_alignment(cxt); + + DBG(GEOMETRY, dbgprint("new C/H/S: %u/%u/%u", + (unsigned) cxt->geom.cylinders, + (unsigned) cxt->geom.heads, + (unsigned) cxt->geom.sectors)); + DBG(TOPOLOGY, dbgprint("new log/phy sector size: %u/%u", + (unsigned) cxt->sector_size, + (unsigned) cxt->phy_sector_size)); + + return 0; +} + +void fdisk_zeroize_device_properties(struct fdisk_context *cxt) +{ + assert(cxt); + + 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)); +} + +int fdisk_reset_device_properties(struct fdisk_context *cxt) +{ + int rc; + + if (!cxt) + return -EINVAL; + + DBG(TOPOLOGY, dbgprint("*** reseting device properties")); + + fdisk_zeroize_device_properties(cxt); + fdisk_discover_topology(cxt); + fdisk_discover_geometry(cxt); + + rc = fdisk_read_firstsector(cxt); + if (rc) + return rc; + + fdisk_apply_user_device_properties(cxt); return 0; } @@ -182,31 +295,26 @@ int fdisk_override_geometry(struct fdisk_context *cxt, int fdisk_discover_geometry(struct fdisk_context *cxt) { sector_t nsects; - unsigned int h = 0, s = 0; assert(cxt); assert(cxt->geom.heads == 0); + DBG(GEOMETRY, dbgprint("%s: discovering geometry...", cxt->dev_path)); + /* get number of 512-byte sectors, and convert it the real sectors */ if (!blkdev_get_sectors(cxt->dev_fd, &nsects)) cxt->total_sectors = (nsects / (cxt->sector_size >> 9)); /* what the kernel/bios thinks the geometry is */ - blkdev_get_geometry(cxt->dev_fd, &h, &s); - if (!h && !s) { - /* unable to discover geometry, use default values */ - s = 63; - h = 255; - } + blkdev_get_geometry(cxt->dev_fd, &cxt->geom.heads, (unsigned int *) &cxt->geom.sectors); /* obtained heads and sectors */ - cxt->geom.heads = h; - cxt->geom.sectors = s; recount_geometry(cxt); - DBG(GEOMETRY, dbgprint("geometry discovered for %s: C/H/S: %lld/%d/%lld", - cxt->dev_path, cxt->geom.cylinders, - cxt->geom.heads, cxt->geom.sectors)); + DBG(GEOMETRY, dbgprint("result: C/H/S: %u/%u/%u", + (unsigned) cxt->geom.cylinders, + (unsigned) cxt->geom.heads, + (unsigned) cxt->geom.sectors)); return 0; } @@ -218,6 +326,7 @@ int fdisk_discover_topology(struct fdisk_context *cxt) assert(cxt); assert(cxt->sector_size == 0); + DBG(TOPOLOGY, dbgprint("%s: discovering topology...", cxt->dev_path)); #ifdef HAVE_LIBBLKID DBG(TOPOLOGY, dbgprint("initialize libblkid prober")); @@ -251,11 +360,10 @@ int fdisk_discover_topology(struct fdisk_context *cxt) if (!cxt->io_size) cxt->io_size = cxt->sector_size; - DBG(TOPOLOGY, dbgprint("topology discovered for %s:\n" - "\tlogical/physical sector sizes: %ld/%ld\n" - "\tfdisk/minimal/optimal io sizes: %ld/%ld/%ld\n", - cxt->dev_path, cxt->sector_size, cxt->phy_sector_size, - cxt->io_size, cxt->optimal_io_size, cxt->min_io_size)); + DBG(TOPOLOGY, dbgprint("result: log/phy sector size: %ld/%ld", + cxt->sector_size, cxt->phy_sector_size)); + DBG(TOPOLOGY, dbgprint("result: fdisk/min/optimal io: %ld/%ld/%ld", + cxt->io_size, cxt->optimal_io_size, cxt->min_io_size)); return 0; } @@ -364,6 +472,8 @@ int fdisk_reset_alignment(struct fdisk_context *cxt) if (!cxt) return -EINVAL; + DBG(TOPOLOGY, dbgprint("reseting alignment...")); + /* default */ cxt->grain = fdisk_topology_get_grain(cxt); cxt->first_lba = fdisk_topology_get_first_lba(cxt); @@ -372,10 +482,55 @@ int fdisk_reset_alignment(struct fdisk_context *cxt) if (cxt->label && cxt->label->op->reset_alignment) rc = cxt->label->op->reset_alignment(cxt); - DBG(LABEL, dbgprint("%s alignment reseted to: " + DBG(TOPOLOGY, dbgprint("%s alignment reseted to: " "first LBA=%ju, grain=%lu [rc=%d]", cxt->label ? cxt->label->name : NULL, (uintmax_t) cxt->first_lba, cxt->grain, rc)); return rc; } + + +sector_t fdisk_scround(struct fdisk_context *cxt, sector_t num) +{ + sector_t un = fdisk_context_get_units_per_sector(cxt); + return (num + un - 1) / un; +} + +sector_t fdisk_cround(struct fdisk_context *cxt, sector_t num) +{ + return fdisk_context_use_cylinders(cxt) ? + (num / fdisk_context_get_units_per_sector(cxt)) + 1 : num; +} + +int fdisk_reread_partition_table(struct fdisk_context *cxt) +{ + int i; + struct stat statbuf; + + assert(cxt); + assert(cxt->dev_fd >= 0); + + i = fstat(cxt->dev_fd, &statbuf); + if (i == 0 && S_ISBLK(statbuf.st_mode)) { + sync(); +#ifdef BLKRRPART + fdisk_info(cxt, _("Calling ioctl() to re-read partition table.")); + i = ioctl(cxt->dev_fd, BLKRRPART); +#else + errno = ENOSYS; + i = 1; +#endif + } + + if (i) { + fdisk_warn(cxt, _("Re-reading the partition table failed.")); + fdisk_info(cxt, _( + "The kernel still uses the old table. The " + "new table will be used at the next reboot " + "or after you run partprobe(8) or kpartx(8).")); + return -errno; + } + + return 0; +} diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c index cdb4d0124..dd152a1f0 100644 --- a/libfdisk/src/ask.c +++ b/libfdisk/src/ask.c @@ -13,15 +13,6 @@ void fdisk_reset_ask(struct fdisk_ask *ask) assert(ask); free(ask->query); - switch (ask->type) { - case FDISK_ASKTYPE_OFFSET: - case FDISK_ASKTYPE_NUMBER: - free(ask->data.num.range); - break; - default: - break; - } - memset(ask, 0, sizeof(*ask)); } @@ -58,6 +49,19 @@ int fdisk_ask_set_type(struct fdisk_ask *ask, int type) return 0; } +unsigned int fdisk_ask_get_flags(struct fdisk_ask *ask) +{ + assert(ask); + return ask->flags; +} + +int fdisk_ask_set_flags(struct fdisk_ask *ask, unsigned int flags) +{ + assert(ask); + ask->flags = flags; + return 0; +} + int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask) { int rc; @@ -65,7 +69,12 @@ int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask) assert(ask); assert(cxt); - DBG(ASK, dbgprint("asking for '%s'", ask->query)); + DBG(ASK, dbgprint("do_ask for '%s'", + ask->query ? ask->query : + ask->type == FDISK_ASKTYPE_INFO ? "info" : + ask->type == FDISK_ASKTYPE_WARNX ? "warnx" : + ask->type == FDISK_ASKTYPE_WARN ? "warn" : + "?nothing?")); if (!cxt->ask_cb) { DBG(ASK, dbgprint("no ask callback specified!")); @@ -90,7 +99,9 @@ const char *fdisk_ask_number_get_range(struct fdisk_ask *ask) int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range) { assert(ask); - return !strdup_to_struct_member(ask, data.num.range, range) ? -ENOMEM : 0; + assert(is_number_ask(ask)); + ask->data.num.range = range; + return 0; } uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask) @@ -192,11 +203,19 @@ int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative) return 0; } +int fdisk_ask_number_inchars(struct fdisk_ask *ask) +{ + assert(ask); + assert(is_number_ask(ask)); + return ask->data.num.inchars; +} + /* * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur' */ +#define tochar(num) ((int) ('a' + num - 1)) static char *mk_string_list(char *ptr, size_t *len, size_t *begin, - size_t *run, ssize_t cur) + size_t *run, ssize_t cur, int inchar) { int rlen; @@ -217,11 +236,16 @@ static char *mk_string_list(char *ptr, size_t *len, size_t *begin, /* add to the list */ if (!*run) - rlen = snprintf(ptr, *len, "%zd,", *begin); + rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) : + snprintf(ptr, *len, "%zu,", *begin); else if (*run == 1) - rlen = snprintf(ptr, *len, "%zd,%zd,", *begin, *begin + 1); + rlen = inchar ? + snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) : + snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1); else - rlen = snprintf(ptr, *len, "%zd-%zd,", *begin, *begin + *run); + rlen = inchar ? + snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) : + snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *run); if (rlen < 0 || (size_t) rlen + 1 > *len) return NULL; @@ -248,7 +272,7 @@ static char *mk_string_list(char *ptr, size_t *len, size_t *begin, /* returns: 1=0 on success, < 0 on error, 1 if no free/used partition */ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) { - int rc = 0; + int rc = 0, inchar = 0; char range[BUFSIZ], *ptr = range; size_t i, len = sizeof(range), begin = 0, run = 0; struct fdisk_ask *ask = NULL; @@ -258,9 +282,15 @@ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) assert(cxt->label); assert(partnum); - DBG(ASK, dbgprint("%s: asking for %s partition number (max: %zd)", - cxt->label->name, wantnew ? "new" : "used", - cxt->label->nparts_max)); + if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO) + inchar = 1; + + DBG(ASK, dbgprint("%s: asking for %s partition number " + "(max: %zu, inchar: %s)", + cxt->label->name, + wantnew ? "new" : "used", + cxt->label->nparts_max, + inchar ? "yes" : "not")); ask = fdisk_new_ask(); if (!ask) @@ -269,6 +299,8 @@ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); num = &ask->data.num; + ask->data.num.inchars = inchar ? 1 : 0; + for (i = 0; i < cxt->label->nparts_max; i++) { int status = 0; @@ -276,7 +308,7 @@ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) if (rc) break; if (wantnew && !(status & FDISK_PARTSTAT_USED)) { - ptr = mk_string_list(ptr, &len, &begin, &run, i); + ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar); if (!ptr) { rc = -EINVAL; break; @@ -285,24 +317,24 @@ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) 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); + ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar); if (!num->low) num->low = i + 1; num->dfl = num->hig = i + 1; } } - DBG(ASK, dbgprint("ask limits: low: %zd, high: %zd, default: %zd", + DBG(ASK, dbgprint("ask limits: low: %ju, high: %ju, default: %ju", num->low, num->hig, num->dfl)); if (!rc && !wantnew && num->low == num->hig) { if (num->low > 0) { /* only one existing partiton, don't ask, return the number */ fdisk_ask_number_set_result(ask, num->low); - fdisk_info(cxt, _("Selected partition %d"), num->low); + fdisk_info(cxt, _("Selected partition %d"), (int) num->low); } else if (num->low == 0) { - fdisk_info(cxt, _("No partition is defined yet!")); + fdisk_warnx(cxt, _("No partition is defined yet!")); rc = 1; } goto dont_ask; @@ -311,16 +343,16 @@ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) if (num->low > 0) { /* only one free partition, don't ask, return the number */ fdisk_ask_number_set_result(ask, num->low); - fdisk_info(cxt, _("Selected partition %d"), num->low); + fdisk_info(cxt, _("Selected partition %d"), (int) num->low); } if (num->low == 0) { - fdisk_info(cxt, _("No free partition available!")); + fdisk_warnx(cxt, _("No free partition available!")); rc = 1; } goto dont_ask; } if (!rc) { - mk_string_list(ptr, &len, &begin, &run, -1); /* terminate the list */ + mk_string_list(ptr, &len, &begin, &run, -1, inchar); /* terminate the list */ rc = fdisk_ask_number_set_range(ask, range); } if (!rc) @@ -334,7 +366,7 @@ dont_ask: if (*partnum) *partnum -= 1; } - DBG(ASK, dbgprint("result: %zd [rc=%d]\n", fdisk_ask_number_get_result(ask), rc)); + DBG(ASK, dbgprint("result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask), rc)); fdisk_free_ask(ask); return rc; } @@ -371,7 +403,53 @@ int fdisk_ask_number(struct fdisk_context *cxt, *result = fdisk_ask_number_get_result(ask); fdisk_free_ask(ask); - DBG(ASK, dbgprint("result: %zd [rc=%d]\n", *result, rc)); + DBG(ASK, dbgprint("result: %ju [rc=%d]\n", *result, rc)); + return rc; +} + +char *fdisk_ask_string_get_result(struct fdisk_ask *ask) +{ + assert(ask); + assert(fdisk_is_ask(ask, STRING)); + return ask->data.str.result; +} + +/* + * The @result has to be poiter to the allocated buffer. + */ +int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result) +{ + assert(ask); + ask->data.str.result = result; + return 0; +} + +/* + * Don't forget to deallocate @result. + */ +int fdisk_ask_string(struct fdisk_context *cxt, + const char *query, + char **result) +{ + struct fdisk_ask *ask; + int rc; + + assert(cxt); + + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING); + if (!rc) + fdisk_ask_set_query(ask, query); + if (!rc) + rc = fdisk_do_ask(cxt, ask); + if (!rc) + *result = fdisk_ask_string_get_result(ask); + + fdisk_free_ask(ask); + DBG(ASK, dbgprint("result: %s [rc=%d]\n", *result, rc)); return rc; } @@ -415,6 +493,33 @@ int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, uint64_t result) return 0; } +struct tt *fdisk_ask_get_table(struct fdisk_ask *ask) +{ + assert(ask); + assert(fdisk_is_ask(ask, TABLE)); + return ask->data.table; +} + +int fdisk_print_table(struct fdisk_context *cxt, struct tt *tb) +{ + struct fdisk_ask *ask; + int rc; + + assert(cxt); + assert(tb); + + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + fdisk_ask_set_type(ask, FDISK_ASKTYPE_TABLE); + ask->data.table = tb; + + rc = fdisk_do_ask(cxt, ask); + + fdisk_free_ask(ask); + return rc; +} #define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO)) @@ -448,7 +553,7 @@ int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg) } static int do_vprint(struct fdisk_context *cxt, int errnum, int type, - const char *fmt, va_list va) + unsigned int flags, const char *fmt, va_list va) { struct fdisk_ask *ask; int rc; @@ -466,6 +571,7 @@ static int do_vprint(struct fdisk_context *cxt, int errnum, int type, } fdisk_ask_set_type(ask, type); + fdisk_ask_set_flags(ask, flags); fdisk_ask_print_set_mesg(ask, mesg); if (errnum >= 0) fdisk_ask_print_set_errno(ask, errnum); @@ -483,11 +589,39 @@ int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...) assert(cxt); va_start(ap, fmt); - rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, fmt, ap); + rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, 0, fmt, ap); va_end(ap); return rc; } +/* "smart" version, allows to set flags for the message */ +int fdisk_sinfo(struct fdisk_context *cxt, + unsigned int flags, const char *fmt, ...) +{ + int rc; + va_list ap; + + assert(cxt); + va_start(ap, fmt); + rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, flags, fmt, ap); + va_end(ap); + return rc; + +} + +int fdisk_colon(struct fdisk_context *cxt, const char *fmt, ...) +{ + int rc; + va_list ap; + + assert(cxt); + va_start(ap, fmt); + rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, FDISK_INFO_COLON, fmt, ap); + va_end(ap); + return rc; + +} + int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...) { int rc; @@ -495,7 +629,7 @@ int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...) assert(cxt); va_start(ap, fmt); - rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, fmt, ap); + rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, 0, fmt, ap); va_end(ap); return rc; } @@ -507,18 +641,28 @@ int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...) assert(cxt); va_start(ap, fmt); - rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, fmt, ap); + rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, 0, fmt, ap); va_end(ap); return rc; } -#ifdef TEST_PROGRAM -struct fdisk_label *fdisk_new_dos_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; } +int fdisk_info_new_partition( + struct fdisk_context *cxt, + int num, sector_t start, sector_t stop, + struct fdisk_parttype *t) +{ + int rc; + char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, + (uint64_t)(stop - start + 1) * cxt->sector_size); + + rc = fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Created a new partition %d of type '%s' and of size %s."), + num, t ? t->name : _("Unknown"), str); + free(str); + return rc; +} +#ifdef TEST_PROGRAM int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) { /* 1 - 3, 6, 8, 9, 11 13 */ @@ -530,9 +674,9 @@ int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) for (i = 0; i < ARRAY_SIZE(nums); i++) { if (!nums[i]) continue; - ptr = mk_string_list(ptr, &len, &begin, &run, i); + ptr = mk_string_list(ptr, &len, &begin, &run, i, 0); } - mk_string_list(ptr, &len, &begin, &run, -1); + mk_string_list(ptr, &len, &begin, &run, -1, 0); printf("list: '%s'\n", range); ptr = range; @@ -540,9 +684,9 @@ int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) for (i = 0; i < ARRAY_SIZE(numx); i++) { if (!numx[i]) continue; - ptr = mk_string_list(ptr, &len, &begin, &run, i); + ptr = mk_string_list(ptr, &len, &begin, &run, i, 0); } - mk_string_list(ptr, &len, &begin, &run, -1); + mk_string_list(ptr, &len, &begin, &run, -1, 0); printf("empty list: '%s'\n", range); return 0; diff --git a/libfdisk/src/bsd.c b/libfdisk/src/bsd.c new file mode 100644 index 000000000..e898319fb --- /dev/null +++ b/libfdisk/src/bsd.c @@ -0,0 +1,943 @@ +/* + * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com> + * + * Based on the original code from fdisk + * written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de) + * with code from the NetBSD disklabel command. + * + * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, March 1999 + * David Huggins-Daines <dhuggins@linuxcare.com>, January 2000 + */ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/param.h> + +#include "nls.h" +#include "blkdev.h" +#include "fdiskP.h" +#include "pt-mbr.h" +#include "pt-bsd.h" +#include "all-io.h" + +static const char *bsd_dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define BSD_DKMAXTYPES (ARRAY_SIZE(bsd_dktypenames) - 1) + +static struct fdisk_parttype bsd_fstypes[] = { + {BSD_FS_UNUSED, "unused"}, + {BSD_FS_SWAP, "swap"}, + {BSD_FS_V6, "Version 6"}, + {BSD_FS_V7, "Version 7"}, + {BSD_FS_SYSV, "System V"}, + {BSD_FS_V71K, "4.1BSD"}, + {BSD_FS_V8, "Eighth Edition"}, + {BSD_FS_BSDFFS, "4.2BSD"}, +#ifdef __alpha__ + {BSD_FS_EXT2, "ext2"}, +#else + {BSD_FS_MSDOS, "MS-DOS"}, +#endif + {BSD_FS_BSDLFS, "4.4LFS"}, + {BSD_FS_OTHER, "unknown"}, + {BSD_FS_HPFS, "HPFS"}, + {BSD_FS_ISO9660,"ISO-9660"}, + {BSD_FS_BOOT, "boot"}, + {BSD_FS_ADOS, "ADOS"}, + {BSD_FS_HFS, "HFS"}, + {BSD_FS_ADVFS, "AdvFS"}, + { 0, NULL } +}; +#define BSD_FSMAXTYPES (ARRAY_SIZE(bsd_fstypes)-1) + +/* + * in-memory fdisk BSD stuff + */ +struct fdisk_bsd_label { + struct fdisk_label head; /* generic part */ + + struct dos_partition *dos_part; /* parent */ + struct bsd_disklabel bsd; /* on disk label */ +#if defined (__alpha__) + /* We access this through a u_int64_t * when checksumming */ + char bsdbuffer[BSD_BBSIZE] __attribute__((aligned(8))); +#else + char bsdbuffer[BSD_BBSIZE]; +#endif +}; + +static int bsd_list_disklabel(struct fdisk_context *cxt); +static int bsd_initlabel(struct fdisk_context *cxt); +static int bsd_readlabel(struct fdisk_context *cxt); +static void sync_disks(struct fdisk_context *cxt); + +#define bsd_cround(c, n) \ + (fdisk_context_use_cylinders(c) ? ((n)/self_disklabel(c)->d_secpercyl) + 1 : (n)) + +static inline struct fdisk_bsd_label *self_label(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, BSD)); + + return (struct fdisk_bsd_label *) cxt->label; +} + +static inline struct bsd_disklabel *self_disklabel(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, BSD)); + + return &((struct fdisk_bsd_label *) cxt->label)->bsd; +} + +#if defined (__alpha__) +static void alpha_bootblock_checksum (char *boot) +{ + uint64_t *dp = (uint64_t *) boot, sum = 0; + int i; + + for (i = 0; i < 63; i++) + sum += dp[i]; + dp[63] = sum; +} +#endif /* __alpha__ */ + +#define HIDDEN_MASK 0x10 + +static int is_bsd_partition_type(int type) +{ + return (type == MBR_FREEBSD_PARTITION || + type == (MBR_FREEBSD_PARTITION ^ HIDDEN_MASK) || + type == MBR_NETBSD_PARTITION || + type == (MBR_NETBSD_PARTITION ^ HIDDEN_MASK) || + type == MBR_OPENBSD_PARTITION || + type == (MBR_OPENBSD_PARTITION ^ HIDDEN_MASK)); +} + +/* + * look for DOS partition usable for nested BSD partition table + */ +static int bsd_assign_dos_partition(struct fdisk_context *cxt) +{ + struct fdisk_bsd_label *l = self_label(cxt); + size_t i; + + for (i = 0; i < 4; i++) { + sector_t ss; + + l->dos_part = fdisk_dos_get_partition(cxt->parent, i); + + if (!l->dos_part || !is_bsd_partition_type(l->dos_part->sys_ind)) + continue; + + ss = dos_partition_get_start(l->dos_part); + if (!ss) { + fdisk_warnx(cxt, _("Partition %zd: has invalid starting " + "sector 0."), i + 1); + return -1; + } + + if (cxt->parent->dev_path) { + free(cxt->dev_path); + cxt->dev_path = fdisk_partname( + cxt->parent->dev_path, i + 1); + } + + DBG(LABEL, dbgprint("partition %zu assigned to BSD", i + 1)); + return 0; + } + + fdisk_warnx(cxt, _("There is no *BSD partition on %s."), + cxt->parent->dev_path); + free(cxt->dev_path); + cxt->dev_path = NULL; + l->dos_part = NULL; + return 1; +} + +static int bsd_probe_label(struct fdisk_context *cxt) +{ + int rc = 0; + + if (cxt->parent) + rc = bsd_assign_dos_partition(cxt); /* nested BSD partiotn table */ + if (!rc) + rc = bsd_readlabel(cxt); + if (!rc) + return 1; /* found BSD */ + return 0; /* not found */ +} + +static int bsd_add_part (struct fdisk_context *cxt, + size_t i, + struct fdisk_parttype *t __attribute__((__unused__))) +{ + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + struct fdisk_ask *ask; + unsigned int begin = 0, end; + int rc; + + if (i >= BSD_MAXPARTITIONS) + return -EINVAL; + + if (l->dos_part) { + begin = dos_partition_get_start(l->dos_part); + end = begin + dos_partition_get_size(l->dos_part) - 1; + } else + end = d->d_secperunit - 1; + + ask = fdisk_new_ask(); + + /* + * First sector + */ + if (fdisk_context_use_cylinders(cxt)) + fdisk_ask_set_query(ask, _("First cylinder")); + else + fdisk_ask_set_query(ask, _("First sector")); + + fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + fdisk_ask_number_set_low(ask, bsd_cround(cxt, begin)); + fdisk_ask_number_set_default(ask, bsd_cround(cxt, begin)); + fdisk_ask_number_set_high(ask, bsd_cround(cxt, end)); + + rc = fdisk_do_ask(cxt, ask); + if (rc) { + fdisk_free_ask(ask); + return rc; + } + begin = fdisk_ask_number_get_result(ask); + + if (fdisk_context_use_cylinders(cxt)) + begin = (begin - 1) * d->d_secpercyl; + + fdisk_reset_ask(ask); + + /* + * Last sector + */ + fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET); + + if (fdisk_context_use_cylinders(cxt)) { + fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}")); + fdisk_ask_number_set_unit(ask, + cxt->sector_size * + fdisk_context_get_units_per_sector(cxt)); + } else { + fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}")); + fdisk_ask_number_set_unit(ask,cxt->sector_size); + } + + fdisk_ask_number_set_low(ask, bsd_cround(cxt, begin)); + fdisk_ask_number_set_default(ask, bsd_cround(cxt, end)); + fdisk_ask_number_set_high(ask, bsd_cround(cxt, end)); + fdisk_ask_number_set_base(ask, bsd_cround(cxt, begin)); + + rc = fdisk_do_ask(cxt, ask); + end = fdisk_ask_number_get_result(ask); + fdisk_free_ask(ask); + if (rc) + return rc; + + if (fdisk_context_use_cylinders(cxt)) + end = end * d->d_secpercyl - 1; + + d->d_partitions[i].p_size = end - begin + 1; + d->d_partitions[i].p_offset = begin; + d->d_partitions[i].p_fstype = BSD_FS_UNUSED; + + if (i >= d->d_npartitions) + d->d_npartitions = i + 1; + cxt->label->nparts_cur = d->d_npartitions; + + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +/* Returns 0 on success, < 0 on error. */ +static int bsd_create_disklabel(struct fdisk_context *cxt) +{ + int rc, yes = 0; + struct bsd_disklabel *d = self_disklabel(cxt); + + fdisk_info(cxt, _("The device %s does not contain BSD disklabel."), cxt->dev_path); + rc = fdisk_ask_yesno(cxt, + _("Do you want to create a BSD disklabel?"), + &yes); + if (rc) + return rc; + if (!yes) + return 1; + if (cxt->parent) { + rc = bsd_assign_dos_partition(cxt); + if (rc == 1) + /* not found DOS partition usable for BSD label */ + rc = -EINVAL; + } + if (rc) + return rc; + + rc = bsd_initlabel(cxt); + if (!rc) { + int org = fdisk_context_display_details(cxt); + + cxt->label->nparts_cur = d->d_npartitions; + cxt->label->nparts_max = BSD_MAXPARTITIONS; + + fdisk_context_enable_details(cxt, 1); + bsd_list_disklabel(cxt); + fdisk_context_enable_details(cxt, org); + } + + return rc; +} + +static int bsd_delete_part( + struct fdisk_context *cxt, + size_t partnum) +{ + struct bsd_disklabel *d = self_disklabel(cxt); + + d->d_partitions[partnum].p_size = 0; + d->d_partitions[partnum].p_offset = 0; + d->d_partitions[partnum].p_fstype = BSD_FS_UNUSED; + + if (d->d_npartitions == partnum + 1) + while (!d->d_partitions[d->d_npartitions - 1].p_size) + d->d_npartitions--; + + cxt->label->nparts_cur = d->d_npartitions; + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +static int bsd_list_disklabel(struct fdisk_context *cxt) +{ + struct bsd_disklabel *d = self_disklabel(cxt); + struct bsd_partition *p; + struct tt *tb = NULL; + int i, rc, trunc = TT_FL_TRUNC; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, BSD)); + + if (fdisk_context_display_details(cxt)) { + fdisk_colon(cxt, "# %s:", cxt->dev_path); + + if ((unsigned) d->d_type < BSD_DKMAXTYPES) + fdisk_colon(cxt, _("type: %s"), bsd_dktypenames[d->d_type]); + else + fdisk_colon(cxt, _("type: %d"), d->d_type); + + fdisk_colon(cxt, _("disk: %.*s"), (int) sizeof(d->d_typename), d->d_typename); + fdisk_colon(cxt, _("label: %.*s"), (int) sizeof(d->d_packname), d->d_packname); + + fdisk_colon(cxt, _("flags: %s"), + d->d_flags & BSD_D_REMOVABLE ? _(" removable") : + d->d_flags & BSD_D_ECC ? _(" ecc") : + d->d_flags & BSD_D_BADSECT ? _(" badsect") : ""); + + /* On various machines the fields of *lp are short/int/long */ + /* In order to avoid problems, we cast them all to long. */ + fdisk_colon(cxt, _("bytes/sector: %ld"), (long) d->d_secsize); + fdisk_colon(cxt, _("sectors/track: %ld"), (long) d->d_nsectors); + fdisk_colon(cxt, _("tracks/cylinder: %ld"), (long) d->d_ntracks); + fdisk_colon(cxt, _("sectors/cylinder: %ld"), (long) d->d_secpercyl); + fdisk_colon(cxt, _("cylinders: %ld"), (long) d->d_ncylinders); + fdisk_colon(cxt, _("rpm: %d"), d->d_rpm); + fdisk_colon(cxt, _("interleave: %d"), d->d_interleave); + fdisk_colon(cxt, _("trackskew: %d"), d->d_trackskew); + fdisk_colon(cxt, _("cylinderskew: %d"), d->d_cylskew); + fdisk_colon(cxt, _("headswitch: %ld (milliseconds)"), (long) d->d_headswitch); + fdisk_colon(cxt, _("track-to-track seek: %ld (milliseconds)"), (long) d->d_trkseek); + /* + fdisk_colon(cxt, _("drivedata: ")); + for (i = ARRAY_SIZE(d->d_drivedata)- 1; i >= 0; i--) + if (d->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + fdisk_info(cxt, "%ld ", (long) d->d_drivedata[j]); + */ + } + + fdisk_colon(cxt, _("partitions: %d"), d->d_npartitions); + + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + /* don't trunc anything in expert mode */ + if (fdisk_context_display_details(cxt)) + trunc = 0; + + tt_define_column(tb, _("#"), 1, 0); + tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("End"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Size"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Type"), 8, 0); + tt_define_column(tb, _("fsize"), 5, trunc); + tt_define_column(tb, _("bsize"), 5, trunc); + tt_define_column(tb, _("cpg"), 5, trunc); + + for (i = 0, p = d->d_partitions; i < d->d_npartitions; i++, p++) { + char *s; + struct tt_line *ln; + + if (!p->p_size) + continue; + ln = tt_add_line(tb, NULL); + if (!ln) + continue; + + if (asprintf(&s, "%c", i + 'a') > 0) + tt_line_set_data(ln, 0, s); + + if (fdisk_context_use_cylinders(cxt) && d->d_secpercyl) { + if (asprintf(&s, "%u%c", + p->p_offset / d->d_secpercyl + 1, + p->p_offset % d->d_secpercyl ? '*' : ' ') > 0) + tt_line_set_data(ln, 1, s); + if (asprintf(&s, "%u%c", + (p->p_offset + p->p_size + d->d_secpercyl - 1) / d->d_secpercyl, + (p->p_offset + p->p_size) % d->d_secpercyl ? '*' : ' ') > 0) + tt_line_set_data(ln, 2, s); + if (asprintf(&s, "%u%c", + p->p_size / d->d_secpercyl, + p->p_size % d->d_secpercyl ? '*' : ' ') > 0) + tt_line_set_data(ln, 3, s); + } else { + if (asprintf(&s, "%u", p->p_offset) > 0) + tt_line_set_data(ln, 1, s); + if (asprintf(&s, "%u", p->p_offset + p->p_size - 1) > 0) + tt_line_set_data(ln, 2, s); + if (asprintf(&s, "%u", p->p_size) > 0) + tt_line_set_data(ln, 3, s); + } + + if ((unsigned) p->p_fstype < BSD_FSMAXTYPES) + rc = asprintf(&s, "%s", bsd_fstypes[p->p_fstype].name); + else + rc = asprintf(&s, "%x", p->p_fstype); + if (rc > 0) + tt_line_set_data(ln, 4, s); + + if (p->p_fstype == BSD_FS_UNUSED + || p->p_fstype == BSD_FS_BSDFFS) { + if (asprintf(&s, "%u", p->p_fsize) > 0) + tt_line_set_data(ln, 5, s); + if (asprintf(&s, "%u", p->p_fsize * p->p_frag) > 0) + tt_line_set_data(ln, 6, s); + } + if (p->p_fstype == BSD_FS_BSDFFS + && asprintf(&s, "%u", p->p_cpg) > 0) + tt_line_set_data(ln, 7, s); + } + + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + + return rc; +} + +static uint32_t ask_uint32(struct fdisk_context *cxt, + uint32_t dflt, char *mesg) +{ + uintmax_t res; + + if (fdisk_ask_number(cxt, min(dflt, (uint32_t) 1), dflt, + UINT32_MAX, mesg, &res) == 0) + return res; + return dflt; +} + +static uint16_t ask_uint16(struct fdisk_context *cxt, + uint16_t dflt, char *mesg) +{ + uintmax_t res; + + if (fdisk_ask_number(cxt, min(dflt, (uint16_t) 1), + dflt, UINT16_MAX, mesg, &res) == 0) + return res; + return dflt; +} + +int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt) +{ + struct bsd_disklabel *d = self_disklabel(cxt); + uintmax_t res; + +#if defined (__alpha__) || defined (__ia64__) + if (fdisk_ask_number(cxt, DEFAULT_SECTOR_SIZE, d->d_secsize, + UINT32_MAX, _("bytes/sector"), &res) == 0) + d->d_secsize = res; + + d->d_nsectors = ask_uint32(cxt, d->d_nsectors, _("sectors/track")); + d->d_ntracks = ask_uint32(cxt, d->d_ntracks, _("tracks/cylinder")); + d->d_ncylinders = ask_uint32(cxt, d->d_ncylinders ,_("cylinders")); +#endif + if (fdisk_ask_number(cxt, 1, d->d_nsectors * d->d_ntracks, + d->d_nsectors * d->d_ntracks, + _("sectors/cylinder"), &res) == 0) + d->d_secpercyl = res; + + d->d_rpm = ask_uint16(cxt, d->d_rpm, _("rpm")); + d->d_interleave = ask_uint16(cxt, d->d_interleave, _("interleave")); + d->d_trackskew = ask_uint16(cxt, d->d_trackskew, _("trackskew")); + d->d_cylskew = ask_uint16(cxt, d->d_cylskew, _("cylinderskew")); + + d->d_headswitch = ask_uint32(cxt, d->d_headswitch, _("headswitch")); + d->d_trkseek = ask_uint32(cxt, d->d_trkseek, _("track-to-track seek")); + + d->d_secperunit = d->d_secpercyl * d->d_ncylinders; + return 0; +} + +static int bsd_get_bootstrap(struct fdisk_context *cxt, + char *path, void *ptr, int size) +{ + int fd; + + if ((fd = open(path, O_RDONLY)) < 0) { + fdisk_warn(cxt, _("cannot open %s"), path); + return -errno; + } + + if (read_all(fd, ptr, size) != size) { + fdisk_warn(cxt, _("cannot read %s"), path); + close(fd); + return -errno; + } + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("The bootstrap file %s successfully loaded."), path); + close (fd); + return 0; +} + +int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt) +{ + struct bsd_disklabel dl, *d = self_disklabel(cxt); + struct fdisk_bsd_label *l = self_label(cxt); + char *name = d->d_type == BSD_DTYPE_SCSI ? "sd" : "wd"; + char buf[BUFSIZ]; + char *res, *dp, *p; + int rc; + sector_t sector; + + snprintf(buf, sizeof(buf), + _("Bootstrap: %1$sboot -> boot%1$s (default %1$s)"), + name); + rc = fdisk_ask_string(cxt, buf, &res); + if (rc) + goto done; + if (res && *res) + name = res; + + snprintf(buf, sizeof(buf), "%s/%sboot", BSD_LINUX_BOOTDIR, name); + rc = bsd_get_bootstrap(cxt, buf, l->bsdbuffer, (int) d->d_secsize); + if (rc) + goto done; + + /* We need a backup of the disklabel (might have changed). */ + dp = &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE]; + memmove(&dl, dp, sizeof(struct bsd_disklabel)); + + /* The disklabel will be overwritten by 0's from bootxx anyway */ + memset(dp, 0, sizeof(struct bsd_disklabel)); + + snprintf(buf, sizeof(buf), "%s/boot%s", BSD_LINUX_BOOTDIR, name); + rc = bsd_get_bootstrap(cxt, buf, + &l->bsdbuffer[d->d_secsize], + (int) d->d_bbsize - d->d_secsize); + if (rc) + goto done; + + /* check end of the bootstrap */ + for (p = dp; p < dp + sizeof(struct bsd_disklabel); p++) { + if (!*p) + continue; + fdisk_warnx(cxt, _("Bootstrap overlaps with disklabel!")); + return -EINVAL; + } + + /* move disklabel back */ + memmove(dp, &dl, sizeof(struct bsd_disklabel)); + + sector = 0; + if (l->dos_part) + sector = dos_partition_get_start(l->dos_part); +#if defined (__alpha__) + alpha_bootblock_checksum(l->bsdbuffer); +#endif + if (lseek(cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) { + fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path); + rc = -errno; + goto done; + } + if (write_all(cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) { + fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path); + rc = -errno; + goto done; + } + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Bootstrap installed on %s."), cxt->dev_path); + sync_disks(cxt); + + rc = 0; +done: + free(res); + return rc; +} + +static unsigned short bsd_dkcksum (struct bsd_disklabel *lp) +{ + unsigned short *start, *end; + unsigned short sum = 0; + + start = (unsigned short *) lp; + end = (unsigned short *) &lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return sum; +} + +static int bsd_initlabel (struct fdisk_context *cxt) +{ + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + struct bsd_partition *pp; + + memset (d, 0, sizeof (struct bsd_disklabel)); + + d -> d_magic = BSD_DISKMAGIC; + + if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0) + d -> d_type = BSD_DTYPE_SCSI; + else + d -> d_type = BSD_DTYPE_ST506; + +#if !defined (__alpha__) + d -> d_flags = BSD_D_DOSPART; +#else + d -> d_flags = 0; +#endif + d -> d_secsize = DEFAULT_SECTOR_SIZE; /* bytes/sector */ + d -> d_nsectors = cxt->geom.sectors; /* sectors/track */ + d -> d_ntracks = cxt->geom.heads; /* tracks/cylinder (heads) */ + d -> d_ncylinders = cxt->geom.cylinders; + d -> d_secpercyl = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */ + if (d -> d_secpercyl == 0) + d -> d_secpercyl = 1; /* avoid segfaults */ + d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders; + + d -> d_rpm = 3600; + d -> d_interleave = 1; + d -> d_trackskew = 0; + d -> d_cylskew = 0; + d -> d_headswitch = 0; + d -> d_trkseek = 0; + + d -> d_magic2 = BSD_DISKMAGIC; + d -> d_bbsize = BSD_BBSIZE; + d -> d_sbsize = BSD_SBSIZE; + + if (l->dos_part) { + d->d_npartitions = 4; + pp = &d->d_partitions[2]; /* Partition C should be + the NetBSD partition */ + pp->p_offset = dos_partition_get_start(l->dos_part); + pp->p_size = dos_partition_get_size(l->dos_part); + pp->p_fstype = BSD_FS_UNUSED; + pp = &d -> d_partitions[3]; /* Partition D should be + the whole disk */ + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; + } else { + d->d_npartitions = 3; + pp = &d->d_partitions[2]; /* Partition C should be + the whole disk */ + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; + } + + return 0; +} + +/* + * Read a bsd_disklabel from sector 0 or from the starting sector of p. + * If it has the right magic, return 0. + */ +static int bsd_readlabel(struct fdisk_context *cxt) +{ + struct fdisk_bsd_label *l; + struct bsd_disklabel *d; + int t; + off_t offset = 0; + + l = self_label(cxt); + d = self_disklabel(cxt); + + if (l->dos_part) + /* BSD is nested within DOS partition, get the begin of the + * partition. Note that DOS uses native sector size. */ + offset = dos_partition_get_start(l->dos_part) * cxt->sector_size; + + if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) + return -1; + if (read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0) + return errno ? -errno : -1; + + /* The offset to begin of the disk label. Note that BSD uses + * 512-byte (default) sectors. */ + memmove(d, &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE + + BSD_LABELOFFSET], sizeof(*d)); + + if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) { + DBG(LABEL, dbgprint("not found magic")); + return -1; + } + + for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) { + d->d_partitions[t].p_size = 0; + d->d_partitions[t].p_offset = 0; + d->d_partitions[t].p_fstype = BSD_FS_UNUSED; + } + + if (d->d_npartitions > BSD_MAXPARTITIONS) + fdisk_warnx(cxt, ("Too many partitions (%d, maximum is %d)."), + d->d_npartitions, BSD_MAXPARTITIONS); + + cxt->label->nparts_cur = d->d_npartitions; + cxt->label->nparts_max = BSD_MAXPARTITIONS; + DBG(LABEL, dbgprint("read BSD label")); + return 0; +} + +static int bsd_write_disklabel(struct fdisk_context *cxt) +{ + off_t offset = 0; + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + + + if (l->dos_part) + offset = dos_partition_get_start(l->dos_part) * cxt->sector_size; + + d->d_checksum = 0; + d->d_checksum = bsd_dkcksum(d); + + /* Update label within boot block. */ + memmove(&l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE + + BSD_LABELOFFSET], d, sizeof(*d)); + +#if defined (__alpha__) && BSD_LABELSECTOR == 0 + /* Write the checksum to the end of the first sector. */ + alpha_bootblock_checksum(l->bsdbuffer); +#endif + if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) { + fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path); + return -errno; + } + if (write_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) { + fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path); + return -errno; + } + sync_disks(cxt); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Disklabel written to %s."), cxt->dev_path); + return 0; +} + +static void sync_disks(struct fdisk_context *cxt) +{ + fdisk_info(cxt, _("Syncing disks.")); + sync(); +} + +static int bsd_translate_fstype (int linux_type) +{ + switch (linux_type) { + case 0x01: /* DOS 12-bit FAT */ + case 0x04: /* DOS 16-bit <32M */ + case 0x06: /* DOS 16-bit >=32M */ + case 0xe1: /* DOS access */ + case 0xe3: /* DOS R/O */ + case 0xf2: /* DOS secondary */ + return BSD_FS_MSDOS; + case 0x07: /* OS/2 HPFS */ + return BSD_FS_HPFS; + default: + break; + } + + return BSD_FS_OTHER; +} + +/* + * link partition from parent (DOS) to nested BSD partition table + */ +int fdisk_bsd_link_partition(struct fdisk_context *cxt) +{ + size_t k, i; + int rc; + struct dos_partition *p; + struct bsd_disklabel *d = self_disklabel(cxt); + + if (!cxt->parent || !fdisk_is_disklabel(cxt->parent, DOS)) { + fdisk_warnx(cxt, _("BSD label is not nested within a DOS partition")); + return -EINVAL; + } + + /* ask for DOS partition */ + rc = fdisk_ask_partnum(cxt->parent, &k, FALSE); + if (rc) + return rc; + /* ask for BSD partition */ + rc = fdisk_ask_partnum(cxt, &i, TRUE); + if (rc) + return rc; + + if (i >= BSD_MAXPARTITIONS) + return -EINVAL; + + p = fdisk_dos_get_partition(cxt->parent, k); + + d->d_partitions[i].p_size = dos_partition_get_size(p); + d->d_partitions[i].p_offset = dos_partition_get_start(p); + d->d_partitions[i].p_fstype = bsd_translate_fstype(p->sys_ind); + + if (i >= d->d_npartitions) + d->d_npartitions = i + 1; + + cxt->label->nparts_cur = d->d_npartitions; + fdisk_label_set_changed(cxt->label, 1); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("BSD partition '%c' linked to DOS partition %d."), + 'a' + (int) i, (int) k + 1); + return 0; +} + +static struct fdisk_parttype *bsd_get_parttype( + struct fdisk_context *cxt, + size_t n) +{ + struct fdisk_parttype *t; + struct bsd_disklabel *d = self_disklabel(cxt); + + if (n >= d->d_npartitions) + return NULL; + + t = fdisk_get_parttype_from_code(cxt, d->d_partitions[n].p_fstype); + if (!t) + t = fdisk_new_unknown_parttype(d->d_partitions[n].p_fstype, NULL); + return t; +} + +static int bsd_set_parttype( + struct fdisk_context *cxt, + size_t partnum, + struct fdisk_parttype *t) +{ + struct bsd_partition *p; + struct bsd_disklabel *d = self_disklabel(cxt); + + if (partnum >= d->d_npartitions || !t || t->type > UINT8_MAX) + return -EINVAL; + + p = &d->d_partitions[partnum]; + if (t->type == p->p_fstype) + return 0; + + p->p_fstype = t->type; + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +static int bsd_get_partition_status( + struct fdisk_context *cxt, + size_t partnum, + int *status) +{ + struct bsd_partition *p; + struct bsd_disklabel *d = self_disklabel(cxt); + + if (!status || partnum >= BSD_MAXPARTITIONS) + return -EINVAL; + + p = &d->d_partitions[partnum]; + *status = FDISK_PARTSTAT_NONE; + + if (p->p_size) + *status = FDISK_PARTSTAT_USED; + + return 0; +} + + +static const struct fdisk_label_operations bsd_operations = +{ + .probe = bsd_probe_label, + .list = bsd_list_disklabel, + .write = bsd_write_disklabel, + .create = bsd_create_disklabel, + .part_add = bsd_add_part, + .part_delete = bsd_delete_part, + .part_get_type = bsd_get_parttype, + .part_set_type = bsd_set_parttype, + .part_get_status= bsd_get_partition_status, +}; + + +/* + * allocates BSD label driver + */ +struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) +{ + struct fdisk_label *lb; + struct fdisk_bsd_label *bsd; + + assert(cxt); + + bsd = calloc(1, sizeof(*bsd)); + if (!bsd) + return NULL; + + /* initialize generic part of the driver */ + lb = (struct fdisk_label *) bsd; + lb->name = "bsd"; + lb->id = FDISK_DISKLABEL_BSD; + lb->op = &bsd_operations; + lb->parttypes = bsd_fstypes; + lb->nparttypes = ARRAY_SIZE(bsd_fstypes); + + lb->flags |= FDISK_LABEL_FL_INCHARS_PARTNO; + lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY; + + return lb; +} diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 7569e5b52..0de2d2a59 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -21,7 +21,6 @@ struct fdisk_context *fdisk_new_context(void) cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt); cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt); cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt); - cxt->labels[ cxt->nlabels++ ] = fdisk_new_mac_label(cxt); cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt); cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt); @@ -33,9 +32,9 @@ struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name) { struct fdisk_context *cxt; + struct fdisk_label *lb = NULL; assert(parent); - assert(name); cxt = calloc(1, sizeof(*cxt)); if (!cxt) @@ -55,10 +54,28 @@ struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, cxt->first_lba = parent->first_lba; cxt->total_sectors = parent->total_sectors; + cxt->ask_cb = parent->ask_cb; + cxt->ask_data = parent->ask_data; + cxt->geom = parent->geom; - if (strcmp(name, "bsd") == 0) - cxt->label = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt); + if (name && strcmp(name, "bsd") == 0) + lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt); + + if (lb) { + DBG(LABEL, dbgprint("probing for nested %s", lb->name)); + + cxt->label = lb; + + if (lb->op->probe(cxt) == 1) + __fdisk_context_switch_label(cxt, lb); + else { + DBG(LABEL, dbgprint("not found %s label", lb->name)); + if (lb->op->deinit) + lb->op->deinit(lb); + cxt->label = NULL; + } + } return cxt; } @@ -77,18 +94,46 @@ struct fdisk_label *fdisk_context_get_label(struct fdisk_context *cxt, const cha return cxt->label; for (i = 0; i < cxt->nlabels; i++) - if (strcmp(cxt->labels[i]->name, name) == 0) + if (cxt->labels[i] + && strcmp(cxt->labels[i]->name, name) == 0) return cxt->labels[i]; DBG(LABEL, dbgprint("failed to found %s label driver\n", name)); return NULL; } +int fdisk_context_next_label(struct fdisk_context *cxt, struct fdisk_label **lb) +{ + size_t i; + struct fdisk_label *res = NULL; + + if (!lb || !cxt) + return -EINVAL; + + if (!*lb) + res = cxt->labels[0]; + else { + for (i = 1; i < cxt->nlabels; i++) { + if (*lb == cxt->labels[i - 1]) { + res = cxt->labels[i]; + break; + } + } + } + + *lb = res; + return res ? 0 : 1; +} + int __fdisk_context_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb) { - if (!lb) + if (!lb || !cxt) return -EINVAL; + if (lb->disabled) { + DBG(LABEL, dbgprint("*** attempt to switch to disabled label %s -- ignore!", lb->name)); + return -EINVAL; + } cxt->label = lb; DBG(LABEL, dbgprint("--> switching context to %s!", lb->name)); return 0; @@ -105,7 +150,7 @@ static void reset_context(struct fdisk_context *cxt) { size_t i; - DBG(CONTEXT, dbgprint("\n-----\nresetting context %p", cxt)); + DBG(CONTEXT, dbgprint("*** resetting context %p", cxt)); /* reset drives' private data */ for (i = 0; i < cxt->nlabels; i++) @@ -122,17 +167,7 @@ static void reset_context(struct fdisk_context *cxt) 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)); + fdisk_zeroize_device_properties(cxt); cxt->label = NULL; } @@ -158,12 +193,11 @@ int fdisk_context_assign_device(struct fdisk_context *cxt, reset_context(cxt); - if (readonly == 1 || (fd = open(fname, O_RDWR|O_CLOEXEC)) < 0) { - if ((fd = open(fname, O_RDONLY|O_CLOEXEC)) < 0) - return -errno; - readonly = 1; - } + fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC); + if (fd < 0) + return -errno; + cxt->readonly = readonly; cxt->dev_fd = fd; cxt->dev_path = strdup(fname); if (!cxt->dev_path) @@ -178,7 +212,10 @@ int fdisk_context_assign_device(struct fdisk_context *cxt, /* detect labels and apply labes specific stuff (e.g geomery) * to the context */ fdisk_probe_labels(cxt); - fdisk_reset_alignment(cxt); + + /* let's apply user geometry *after* label prober + * to make it possible to override in-label setting */ + fdisk_apply_user_device_properties(cxt); DBG(CONTEXT, dbgprint("context %p initialized for %s [%s]", cxt, fname, @@ -189,6 +226,29 @@ fail: return -errno; } +int fdisk_context_deassign_device(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->dev_fd >= 0); + + if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) { + fdisk_warn(cxt, _("%s: close device failed"), cxt->dev_path); + return -errno; + } + + fdisk_info(cxt, _("Syncing disks.")); + sync(); + + cxt->dev_fd = -1; + return 0; +} + +int fdisk_context_is_readonly(struct fdisk_context *cxt) +{ + assert(cxt); + return cxt->readonly; +} + /** * fdisk_free_context: * @cxt: fdisk context @@ -237,6 +297,50 @@ int fdisk_context_set_ask(struct fdisk_context *cxt, return 0; } +/** + * fdisk_context_enable_details: + * cxt: context + * enable: true/flase + * + * Enables or disables "details" display mode. + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_context_enable_details(struct fdisk_context *cxt, int enable) +{ + assert(cxt); + cxt->display_details = enable ? 1 : 0; + return 0; +} + +int fdisk_context_display_details(struct fdisk_context *cxt) +{ + assert(cxt); + return cxt->display_details == 1; +} + +/** + * fdisk_context_enable_listonly: + * cxt: context + * enable: true/flase + * + * Just list partition only, don't care about another details, mistakes, ... + * + * Returns: 0 on success, < 0 on error. + */ +int fdisk_context_enable_listonly(struct fdisk_context *cxt, int enable) +{ + assert(cxt); + cxt->listonly = enable ? 1 : 0; + return 0; +} + +int fdisk_context_listonly(struct fdisk_context *cxt) +{ + assert(cxt); + return cxt->listonly == 1; +} + /* * @str: "cylinder" or "sector". diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c new file mode 100644 index 000000000..1f5d8b33f --- /dev/null +++ b/libfdisk/src/dos.c @@ -0,0 +1,2086 @@ +/* + * + * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com> + * 2012 Davidlohr Bueso <dave@gnu.org> + * + * This is re-written version for libfdisk, the original was fdiskdoslabel.c + * from util-linux fdisk. + */ +#include "c.h" +#include "nls.h" +#include "randutils.h" +#include "pt-mbr.h" +#include "strutils.h" + +#include "fdiskP.h" + +#include <ctype.h> + +#define MAXIMUM_PARTS 60 +#define ACTIVE_FLAG 0x80 + +#define IS_EXTENDED(i) \ + ((i) == MBR_DOS_EXTENDED_PARTITION \ + || (i) == MBR_W95_EXTENDED_PARTITION \ + || (i) == MBR_LINUX_EXTENDED_PARTITION) + +/* + * per partition table entry data + * + * The four primary partitions have the same sectorbuffer + * and have NULL ex_entry. + * + * Each logical partition table entry has two pointers, one for the + * partition and one link to the next one. + */ +struct pte { + struct dos_partition *pt_entry; /* on-disk MBR entry */ + struct dos_partition *ex_entry; /* on-disk EBR entry */ + sector_t offset; /* disk sector number */ + unsigned char *sectorbuffer; /* disk sector contents */ + + unsigned int changed : 1, + private_sectorbuffer : 1; +}; + +/* + * in-memory fdisk GPT stuff + */ +struct fdisk_dos_label { + struct fdisk_label head; /* generic part */ + + struct pte ptes[MAXIMUM_PARTS]; /* partition */ + sector_t ext_offset; + size_t ext_index; + unsigned int compatible : 1, /* is DOS compatible? */ + non_pt_changed : 1; /* MBR, but no PT changed */ +}; + +/* + * Partition types + */ +static struct fdisk_parttype dos_parttypes[] = { + #include "pt-mbr-partnames.h" +}; + +#define set_hsc(h,s,c,sector) { \ + s = sector % cxt->geom.sectors + 1; \ + sector /= cxt->geom.sectors; \ + h = sector % cxt->geom.heads; \ + sector /= cxt->geom.heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ + } + + +#define sector(s) ((s) & 0x3f) +#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) + +#define alignment_required(_x) ((_x)->grain != (_x)->sector_size) + +#define is_dos_compatible(_x) \ + (fdisk_is_disklabel(_x, DOS) && \ + fdisk_dos_is_compatible(fdisk_context_get_label(_x, NULL))) + +#define cround(c, n) fdisk_cround(c, n) + + +static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + return (struct fdisk_dos_label *) cxt->label; +} + +static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i) +{ + struct fdisk_dos_label *l = self_label(cxt); + + if (i >= ARRAY_SIZE(l->ptes)) + return NULL; + + return &l->ptes[i]; +} + +static inline struct dos_partition *self_partition( + struct fdisk_context *cxt, + size_t i) +{ + struct pte *pe = self_pte(cxt, i); + return pe ? pe->pt_entry : NULL; +} + +struct dos_partition *fdisk_dos_get_partition( + struct fdisk_context *cxt, + size_t i) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + return self_partition(cxt, i); +} + +static void partition_set_changed( + struct fdisk_context *cxt, + size_t i, + int changed) +{ + struct pte *pe = self_pte(cxt, i); + + if (!pe) + return; + + DBG(LABEL, dbgprint("DOS: setting %zu partition changed", i)); + + pe->changed = changed ? 1 : 0; + if (changed) + fdisk_label_set_changed(cxt->label, 1); +} + +static sector_t get_abs_partition_start(struct pte *pe) +{ + assert(pe); + assert(pe->pt_entry); + + return pe->offset + dos_partition_get_start(pe->pt_entry); +} + +static sector_t get_abs_partition_end(struct pte *pe) +{ + sector_t size; + + assert(pe); + assert(pe->pt_entry); + + size = dos_partition_get_size(pe->pt_entry); + return get_abs_partition_start(pe) + size - (size ? 1 : 0); +} + +/* + * Linux kernel cares about partition size only. Things like + * partition type or so are completely irrelevant -- kzak Nov-2013 + */ +static int is_used_partition(struct dos_partition *p) +{ + return p && dos_partition_get_size(p) != 0; +} + +static int is_cleared_partition(struct dos_partition *p) +{ + return !(!p || p->boot_ind || p->bh || p->bs || p->bc || + p->sys_ind || p->eh || p->es || p->ec || + dos_partition_get_start(p) || dos_partition_get_size(p)); +} + +static int get_partition_unused_primary(struct fdisk_context *cxt) +{ + size_t orgmax = cxt->label->nparts_max; + size_t n; + int rc; + + cxt->label->nparts_max = 4; + rc = fdisk_ask_partnum(cxt, &n, TRUE); + cxt->label->nparts_max = orgmax; + + switch (rc) { + case 1: + fdisk_info(cxt, _("All primary partitions have been defined already.")); + return -1; + case 0: + return n; + default: + return rc; + } +} + +static int seek_sector(struct fdisk_context *cxt, sector_t secno) +{ + off_t offset = (off_t) secno * cxt->sector_size; + + return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0; +} + +static int read_sector(struct fdisk_context *cxt, sector_t secno, + unsigned char *buf) +{ + int rc = seek_sector(cxt, secno); + + if (rc < 0) + return rc; + + return read(cxt->dev_fd, buf, cxt->sector_size) != + (ssize_t) cxt->sector_size ? -errno : 0; +} + +/* Allocate a buffer and read a partition table sector */ +static int read_pte(struct fdisk_context *cxt, int pno, sector_t offset) +{ + unsigned char *buf; + struct pte *pe = self_pte(cxt, pno); + + buf = calloc(1, cxt->sector_size); + if (!buf) + return -ENOMEM; + + DBG(LABEL, dbgprint("DOS: reading pte %d sector buffer %p", pno, buf)); + + pe->offset = offset; + pe->sectorbuffer = buf; + pe->private_sectorbuffer = 1; + + if (read_sector(cxt, offset, pe->sectorbuffer) != 0) + fdisk_warn(cxt, _("Failed to read extended partition table " + "(offset=%jd)"), (uintmax_t) offset); + pe->changed = 0; + pe->pt_entry = pe->ex_entry = NULL; + return 0; +} + + +static void clear_partition(struct dos_partition *p) +{ + if (!p) + return; + p->boot_ind = 0; + p->bh = 0; + p->bs = 0; + p->bc = 0; + p->sys_ind = 0; + p->eh = 0; + p->es = 0; + p->ec = 0; + dos_partition_set_start(p,0); + dos_partition_set_size(p,0); +} + +static void dos_init(struct fdisk_context *cxt) +{ + struct fdisk_dos_label *l = self_label(cxt); + size_t i; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + DBG(LABEL, dbgprint("DOS: initialize, first sector buffer %p", cxt->firstsector)); + + cxt->label->nparts_max = 4; /* default, unlimited number of logical */ + + l->ext_index = 0; + l->ext_offset = 0; + l->non_pt_changed = 0; + + memset(l->ptes, 0, sizeof(l->ptes)); + + for (i = 0; i < 4; i++) { + struct pte *pe = self_pte(cxt, i); + + pe->pt_entry = mbr_get_partition(cxt->firstsector, i); + pe->ex_entry = NULL; + pe->offset = 0; + pe->sectorbuffer = cxt->firstsector; + pe->private_sectorbuffer = 0; + pe->changed = 0; + } + + if (fdisk_context_listonly(cxt)) + return; + /* + * Various warnings... + */ + if (fdisk_missing_geometry(cxt)) + fdisk_warnx(cxt, _("You can set geometry from the extra functions menu.")); + + if (is_dos_compatible(cxt)) { + fdisk_warnx(cxt, _("DOS-compatible mode is deprecated.")); + + if (cxt->sector_size != cxt->phy_sector_size) + fdisk_info(cxt, _( + "The device presents a logical sector size that is smaller than " + "the physical sector size. Aligning to a physical sector (or optimal " + "I/O) size boundary is recommended, or performance may be impacted.")); + } + + if (fdisk_context_use_cylinders(cxt)) + fdisk_warnx(cxt, _("Cylinders as display units are deprecated.")); + + if (cxt->total_sectors > UINT_MAX) { + uint64_t bytes = cxt->total_sectors * cxt->sector_size; + char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE + | SIZE_SUFFIX_3LETTER, bytes); + fdisk_warnx(cxt, + _("The size of this disk is %s (%llu bytes). DOS " + "partition table format can not be used on drives for " + "volumes larger than (%llu bytes) for %ld-byte " + "sectors. Use GUID partition table format (GPT)."), + szstr, (unsigned long long) bytes, + (unsigned long long) UINT_MAX * cxt->sector_size, + cxt->sector_size); + free(szstr); + } +} + +/* callback called by libfdisk */ +static void dos_deinit(struct fdisk_label *lb) +{ + size_t i; + struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb; + + for (i = 0; i < ARRAY_SIZE(l->ptes); i++) { + struct pte *pe = &l->ptes[i]; + + if (pe->private_sectorbuffer && pe->sectorbuffer) { + DBG(LABEL, dbgprint("DOS: freeing pte %zu sector buffer %p", + i, pe->sectorbuffer)); + free(pe->sectorbuffer); + } + pe->sectorbuffer = NULL; + pe->private_sectorbuffer = 0; + } + + memset(l->ptes, 0, sizeof(l->ptes)); +} + +static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum) +{ + struct fdisk_dos_label *l; + struct pte *pe; + struct dos_partition *p; + struct dos_partition *q; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + pe = self_pte(cxt, partnum); + if (!pe) + return -EINVAL; + + DBG(LABEL, dbgprint("DOS: delete partiton %zu", partnum)); + + l = self_label(cxt); + p = pe->pt_entry; + q = pe->ex_entry; + + /* Note that for the fifth partition (partnum == 4) we don't actually + decrement partitions. */ + if (partnum < 4) { + DBG(LABEL, dbgprint("--> delete primary")); + if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) { + cxt->label->nparts_max = 4; + l->ptes[l->ext_index].ex_entry = NULL; + l->ext_offset = 0; + } + partition_set_changed(cxt, partnum, 1); + clear_partition(p); + } else if (!q->sys_ind && partnum > 4) { + DBG(LABEL, dbgprint("--> delete logical [last in the chain]")); + --cxt->label->nparts_max; + --partnum; + clear_partition(l->ptes[partnum].ex_entry); + partition_set_changed(cxt, partnum, 1); + } else { + DBG(LABEL, dbgprint("--> delete logical [non-last, move down]")); + if (partnum > 4) { + /* delete this link in the chain */ + p = l->ptes[partnum - 1].ex_entry; + *p = *q; + dos_partition_set_start(p, dos_partition_get_start(q)); + dos_partition_set_size(p, dos_partition_get_size(q)); + partition_set_changed(cxt, partnum - 1, 1); + } else if (cxt->label->nparts_max > 5) { /* 5 will be moved to 4 */ + /* the first logical in a longer chain */ + pe = &l->ptes[5]; + + if (pe->pt_entry) /* prevent SEGFAULT */ + dos_partition_set_start(pe->pt_entry, + get_abs_partition_start(pe) - + l->ext_offset); + pe->offset = l->ext_offset; + partition_set_changed(cxt, 5, 1); + } + + if (cxt->label->nparts_max > 5) { + cxt->label->nparts_max--; + if (l->ptes[partnum].private_sectorbuffer) { + DBG(LABEL, dbgprint("--> freeing pte %zu sector buffer %p", + partnum, l->ptes[partnum].sectorbuffer)); + free(l->ptes[partnum].sectorbuffer); + } + while (partnum < cxt->label->nparts_max) { + DBG(LABEL, dbgprint("--> moving pte %zu <-- %zd", partnum, partnum + 1)); + l->ptes[partnum] = l->ptes[partnum + 1]; + partnum++; + } + memset(&l->ptes[partnum], 0, sizeof(struct pte)); + } else + /* the only logical: clear only */ + clear_partition(l->ptes[partnum].pt_entry); + } + + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +static void read_extended(struct fdisk_context *cxt, int ext) +{ + size_t i; + struct pte *pex; + struct dos_partition *p, *q; + struct fdisk_dos_label *l = self_label(cxt); + + l->ext_index = ext; + pex = self_pte(cxt, ext); + pex->ex_entry = pex->pt_entry; + + p = pex->pt_entry; + if (!dos_partition_get_start(p)) { + fdisk_warnx(cxt, _("Bad offset in primary extended partition.")); + return; + } + + DBG(LABEL, dbgprint("DOS: REading extended %d", ext)); + + while (IS_EXTENDED (p->sys_ind)) { + struct pte *pe = self_pte(cxt, cxt->label->nparts_max); + + if (cxt->label->nparts_max >= MAXIMUM_PARTS) { + /* This is not a Linux restriction, but + this program uses arrays of size MAXIMUM_PARTS. + Do not try to `improve' this test. */ + struct pte *pre = self_pte(cxt, + cxt->label->nparts_max - 1); + fdisk_warnx(cxt, + _("Omitting partitions after #%zd. They will be deleted " + "if you save this partition table."), + cxt->label->nparts_max); + + clear_partition(pre->ex_entry); + partition_set_changed(cxt, + cxt->label->nparts_max - 1, 1); + return; + } + + read_pte(cxt, cxt->label->nparts_max, + l->ext_offset + dos_partition_get_start(p)); + + if (!l->ext_offset) + l->ext_offset = dos_partition_get_start(p); + + q = p = mbr_get_partition(pe->sectorbuffer, 0); + + for (i = 0; i < 4; i++, p++) if (dos_partition_get_size(p)) { + if (IS_EXTENDED (p->sys_ind)) { + if (pe->ex_entry) + fdisk_warnx(cxt, _( + "Extra link pointer in partition " + "table %zd."), + cxt->label->nparts_max + 1); + else + pe->ex_entry = p; + } else if (p->sys_ind) { + if (pe->pt_entry) + fdisk_warnx(cxt, _( + "Ignoring extra data in partition " + "table %zd."), + cxt->label->nparts_max + 1); + else + pe->pt_entry = p; + } + } + + /* very strange code here... */ + if (!pe->pt_entry) { + if (q != pe->ex_entry) + pe->pt_entry = q; + else + pe->pt_entry = q + 1; + } + if (!pe->ex_entry) { + if (q != pe->pt_entry) + pe->ex_entry = q; + else + pe->ex_entry = q + 1; + } + + p = pe->ex_entry; + cxt->label->nparts_cur = ++cxt->label->nparts_max; + } + + /* remove empty links */ + remove: + q = self_partition(cxt, 4); + for (i = 4; i < cxt->label->nparts_max; i++) { + p = self_partition(cxt, i); + + if (!dos_partition_get_size(p) && + (cxt->label->nparts_max > 5 || q->sys_ind)) { + fdisk_info(cxt, _("omitting empty partition (%zd)"), i+1); + dos_delete_partition(cxt, i); + goto remove; /* numbering changed */ + } + } +} + +static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id) +{ + unsigned int num; + + assert(cxt); + assert(id); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + num = mbr_get_id(cxt->firstsector); + if (asprintf(id, "0x%08x", num) > 0) + return 0; + + return -ENOMEM; +} + +static int dos_create_disklabel(struct fdisk_context *cxt) +{ + unsigned int id; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + DBG(LABEL, dbgprint("DOS: creating new disklabel")); + + /* random disk signature */ + random_get_bytes(&id, sizeof(id)); + + dos_init(cxt); + fdisk_zeroize_firstsector(cxt); + fdisk_label_set_changed(cxt->label, 1); + + /* Generate an MBR ID for this disk */ + mbr_set_id(cxt->firstsector, id); + + /* Put MBR signature */ + mbr_set_magic(cxt->firstsector); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + ("Created a new DOS disklabel with disk " + "identifier 0x%08x."), id); + return 0; +} + +static int dos_set_disklabel_id(struct fdisk_context *cxt) +{ + char *end = NULL, *str = NULL; + unsigned int id, old; + struct fdisk_dos_label *l; + int rc; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + DBG(LABEL, dbgprint("DOS: setting Id")); + + l = self_label(cxt); + old = mbr_get_id(cxt->firstsector); + rc = fdisk_ask_string(cxt, + _("Enter the new disk identifier"), &str); + if (rc) + return rc; + + errno = 0; + id = strtoul(str, &end, 0); + if (errno || str == end || (end && *end)) { + fdisk_warnx(cxt, _("Incorrect value.")); + return -EINVAL; + } + + + mbr_set_id(cxt->firstsector, id); + l->non_pt_changed = 1; + fdisk_label_set_changed(cxt->label, 1); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Disk identifier changed from 0x%08x to 0x%08x."), + old, id); + return 0; +} + +static void get_partition_table_geometry(struct fdisk_context *cxt, + unsigned int *ph, unsigned int *ps) +{ + unsigned char *bufp = cxt->firstsector; + struct dos_partition *p; + int i, h, s, hh, ss; + int first = 1; + int bad = 0; + + hh = ss = 0; + for (i = 0; i < 4; i++) { + p = mbr_get_partition(bufp, i); + if (p->sys_ind != 0) { + h = p->eh + 1; + s = (p->es & 077); + if (first) { + hh = h; + ss = s; + first = 0; + } else if (hh != h || ss != s) + bad = 1; + } + } + + if (!first && !bad) { + *ph = hh; + *ps = ss; + } + + DBG(LABEL, dbgprint("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps)); +} + +static int dos_reset_alignment(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + /* overwrite necessary stuff by DOS deprecated stuff */ + if (is_dos_compatible(cxt)) { + DBG(LABEL, dbgprint("DOS: reseting alignemnt for DOS-comaptiblem PT")); + if (cxt->geom.sectors) + cxt->first_lba = cxt->geom.sectors; /* usually 63 */ + + cxt->grain = cxt->sector_size; /* usually 512 */ + } + + return 0; +} + +/* TODO: move to include/pt-dos.h and share with libblkid */ +#define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1" +#define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1) + +static int dos_probe_label(struct fdisk_context *cxt) +{ + size_t i; + unsigned int h = 0, s = 0; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + /* ignore disks with AIX magic number */ + if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0) + return 0; + + if (!mbr_is_valid_magic(cxt->firstsector)) + return 0; + + dos_init(cxt); + + get_partition_table_geometry(cxt, &h, &s); + if (h && s) { + cxt->geom.heads = h; + cxt->geom.sectors = s; + } + + for (i = 0; i < 4; i++) { + struct pte *pe = self_pte(cxt, i); + + if (is_used_partition(pe->pt_entry)) + cxt->label->nparts_cur++; + + if (IS_EXTENDED (pe->pt_entry->sys_ind)) { + if (cxt->label->nparts_max != 4) + fdisk_warnx(cxt, _( + "Ignoring extra extended partition %zd"), + i + 1); + else + read_extended(cxt, i); + } + } + + for (i = 3; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + + if (!mbr_is_valid_magic(pe->sectorbuffer)) { + fdisk_info(cxt, _( + "Invalid flag 0x%02x%02x of partition table %zd will " + "be corrected by w(rite)"), + pe->sectorbuffer[510], + pe->sectorbuffer[511], + i + 1); + partition_set_changed(cxt, 1, 1); + } + } + + return 1; +} + +/* + * Avoid warning about DOS partitions when no DOS partition was changed. + * Here a heuristic "is probably dos partition". + * We might also do the opposite and warn in all cases except + * for "is probably nondos partition". + */ +static int is_dos_partition(int t) +{ + return (t == 1 || t == 4 || t == 6 || + t == 0x0b || t == 0x0c || t == 0x0e || + t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 || + t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 || + t == 0xc1 || t == 0xc4 || t == 0xc6); +} + +static void set_partition(struct fdisk_context *cxt, + int i, int doext, sector_t start, + sector_t stop, int sysid) +{ + struct pte *pe = self_pte(cxt, i); + struct dos_partition *p; + sector_t offset; + + DBG(LABEL, dbgprint("DOS: setting partition %d%s, start=%zu, stop=%zu, sysid=%02x", + i, doext ? " [extended]" : "", + (size_t) start, (size_t) stop, sysid)); + + if (doext) { + struct fdisk_dos_label *l = self_label(cxt); + p = pe->ex_entry; + offset = l->ext_offset; + } else { + p = pe->pt_entry; + offset = pe->offset; + } + p->boot_ind = 0; + p->sys_ind = sysid; + dos_partition_set_start(p, start - offset); + dos_partition_set_size(p, stop - start + 1); + + if (!doext) { + struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid); + fdisk_info_new_partition(cxt, i + 1, start, stop, t); + } + if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023)) + start = cxt->geom.heads*cxt->geom.sectors*1024 - 1; + set_hsc(p->bh, p->bs, p->bc, start); + if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023)) + stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1; + set_hsc(p->eh, p->es, p->ec, stop); + partition_set_changed(cxt, i, 1); +} + +static sector_t get_unused_start(struct fdisk_context *cxt, + int part_n, sector_t start, + sector_t first[], sector_t last[]) +{ + size_t i; + + for (i = 0; i < cxt->label->nparts_max; i++) { + sector_t lastplusoff; + struct pte *pe = self_pte(cxt, i); + + if (start == pe->offset) + start += cxt->first_lba; + lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba); + if (start >= first[i] && start <= lastplusoff) + start = lastplusoff + 1; + } + + return start; +} + +static void fill_bounds(struct fdisk_context *cxt, + sector_t *first, sector_t *last) +{ + size_t i; + struct pte *pe = self_pte(cxt, 0); + struct dos_partition *p; + + for (i = 0; i < cxt->label->nparts_max; pe++,i++) { + p = pe->pt_entry; + if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) { + first[i] = 0xffffffff; + last[i] = 0; + } else { + first[i] = get_abs_partition_start(pe); + last[i] = first[i] + dos_partition_get_size(p) - 1; + } + } +} + +static int add_partition(struct fdisk_context *cxt, int n, struct fdisk_parttype *t) +{ + int sys, read = 0, rc; + size_t i; + struct fdisk_dos_label *l = self_label(cxt); + struct dos_partition *p = self_partition(cxt, n); + struct dos_partition *q = self_partition(cxt, l->ext_index); + + sector_t start, stop = 0, limit, temp, + first[cxt->label->nparts_max], + last[cxt->label->nparts_max]; + + DBG(LABEL, dbgprint("DOS: adding partition %d", n)); + + sys = t ? t->type : MBR_LINUX_DATA_PARTITION; + + if (is_used_partition(p)) { + fdisk_warnx(cxt, _("Partition %d is already defined. " + "Delete it before re-adding it."), + n + 1); + return -EINVAL; + } + fill_bounds(cxt, first, last); + if (n < 4) { + start = cxt->first_lba; + if (fdisk_context_use_cylinders(cxt) || !cxt->total_sectors) + limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1; + else + limit = cxt->total_sectors - 1; + + if (limit > UINT_MAX) + limit = UINT_MAX; + + if (l->ext_offset) { + first[l->ext_index] = l->ext_offset; + last[l->ext_index] = dos_partition_get_start(q) + + dos_partition_get_size(q) - 1; + } + } else { + start = l->ext_offset + cxt->first_lba; + limit = dos_partition_get_start(q) + + dos_partition_get_size(q) - 1; + } + if (fdisk_context_use_cylinders(cxt)) + for (i = 0; i < cxt->label->nparts_max; i++) { + first[i] = (cround(cxt, first[i]) - 1) + * fdisk_context_get_units_per_sector(cxt); + } + + /* + * Ask for first sector + */ + do { + sector_t dflt, aligned; + + temp = start; + dflt = start = get_unused_start(cxt, n, start, first, last); + + /* the default sector should be aligned and unused */ + do { + aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit); + dflt = get_unused_start(cxt, n, aligned, first, last); + } while (dflt != aligned && dflt > aligned && dflt < limit); + + if (dflt >= limit) + dflt = start; + if (start > limit) + break; + if (start >= temp + fdisk_context_get_units_per_sector(cxt) + && read) { + fdisk_info(cxt, _("Sector %llu is already allocated."), + temp); + temp = start; + read = 0; + } + + if (!read && start == temp) { + sector_t j = start; + struct fdisk_ask *ask = fdisk_new_ask(); + + if (fdisk_context_use_cylinders(cxt)) + fdisk_ask_set_query(ask, _("First cylinder")); + else + fdisk_ask_set_query(ask, _("First sector")); + + fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + fdisk_ask_number_set_low(ask, cround(cxt, j)); + fdisk_ask_number_set_default(ask, cround(cxt, dflt)); + fdisk_ask_number_set_high(ask, cround(cxt, limit)); + + rc = fdisk_do_ask(cxt, ask); + if (!rc) + start = fdisk_ask_number_get_result(ask); + fdisk_free_ask(ask); + if (rc) + return rc; + + if (fdisk_context_use_cylinders(cxt)) { + start = (start - 1) + * fdisk_context_get_units_per_sector(cxt); + if (start < j) + start = j; + } + read = 1; + } + } while (start != temp || !read); + + if (n > 4) { /* NOT for fifth partition */ + struct pte *pe = self_pte(cxt, n); + + pe->offset = start - cxt->first_lba; + if (pe->offset == l->ext_offset) { /* must be corrected */ + pe->offset++; + if (cxt->first_lba == 1) + start++; + } + } + + for (i = 0; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + + if (start < pe->offset && limit >= pe->offset) + limit = pe->offset - 1; + if (start < first[i] && limit >= first[i]) + limit = first[i] - 1; + } + if (start > limit) { + fdisk_info(cxt, _("No free sectors available.")); + if (n > 4) + cxt->label->nparts_max--; + return -ENOSPC; + } + if (cround(cxt, start) == cround(cxt, limit)) { + stop = limit; + } else { + /* + * Ask for last sector + */ + struct fdisk_ask *ask = fdisk_new_ask(); + + fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET); + + if (fdisk_context_use_cylinders(cxt)) { + fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}")); + fdisk_ask_number_set_unit(ask, + cxt->sector_size * + fdisk_context_get_units_per_sector(cxt)); + } else { + fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}")); + fdisk_ask_number_set_unit(ask,cxt->sector_size); + } + + fdisk_ask_number_set_low(ask, cround(cxt, start)); + fdisk_ask_number_set_default(ask, cround(cxt, limit)); + fdisk_ask_number_set_high(ask, cround(cxt, limit)); + fdisk_ask_number_set_base(ask, cround(cxt, start)); /* base for relative input */ + + rc = fdisk_do_ask(cxt, ask); + if (rc) { + fdisk_free_ask(ask); + return rc; + } + + stop = fdisk_ask_number_get_result(ask); + + if (fdisk_context_use_cylinders(cxt)) { + stop = stop * fdisk_context_get_units_per_sector(cxt) - 1; + if (stop >limit) + stop = limit; + } + if (fdisk_ask_number_is_relative(ask) + && alignment_required(cxt)) { + /* the last sector has not been exactly requested (but + * defined by +size{K,M,G} convention), so be smart and + * align the end of the partition. The next partition + * will start at phy.block boundary. + */ + stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1; + if (stop > limit) + stop = limit; + } + fdisk_free_ask(ask); + } + + set_partition(cxt, n, 0, start, stop, sys); + if (n > 4) { + struct pte *pe = self_pte(cxt, n); + set_partition(cxt, n - 1, 1, pe->offset, stop, + MBR_DOS_EXTENDED_PARTITION); + } + + if (IS_EXTENDED(sys)) { + struct pte *pe4 = self_pte(cxt, 4); + struct pte *pen = self_pte(cxt, n); + + l->ext_index = n; + pen->ex_entry = p; + pe4->offset = l->ext_offset = start; + pe4->sectorbuffer = calloc(1, cxt->sector_size); + if (!pe4->sectorbuffer) + return -ENOMEM; + DBG(LABEL, dbgprint("DOS: add partition, sector buffer %p", pe4->sectorbuffer)); + pe4->private_sectorbuffer = 1; + pe4->pt_entry = mbr_get_partition(pe4->sectorbuffer, 0); + pe4->ex_entry = pe4->pt_entry + 1; + + partition_set_changed(cxt, 4, 1); + cxt->label->nparts_max = 5; + } + + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +static int add_logical(struct fdisk_context *cxt) +{ + struct dos_partition *p4 = self_partition(cxt, 4); + + assert(cxt); + assert(cxt->label); + + if (cxt->label->nparts_max > 5 || !is_cleared_partition(p4)) { + struct pte *pe = self_pte(cxt, cxt->label->nparts_max); + + pe->sectorbuffer = calloc(1, cxt->sector_size); + if (!pe->sectorbuffer) + return -ENOMEM; + DBG(LABEL, dbgprint("DOS: add logical, sector buffer %p", pe->sectorbuffer)); + pe->private_sectorbuffer = 1; + pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0); + pe->ex_entry = pe->pt_entry + 1; + pe->offset = 0; + + partition_set_changed(cxt, cxt->label->nparts_max, 1); + cxt->label->nparts_max++; + } + fdisk_info(cxt, _("Adding logical partition %zd"), + cxt->label->nparts_max); + return add_partition(cxt, cxt->label->nparts_max - 1, NULL); +} + +static void check(struct fdisk_context *cxt, size_t n, + unsigned int h, unsigned int s, unsigned int c, + unsigned int start) +{ + unsigned int total, real_s, real_c; + + real_s = sector(s) - 1; + real_c = cylinder(s, c); + total = (real_c * cxt->geom.sectors + real_s) * cxt->geom.heads + h; + + if (!total) + fdisk_warnx(cxt, _("Partition %zd: contains sector 0"), n); + if (h >= cxt->geom.heads) + fdisk_warnx(cxt, _("Partition %zd: head %d greater than " + "maximum %d"), n, h + 1, cxt->geom.heads); + if (real_s >= cxt->geom.sectors) + fdisk_warnx(cxt, _("Partition %zd: sector %d greater than " + "maximum %llu"), n, s, cxt->geom.sectors); + if (real_c >= cxt->geom.cylinders) + fdisk_warnx(cxt, _("Partition %zd: cylinder %d greater than " + "maximum %llu"), + n, real_c + 1, + cxt->geom.cylinders); + + if (cxt->geom.cylinders <= 1024 && start != total) + fdisk_warnx(cxt, _("Partition %zd: previous sectors %d " + "disagrees with total %d"), n, start, total); +} + +/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993, + * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, + * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. + * Lubkin Oct. 1991). */ + +static void +long2chs(struct fdisk_context *cxt, unsigned long ls, + unsigned int *c, unsigned int *h, unsigned int *s) { + int spc = cxt->geom.heads * cxt->geom.sectors; + + *c = ls / spc; + ls = ls % spc; + *h = ls / cxt->geom.sectors; + *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */ +} + +static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p, + size_t partition) +{ + unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */ + unsigned int pec, peh, pes; /* physical ending c, h, s */ + unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */ + unsigned int lec, leh, les; /* logical ending c, h, s */ + + if (!is_dos_compatible(cxt)) + return; + + if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4)) + return; /* do not check extended partitions */ + + /* physical beginning c, h, s */ + pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300); + pbh = p->bh; + pbs = p->bs & 0x3f; + + /* physical ending c, h, s */ + pec = (p->ec & 0xff) | ((p->es << 2) & 0x300); + peh = p->eh; + pes = p->es & 0x3f; + + /* compute logical beginning (c, h, s) */ + long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs); + + /* compute logical ending (c, h, s) */ + long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les); + + /* Same physical / logical beginning? */ + if (cxt->geom.cylinders <= 1024 + && (pbc != lbc || pbh != lbh || pbs != lbs)) { + fdisk_warnx(cxt, _("Partition %zd: different physical/logical " + "beginnings (non-Linux?): " + "phys=(%d, %d, %d), logical=(%d, %d, %d)"), + partition + 1, + pbc, pbh, pbs, + lbc, lbh, lbs); + } + + /* Same physical / logical ending? */ + if (cxt->geom.cylinders <= 1024 + && (pec != lec || peh != leh || pes != les)) { + fdisk_warnx(cxt, _("Partition %zd: different physical/logical " + "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"), + partition + 1, + pec, peh, pes, + lec, leh, les); + } + + /* Ending on cylinder boundary? */ + if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) { + fdisk_warnx(cxt, _("Partition %zd: does not end on " + "cylinder boundary."), + partition + 1); + } +} + +static int dos_verify_disklabel(struct fdisk_context *cxt) +{ + size_t i, j; + sector_t total = 1, n_sectors = cxt->total_sectors; + unsigned long long first[cxt->label->nparts_max], + last[cxt->label->nparts_max]; + struct dos_partition *p; + struct fdisk_dos_label *l = self_label(cxt); + + assert(fdisk_is_disklabel(cxt, DOS)); + + fill_bounds(cxt, first, last); + for (i = 0; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + + p = self_partition(cxt, i); + if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) { + check_consistency(cxt, p, i); + fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i); + if (get_abs_partition_start(pe) < first[i]) + fdisk_warnx(cxt, _( + "Partition %zd: bad start-of-data."), + i + 1); + + check(cxt, i + 1, p->eh, p->es, p->ec, last[i]); + total += last[i] + 1 - first[i]; + + for (j = 0; j < i; j++) { + if ((first[i] >= first[j] && first[i] <= last[j]) + || ((last[i] <= last[j] && last[i] >= first[j]))) { + + fdisk_warnx(cxt, _("Partition %zd: " + "overlaps partition %zd."), + j + 1, i + 1); + + total += first[i] >= first[j] ? + first[i] : first[j]; + total -= last[i] <= last[j] ? + last[i] : last[j]; + } + } + } + } + + if (l->ext_offset) { + sector_t e_last; + p = self_partition(cxt, l->ext_index); + e_last = dos_partition_get_start(p) + + dos_partition_get_size(p) - 1; + + for (i = 4; i < cxt->label->nparts_max; i++) { + total++; + p = self_partition(cxt, i); + + if (!p->sys_ind) { + if (i != 4 || i + 1 < cxt->label->nparts_max) + fdisk_warnx(cxt, + _("Partition %zd: empty."), + i + 1); + } else if (first[i] < l->ext_offset + || last[i] > e_last) { + + fdisk_warnx(cxt, _("Logical partition %zd: " + "not entirely in partition %zd."), + i + 1, l->ext_index + 1); + } + } + } + + if (total > n_sectors) + fdisk_warnx(cxt, _("Total allocated sectors %llu greater " + "than the maximum %llu."), total, n_sectors); + else if (total < n_sectors) + fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte " + "sectors."), n_sectors - total, cxt->sector_size); + + return 0; +} + +/* + * Ask the user for new partition type information (logical, extended). + * This function calls the actual partition adding logic - add_partition. + * + * API callback. + */ +static int dos_add_partition( + struct fdisk_context *cxt, + size_t partnum __attribute__ ((__unused__)), + struct fdisk_parttype *t) +{ + size_t i, free_primary = 0; + int rc = 0; + struct fdisk_dos_label *l = self_label(cxt); + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + for (i = 0; i < 4; i++) { + struct dos_partition *p = self_partition(cxt, i); + free_primary += !is_used_partition(p); + } + + if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) { + fdisk_info(cxt, _("The maximum number of partitions has " + "been created.")); + return -EINVAL; + } + rc = 1; + + if (!free_primary) { + if (l->ext_offset) { + fdisk_info(cxt, _("All primary partitions are in use.")); + rc = add_logical(cxt); + } else + fdisk_info(cxt, _("If you want to create more than " + "four partitions, you must replace a " + "primary partition with an extended " + "partition first.")); + + } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) { + int j; + + fdisk_info(cxt, _("All logical partitions are in use. " + "Adding a primary partition.")); + j = get_partition_unused_primary(cxt); + if (j >= 0) + rc = add_partition(cxt, j, t); + } else { + char *buf; + char c, prompt[BUFSIZ]; + int dflt; + + dflt = (free_primary == 1 && !l->ext_offset) ? 'e' : 'p'; + + snprintf(prompt, sizeof(prompt), + _("Partition type:\n" + " p primary (%zd primary, %d extended, %zd free)\n" + "%s\n" + "Select (default %c)"), + 4 - (l->ext_offset ? 1 : 0) - free_primary, + l->ext_offset ? 1 : 0, free_primary, + l->ext_offset ? _(" l logical (numbered from 5)") : _(" e extended"), + dflt); + + rc = fdisk_ask_string(cxt, prompt, &buf); + if (rc) + return rc; + if (!buf[0]) { + c = dflt; + fdisk_info(cxt, _("Using default response %c."), c); + } else + c = tolower(buf[0]); + free(buf); + + if (c == 'p') { + int j = get_partition_unused_primary(cxt); + if (j >= 0) + rc = add_partition(cxt, j, t); + goto done; + } else if (c == 'l' && l->ext_offset) { + rc = add_logical(cxt); + goto done; + } else if (c == 'e' && !l->ext_offset) { + int j = get_partition_unused_primary(cxt); + if (j >= 0) { + t = fdisk_get_parttype_from_code(cxt, + MBR_DOS_EXTENDED_PARTITION); + rc = add_partition(cxt, j, t); + } + goto done; + } else + fdisk_warnx(cxt, _("Invalid partition type `%c'."), c); + } +done: + if (rc == 0) + cxt->label->nparts_cur++; + return rc; +} + +static int write_sector(struct fdisk_context *cxt, sector_t secno, + unsigned char *buf) +{ + int rc; + + rc = seek_sector(cxt, secno); + if (rc != 0) { + fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"), + (uintmax_t) secno); + return rc; + } + if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size) + return -errno; + return 0; +} + +static int dos_write_disklabel(struct fdisk_context *cxt) +{ + struct fdisk_dos_label *l = self_label(cxt); + size_t i; + int rc = 0, mbr_changed = 0; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + mbr_changed = l->non_pt_changed; + + /* MBR (primary partitions) */ + if (!mbr_changed) { + for (i = 0; i < 4; i++) { + struct pte *pe = self_pte(cxt, i); + if (pe->changed) + mbr_changed = 1; + } + } + if (mbr_changed) { + mbr_set_magic(cxt->firstsector); + rc = write_sector(cxt, 0, cxt->firstsector); + if (rc) + goto done; + } + + /* EBR (logical partitions) */ + for (i = 4; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + + if (pe->changed) { + mbr_set_magic(pe->sectorbuffer); + rc = write_sector(cxt, pe->offset, pe->sectorbuffer); + if (rc) + goto done; + } + } + +done: + return rc; +} + +static int dos_locate_disklabel(struct fdisk_context *cxt, int n, + const char **name, off_t *offset, size_t *size) +{ + assert(cxt); + + *name = NULL; + *offset = 0; + *size = 0; + + switch (n) { + case 0: + *name = "MBR"; + *offset = 0; + *size = 512; + break; + default: + /* extended partitions */ + if (n - 1 + 4 < cxt->label->nparts_max) { + struct pte *pe = self_pte(cxt, n - 1 + 4); + + assert(pe->private_sectorbuffer); + + *name = "EBR"; + *offset = pe->offset * cxt->sector_size; + *size = 512; + } else + return 1; + break; + } + + return 0; +} + +static struct fdisk_parttype *dos_get_parttype( + struct fdisk_context *cxt, + size_t partnum) +{ + struct fdisk_parttype *t; + struct dos_partition *p; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + if (partnum >= cxt->label->nparts_max) + return NULL; + + p = self_partition(cxt, partnum); + t = fdisk_get_parttype_from_code(cxt, p->sys_ind); + if (!t) + t = fdisk_new_unknown_parttype(p->sys_ind, NULL); + return t; +} + +static int dos_set_parttype( + struct fdisk_context *cxt, + size_t partnum, + struct fdisk_parttype *t) +{ + struct dos_partition *p; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + if (partnum >= cxt->label->nparts_max || !t || t->type > UINT8_MAX) + return -EINVAL; + + p = self_partition(cxt, partnum); + if (t->type == p->sys_ind) + return 0; + + if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->type)) { + fdisk_warnx(cxt, _("You cannot change a partition into an " + "extended one or vice versa. Delete it first.")); + return -EINVAL; + } + + if (is_dos_partition(t->type) || is_dos_partition(p->sys_ind)) + fdisk_info(cxt, _("If you have created or modified any DOS 6.x " + "partitions, please see the fdisk documentation for additional " + "information.")); + + if (!t->type) + fdisk_warnx(cxt, _("Type 0 means free space to many systems. " + "Having partitions of type 0 is probably unwise.")); + p->sys_ind = t->type; + + partition_set_changed(cxt, partnum, 1); + return 0; +} + +/* + * Check whether partition entries are ordered by their starting positions. + * Return 0 if OK. Return i if partition i should have been earlier. + * Two separate checks: primary and logical partitions. + */ +static int wrong_p_order(struct fdisk_context *cxt, size_t *prev) +{ + size_t last_p_start_pos = 0, p_start_pos; + size_t i, last_i = 0; + + for (i = 0 ; i < cxt->label->nparts_max; i++) { + + struct pte *pe = self_pte(cxt, i); + struct dos_partition *p = pe->pt_entry; + + if (i == 4) { + last_i = 4; + last_p_start_pos = 0; + } + if (is_used_partition(p)) { + p_start_pos = get_abs_partition_start(pe); + + if (last_p_start_pos > p_start_pos) { + if (prev) + *prev = last_i; + return i; + } + + last_p_start_pos = p_start_pos; + last_i = i; + } + } + return 0; +} + +static int is_garbage_table(struct fdisk_context *cxt) +{ + size_t i; + + for (i = 0; i < 4; i++) { + struct dos_partition *p = self_partition(cxt, i); + + if (p->boot_ind != 0 && p->boot_ind != 0x80) + return 1; + } + return 0; +} + +/* + * List all PT fields. + * + * This is useful for PT debugging (or for 70's Hippies + * who are on permanent LSD trip). + */ +static int dos_fulllist_disklabel(struct fdisk_context *cxt, int ext) +{ + int rc; + size_t i; + struct tt *tb = NULL; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + tt_define_column(tb, _("Nr"), 2, TT_FL_RIGHT); + tt_define_column(tb, _("AF"), 2, TT_FL_RIGHT); + + tt_define_column(tb, _("Hd"), 4, TT_FL_RIGHT); + tt_define_column(tb, _("Sec"), 4, TT_FL_RIGHT); + tt_define_column(tb, _("Cyl"), 5, TT_FL_RIGHT); + + tt_define_column(tb, _("Hd"), 4, TT_FL_RIGHT); + tt_define_column(tb, _("Sec"), 4, TT_FL_RIGHT); + tt_define_column(tb, _("Cyl"), 5, TT_FL_RIGHT); + + tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Size"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT); + + for (i = 0 ; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + struct dos_partition *p; + struct tt_line *ln; + char *str; + + p = ext ? pe->ex_entry : pe->pt_entry; + if (!p) + continue; + ln = tt_add_line(tb, NULL); + if (!ln) + continue; + + if (asprintf(&str, "%zd", i + 1) > 0) + tt_line_set_data(ln, 0, str); /* Nr */ + if (asprintf(&str, "%02x", p->boot_ind) > 0) + tt_line_set_data(ln, 1, str); /* AF */ + + if (asprintf(&str, "%d", p->bh) > 0) + tt_line_set_data(ln, 2, str); /* Hd */ + if (asprintf(&str, "%d", sector(p->bs)) > 0) + tt_line_set_data(ln, 3, str); /* Sec */ + if (asprintf(&str, "%d", cylinder(p->bs, p->bc)) > 0) + tt_line_set_data(ln, 4, str); /* Cyl */ + + if (asprintf(&str, "%d", p->eh) > 0) + tt_line_set_data(ln, 5, str); /* Hd */ + if (asprintf(&str, "%d", sector(p->es)) > 0) + tt_line_set_data(ln, 6, str); /* Sec */ + if (asprintf(&str, "%d", cylinder(p->es, p->ec)) > 0) + tt_line_set_data(ln, 7, str); /* Cyl */ + + if (asprintf(&str, "%lu", + (unsigned long) dos_partition_get_start(p)) > 0) + tt_line_set_data(ln, 8, str); /* Start */ + if (asprintf(&str, "%lu", + (unsigned long) dos_partition_get_size(p)) > 0) + tt_line_set_data(ln, 9, str); /* End */ + + if (asprintf(&str, "%02x", p->sys_ind) > 0) + tt_line_set_data(ln, 10, str); /* Id */ + + check_consistency(cxt, p, i); + fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i); + } + + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + + return rc; +} + +int fdisk_dos_list_extended(struct fdisk_context *cxt) +{ + return dos_fulllist_disklabel(cxt, 1); +} + +static int dos_list_disklabel(struct fdisk_context *cxt) +{ + int rc = 0, trunc = TT_FL_TRUNC; + size_t i; + struct tt *tb = NULL; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + if (is_garbage_table(cxt)) { + fdisk_warnx(cxt, _( + "This doesn't look like a partition table. " + "Probably you selected the wrong device.")); + } + + if (fdisk_context_display_details(cxt)) + return dos_fulllist_disklabel(cxt, 0); + + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + /* don't trunc anything in expert mode */ + if (fdisk_context_display_details(cxt)) + trunc = 0; + + tt_define_column(tb, _("Device"), 0.1, 0); + tt_define_column(tb, _("Boot"), 1, 0); + tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("End"), 9, TT_FL_RIGHT); + /* TRANSLATORS: keep one blank space behind 'Blocks' */ + tt_define_column(tb, _("Blocks "), 5, TT_FL_RIGHT); + tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT); + tt_define_column(tb, _("System"), 0.1, trunc); + + for (i = 0; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + struct dos_partition *p = pe->pt_entry; + unsigned int psects, pblocks, podd = 0; + struct fdisk_parttype *type; + struct tt_line *ln; + char *str; + + if (!is_used_partition(p)) + continue; + ln = tt_add_line(tb, NULL); + if (!ln) + continue; + + pblocks = psects = dos_partition_get_size(p); + type = fdisk_get_parttype_from_code(cxt, p->sys_ind); + + if (cxt->sector_size < 1024) { + pblocks /= (1024 / cxt->sector_size); + podd = psects % (1024 / cxt->sector_size); + } + if (cxt->sector_size > 1024) + pblocks *= (cxt->sector_size / 1024); + + str = fdisk_partname(cxt->dev_path, i + 1); + if (str) + tt_line_set_data(ln, 0, str); /* device */ + + str = strdup(p->boot_ind ? + p->boot_ind == ACTIVE_FLAG ? "*" : "?" : " "); + if (str) + tt_line_set_data(ln, 1, str); /* boot flag */ + + if (asprintf(&str, "%lu", (unsigned long) cround(cxt, + get_abs_partition_start(pe))) > 0) + tt_line_set_data(ln, 2, str); /* start */ + + if (asprintf(&str, "%lu", (unsigned long) cround(cxt, + get_abs_partition_start(pe) + + psects - (psects ? 1 : 0))) > 0) + tt_line_set_data(ln, 3, str); /* end */ + + if (asprintf(&str, "%lu%c", (unsigned long) pblocks, + podd ? '+' : ' ') > 0) + tt_line_set_data(ln, 4, str); /* blocks<flag> */ + + if (asprintf(&str, "%x", p->sys_ind) > 0) + tt_line_set_data(ln, 5, str); /* id */ + + str = strdup(type ? type->name : _("Unknown")); + if (str) + tt_line_set_data(ln, 6, str); + + check_consistency(cxt, p, i); + fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i); + fdisk_free_parttype(type); + } + + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + + /* Is partition table in disk order? It need not be, but... */ + /* partition table entries are not checked for correct order if this + is a sgi, sun labeled disk... */ + if (wrong_p_order(cxt, NULL)) + fdisk_info(cxt, _("Partition table entries are not in " + "disk order.")); + + return rc; +} + +static void print_chain_of_logicals(struct fdisk_context *cxt) +{ + size_t i; + struct fdisk_dos_label *l = self_label(cxt); + + fputc('\n', stdout); + + for (i = 4; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + + printf("#%02zu EBR [%10ju], " + "data[start=%10ju (%10ju), size=%10ju], " + "link[start=%10ju (%10ju), size=%10ju]\n", + i, (uintmax_t) pe->offset, + /* data */ + (uintmax_t) dos_partition_get_start(pe->pt_entry), + (uintmax_t) get_abs_partition_start(pe), + (uintmax_t) dos_partition_get_size(pe->pt_entry), + /* link */ + (uintmax_t) dos_partition_get_start(pe->ex_entry), + (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry), + (uintmax_t) dos_partition_get_size(pe->ex_entry)); + } +} + +static int cmp_ebr_offsets(const void *a, const void *b) +{ + struct pte *ae = (struct pte *) a, + *be = (struct pte *) b; + + if (ae->offset == 0 && ae->offset == 0) + return 0; + if (ae->offset == 0) + return 1; + if (be->offset == 0) + return -1; + + return ae->offset - be->offset; +} +/* + * Fix the chain of logicals. + * + * The function does not modify data partitions within EBR tables + * (pte->pt_entry). It sorts the chain by EBR offsets and then update links + * (pte->ex_entry) between EBR tables. + * + */ +static void fix_chain_of_logicals(struct fdisk_context *cxt) +{ + struct fdisk_dos_label *l = self_label(cxt); + size_t i; + + DBG(LABEL, print_chain_of_logicals(cxt)); + + /* Sort chain by EBR offsets */ + qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte), + cmp_ebr_offsets); + +again: + /* Sort data partitions by start */ + for (i = 4; i < cxt->label->nparts_max - 1; i++) { + struct pte *cur = self_pte(cxt, i), + *nxt = self_pte(cxt, i + 1); + + if (get_abs_partition_start(cur) > + get_abs_partition_start(nxt)) { + + struct dos_partition tmp = *cur->pt_entry; + sector_t cur_start = get_abs_partition_start(cur), + nxt_start = get_abs_partition_start(nxt); + + /* swap data partitions */ + *cur->pt_entry = *nxt->pt_entry; + *nxt->pt_entry = tmp; + + /* Recount starts according to EBR offsets, the absolute + * address tas to be still the same! */ + dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset); + dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset); + + partition_set_changed(cxt, i, 1); + partition_set_changed(cxt, i + 1, 1); + goto again; + } + } + + /* Update EBR links */ + for (i = 4; i < cxt->label->nparts_max - 1; i++) { + struct pte *cur = self_pte(cxt, i), + *nxt = self_pte(cxt, i + 1); + + sector_t noff = nxt->offset - l->ext_offset, + ooff = dos_partition_get_start(cur->ex_entry); + + if (noff == ooff) + continue; + + DBG(LABEL, dbgprint("DOS: fix EBR [%10ju] link %ju -> %ju", + (uintmax_t) cur->offset, + (uintmax_t) ooff, (uintmax_t) noff)); + + set_partition(cxt, i, 1, nxt->offset, + get_abs_partition_end(nxt), MBR_DOS_EXTENDED_PARTITION); + + if (i + 1 == cxt->label->nparts_max - 1) { + clear_partition(nxt->ex_entry); + partition_set_changed(cxt, i + 1, 1); + } + + } + DBG(LABEL, print_chain_of_logicals(cxt)); +} + +int fdisk_dos_fix_order(struct fdisk_context *cxt) +{ + struct pte *pei, *pek; + size_t i,k; + + if (!wrong_p_order(cxt, NULL)) { + fdisk_info(cxt, _("Nothing to do. Ordering is correct already.")); + return 0; + } + + while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) { + /* partition i should have come earlier, move it */ + /* We have to move data in the MBR */ + struct dos_partition *pi, *pk, *pe, pbuf; + pei = self_pte(cxt, i); + pek = self_pte(cxt, k); + + pe = pei->ex_entry; + pei->ex_entry = pek->ex_entry; + pek->ex_entry = pe; + + pi = pei->pt_entry; + pk = pek->pt_entry; + + memmove(&pbuf, pi, sizeof(struct dos_partition)); + memmove(pi, pk, sizeof(struct dos_partition)); + memmove(pk, &pbuf, sizeof(struct dos_partition)); + + partition_set_changed(cxt, i, 1); + partition_set_changed(cxt, k, 1); + } + + if (i) + fix_chain_of_logicals(cxt); + + fdisk_info(cxt, _("Done.")); + return 0; +} + +int fdisk_dos_move_begin(struct fdisk_context *cxt, int i) +{ + struct pte *pe; + struct dos_partition *p; + unsigned int new, free_start, curr_start, last; + uintmax_t res = 0; + size_t x; + int rc; + + assert(cxt); + assert(fdisk_is_disklabel(cxt, DOS)); + + pe = self_pte(cxt, i); + p = pe->pt_entry; + + if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) { + fdisk_warnx(cxt, _("Partition %d: no data area."), i + 1); + return 0; + } + + /* the default start is at the second sector of the disk or at the + * second sector of the extended partition + */ + free_start = pe->offset ? pe->offset + 1 : 1; + + curr_start = get_abs_partition_start(pe); + + /* look for a free space before the current start of the partition */ + for (x = 0; x < cxt->label->nparts_max; x++) { + unsigned int end; + struct pte *prev_pe = self_pte(cxt, x); + struct dos_partition *prev_p = prev_pe->pt_entry; + + if (!prev_p) + continue; + end = get_abs_partition_start(prev_pe) + + dos_partition_get_size(prev_p); + + if (is_used_partition(prev_p) && + end > free_start && end <= curr_start) + free_start = end; + } + + last = get_abs_partition_start(pe) + dos_partition_get_size(p) - 1; + + rc = fdisk_ask_number(cxt, free_start, curr_start, last, + _("New beginning of data"), &res); + if (rc) + return rc; + + new = res - pe->offset; + + if (new != dos_partition_get_size(p)) { + unsigned int sects = dos_partition_get_size(p) + + dos_partition_get_start(p) - new; + + dos_partition_set_size(p, sects); + dos_partition_set_start(p, new); + + partition_set_changed(cxt, i, 1); + } + + return rc; +} + +static int dos_get_partition_status( + struct fdisk_context *cxt, + size_t i, + int *status) +{ + struct dos_partition *p; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + if (!status || i >= cxt->label->nparts_max) + return -EINVAL; + + p = self_partition(cxt, i); + + if (is_used_partition(p)) + *status = FDISK_PARTSTAT_USED; + else + *status = FDISK_PARTSTAT_NONE; + + return 0; +} + +static int dos_toggle_partition_flag( + struct fdisk_context *cxt, + size_t i, + unsigned long flag) +{ + struct dos_partition *p; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, DOS)); + + if (i >= cxt->label->nparts_max) + return -EINVAL; + + p = self_partition(cxt, i); + + switch (flag) { + case DOS_FLAG_ACTIVE: + if (IS_EXTENDED(p->sys_ind) && !p->boot_ind) + fdisk_warnx(cxt, _("Partition %d: is an extended " + "partition."), (int) i + 1); + + p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG); + partition_set_changed(cxt, i, 1); + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + p->boot_ind ? + _("The bootable flag on partition %zu is enabled now.") : + _("The bootable flag on partition %zu is disabled now."), + i + 1); + break; + default: + return 1; + } + + return 0; +} + +static const struct fdisk_label_operations dos_operations = +{ + .probe = dos_probe_label, + .write = dos_write_disklabel, + .verify = dos_verify_disklabel, + .create = dos_create_disklabel, + .locate = dos_locate_disklabel, + .list = dos_list_disklabel, + .get_id = dos_get_disklabel_id, + .set_id = dos_set_disklabel_id, + + .part_add = dos_add_partition, + .part_delete = dos_delete_partition, + .part_get_type = dos_get_parttype, + .part_set_type = dos_set_parttype, + + .part_toggle_flag = dos_toggle_partition_flag, + .part_get_status = dos_get_partition_status, + + .reset_alignment = dos_reset_alignment, + + .deinit = dos_deinit, +}; + +/* + * allocates DOS in-memory stuff + */ +struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) +{ + struct fdisk_label *lb; + struct fdisk_dos_label *dos; + + assert(cxt); + + dos = calloc(1, sizeof(*dos)); + if (!dos) + return NULL; + + /* initialize generic part of the driver */ + lb = (struct fdisk_label *) dos; + lb->name = "dos"; + lb->id = FDISK_DISKLABEL_DOS; + lb->op = &dos_operations; + lb->parttypes = dos_parttypes; + lb->nparttypes = ARRAY_SIZE(dos_parttypes); + + lb->flags |= FDISK_LABEL_FL_ADDPART_NOPARTNO; + + return lb; +} + +/* + * Public label specific functions + */ + +int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable) +{ + struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb; + + if (!lb) + return -EINVAL; + + dos->compatible = enable; + if (enable) + lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY; + return 0; +} + +int fdisk_dos_is_compatible(struct fdisk_label *lb) +{ + return ((struct fdisk_dos_label *) lb)->compatible; +} diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index ce42860d9..a70848ca0 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -21,6 +21,7 @@ #include "libfdisk.h" #include "nls.h" /* temporary before dialog API will be implamented */ +#include "tt.h" /* features */ #define CONFIG_LIBFDISK_ASSERT @@ -48,6 +49,7 @@ #define FDISK_DEBUG_GEOMETRY (1 << 4) #define FDISK_DEBUG_LABEL (1 << 5) #define FDISK_DEBUG_ASK (1 << 6) +#define FDISK_DEBUG_FRONTEND (1 << 7) #define FDISK_DEBUG_ALL 0xFFFF # define ON_DBG(m, x) do { \ @@ -144,6 +146,16 @@ struct fdisk_label_operations { int (*verify)(struct fdisk_context *cxt); /* create new disk label */ int (*create)(struct fdisk_context *cxt); + /* list partition table */ + int (*list)(struct fdisk_context *cxt); + /* returns offset and size of the 'n' part of the PT */ + int (*locate)(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size); + + /* get disk label ID */ + int (*get_id)(struct fdisk_context *cxt, char **id); + /* set disk label ID */ + int (*set_id)(struct fdisk_context *cxt); + /* new partition */ int (*part_add)(struct fdisk_context *cxt, size_t partnum, @@ -190,21 +202,23 @@ struct fdisk_label { int flags; /* FDISK_LABEL_FL_* flags */ - unsigned int changed:1; /* label has been modified */ + unsigned int changed:1, /* label has been modified */ + disabled:1; /* this driver is disabled at all */ const struct fdisk_label_operations *op; }; /* label driver flags */ enum { - FDISK_LABEL_FL_ADDPART_NOPARTNO = (1 << 1) + FDISK_LABEL_FL_ADDPART_NOPARTNO = (1 << 1), + FDISK_LABEL_FL_REQUIRE_GEOMETRY = (1 << 2), + FDISK_LABEL_FL_INCHARS_PARTNO = (1 << 3) }; /* label allocators */ extern struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt); extern struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt); extern struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt); -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); @@ -215,6 +229,7 @@ extern struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt); struct fdisk_ask { int type; /* FDISK_ASKTYPE_* */ char *query; + unsigned int flags; union { /* FDISK_ASKTYPE_{NUMBER,OFFSET} */ @@ -225,8 +240,9 @@ struct fdisk_ask { uint64_t result; uint64_t base; /* for relative results */ uint64_t unit; /* unit for offsets */ - char *range; /* by library generated list */ - unsigned int relative:1; + const char *range; /* by library generated list */ + unsigned int relative :1, + inchars :1; } num; /* FDISK_ASKTYPE_{WARN,WARNX,..} */ struct ask_print { @@ -237,6 +253,12 @@ struct fdisk_ask { struct ask_yesno { int result; /* TRUE or FALSE */ } yesno; + /* FDISK_ASKTYPE_STRING */ + struct ask_string { + char *result; /* allocated */ + } str; + /* FDISK_ASKTYPE_TABLE, see include/tt.h */ + struct tt *table; } data; }; @@ -253,16 +275,24 @@ struct fdisk_context { unsigned long sector_size; /* logical size */ unsigned long alignment_offset; - int display_in_cyl_units; /* for obscure labels */ + unsigned int readonly : 1, /* don't write to the device */ + display_in_cyl_units : 1, /* for obscure labels */ + display_details : 1, /* expert display mode */ + listonly : 1; /* list partition, nothing else */ /* alignment */ unsigned long grain; /* alignment unit */ sector_t first_lba; /* recommended begin of the first partition */ /* geometry */ - sector_t total_sectors; /* in logical sectors */ + sector_t total_sectors; /* in logical sectors */ struct fdisk_geometry geom; + /* user setting to overwrite device default */ + struct fdisk_geometry user_geom; + unsigned long user_pyh_sector; + unsigned long user_log_sector; + struct fdisk_label *label; /* current label, pointer to labels[] */ size_t nlabels; /* number of initialized label drivers */ @@ -279,7 +309,16 @@ struct fdisk_context { extern int __fdisk_context_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb); +extern int fdisk_context_use_cylinders(struct fdisk_context *cxt); +extern int fdisk_context_display_details(struct fdisk_context *cxt); +extern int fdisk_context_enable_listonly(struct fdisk_context *cxt, int enable); +extern int fdisk_context_listonly(struct fdisk_context *cxt); + + /* alignment.c */ +extern sector_t fdisk_scround(struct fdisk_context *cxt, sector_t num); +extern sector_t fdisk_cround(struct fdisk_context *cxt, sector_t num); + extern sector_t fdisk_topology_get_first_lba(struct fdisk_context *cxt); extern unsigned long fdisk_topology_get_grain(struct fdisk_context *cxt); @@ -296,7 +335,6 @@ extern sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt, sector_t lba sector_t start, sector_t stop); -extern int fdisk_override_sector_size(struct fdisk_context *cxt, sector_t s); extern int fdisk_override_geometry(struct fdisk_context *cxt, unsigned int cylinders, unsigned int heads, unsigned int sectors); @@ -304,19 +342,28 @@ extern int fdisk_override_geometry(struct fdisk_context *cxt, extern int fdisk_discover_geometry(struct fdisk_context *cxt); extern int fdisk_discover_topology(struct fdisk_context *cxt); +extern int fdisk_apply_user_device_properties(struct fdisk_context *cxt); +extern void fdisk_zeroize_device_properties(struct fdisk_context *cxt); + /* utils.c */ extern void fdisk_zeroize_firstsector(struct fdisk_context *cxt); extern int fdisk_read_firstsector(struct fdisk_context *cxt); +extern char *fdisk_partname(const char *dev, size_t partno); /* label.c */ extern int fdisk_probe_labels(struct fdisk_context *cxt); 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); +extern struct tt *fdisk_ask_get_table(struct fdisk_ask *ask); +extern int fdisk_print_table(struct fdisk_context *cxt, struct tt *tb); + +extern int fdisk_info_new_partition( + struct fdisk_context *cxt, + int num, sector_t start, sector_t stop, + struct fdisk_parttype *t); + #endif /* _LIBFDISK_PRIVATE_H */ diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c index 482d4537c..2acd15214 100644 --- a/libfdisk/src/gpt.c +++ b/libfdisk/src/gpt.c @@ -5,22 +5,7 @@ * GUID Partition Table (GPT) support. Based on UEFI Specs 2.3.1 * Chapter 5: GUID Partition Table (GPT) Disk Layout (Jun 27th, 2012). * Some ideas and inspiration from GNU parted and gptfdisk. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -57,7 +42,7 @@ #define EFI_PMBR_OSTYPE 0xEE #define MSDOS_MBR_SIGNATURE 0xAA55 -#define GPT_PART_NAME_LEN 72 / sizeof(uint16_t) +#define GPT_PART_NAME_LEN (72 / sizeof(uint16_t)) #define GPT_NPARTITIONS 128 /* Globally unique identifier */ @@ -92,12 +77,12 @@ struct gpt_attr { /* The GPT Partition entry array contains an array of GPT entries. */ struct gpt_entry { - struct gpt_guid partition_type_guid; /* purpose and type of the partition */ - struct gpt_guid unique_partition_guid; + struct gpt_guid type; /* purpose and type of the partition */ + struct gpt_guid partition_guid; uint64_t lba_start; uint64_t lba_end; struct gpt_attr attr; - uint16_t partition_name[GPT_PART_NAME_LEN]; + uint16_t name[GPT_PART_NAME_LEN]; } __attribute__ ((packed)); /* GPT header */ @@ -157,6 +142,8 @@ static struct fdisk_parttype gpt_parttypes[] = DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")), DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")), + DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")), + /* Hah!IdontneedEFI */ DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot partition")), @@ -165,7 +152,7 @@ static struct fdisk_parttype gpt_parttypes[] = DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")), DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")), DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")), - DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery evironmnet")), + DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")), DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")), /* HP-UX */ @@ -178,6 +165,7 @@ static struct fdisk_parttype gpt_parttypes[] = DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")), DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")), DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")), + DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux /home partition")), /* FreeBSD */ DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")), @@ -252,6 +240,7 @@ struct fdisk_gpt_label { }; static void gpt_deinit(struct fdisk_label *lb); +static struct fdisk_parttype *gpt_get_partition_type(struct fdisk_context *cxt, size_t i); static inline struct fdisk_gpt_label *self_label(struct fdisk_context *cxt) { @@ -337,7 +326,7 @@ unknown: static inline int partition_unused(const struct gpt_entry *e) { - return !memcmp(&e->partition_type_guid, &GPT_UNUSED_ENTRY_GUID, + return !memcmp(&e->type, &GPT_UNUSED_ENTRY_GUID, sizeof(struct gpt_guid)); } @@ -422,6 +411,46 @@ static int gpt_mknew_header_from_bkp(struct fdisk_context *cxt, return 0; } +static struct gpt_header *gpt_copy_header(struct fdisk_context *cxt, + struct gpt_header *src) +{ + struct gpt_header *res; + + if (!cxt || !src) + return NULL; + + res = calloc(1, sizeof(*res)); + if (!res) { + fdisk_warn(cxt, _("failed to allocate GPT header")); + return NULL; + } + + res->my_lba = src->alternative_lba; + res->alternative_lba = src->my_lba; + + res->signature = src->signature; + res->revision = src->revision; + res->size = src->size; + res->npartition_entries = src->npartition_entries; + res->sizeof_partition_entry = src->sizeof_partition_entry; + res->first_usable_lba = src->first_usable_lba; + res->last_usable_lba = src->last_usable_lba; + + memcpy(&res->disk_guid, &src->disk_guid, sizeof(src->disk_guid)); + + + if (res->my_lba == GPT_PRIMARY_PARTITION_TABLE_LBA) + res->partition_entry_lba = cpu_to_le64(2); + else { + uint64_t esz = le32_to_cpu(src->npartition_entries) * sizeof(struct gpt_entry); + uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size; + + res->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects); + } + + return res; +} + /* * Builds a clean new GPT header (currently under revision 1.0). * @@ -475,8 +504,9 @@ static int gpt_mknew_header(struct fdisk_context *cxt, */ static int valid_pmbr(struct fdisk_context *cxt) { - int i, ret = 0; /* invalid by default */ + int i, part = 0, ret = 0; /* invalid by default */ struct gpt_legacy_mbr *pmbr = NULL; + uint32_t sz_lba = 0; if (!cxt->firstsector) goto done; @@ -499,13 +529,15 @@ static int valid_pmbr(struct fdisk_context *cxt) * now check if there are other partition types for * hybrid MBR. */ + part = i; ret = GPT_MBR_PROTECTIVE; goto check_hybrid; } } -check_hybrid: + if (ret != GPT_MBR_PROTECTIVE) goto done; +check_hybrid: for (i = 0 ; i < 4; i++) { if ((pmbr->partition_record[i].os_type != EFI_PMBR_OSTYPE) && (pmbr->partition_record[i].os_type != 0x00)) @@ -515,13 +547,24 @@ check_hybrid: /* * Protective MBRs take up the lesser of the whole disk * or 2 TiB (32bit LBA), ignoring the rest of the disk. + * Some partitioning programs, nonetheless, choose to set + * the size to the maximum 32-bit limitation, disregarding + * the disk size. * * Hybrid MBRs do not necessarily comply with this. + * + * Consider a bad value here to be a warning to support dd-ing + * an image from a smaller disk to a bigger disk. */ if (ret == GPT_MBR_PROTECTIVE) { - if (le32_to_cpu(pmbr->partition_record[0].size_in_lba) != - min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF)) - ret = 0; + sz_lba = le32_to_cpu(pmbr->partition_record[part].size_in_lba); + if (sz_lba != (uint32_t) cxt->total_sectors - 1 && sz_lba != 0xFFFFFFFF) { + fdisk_warnx(cxt, _("GPT PMBR size mismatch (%u != %u) " + "will be corrected by w(rite)."), + sz_lba, + (uint32_t) cxt->total_sectors - 1); + fdisk_label_set_changed(cxt->label, 1); + } } done: return ret; @@ -774,13 +817,55 @@ static struct gpt_header *gpt_read_header(struct fdisk_context *cxt, else free(ents); + DBG(LABEL, dbgprint("found valid GPT Header on LBA %ju", lba)); return header; invalid: free(header); free(ents); + + DBG(LABEL, dbgprint("read GPT Header on LBA %ju failed", lba)); return NULL; } + +static int gpt_locate_disklabel(struct fdisk_context *cxt, int n, + const char **name, off_t *offset, size_t *size) +{ + struct fdisk_gpt_label *gpt; + + assert(cxt); + + *name = NULL; + *offset = 0; + *size = 0; + + switch (n) { + case 0: + *name = "PMBR"; + *offset = 0; + *size = 512; + break; + case 1: + *name = _("GPT Header"); + *offset = GPT_PRIMARY_PARTITION_TABLE_LBA * cxt->sector_size; + *size = sizeof(struct gpt_header); + break; + case 2: + *name = _("GPT Entries"); + gpt = self_label(cxt); + *offset = le64_to_cpu(gpt->pheader->partition_entry_lba) * cxt->sector_size; + *size = le32_to_cpu(gpt->pheader->npartition_entries) * + le32_to_cpu(gpt->pheader->sizeof_partition_entry); + break; + default: + return 1; /* no more chunks */ + } + + return 0; +} + + + /* * Returns the number of partitions that are in use. */ @@ -1050,6 +1135,8 @@ static int gpt_probe_label(struct fdisk_context *cxt) gpt = self_label(cxt); + /* TODO: it would be nice to support scenario when GPT headers are OK, + * but PMBR is corrupt */ mbr_type = valid_pmbr(cxt); if (!mbr_type) goto failed; @@ -1061,27 +1148,39 @@ static int gpt_probe_label(struct fdisk_context *cxt) gpt->pheader = gpt_read_header(cxt, GPT_PRIMARY_PARTITION_TABLE_LBA, &gpt->ents); - /* - * TODO: If the primary GPT is corrupt, we must check the last LBA of the - * device to see if it has a valid GPT Header and point to a valid GPT - * Partition Entry Array. - * If it points to a valid GPT Partition Entry Array, then software should - * restore the primary GPT if allowed by platform policy settings. - * - * For now we just abort GPT probing! - */ - if (!gpt->pheader || !gpt->ents) + if (gpt->pheader) + /* primary OK, try backup from alternative LBA */ + gpt->bheader = gpt_read_header(cxt, + le64_to_cpu(gpt->pheader->alternative_lba), + NULL); + else + /* primary corrupted -- try last LBA */ + gpt->bheader = gpt_read_header(cxt, last_lba(cxt), &gpt->ents); + + if (!gpt->pheader && !gpt->bheader) goto failed; - /* OK, probing passed, now initialize backup header and fdisk variables. */ - gpt->bheader = gpt_read_header(cxt, last_lba(cxt), NULL); + /* primary OK, backup corrupted -- recovery */ + if (gpt->pheader && !gpt->bheader) { + fdisk_warnx(cxt, _("The backup GPT table is corrupt, but the " + "primary appears OK, so that will be used.")); + gpt->bheader = gpt_copy_header(cxt, gpt->pheader); + if (!gpt->bheader) + goto failed; + gpt_recompute_crc(gpt->bheader, gpt->ents); + + /* primary corrupted, backup OK -- recovery */ + } else if (!gpt->pheader && gpt->bheader) { + fdisk_warnx(cxt, _("The primary GPT table is corrupt, but the " + "backup appears OK, so that will be used.")); + gpt->pheader = gpt_copy_header(cxt, gpt->bheader); + if (!gpt->pheader) + goto failed; + gpt_recompute_crc(gpt->pheader, gpt->ents); + } cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries); cxt->label->nparts_cur = partitions_in_use(gpt->pheader, gpt->ents); - - fdisk_warnx(cxt, _("WARNING: fdisk GPT support is currently new, and therefore " - "in an experimental phase. Use at your own discretion.")); - return 1; failed: DBG(LABEL, dbgprint("GPT probe failed")); @@ -1137,64 +1236,108 @@ static char *encode_to_utf8(unsigned char *src, size_t count) * requirements, but once partition semantics are added to the fdisk * API it can be removed for custom implementation (see gpt_label struct). */ -void gpt_list_table(struct fdisk_context *cxt, - int xtra __attribute__ ((__unused__))) +static int gpt_list_disklabel(struct fdisk_context *cxt) { + int rc, trunc = TT_FL_TRUNC; uint32_t i; struct fdisk_gpt_label *gpt; + struct gpt_header *h; uint64_t fu; uint64_t lu; + struct tt *tb = NULL; assert(cxt); assert(cxt->label); assert(fdisk_is_disklabel(cxt, GPT)); gpt = self_label(cxt); + h = gpt->pheader; fu = le64_to_cpu(gpt->pheader->first_usable_lba); lu = le64_to_cpu(gpt->pheader->last_usable_lba); - printf("\n# Start End Size Type Name\n"); + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + /* don't trunc anything in expert mode */ + if (fdisk_context_display_details(cxt)) { + trunc = 0; + fdisk_colon(cxt, _("First LBA: %ju"), h->first_usable_lba); + fdisk_colon(cxt, _("Last LBA: %ju"), h->last_usable_lba); + fdisk_colon(cxt, _("Alternative LBA: %ju"), h->alternative_lba); + fdisk_colon(cxt, _("Partitions entries LBA: %ju"), h->partition_entry_lba); + fdisk_colon(cxt, _("Allocated partition entries: %ju"), (uintmax_t) h->npartition_entries); + } + tt_define_column(tb, _("Device"), 0.1, 0); + tt_define_column(tb, _("Start"), 12, TT_FL_RIGHT); + tt_define_column(tb, _("End"), 12, TT_FL_RIGHT); + tt_define_column(tb, _("Size"), 6, TT_FL_RIGHT); + tt_define_column(tb, _("Type"), 0.1, trunc); + + if (fdisk_context_display_details(cxt)) { + tt_define_column(tb, _("UUID"), 36, 0); + tt_define_column(tb, _("Name"), 0.2, trunc); + } - for (i = 0; i < le32_to_cpu(gpt->pheader->npartition_entries); i++) { - char *name = NULL, *sizestr = NULL; - uint64_t start = gpt_partition_start(&gpt->ents[i]); - uint64_t size = gpt_partition_size(&gpt->ents[i]); + for (i = 0; i < le32_to_cpu(h->npartition_entries); i++) { + struct gpt_entry *e = &gpt->ents[i]; + char *sizestr = NULL, *p; + uint64_t start = gpt_partition_start(e); + uint64_t size = gpt_partition_size(e); struct fdisk_parttype *t; + struct tt_line *ln; + char u_str[37]; if (partition_unused(&gpt->ents[i]) || start == 0) continue; - /* the partition has to inside usable range */ if (start < fu || start + size - 1 > lu) continue; - - name = encode_to_utf8((unsigned char *)gpt->ents[i].partition_name, - sizeof(gpt->ents[i].partition_name)); - if (!name) - continue; - sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, - size * cxt->sector_size); - if (!sizestr) { - free(name); + ln = tt_add_line(tb, NULL); + if (!ln) continue; - } + if (fdisk_context_display_details(cxt) && + asprintf(&p, "%ju", size * cxt->sector_size) > 0) + sizestr = p; + else + sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, + size * cxt->sector_size); t = fdisk_get_partition_type(cxt, i); - printf("%2d %12ju %12ju %6s %-15.15s %s\n", - i+1, - start, - gpt_partition_end(&gpt->ents[i]), - sizestr, - t->name, - name); + /* basic columns */ + p = fdisk_partname(cxt->dev_path, i + 1); + if (p) + tt_line_set_data(ln, 0, p); + if (asprintf(&p, "%ju", start) > 0) + tt_line_set_data(ln, 1, p); + if (asprintf(&p, "%ju", gpt_partition_end(e)) > 0) + tt_line_set_data(ln, 2, p); + if (sizestr) + tt_line_set_data(ln, 3, sizestr); + if (t && t->name) + tt_line_set_data(ln, 4, strdup(t->name)); + + /* expert menu column(s) */ + if (fdisk_context_display_details(cxt)) { + char *name = encode_to_utf8( + (unsigned char *)e->name, + sizeof(e->name)); + + if (guid_to_string(&e->partition_guid, u_str)) + tt_line_set_data(ln, 5, strdup(u_str)); + if (name) + tt_line_set_data(ln, 6, name); + } fdisk_warn_alignment(cxt, start, i); - - free(name); - free(sizestr); fdisk_free_parttype(t); } + + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + + return rc; } /* @@ -1408,7 +1551,7 @@ static int gpt_verify_disklabel(struct fdisk_context *cxt) } if (le64_to_cpu(gpt->pheader->alternative_lba) >= cxt->total_sectors) { nerror++; - fdisk_warnx(cxt, _("Disk is to small to hold all data.")); + fdisk_warnx(cxt, _("Disk is too small to hold all data.")); } /* @@ -1446,7 +1589,7 @@ static int gpt_verify_disklabel(struct fdisk_context *cxt) uint32_t nsegments = 0; uint64_t free_sectors = 0, largest_segment = 0; - fdisk_info(cxt, _("No errors detected")); + fdisk_info(cxt, _("No errors detected.")); fdisk_info(cxt, _("Header version: %s"), gpt_get_header_revstr(gpt->pheader)); fdisk_info(cxt, _("Using %u out of %d partitions."), partitions_in_use(gpt->pheader, gpt->ents), @@ -1454,11 +1597,15 @@ static int gpt_verify_disklabel(struct fdisk_context *cxt) free_sectors = get_free_sectors(cxt, gpt->pheader, gpt->ents, &nsegments, &largest_segment); - fdisk_info(cxt, _("A total of %ld free sectors available in %d segment(s) " - "(largest %ld)."), - free_sectors, nsegments, largest_segment); + fdisk_info(cxt, + P_("A total of %ju free sectors is available in %d segment.", + "A total of %ju free sectors is available in %d segments " + "(the largest is %ju).", nsegments), + free_sectors, nsegments, largest_segment); } else - fdisk_warnx(cxt, _("Detected %d error(s)."), nerror); + fdisk_warnx(cxt, + P_("%d error detected.", "%d errors detected.", nerror), + nerror); return 0; } @@ -1495,8 +1642,8 @@ static int gpt_delete_partition(struct fdisk_context *cxt, static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid) { - e->partition_type_guid = *uuid; - DBG(LABEL, dbgprint_uuid("new type", &(e->partition_type_guid))); + e->type = *uuid; + DBG(LABEL, dbgprint_uuid("new type", &(e->type))); } /* @@ -1534,8 +1681,8 @@ static int gpt_create_new_partition(struct fdisk_context *cxt, * generated for that partition, and every partition is guaranteed * to have a unique GUID. */ - uuid_generate_random((unsigned char *) &e->unique_partition_guid); - swap_efi_guid(&e->unique_partition_guid); + uuid_generate_random((unsigned char *) &e->partition_guid); + swap_efi_guid(&e->partition_guid); memcpy(&entries[partnum], e, sizeof(*e)); @@ -1575,8 +1722,8 @@ static int gpt_add_partition( ents = gpt->ents; if (!partition_unused(&ents[partnum])) { - fdisk_warnx(cxt, _("Partition %zd is already defined. " - "Delete it before re-adding it."), partnum +1); + fdisk_warnx(cxt, _("Partition %zd is already defined. " + "Delete it before re-adding it."), partnum +1); return -EINVAL; } if (le32_to_cpu(pheader->npartition_entries) == @@ -1654,9 +1801,14 @@ static int gpt_add_partition( user_f, user_l, &typeid, ents) != 0) fdisk_warnx(cxt, _("Could not create partition %zd"), partnum + 1); else { - fdisk_info(cxt, _("Created partition %zd\n"), partnum + 1); + struct fdisk_parttype *t; + cxt->label->nparts_cur++; fdisk_label_set_changed(cxt->label, 1); + + t = gpt_get_partition_type(cxt, partnum); + fdisk_info_new_partition(cxt, partnum + 1, user_f, user_l, t); + fdisk_free_parttype(t); } rc = 0; @@ -1672,7 +1824,7 @@ static int gpt_create_disklabel(struct fdisk_context *cxt) { int rc = 0; ssize_t esz = 0; - struct gpt_guid *uid; + char str[37]; struct fdisk_gpt_label *gpt; assert(cxt); @@ -1728,21 +1880,77 @@ static int gpt_create_disklabel(struct fdisk_context *cxt) cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries); cxt->label->nparts_cur = 0; - uid = &gpt->pheader->disk_guid; - fdisk_info(cxt, _("Building a new GPT disklabel " - "(GUID: %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)\n"), - uid->time_low, uid->time_mid, - uid->time_hi_and_version, - uid->clock_seq_hi, - uid->clock_seq_low, - uid->node[0], uid->node[1], - uid->node[2], uid->node[3], - uid->node[4], uid->node[5]); + guid_to_string(&gpt->pheader->disk_guid, str); fdisk_label_set_changed(cxt->label, 1); + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Created a new GPT disklabel (GUID: %s)."), str); done: return rc; } +static int gpt_get_disklabel_id(struct fdisk_context *cxt, char **id) +{ + struct fdisk_gpt_label *gpt; + char str[37]; + + assert(cxt); + assert(id); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, GPT)); + + gpt = self_label(cxt); + guid_to_string(&gpt->pheader->disk_guid, str); + + *id = strdup(str); + if (!*id) + return -ENOMEM; + return 0; +} + +static int gpt_set_disklabel_id(struct fdisk_context *cxt) +{ + struct fdisk_gpt_label *gpt; + struct gpt_guid uuid; + char *str, *old, *new; + int rc; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, GPT)); + + gpt = self_label(cxt); + if (fdisk_ask_string(cxt, + _("Enter new disk UUID (in 8-4-4-4-12 format)"), &str)) + return -EINVAL; + + rc = string_to_guid(str, &uuid); + free(str); + + if (rc) { + fdisk_warnx(cxt, _("Failed to parse your UUID.")); + return rc; + } + + gpt_get_disklabel_id(cxt, &old); + + gpt->pheader->disk_guid = uuid; + gpt->bheader->disk_guid = uuid; + + gpt_recompute_crc(gpt->pheader, gpt->ents); + gpt_recompute_crc(gpt->bheader, gpt->ents); + + gpt_get_disklabel_id(cxt, &new); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Disk identifier changed from %s to %s."), old, new); + + free(old); + free(new); + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + + static struct fdisk_parttype *gpt_get_partition_type( struct fdisk_context *cxt, size_t i) @@ -1760,7 +1968,7 @@ static struct fdisk_parttype *gpt_get_partition_type( if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries)) return NULL; - guid_to_string(&gpt->ents[i].partition_type_guid, str); + guid_to_string(&gpt->ents[i].type, str); t = fdisk_get_parttype_from_string(cxt, str); if (!t) t = fdisk_new_unknown_parttype(0, str); @@ -1820,6 +2028,101 @@ static int gpt_get_partition_status( return 0; } +int fdisk_gpt_partition_set_uuid(struct fdisk_context *cxt, size_t i) +{ + struct fdisk_gpt_label *gpt; + struct gpt_entry *e; + struct gpt_guid uuid; + char *str, new_u[37], old_u[37]; + int rc; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, GPT)); + + DBG(LABEL, dbgprint("UUID change requested partno=%zd", i)); + + gpt = self_label(cxt); + + if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries)) + return -EINVAL; + + if (fdisk_ask_string(cxt, + _("New UUID (in 8-4-4-4-12 format)"), &str)) + return -EINVAL; + + rc = string_to_guid(str, &uuid); + free(str); + + if (rc) { + fdisk_warnx(cxt, _("Failed to parse your UUID.")); + return rc; + } + + e = &gpt->ents[i]; + + guid_to_string(&e->partition_guid, old_u); + guid_to_string(&uuid, new_u); + + e->partition_guid = uuid; + gpt_recompute_crc(gpt->pheader, gpt->ents); + gpt_recompute_crc(gpt->bheader, gpt->ents); + fdisk_label_set_changed(cxt->label, 1); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Partition UUID changed from %s to %s."), + old_u, new_u); + return 0; +} + +int fdisk_gpt_partition_set_name(struct fdisk_context *cxt, size_t i) +{ + struct fdisk_gpt_label *gpt; + struct gpt_entry *e; + char *str, *old, name[GPT_PART_NAME_LEN] = { 0 }; + size_t sz; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, GPT)); + + DBG(LABEL, dbgprint("NAME change requested partno=%zd", i)); + + gpt = self_label(cxt); + + if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries)) + return -EINVAL; + + if (fdisk_ask_string(cxt, _("New name"), &str)) + return -EINVAL; + + e = &gpt->ents[i]; + old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name)); + + sz = strlen(str); + if (sz) { + if (sz > GPT_PART_NAME_LEN) + sz = GPT_PART_NAME_LEN; + memcpy(name, str, sz); + } + + for (i = 0; i < GPT_PART_NAME_LEN; i++) + e->name[i] = cpu_to_le16((uint16_t) name[i]); + + gpt_recompute_crc(gpt->pheader, gpt->ents); + gpt_recompute_crc(gpt->bheader, gpt->ents); + + fdisk_label_set_changed(cxt->label, 1); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Partition name changed from '%s' to '%.*s'."), + old, (int) GPT_PART_NAME_LEN, str); + free(str); + free(old); + + return 0; +} + /* * Deinitialize fdisk-specific variables @@ -1846,6 +2149,11 @@ static const struct fdisk_label_operations gpt_operations = .write = gpt_write_disklabel, .verify = gpt_verify_disklabel, .create = gpt_create_disklabel, + .list = gpt_list_disklabel, + .locate = gpt_locate_disklabel, + .get_id = gpt_get_disklabel_id, + .set_id = gpt_set_disklabel_id, + .part_add = gpt_add_partition, .part_delete = gpt_delete_partition, .part_get_type = gpt_get_partition_type, diff --git a/libfdisk/src/label.c b/libfdisk/src/label.c index cc21f759e..a923a898f 100644 --- a/libfdisk/src/label.c +++ b/libfdisk/src/label.c @@ -17,7 +17,10 @@ int fdisk_probe_labels(struct fdisk_context *cxt) if (!lb->op->probe) continue; - + if (lb->disabled) { + DBG(LABEL, dbgprint("%s disabled -- ignore", lb->name)); + continue; + } DBG(LABEL, dbgprint("probing for %s", lb->name)); cxt->label = lb; @@ -72,14 +75,37 @@ int fdisk_dev_is_disklabel(struct fdisk_context *cxt, enum fdisk_labeltype l) */ int fdisk_write_disklabel(struct fdisk_context *cxt) { - if (!cxt || !cxt->label) + if (!cxt || !cxt->label || cxt->readonly) return -EINVAL; if (!cxt->label->op->write) return -ENOSYS; - return cxt->label->op->write(cxt); } +int fdisk_require_geometry(struct fdisk_context *cxt) +{ + assert(cxt); + + return cxt->label + && cxt->label->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0; +} + +int fdisk_missing_geometry(struct fdisk_context *cxt) +{ + int rc; + + assert(cxt); + + rc = (fdisk_require_geometry(cxt) && + (!cxt->geom.heads || !cxt->geom.sectors + || !cxt->geom.cylinders)); + + if (rc && !fdisk_context_listonly(cxt)) + fdisk_warnx(cxt, _("Incomplete geometry setting.")); + + return rc; +} + /** * fdisk_verify_disklabel: * @cxt: fdisk context @@ -94,16 +120,36 @@ int fdisk_verify_disklabel(struct fdisk_context *cxt) return -EINVAL; if (!cxt->label->op->verify) return -ENOSYS; + if (fdisk_missing_geometry(cxt)) + return -EINVAL; return cxt->label->op->verify(cxt); } /** + * fdisk_list_disklabel: + * @cxt: fdisk context + * + * Lists in-memory partition table + * + * Returns 0 on success, otherwise, a corresponding error. + */ +int fdisk_list_disklabel(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->list) + return -ENOSYS; + + return cxt->label->op->list(cxt); +} + +/** * fdisk_add_partition: * @cxt: fdisk context * @t: partition type to create or NULL for label-specific default * - * Creates a new partition, with number @partnum and type @parttype. + * Creates a new partition with type @parttype. * * Returns 0. */ @@ -119,6 +165,8 @@ int fdisk_add_partition(struct fdisk_context *cxt, return -EINVAL; if (!cxt->label->op->part_add) return -ENOSYS; + if (fdisk_missing_geometry(cxt)) + return -EINVAL; if (!(cxt->label->flags & FDISK_LABEL_FL_ADDPART_NOPARTNO)) { int rc = fdisk_ask_partnum(cxt, &partnum, 1); @@ -164,6 +212,9 @@ int fdisk_delete_partition(struct fdisk_context *cxt, size_t partnum) */ int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name) { + int haslabel = 0; + struct fdisk_label *lb; + if (!cxt) return -EINVAL; @@ -175,21 +226,75 @@ int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name) #endif } - if (cxt->label) + if (cxt->label) { fdisk_deinit_label(cxt->label); + haslabel = 1; + } - cxt->label = fdisk_context_get_label(cxt, name); - if (!cxt->label) + lb = fdisk_context_get_label(cxt, name); + if (!lb || lb->disabled) return -EINVAL; - - DBG(LABEL, dbgprint("changing to %s label\n", cxt->label->name)); - if (!cxt->label->op->create) + if (!lb->op->create) return -ENOSYS; - fdisk_reset_alignment(cxt); + __fdisk_context_switch_label(cxt, lb); + + if (haslabel && !cxt->parent) + fdisk_reset_device_properties(cxt); + + DBG(LABEL, dbgprint("create a new %s label", lb->name)); return cxt->label->op->create(cxt); } + +int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, + off_t *offset, size_t *size) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->locate) + return -ENOSYS; + + DBG(LABEL, dbgprint("locating %d chunk of %s.", n, cxt->label->name)); + return cxt->label->op->locate(cxt, n, name, offset, size); +} + + +/** + * fdisk_get_disklabel_id: + * @cxt: fdisk context + * @id: returns pointer to allocated string + * + * Returns 0 on success, otherwise, a corresponding error. + */ +int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->get_id) + return -ENOSYS; + + DBG(LABEL, dbgprint("asking for disk %s ID", cxt->label->name)); + return cxt->label->op->get_id(cxt, id); +} + +/** + * fdisk_get_disklabel_id: + * @cxt: fdisk context + * + * Returns 0 on success, otherwise, a corresponding error. + */ +int fdisk_set_disklabel_id(struct fdisk_context *cxt) +{ + if (!cxt || !cxt->label) + return -EINVAL; + if (!cxt->label->op->set_id) + return -ENOSYS; + + DBG(LABEL, dbgprint("setting %s disk ID", cxt->label->name)); + return cxt->label->op->set_id(cxt); +} + /** * fdisk_get_partition_type: * @cxt: fdisk context @@ -333,3 +438,19 @@ int fdisk_label_is_changed(struct fdisk_label *lb) assert(lb); return lb ? lb->changed : 0; } + +void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled) +{ + assert(lb); + + DBG(LABEL, dbgprint("%s label %s", + lb->name, + disabled ? "DISABLED" : "ENABLED")); + lb->disabled = disabled ? 1 : 0; +} + +int fdisk_label_is_disabled(struct fdisk_label *lb) +{ + assert(lb); + return lb ? lb->disabled : 0; +} diff --git a/libfdisk/src/libfdisk.h b/libfdisk/src/libfdisk.h index 8a440672b..dd83a2717 100644 --- a/libfdisk/src/libfdisk.h +++ b/libfdisk/src/libfdisk.h @@ -32,18 +32,17 @@ struct fdisk_context; struct fdisk_label; struct fdisk_parttype; struct fdisk_ask; +struct tt; /* * Supported partition table types (labels) */ enum fdisk_labeltype { - FDISK_DISKLABEL_DOS = 1, - FDISK_DISKLABEL_SUN = 2, - FDISK_DISKLABEL_SGI = 4, - FDISK_DISKLABEL_OSF = 8, - FDISK_DISKLABEL_MAC = 16, - FDISK_DISKLABEL_GPT = 32, - FDISK_DISKLABEL_ANY = -1 + FDISK_DISKLABEL_DOS = (1 << 1), + FDISK_DISKLABEL_SUN = (1 << 2), + FDISK_DISKLABEL_SGI = (1 << 3), + FDISK_DISKLABEL_BSD = (1 << 4), + FDISK_DISKLABEL_GPT = (1 << 5) }; enum { @@ -58,10 +57,16 @@ enum { FDISK_ASKTYPE_WARN, FDISK_ASKTYPE_WARNX, FDISK_ASKTYPE_INFO, - FDISK_ASKTYPE_YESNO + FDISK_ASKTYPE_YESNO, + FDISK_ASKTYPE_TABLE, + FDISK_ASKTYPE_STRING }; - +/* extra flags for info massages (see fdisk_sinfo() */ +enum { + FDISK_INFO_COLON = 1, /* colorize "foo bar:" prefix in the message */ + FDISK_INFO_SUCCESS, /* info after successful action */ +}; /* init.c */ extern void fdisk_init_debug(int mask); @@ -76,20 +81,28 @@ 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_is_readonly(struct fdisk_context *cxt); extern int fdisk_context_assign_device(struct fdisk_context *cxt, const char *fname, int readonly); +extern int fdisk_context_deassign_device(struct fdisk_context *cxt); extern struct fdisk_label *fdisk_context_get_label(struct fdisk_context *cxt, const char *name); +extern int fdisk_context_next_label(struct fdisk_context *cxt, struct fdisk_label **lb); extern int fdisk_context_switch_label(struct fdisk_context *cxt, const char *name); extern int fdisk_context_set_unit(struct fdisk_context *cxt, const char *str); + +#define PLURAL 0 +#define SINGULAR 1 extern const char *fdisk_context_get_unit(struct fdisk_context *cxt, int n); -extern int fdisk_context_use_cylinders(struct fdisk_context *cxt); + extern unsigned int fdisk_context_get_units_per_sector(struct fdisk_context *cxt); +extern int fdisk_context_enable_details(struct fdisk_context *cxt, int enable); + /* parttype.c */ extern struct fdisk_parttype *fdisk_get_parttype_from_code(struct fdisk_context *cxt, unsigned int code); @@ -101,7 +114,12 @@ extern struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int type, cons extern void fdisk_free_parttype(struct fdisk_parttype *type); extern size_t fdisk_get_nparttypes(struct fdisk_context *cxt); +extern int fdisk_is_parttype_string(struct fdisk_context *cxt); + /* label.c */ +extern int fdisk_require_geometry(struct fdisk_context *cxt); +extern int fdisk_missing_geometry(struct fdisk_context *cxt); + extern int fdisk_dev_has_disklabel(struct fdisk_context *cxt); extern int fdisk_dev_is_disklabel(struct fdisk_context *cxt, enum fdisk_labeltype l); @@ -110,6 +128,11 @@ extern int fdisk_dev_is_disklabel(struct fdisk_context *cxt, enum fdisk_labeltyp 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_list_disklabel(struct fdisk_context *cxt); +extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size); + +extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id); +extern int fdisk_set_disklabel_id(struct fdisk_context *cxt); 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); @@ -121,18 +144,67 @@ extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum, extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed); extern int fdisk_label_is_changed(struct fdisk_label *lb); +extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled); +extern int fdisk_label_is_disabled(struct fdisk_label *lb); + extern int fdisk_partition_get_status(struct fdisk_context *cxt, size_t partnum, int *status); extern int fdisk_partition_is_used(struct fdisk_context *cxt, size_t partnum); extern int fdisk_partition_toggle_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag); /* alignment.c */ extern int fdisk_reset_alignment(struct fdisk_context *cxt); +extern int fdisk_reset_device_properties(struct fdisk_context *cxt); + +extern int fdisk_save_user_geometry(struct fdisk_context *cxt, + unsigned int cylinders, + unsigned int heads, + unsigned int sectors); + +extern int fdisk_save_user_sector_size(struct fdisk_context *cxt, + unsigned int phy, + unsigned int log); + +extern int fdisk_has_user_device_properties(struct fdisk_context *cxt); +extern int fdisk_reread_partition_table(struct fdisk_context *cxt); /* dos.c */ extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable); extern int fdisk_dos_is_compatible(struct fdisk_label *lb); +/* sun.h */ +extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt); +extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt); +extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt); +extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt); +extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt); + +/* bsd.c */ +extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt); +extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt); +extern int fdisk_bsd_link_partition(struct fdisk_context *cxt); + +/* sgi.h */ +#define SGI_FLAG_BOOT 1 +#define SGI_FLAG_SWAP 2 +extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt); +extern int fdisk_sgi_create_info(struct fdisk_context *cxt); + +/* gpt */ +extern int fdisk_gpt_partition_set_uuid(struct fdisk_context *cxt, size_t i); +extern int fdisk_gpt_partition_set_name(struct fdisk_context *cxt, size_t i); + +/* dos.c */ +extern struct dos_partition *fdisk_dos_get_partition( + struct fdisk_context *cxt, + size_t i); + +extern int fdisk_dos_fix_order(struct fdisk_context *cxt); +extern int fdisk_dos_move_begin(struct fdisk_context *cxt, int i); +extern int fdisk_dos_list_extended(struct fdisk_context *cxt); + +#define DOS_FLAG_ACTIVE 1 + /* ask.c */ #define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x) @@ -143,6 +215,9 @@ extern const char *fdisk_ask_get_query(struct fdisk_ask *ask); extern int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str); extern int fdisk_ask_get_type(struct fdisk_ask *ask); extern int fdisk_ask_set_type(struct fdisk_ask *ask, int type); +extern int fdisk_ask_set_flags(struct fdisk_ask *ask, unsigned int flags); +extern unsigned int fdisk_ask_get_flags(struct fdisk_ask *ask); + extern int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask); extern const char *fdisk_ask_number_get_range(struct fdisk_ask *ask); @@ -161,7 +236,7 @@ extern uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask); extern int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result); extern int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative); extern int fdisk_ask_number_is_relative(struct fdisk_ask *ask); - +extern int fdisk_ask_number_inchars(struct fdisk_ask *ask); extern int fdisk_ask_number(struct fdisk_context *cxt, uintmax_t low, @@ -170,13 +245,28 @@ extern int fdisk_ask_number(struct fdisk_context *cxt, const char *query, uintmax_t *result); +extern int fdisk_ask_string(struct fdisk_context *cxt, + const char *query, + char **result); + +extern char *fdisk_ask_string_get_result(struct fdisk_ask *ask); +extern int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result); + extern int fdisk_ask_yesno(struct fdisk_context *cxt, const char *query, int *result); extern uint64_t fdisk_ask_yesno_get_result(struct fdisk_ask *ask); extern int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, uint64_t result); -extern int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...); -extern int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...); -extern int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...); +extern int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +extern int fdisk_colon(struct fdisk_context *cxt, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +extern int fdisk_sinfo(struct fdisk_context *cxt, unsigned int flags, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); + +extern int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +extern int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); extern int fdisk_ask_print_get_errno(struct fdisk_ask *ask); extern int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum); diff --git a/libfdisk/src/parttype.c b/libfdisk/src/parttype.c index e78b35594..8a13d7211 100644 --- a/libfdisk/src/parttype.c +++ b/libfdisk/src/parttype.c @@ -165,4 +165,19 @@ void fdisk_free_parttype(struct fdisk_parttype *t) } } +/** + * fdisk_is_parttype_string: + * @cxt: context + * + * Returns: 1 if the current label uses strings as partition type + * identifiers (e.g. GPT UUIDS) or 0. + */ +int fdisk_is_parttype_string(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + if (cxt->label->parttypes && cxt->label->parttypes[0].typestr) + return 1; + return 0; +} diff --git a/libfdisk/src/sgi.c b/libfdisk/src/sgi.c new file mode 100644 index 000000000..d4fc096eb --- /dev/null +++ b/libfdisk/src/sgi.c @@ -0,0 +1,1189 @@ +/* + * + * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org> + * 2013 Karel Zak <kzak@redhat.com> + * + * This is a re-written version for libfdisk, the original was fdisksgilabel.c + * from util-linux fdisk, by: + * + * Andreas Neuper, Sep 1998, + * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, Mar 1999, + * Phillip Kesling <pkesling@sgi.com>, Mar 2003. + */ +#include "c.h" +#include "nls.h" +#include "all-io.h" + +#include "blkdev.h" + +#include "bitops.h" +#include "pt-sgi.h" +#include "pt-mbr.h" +#include "fdiskP.h" + +/* + * in-memory fdisk SGI stuff + */ +struct fdisk_sgi_label { + struct fdisk_label head; /* generic fdisk part */ + struct sgi_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */ + + struct sgi_freeblocks { + unsigned int first; + unsigned int last; + } freelist[SGI_MAXPARTITIONS + 1]; +}; + +static struct fdisk_parttype sgi_parttypes[] = +{ + {SGI_TYPE_VOLHDR, N_("SGI volhdr")}, + {SGI_TYPE_TRKREPL, N_("SGI trkrepl")}, + {SGI_TYPE_SECREPL, N_("SGI secrepl")}, + {SGI_TYPE_SWAP, N_("SGI raw")}, + {SGI_TYPE_BSD, N_("SGI bsd")}, + {SGI_TYPE_SYSV, N_("SGI sysv")}, + {SGI_TYPE_ENTIRE_DISK, N_("SGI volume")}, + {SGI_TYPE_EFS, N_("SGI efs")}, + {SGI_TYPE_LVOL, N_("SGI lvol")}, + {SGI_TYPE_RLVOL, N_("SGI rlvol")}, + {SGI_TYPE_XFS, N_("SGI xfs")}, + {SGI_TYPE_XFSLOG, N_("SGI xfslog")}, + {SGI_TYPE_XLV, N_("SGI xlv")}, + {SGI_TYPE_XVM, N_("SGI xvm")}, + {MBR_LINUX_SWAP_PARTITION, N_("Linux swap")}, + {MBR_LINUX_DATA_PARTITION, N_("Linux native")}, + {MBR_LINUX_LVM_PARTITION, N_("Linux LVM")}, + {MBR_LINUX_RAID_PARTITION, N_("Linux RAID")}, + {0, NULL } +}; + +static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i ); +static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i ); +static int sgi_get_bootpartition(struct fdisk_context *cxt); +static int sgi_get_swappartition(struct fdisk_context *cxt); + +/* Returns a pointer buffer with on-disk data. */ +static inline struct sgi_disklabel *self_disklabel(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + return ((struct fdisk_sgi_label *) cxt->label)->header; +} + +/* Returns in-memory fdisk data. */ +static inline struct fdisk_sgi_label *self_label(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + return (struct fdisk_sgi_label *) cxt->label; +} + +/* + * Information within second on-disk block + */ +#define SGI_INFO_MAGIC 0x00072959 + +struct sgi_info { + unsigned int magic; /* looks like a magic number */ + unsigned int a2; + unsigned int a3; + unsigned int a4; + unsigned int b1; + unsigned short b2; + unsigned short b3; + unsigned int c[16]; + unsigned short d[3]; + unsigned char scsi_string[50]; + unsigned char serial[137]; + unsigned short check1816; + unsigned char installer[225]; +}; + +static struct sgi_info *sgi_new_info(void) +{ + struct sgi_info *info = calloc(1, sizeof(struct sgi_info)); + + if (!info) + return NULL; + + info->magic = cpu_to_be32(SGI_INFO_MAGIC); + info->b1 = cpu_to_be32(-1); + info->b2 = cpu_to_be16(-1); + info->b3 = cpu_to_be16(1); + + /* You may want to replace this string !!!!!!! */ + strcpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30"); + strcpy((char *) info->serial, "0000"); + info->check1816 = cpu_to_be16(18 * 256 + 16); + strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994"); + + return info; +} + +static void sgi_free_info(struct sgi_info *info) +{ + free(info); +} + +int fdisk_sgi_create_info(struct fdisk_context *cxt) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + + /* I keep SGI's habit to write the sgilabel to the second block */ + sgilabel->volume[0].block_num = cpu_to_be32(2); + sgilabel->volume[0].num_bytes = cpu_to_be32(sizeof(struct sgi_info)); + strncpy((char *) sgilabel->volume[0].name, "sgilabel", 8); + + fdisk_info(cxt, _("SGI info created on second sector")); + return 0; +} + + +/* + * only dealing with free blocks here + */ +static void set_freelist(struct fdisk_context *cxt, + size_t i, unsigned int f, unsigned int l) +{ + struct fdisk_sgi_label *sgi = self_label(cxt); + + if (i < ARRAY_SIZE(sgi->freelist)) { + sgi->freelist[i].first = f; + sgi->freelist[i].last = l; + } +} + +static void add_to_freelist(struct fdisk_context *cxt, + unsigned int f, unsigned int l) +{ + struct fdisk_sgi_label *sgi = self_label(cxt); + size_t i; + + for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) { + if (sgi->freelist[i].last == 0) + break; + } + set_freelist(cxt, i, f, l); +} + +static void clear_freelist(struct fdisk_context *cxt) +{ + struct fdisk_sgi_label *sgi = self_label(cxt); + + memset(sgi->freelist, 0, sizeof(sgi->freelist)); +} + +static unsigned int is_in_freelist(struct fdisk_context *cxt, unsigned int b) +{ + struct fdisk_sgi_label *sgi = self_label(cxt); + size_t i; + + for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) { + if (sgi->freelist[i].first <= b + && sgi->freelist[i].last >= b) + return sgi->freelist[i].last; + } + + return 0; +} + + +static int sgi_get_nsect(struct fdisk_context *cxt) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be16_to_cpu(sgilabel->devparam.nsect); +} + +static int sgi_get_ntrks(struct fdisk_context *cxt) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be16_to_cpu(sgilabel->devparam.ntrks); +} + +static size_t count_used_partitions(struct fdisk_context *cxt) +{ + size_t i, ct = 0; + + for (i = 0; i < cxt->label->nparts_max; i++) + ct += sgi_get_num_sectors(cxt, i) > 0; + + return ct; +} + +static int sgi_probe_label(struct fdisk_context *cxt) +{ + struct fdisk_sgi_label *sgi; + struct sgi_disklabel *sgilabel; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + assert(sizeof(struct sgi_disklabel) <= 512); + + /* map first sector to header */ + sgi = (struct fdisk_sgi_label *) cxt->label; + sgi->header = (struct sgi_disklabel *) cxt->firstsector; + sgilabel = sgi->header; + + if (be32_to_cpu(sgilabel->magic) != SGI_LABEL_MAGIC) { + sgi->header = NULL; + return 0; + } + + /* + * test for correct checksum + */ + if (sgi_pt_checksum(sgilabel) != 0) + fdisk_warnx(cxt, _("Detected an SGI disklabel with wrong checksum.")); + + clear_freelist(cxt); + cxt->label->nparts_max = SGI_MAXPARTITIONS; + cxt->label->nparts_cur = count_used_partitions(cxt); + return 1; +} + +static int sgi_list_table(struct fdisk_context *cxt) +{ + struct tt *tb = NULL; + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + struct sgi_device_parameter *sgiparam = &sgilabel->devparam; + size_t i, used; + char *p; + int rc = 0; + + if (fdisk_context_display_details(cxt)) + fdisk_colon(cxt, _( + "Label geometry: %d heads, %llu sectors\n" + " %llu cylinders, %d physical cylinders\n" + " %d extra sects/cyl, interleave %d:1\n"), + cxt->geom.heads, cxt->geom.sectors, + cxt->geom.cylinders, be16_to_cpu(sgiparam->pcylcount), + (int) sgiparam->sparecyl, be16_to_cpu(sgiparam->ilfact)); + + /* + * Partitions + */ + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + tt_define_column(tb, _("Pt#"), 3, TT_FL_RIGHT); + tt_define_column(tb, _("Device"), 0.2, 0); + tt_define_column(tb, _("Info"), 2, 0); + tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("End"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Sectors"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT); + tt_define_column(tb, _("System"), 0.2, TT_FL_TRUNC); + + for (i = 0, used = 0; i < cxt->label->nparts_max; i++) { + uint32_t start, len; + struct fdisk_parttype *t; + struct tt_line *ln; + + if (sgi_get_num_sectors(cxt, i) == 0) + continue; + + ln = tt_add_line(tb, NULL); + if (!ln) + continue; + start = sgi_get_start_sector(cxt, i); + len = sgi_get_num_sectors(cxt, i); + t = fdisk_get_partition_type(cxt, i); + + if (asprintf(&p, "%zu:", i + 1) > 0) + tt_line_set_data(ln, 0, p); /* # */ + p = fdisk_partname(cxt->dev_path, i + 1); + if (p) + tt_line_set_data(ln, 1, p); /* Device */ + + p = sgi_get_swappartition(cxt) == (int) i ? "swap" : + sgi_get_bootpartition(cxt) == (int) i ? "boot" : NULL; + if (p) + tt_line_set_data(ln, 2, strdup(p)); /* Info */ + + if (asprintf(&p, "%ju", (uintmax_t) fdisk_scround(cxt, start)) > 0) + tt_line_set_data(ln, 3, p); /* Start */ + if (asprintf(&p, "%ju", (uintmax_t) fdisk_scround(cxt, start + len) - 1) > 0) + tt_line_set_data(ln, 4, p); /* End */ + if (asprintf(&p, "%ju", (uintmax_t) len) > 0) + tt_line_set_data(ln, 5, p); /* Sectors*/ + if (asprintf(&p, "%2x", t->type) > 0) + tt_line_set_data(ln, 6, p); /* type ID */ + if (t->name) + tt_line_set_data(ln, 7, strdup(t->name)); /* type Name */ + fdisk_free_parttype(t); + used++; + } + + if (used) + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + if (rc) + return rc; + + /* + * Volumes + */ + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + tt_define_column(tb, _("#"), 3, TT_FL_RIGHT); + tt_define_column(tb, _("Name"), 0.2, 0); + tt_define_column(tb, _("Sector"), 2, TT_FL_RIGHT); + tt_define_column(tb, _("Size"), 9, TT_FL_RIGHT); + + for (i = 0, used = 0; i < SGI_MAXVOLUMES; i++) { + struct tt_line *ln; + uint32_t start = be32_to_cpu(sgilabel->volume[i].block_num), + len = be32_to_cpu(sgilabel->volume[i].num_bytes); + if (!len) + continue; + ln = tt_add_line(tb, NULL); + if (!ln) + continue; + if (asprintf(&p, "%zu:", i) > 0) + tt_line_set_data(ln, 0, p); /* # */ + if (*sgilabel->volume[i].name) + tt_line_set_data(ln, 1, + strndup((char *) sgilabel->volume[i].name, + sizeof(sgilabel->volume[i].name))); /* Name */ + if (asprintf(&p, "%ju", (uintmax_t) start) > 0) + tt_line_set_data(ln, 2, p); /* Sector */ + if (asprintf(&p, "%ju", (uintmax_t) len) > 0) + tt_line_set_data(ln, 3, p); /* Size */ + used++; + } + + if (used) + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + + fdisk_colon(cxt, _("Bootfile: %s"), sgilabel->boot_file); + + return rc; +} + +static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be32_to_cpu(sgilabel->partitions[i].first_block); +} + +static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be32_to_cpu(sgilabel->partitions[i].num_blocks); +} + +static int sgi_get_sysid(struct fdisk_context *cxt, int i) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be32_to_cpu(sgilabel->partitions[i].type); +} + +static int sgi_get_bootpartition(struct fdisk_context *cxt) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be16_to_cpu(sgilabel->root_part_num); +} + +static int sgi_get_swappartition(struct fdisk_context *cxt) +{ + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + return be16_to_cpu(sgilabel->swap_part_num); +} + +static unsigned int sgi_get_lastblock(struct fdisk_context *cxt) +{ + return cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders; +} + +static int sgi_check_bootfile(struct fdisk_context *cxt, const char *name) +{ + size_t sz; + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + + sz = strlen(name); + + if (sz < 3) { + /* "/a\n" is minimum */ + fdisk_warnx(cxt, _("Invalid bootfile! The bootfile must " + "be an absolute non-zero pathname, " + "e.g. \"/unix\" or \"/unix.save\".")); + return -EINVAL; + + } else if (sz > sizeof(sgilabel->boot_file)) { + fdisk_warnx(cxt, P_("Name of bootfile is too long: %zu byte maximum.", + "Name of bootfile is too long: %zu bytes maximum.", + sizeof(sgilabel->boot_file)), + sizeof(sgilabel->boot_file)); + return -EINVAL; + + } else if (*name != '/') { + fdisk_warnx(cxt, _("Bootfile must have a fully qualified pathname.")); + return -EINVAL; + } + + if (strncmp(name, (char *) sgilabel->boot_file, + sizeof(sgilabel->boot_file))) { + fdisk_warnx(cxt, _("Be aware that the bootfile is not checked " + "for existence. SGI's default is \"/unix\", " + "and for backup \"/unix.save\".")); + return 0; /* filename is correct and did change */ + } + + return 1; /* filename did not change */ +} + +int fdisk_sgi_set_bootfile(struct fdisk_context *cxt) +{ + int rc = 0; + size_t sz; + char *name = NULL; + struct sgi_disklabel *sgilabel = self_disklabel(cxt); + + fdisk_info(cxt, _("The current boot file is: %s"), sgilabel->boot_file); + + rc = fdisk_ask_string(cxt, _("Enter of the new boot file"), &name); + if (rc == 0) + rc = sgi_check_bootfile(cxt, name); + if (rc) { + if (rc == 1) + fdisk_info(cxt, _("Boot file is unchanged.")); + goto done; + } + + memset(sgilabel->boot_file, 0, sizeof(sgilabel->boot_file)); + sz = strlen(name); + + assert(sz <= sizeof(sgilabel->boot_file)); /* see sgi_check_bootfile() */ + + memcpy(sgilabel->boot_file, name, sz); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Bootfile has been changed to \"%s\"."), name); +done: + free(name); + return rc; +} + +static int sgi_write_disklabel(struct fdisk_context *cxt) +{ + struct sgi_disklabel *sgilabel; + struct sgi_info *info = NULL; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + sgilabel = self_disklabel(cxt); + sgilabel->csum = 0; + sgilabel->csum = cpu_to_be32(sgi_pt_checksum(sgilabel)); + + assert(sgi_pt_checksum(sgilabel) == 0); + + if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0) + goto err; + if (write_all(cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE)) + goto err; + if (!strncmp((char *) sgilabel->volume[0].name, "sgilabel", 8)) { + /* + * Keep this habit of first writing the "sgilabel". + * I never tested whether it works without. (AN 1998-10-02) + */ + int infostartblock + = be32_to_cpu(sgilabel->volume[0].block_num); + + if (lseek(cxt->dev_fd, (off_t) infostartblock * + DEFAULT_SECTOR_SIZE, SEEK_SET) < 0) + goto err; + info = sgi_new_info(); + if (!info) + goto err; + if (write_all(cxt->dev_fd, info, sizeof(*info))) + goto err; + } + + sgi_free_info(info); + return 0; +err: + sgi_free_info(info); + return -errno; +} + +static int compare_start(struct fdisk_context *cxt, + const void *x, const void *y) +{ + /* + * Sort according to start sectors and prefer the largest partition: + * entry zero is the entire-disk entry. + */ + unsigned int i = *(int *) x; + unsigned int j = *(int *) y; + unsigned int a = sgi_get_start_sector(cxt, i); + unsigned int b = sgi_get_start_sector(cxt, j); + unsigned int c = sgi_get_num_sectors(cxt, i); + unsigned int d = sgi_get_num_sectors(cxt, j); + + if (a == b) + return (d > c) ? 1 : (d == c) ? 0 : -1; + return (a > b) ? 1 : -1; +} + +static void generic_swap(void *a0, void *b0, int size) +{ + char *a = a0, *b = b0; + + for (; size > 0; --size, a++, b++) { + char t = *a; + *a = *b; + *b = t; + } +} + + +/* heap sort, based on Matt Mackall's linux kernel version */ +static void sort(void *base0, size_t num, size_t size, struct fdisk_context *cxt, + int (*cmp_func)(struct fdisk_context *, const void *, const void *)) +{ + /* pre-scale counters for performance */ + int i = (num/2 - 1) * size; + size_t n = num * size, c, r; + char *base = base0; + + /* heapify */ + for ( ; i >= 0; i -= size) { + for (r = i; r * 2 + size < n; r = c) { + c = r * 2 + size; + if (c < n - size && + cmp_func(cxt, base + c, base + c + size) < 0) + c += size; + if (cmp_func(cxt, base + r, base + c) >= 0) + break; + generic_swap(base + r, base + c, size); + } + } + + /* sort */ + for (i = n - size; i > 0; i -= size) { + generic_swap(base, base + i, size); + for (r = 0; r * 2 + size < (size_t) i; r = c) { + c = r * 2 + size; + if (c < i - size && + cmp_func(cxt, base + c, base + c + size) < 0) + c += size; + if (cmp_func(cxt, base + r, base + c) >= 0) + break; + generic_swap(base + r, base + c, size); + } + } +} + +static int verify_disklabel(struct fdisk_context *cxt, int verbose) +{ + int Index[SGI_MAXPARTITIONS]; /* list of valid partitions */ + int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */ + int entire = 0, i = 0; + unsigned int start = 0; + long long gap = 0; /* count unused blocks */ + unsigned int lastblock = sgi_get_lastblock(cxt); + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + clear_freelist(cxt); + memset(Index, 0, sizeof(Index)); + + for (i=0; i < SGI_MAXPARTITIONS; i++) { + if (sgi_get_num_sectors(cxt, i) != 0) { + Index[sortcount++] = i; + if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK + && entire++ == 1) { + if (verbose) + fdisk_info(cxt, _("More than one entire " + "disk entry present.")); + } + } + } + if (sortcount == 0) { + if (verbose) + fdisk_info(cxt, _("No partitions defined.")); + return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1; + } + + sort(Index, sortcount, sizeof(Index[0]), cxt, compare_start); + + if (sgi_get_sysid(cxt, Index[0]) == SGI_TYPE_ENTIRE_DISK) { + if (verbose && Index[0] != 10) + fdisk_info(cxt, _("IRIX likes it when partition 11 " + "covers the entire disk.")); + + if (verbose && sgi_get_start_sector(cxt, Index[0]) != 0) + fdisk_info(cxt, _("The entire disk partition should " + "start at block 0, not at block %d."), + sgi_get_start_sector(cxt, Index[0])); + + if (verbose && sgi_get_num_sectors(cxt, Index[0]) != lastblock) + DBG(LABEL, dbgprint( + "entire disk partition=%ds, but disk=%ds", + sgi_get_num_sectors(cxt, Index[0]), + lastblock)); + lastblock = sgi_get_num_sectors(cxt, Index[0]); + } else if (verbose) { + fdisk_info(cxt, _("Partition 11 should cover the entire disk.")); + DBG(LABEL, dbgprint("sysid=%d\tpartition=%d", + sgi_get_sysid(cxt, Index[0]), Index[0]+1)); + } + for (i=1, start=0; i<sortcount; i++) { + int cylsize = sgi_get_nsect(cxt) * sgi_get_ntrks(cxt); + + if (verbose && cylsize + && (sgi_get_start_sector(cxt, Index[i]) % cylsize) != 0) + DBG(LABEL, dbgprint("partition %d does not start on " + "cylinder boundary.", Index[i]+1)); + + if (verbose && cylsize + && sgi_get_num_sectors(cxt, Index[i]) % cylsize != 0) + DBG(LABEL, dbgprint("partition %d does not end on " + "cylinder boundary.", Index[i]+1)); + + /* We cannot handle several "entire disk" entries. */ + if (sgi_get_sysid(cxt, Index[i]) == SGI_TYPE_ENTIRE_DISK) + continue; + + if (start > sgi_get_start_sector(cxt, Index[i])) { + if (verbose) + fdisk_info(cxt, + P_("Partitions %d and %d overlap by %d sector.", + "Partitions %d and %d overlap by %d sectors.", + start - sgi_get_start_sector(cxt, Index[i])), + Index[i-1]+1, Index[i]+1, + start - sgi_get_start_sector(cxt, Index[i])); + if (gap > 0) gap = -gap; + if (gap == 0) gap = -1; + } + if (start < sgi_get_start_sector(cxt, Index[i])) { + if (verbose) + fdisk_info(cxt, + P_("Unused gap of %8u sector: sector %8u", + "Unused gap of %8u sectors: sectors %8u-%u", + sgi_get_start_sector(cxt, Index[i]) - start), + sgi_get_start_sector(cxt, Index[i]) - start, + start, sgi_get_start_sector(cxt, Index[i])-1); + gap += sgi_get_start_sector(cxt, Index[i]) - start; + add_to_freelist(cxt, start, + sgi_get_start_sector(cxt, Index[i])); + } + start = sgi_get_start_sector(cxt, Index[i]) + + sgi_get_num_sectors(cxt, Index[i]); + /* Align free space on cylinder boundary. */ + if (cylsize && start % cylsize) + start += cylsize - (start % cylsize); + + DBG(LABEL, dbgprint("%2d:%12d\t%12d\t%12d", Index[i], + sgi_get_start_sector(cxt, Index[i]), + sgi_get_num_sectors(cxt, Index[i]), + sgi_get_sysid(cxt, Index[i]))); + } + if (start < lastblock) { + if (verbose) + fdisk_info(cxt, P_("Unused gap of %8u sector: sector %8u", + "Unused gap of %8u sectors: sectors %8u-%u", + lastblock - start), + lastblock - start, start, lastblock-1); + gap += lastblock - start; + add_to_freelist(cxt, start, lastblock); + } + /* + * Done with arithmetics. Go for details now. + */ + if (verbose) { + if (sgi_get_bootpartition(cxt) < 0 + || !sgi_get_num_sectors(cxt, sgi_get_bootpartition(cxt))) + fdisk_info(cxt, _("The boot partition does not exist.")); + + if (sgi_get_swappartition(cxt) < 0 + || !sgi_get_num_sectors(cxt, sgi_get_swappartition(cxt))) + fdisk_info(cxt, _("The swap partition does not exist.")); + + else if (sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != SGI_TYPE_SWAP + && sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != MBR_LINUX_SWAP_PARTITION) + fdisk_info(cxt, _("The swap partition has no swap type.")); + + if (sgi_check_bootfile(cxt, "/unix")) + fdisk_info(cxt, _("You have chosen an unusual bootfile name.")); + } + + return (gap > 0) ? 1 : (gap == 0) ? 0 : -1; +} + +static int sgi_verify_disklabel(struct fdisk_context *cxt) +{ + return verify_disklabel(cxt, 1); +} + +static int sgi_gaps(struct fdisk_context *cxt) +{ + /* + * returned value is: + * = 0 : disk is properly filled to the rim + * < 0 : there is an overlap + * > 0 : there is still some vacant space + */ + return verify_disklabel(cxt, 0); +} + +/* Returns partition index of first entry marked as entire disk. */ +static int sgi_entire(struct fdisk_context *cxt) +{ + size_t i; + + for (i = 0; i < SGI_MAXPARTITIONS; i++) + if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK) + return i; + return -1; +} + +static int sgi_set_partition(struct fdisk_context *cxt, size_t i, + unsigned int start, unsigned int length, int sys) +{ + struct sgi_disklabel *sgilabel; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + sgilabel = self_disklabel(cxt); + sgilabel->partitions[i].type = cpu_to_be32(sys); + sgilabel->partitions[i].num_blocks = cpu_to_be32(length); + sgilabel->partitions[i].first_block = cpu_to_be32(start); + + fdisk_label_set_changed(cxt->label, 1); + + if (sgi_gaps(cxt) < 0) /* rebuild freelist */ + fdisk_warnx(cxt, _("Partition overlap on the disk.")); + if (length) { + struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sys); + fdisk_info_new_partition(cxt, i + 1, start, start + length, t); + } + + return 0; +} + +static void sgi_set_entire(struct fdisk_context *cxt) +{ + size_t n; + + for (n = 10; n < cxt->label->nparts_max; n++) { + if (!sgi_get_num_sectors(cxt, n)) { + sgi_set_partition(cxt, n, 0, sgi_get_lastblock(cxt), SGI_TYPE_ENTIRE_DISK); + break; + } + } +} + +static void sgi_set_volhdr(struct fdisk_context *cxt) +{ + size_t n; + + for (n = 8; n < cxt->label->nparts_max; n++) { + if (!sgi_get_num_sectors(cxt, n)) { + /* Choose same default volume header size as IRIX fx uses. */ + if (4096 < sgi_get_lastblock(cxt)) + sgi_set_partition(cxt, n, 0, 4096, SGI_TYPE_VOLHDR); + break; + } + } +} + +static int sgi_delete_partition(struct fdisk_context *cxt, size_t partnum) +{ + int rc; + + assert(cxt); + assert(cxt->label); + + if (partnum > cxt->label->nparts_max) + return -EINVAL; + + rc = sgi_set_partition(cxt, partnum, 0, 0, 0); + + cxt->label->nparts_cur = count_used_partitions(cxt); + + return rc; +} + +static int sgi_add_partition(struct fdisk_context *cxt, + size_t n, + struct fdisk_parttype *t) +{ + struct fdisk_sgi_label *sgi; + char mesg[256]; + unsigned int first = 0, last = 0; + struct fdisk_ask *ask; + int sys = t ? t->type : SGI_TYPE_XFS; + int rc; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + if (n == 10) + sys = SGI_TYPE_ENTIRE_DISK; + else if (n == 8) + sys = 0; + + sgi = self_label(cxt); + + if (sgi_get_num_sectors(cxt, n)) { + fdisk_warnx(cxt, _("Partition %zd is already defined. " + "Delete it before re-adding it."), n + 1); + return -EINVAL; + } + if (sgi_entire(cxt) == -1 && sys != SGI_TYPE_ENTIRE_DISK) { + fdisk_info(cxt, _("Attempting to generate entire disk entry automatically.")); + sgi_set_entire(cxt); + sgi_set_volhdr(cxt); + } + if (sgi_gaps(cxt) == 0 && sys != SGI_TYPE_ENTIRE_DISK) { + fdisk_warnx(cxt, _("The entire disk is already covered with partitions.")); + return -EINVAL; + } + if (sgi_gaps(cxt) < 0) { + fdisk_warnx(cxt, _("You got a partition overlap on the disk. Fix it first!")); + return -EINVAL; + } + + snprintf(mesg, sizeof(mesg), _("First %s"), + fdisk_context_get_unit(cxt, SINGULAR)); + for (;;) { + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + fdisk_ask_set_query(ask, mesg); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + + if (sys == SGI_TYPE_ENTIRE_DISK) { + last = sgi_get_lastblock(cxt); + fdisk_ask_number_set_low(ask, 0); /* minimal */ + fdisk_ask_number_set_default(ask, 0); /* default */ + fdisk_ask_number_set_high(ask, last - 1); /* maximal */ + } else { + first = sgi->freelist[0].first; + last = sgi->freelist[0].last; + fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */ + fdisk_ask_number_set_default(ask, fdisk_scround(cxt, first)); /* default */ + fdisk_ask_number_set_high(ask, fdisk_scround(cxt, last) - 1); /* maximal */ + } + rc = fdisk_do_ask(cxt, ask); + first = fdisk_ask_number_get_result(ask); + fdisk_free_ask(ask); + + if (rc) + return rc; + + if (first && sys == SGI_TYPE_ENTIRE_DISK) + fdisk_info(cxt, _("It is highly recommended that the " + "eleventh partition covers the entire " + "disk and is of type 'SGI volume'.")); + + if (fdisk_context_use_cylinders(cxt)) + first *= fdisk_context_get_units_per_sector(cxt); + /*else + first = first; * align to cylinder if you know how ... */ + if (!last) + last = is_in_freelist(cxt, first); + if (last == 0) + fdisk_warnx(cxt, _("You will get a partition overlap " + "on the disk. Fix it first!")); + else + break; + } + + snprintf(mesg, sizeof(mesg), + _("Last %s or +%s or +size{K,M,G,T,P}"), + fdisk_context_get_unit(cxt, SINGULAR), + fdisk_context_get_unit(cxt, PLURAL)); + + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + fdisk_ask_set_query(ask, mesg); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET); + + fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */ + fdisk_ask_number_set_default(ask, fdisk_scround(cxt, last) - 1);/* default */ + fdisk_ask_number_set_high(ask, fdisk_scround(cxt, last) - 1);/* maximal */ + fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first)); + + if (fdisk_context_use_cylinders(cxt)) + fdisk_ask_number_set_unit(ask, + cxt->sector_size * + fdisk_context_get_units_per_sector(cxt)); + else + fdisk_ask_number_set_unit(ask,cxt->sector_size); + + rc = fdisk_do_ask(cxt, ask); + last = fdisk_ask_number_get_result(ask) + 1; + + fdisk_free_ask(ask); + if (rc) + return rc; + + if (fdisk_context_use_cylinders(cxt)) + last *= fdisk_context_get_units_per_sector(cxt); + + if (sys == SGI_TYPE_ENTIRE_DISK + && (first != 0 || last != sgi_get_lastblock(cxt))) + fdisk_info(cxt, _("It is highly recommended that the " + "eleventh partition covers the entire " + "disk and is of type 'SGI volume'.")); + + sgi_set_partition(cxt, n, first, last - first, sys); + cxt->label->nparts_cur = count_used_partitions(cxt); + + return 0; +} + +static int sgi_create_disklabel(struct fdisk_context *cxt) +{ + struct fdisk_sgi_label *sgi; + struct sgi_disklabel *sgilabel; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + +#ifdef HDIO_GETGEO + if (cxt->geom.heads && cxt->geom.sectors) { + sector_t llsectors; + + if (blkdev_get_sectors(cxt->dev_fd, &llsectors) == 0) { + /* the get device size ioctl was successful */ + sector_t llcyls; + int sec_fac = cxt->sector_size / 512; + + llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac); + cxt->geom.cylinders = llcyls; + if (cxt->geom.cylinders != llcyls) /* truncated? */ + cxt->geom.cylinders = ~0; + } else { + /* otherwise print error and use truncated version */ + fdisk_warnx(cxt, + _("BLKGETSIZE ioctl failed on %s. " + "Using geometry cylinder value of %llu. " + "This value may be truncated for devices " + "> 33.8 GB."), cxt->dev_path, cxt->geom.cylinders); + } + } +#endif + fdisk_zeroize_firstsector(cxt); + sgi = (struct fdisk_sgi_label *) cxt->label; + sgi->header = (struct sgi_disklabel *) cxt->firstsector; + + sgilabel = sgi->header; + + sgilabel->magic = cpu_to_be32(SGI_LABEL_MAGIC); + sgilabel->root_part_num = cpu_to_be16(0); + sgilabel->swap_part_num = cpu_to_be16(1); + + /* sizeof(sgilabel->boot_file) = 16 > 6 */ + memset(sgilabel->boot_file, 0, 16); + strcpy((char *) sgilabel->boot_file, "/unix"); + + sgilabel->devparam.skew = (0); + sgilabel->devparam.gap1 = (0); + sgilabel->devparam.gap2 = (0); + sgilabel->devparam.sparecyl = (0); + sgilabel->devparam.pcylcount = cpu_to_be16(cxt->geom.cylinders); + sgilabel->devparam.head_vol0 = cpu_to_be16(0); + sgilabel->devparam.ntrks = cpu_to_be16(cxt->geom.heads); + /* tracks/cylinder (heads) */ + sgilabel->devparam.cmd_tag_queue_depth = (0); + sgilabel->devparam.unused0 = (0); + sgilabel->devparam.unused1 = cpu_to_be16(0); + sgilabel->devparam.nsect = cpu_to_be16(cxt->geom.sectors); + /* sectors/track */ + sgilabel->devparam.bytes = cpu_to_be16(cxt->sector_size); + sgilabel->devparam.ilfact = cpu_to_be16(1); + sgilabel->devparam.flags = cpu_to_be32( + SGI_DEVPARAM_TRACK_FWD + | SGI_DEVPARAM_IGNORE_ERRORS + | SGI_DEVPARAM_RESEEK); + sgilabel->devparam.datarate = cpu_to_be32(0); + sgilabel->devparam.retries_on_error = cpu_to_be32(1); + sgilabel->devparam.ms_per_word = cpu_to_be32(0); + sgilabel->devparam.xylogics_gap1 = cpu_to_be16(0); + sgilabel->devparam.xylogics_syncdelay = cpu_to_be16(0); + sgilabel->devparam.xylogics_readdelay = cpu_to_be16(0); + sgilabel->devparam.xylogics_gap2 = cpu_to_be16(0); + sgilabel->devparam.xylogics_readgate = cpu_to_be16(0); + sgilabel->devparam.xylogics_writecont = cpu_to_be16(0); + + memset(&(sgilabel->volume), 0, + sizeof(struct sgi_volume) * SGI_MAXVOLUMES); + memset(&(sgilabel->partitions), 0, + sizeof(struct sgi_partition) * SGI_MAXPARTITIONS); + cxt->label->nparts_max = SGI_MAXPARTITIONS; + sgi_set_entire(cxt); + sgi_set_volhdr(cxt); + + cxt->label->nparts_cur = count_used_partitions(cxt); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Created a new SGI disklabel.")); + return 0; +} + +static struct fdisk_parttype *sgi_get_parttype(struct fdisk_context *cxt, size_t n) +{ + struct fdisk_parttype *t; + + if (n >= cxt->label->nparts_max) + return NULL; + + t = fdisk_get_parttype_from_code(cxt, sgi_get_sysid(cxt, n)); + if (!t) + t = fdisk_new_unknown_parttype(sgi_get_sysid(cxt, n), NULL); + return t; +} + +static int sgi_set_parttype(struct fdisk_context *cxt, + size_t i, + struct fdisk_parttype *t) +{ + struct sgi_disklabel *sgilabel; + + if (i >= cxt->label->nparts_max || !t || t->type > UINT32_MAX) + return -EINVAL; + + if (sgi_get_num_sectors(cxt, i) == 0) /* caught already before, ... */ { + fdisk_warnx(cxt, _("Sorry, only for non-empty partitions you can change the tag.")); + return -EINVAL; + } + + if ((i == 10 && t->type != SGI_TYPE_ENTIRE_DISK) + || (i == 8 && t->type != 0)) + fdisk_info(cxt, _("Consider leaving partition 9 as volume header (0), " + "and partition 11 as entire volume (6), " + "as IRIX expects it.")); + + if (((t->type != SGI_TYPE_ENTIRE_DISK) && (t->type != SGI_TYPE_VOLHDR)) + && (sgi_get_start_sector(cxt, i) < 1)) { + int yes = 0; + fdisk_ask_yesno(cxt, + _("It is highly recommended that the partition at offset 0 " + "is of type \"SGI volhdr\", the IRIX system will rely on it to " + "retrieve from its directory standalone tools like sash and fx. " + "Only the \"SGI volume\" entire disk section may violate this. " + "Are you sure about tagging this partition differently?"), &yes); + if (!yes) + return 1; + } + + sgilabel = self_disklabel(cxt); + sgilabel->partitions[i].type = cpu_to_be32(t->type); + return 0; +} + + +static int sgi_get_partition_status( + struct fdisk_context *cxt, + size_t i, + int *status) +{ + assert(cxt); + assert(fdisk_is_disklabel(cxt, SGI)); + + if (!status || i >= cxt->label->nparts_max) + return -EINVAL; + + *status = FDISK_PARTSTAT_NONE; + + if (sgi_get_num_sectors(cxt, i)) + *status = FDISK_PARTSTAT_USED; + + return 0; +} + +static int sgi_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag) +{ + struct sgi_disklabel *sgilabel; + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SGI)); + + if (i >= cxt->label->nparts_max) + return -EINVAL; + + sgilabel = self_disklabel(cxt); + + switch (flag) { + case SGI_FLAG_BOOT: + sgilabel->root_part_num = + be16_to_cpu(sgilabel->root_part_num) == i ? + 0 : cpu_to_be16(i); + fdisk_label_set_changed(cxt->label, 1); + break; + case SGI_FLAG_SWAP: + sgilabel->swap_part_num = + be16_to_cpu(sgilabel->swap_part_num) == i ? + 0 : cpu_to_be16(i); + fdisk_label_set_changed(cxt->label, 1); + break; + default: + return 1; + } + + return 0; +} + +static const struct fdisk_label_operations sgi_operations = +{ + .probe = sgi_probe_label, + .write = sgi_write_disklabel, + .verify = sgi_verify_disklabel, + .create = sgi_create_disklabel, + .list = sgi_list_table, + .part_add = sgi_add_partition, + .part_delete = sgi_delete_partition, + .part_get_type = sgi_get_parttype, + .part_set_type = sgi_set_parttype, + + .part_get_status = sgi_get_partition_status, + .part_toggle_flag = sgi_toggle_partition_flag +}; + +/* Allocates an SGI label driver. */ +struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt) +{ + struct fdisk_label *lb; + struct fdisk_sgi_label *sgi; + + assert(cxt); + + sgi = calloc(1, sizeof(*sgi)); + if (!sgi) + return NULL; + + /* initialize generic part of the driver */ + lb = (struct fdisk_label *) sgi; + lb->name = "sgi"; + lb->id = FDISK_DISKLABEL_SGI; + lb->op = &sgi_operations; + lb->parttypes = sgi_parttypes; + lb->nparttypes = ARRAY_SIZE(sgi_parttypes); + + lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY; + + return lb; +} diff --git a/libfdisk/src/sun.c b/libfdisk/src/sun.c new file mode 100644 index 000000000..9f1d71524 --- /dev/null +++ b/libfdisk/src/sun.c @@ -0,0 +1,1024 @@ +/* + * Copyright (C) 2013 Karel Zak <kzak@redhat.com> + * + * Based on original code from fdisk: + * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996 + * Merged with fdisk for other architectures, aeb, June 1998. + * Arnaldo Carvalho de Melo <acme@conectiva.com.br> Mar 1999, Internationalization + */ +#include <stdio.h> /* stderr */ +#include <stdlib.h> /* qsort */ +#include <string.h> /* strstr */ +#include <unistd.h> /* write */ +#include <sys/ioctl.h> /* ioctl */ + +#include "nls.h" +#include "blkdev.h" +#include "bitops.h" + +#include "fdiskP.h" +#include "pt-sun.h" +#include "all-io.h" + +/* + * in-memory fdisk SUN stuff + */ +struct fdisk_sun_label { + struct fdisk_label head; /* generic part */ + struct sun_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */ +}; + +static struct fdisk_parttype sun_parttypes[] = { + {SUN_TAG_UNASSIGNED, N_("Unassigned")}, + {SUN_TAG_BOOT, N_("Boot")}, + {SUN_TAG_ROOT, N_("SunOS root")}, + {SUN_TAG_SWAP, N_("SunOS swap")}, + {SUN_TAG_USR, N_("SunOS usr")}, + {SUN_TAG_WHOLEDISK, N_("Whole disk")}, + {SUN_TAG_STAND, N_("SunOS stand")}, + {SUN_TAG_VAR, N_("SunOS var")}, + {SUN_TAG_HOME, N_("SunOS home")}, + {SUN_TAG_ALTSCTR, N_("SunOS alt sectors")}, + {SUN_TAG_CACHE, N_("SunOS cachefs")}, + {SUN_TAG_RESERVED, N_("SunOS reserved")}, + {SUN_TAG_LINUX_SWAP, N_("Linux swap")}, + {SUN_TAG_LINUX_NATIVE, N_("Linux native")}, + {SUN_TAG_LINUX_LVM, N_("Linux LVM")}, + {SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")}, + { 0, NULL } +}; + +/* return poiter buffer with on-disk data */ +static inline struct sun_disklabel *self_disklabel(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + return ((struct fdisk_sun_label *) cxt->label)->header; +} + +/* return in-memory sun fdisk data */ +static inline struct fdisk_sun_label *self_label(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + return (struct fdisk_sun_label *) cxt->label; +} + +static void set_sun_partition(struct fdisk_context *cxt, size_t i, + uint32_t start,uint32_t stop, uint16_t sysid) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid); + + sunlabel->vtoc.infos[i].id = cpu_to_be16(sysid); + sunlabel->vtoc.infos[i].flags = cpu_to_be16(0); + sunlabel->partitions[i].start_cylinder = + cpu_to_be32(start / (cxt->geom.heads * cxt->geom.sectors)); + sunlabel->partitions[i].num_sectors = cpu_to_be32(stop - start); + fdisk_label_set_changed(cxt->label, 1); + + fdisk_info_new_partition(cxt, i + 1, start, stop, t); +} + +static size_t count_used_partitions(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + size_t ct = 0, i; + + assert(sunlabel); + + for (i = 0; i < cxt->label->nparts_max; i++) { + if (sunlabel->partitions[i].num_sectors) + ct++; + } + return ct; +} + +static int sun_probe_label(struct fdisk_context *cxt) +{ + struct fdisk_sun_label *sun; + struct sun_disklabel *sunlabel; + unsigned short *ush; + int csum; + int need_fixing = 0; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + /* map first sector to header */ + sun = (struct fdisk_sun_label *) cxt->label; + sun->header = (struct sun_disklabel *) cxt->firstsector; + sunlabel = sun->header; + + if (be16_to_cpu(sunlabel->magic) != SUN_LABEL_MAGIC) { + sun->header = NULL; + return 0; /* failed */ + } + + ush = ((unsigned short *) (sunlabel + 1)) - 1; + for (csum = 0; ush >= (unsigned short *)sunlabel;) + csum ^= *ush--; + + if (csum) { + fdisk_warnx(cxt, _("Detected sun disklabel with wrong checksum. " + "Probably you'll have to set all the values, " + "e.g. heads, sectors, cylinders and partitions " + "or force a fresh label (s command in main menu)")); + return 1; + } + + cxt->label->nparts_max = SUN_MAXPARTITIONS; + cxt->geom.heads = be16_to_cpu(sunlabel->nhead); + cxt->geom.cylinders = be16_to_cpu(sunlabel->ncyl); + cxt->geom.sectors = be16_to_cpu(sunlabel->nsect); + + if (be32_to_cpu(sunlabel->vtoc.version) != SUN_VTOC_VERSION) { + fdisk_warnx(cxt, _("Detected sun disklabel with wrong version [%d]."), + be32_to_cpu(sunlabel->vtoc.version)); + need_fixing = 1; + } + if (be32_to_cpu(sunlabel->vtoc.sanity) != SUN_VTOC_SANITY) { + fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.sanity [0x%08x]."), + be32_to_cpu(sunlabel->vtoc.sanity)); + need_fixing = 1; + } + if (be16_to_cpu(sunlabel->vtoc.nparts) != SUN_MAXPARTITIONS) { + fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.nparts [%u]."), + be16_to_cpu(sunlabel->vtoc.nparts)); + need_fixing = 1; + } + if (need_fixing) { + fdisk_warnx(cxt, _("Warning: Wrong values need to be fixed up and " + "will be corrected by w(rite)")); + + sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION); + sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY); + sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS); + + ush = (unsigned short *)sunlabel; + csum = 0; + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + + fdisk_label_set_changed(cxt->label, 1); + } + + cxt->label->nparts_cur = count_used_partitions(cxt); + + return 1; +} + +static void ask_geom(struct fdisk_context *cxt) +{ + uintmax_t res; + + assert(cxt); + + if (fdisk_ask_number(cxt, 1, 1, 1024, _("Heads"), &res) == 0) + cxt->geom.heads = res; + if (fdisk_ask_number(cxt, 1, 1, 1024, _("Sectors/track"), &res) == 0) + cxt->geom.sectors = res; + if (fdisk_ask_number(cxt, 1, 1, USHRT_MAX, _("Cylinders"), &res) == 0) + cxt->geom.cylinders = res; +} + +static int sun_create_disklabel(struct fdisk_context *cxt) +{ + unsigned int ndiv; + struct fdisk_sun_label *sun; /* libfdisk sun handler */ + struct sun_disklabel *sunlabel; /* on disk data */ + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + /* map first sector to header */ + fdisk_zeroize_firstsector(cxt); + sun = (struct fdisk_sun_label *) cxt->label; + sun->header = (struct sun_disklabel *) cxt->firstsector; + + sunlabel = sun->header; + + cxt->label->nparts_max = SUN_MAXPARTITIONS; + + sunlabel->magic = cpu_to_be16(SUN_LABEL_MAGIC); + sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION); + sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY); + sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS); + +#ifdef HDIO_GETGEO + if (cxt->geom.heads && cxt->geom.sectors) { + sector_t llsectors; + + if (blkdev_get_sectors(cxt->dev_fd, &llsectors) == 0) { + int sec_fac = cxt->sector_size / 512; + sector_t llcyls; + + llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac); + cxt->geom.cylinders = llcyls; + if (cxt->geom.cylinders != llcyls) + cxt->geom.cylinders = ~0; + } else { + fdisk_warnx(cxt, + _("BLKGETSIZE ioctl failed on %s. " + "Using geometry cylinder value of %llu. " + "This value may be truncated for devices " + "> 33.8 GB."), + cxt->dev_path, cxt->geom.cylinders); + } + } else +#endif + ask_geom(cxt); + + sunlabel->acyl = cpu_to_be16(0); + sunlabel->pcyl = cpu_to_be16(cxt->geom.cylinders); + sunlabel->rpm = cpu_to_be16(5400); + sunlabel->intrlv = cpu_to_be16(1); + sunlabel->apc = cpu_to_be16(0); + + sunlabel->nhead = cpu_to_be16(cxt->geom.heads); + sunlabel->nsect = cpu_to_be16(cxt->geom.sectors); + sunlabel->ncyl = cpu_to_be16(cxt->geom.cylinders); + + snprintf((char *) sunlabel->label_id, sizeof(sunlabel->label_id), + "Linux cyl %llu alt %u hd %u sec %llu", + cxt->geom.cylinders, be16_to_cpu(sunlabel->acyl), + cxt->geom.heads, cxt->geom.sectors); + + if (cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors >= 150 * 2048) { + ndiv = cxt->geom.cylinders - (50 * 2048 / (cxt->geom.heads * cxt->geom.sectors)); /* 50M swap */ + } else + ndiv = cxt->geom.cylinders * 2 / 3; + + set_sun_partition(cxt, 0, 0, ndiv * cxt->geom.heads * cxt->geom.sectors, + SUN_TAG_LINUX_NATIVE); + set_sun_partition(cxt, 1, ndiv * cxt->geom.heads * cxt->geom.sectors, + cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors, + SUN_TAG_LINUX_SWAP); + sunlabel->vtoc.infos[1].flags |= cpu_to_be16(SUN_FLAG_UNMNT); + + set_sun_partition(cxt, 2, 0, + cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors, + SUN_TAG_WHOLEDISK); + + { + unsigned short *ush = (unsigned short *)sunlabel; + unsigned short csum = 0; + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + } + + fdisk_label_set_changed(cxt->label, 1); + cxt->label->nparts_cur = count_used_partitions(cxt); + + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Created a new Sun disklabel.")); + return 0; +} + +static int sun_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag) +{ + struct sun_disklabel *sunlabel; + struct sun_info *p; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + if (i >= cxt->label->nparts_max) + return -EINVAL; + + sunlabel = self_disklabel(cxt); + p = &sunlabel->vtoc.infos[i]; + + switch (flag) { + case SUN_FLAG_UNMNT: + p->flags ^= cpu_to_be16(SUN_FLAG_UNMNT); + fdisk_label_set_changed(cxt->label, 1); + break; + case SUN_FLAG_RONLY: + p->flags ^= cpu_to_be16(SUN_FLAG_RONLY); + fdisk_label_set_changed(cxt->label, 1); + break; + default: + return 1; + } + + return 0; +} + +static void fetch_sun(struct fdisk_context *cxt, + uint32_t *starts, + uint32_t *lens, + uint32_t *start, + uint32_t *stop) +{ + struct sun_disklabel *sunlabel; + int continuous = 1; + size_t i; + + assert(cxt); + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + sunlabel = self_disklabel(cxt); + + *start = 0; + *stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors; + + for (i = 0; i < cxt->label->nparts_max; i++) { + struct sun_partition *part = &sunlabel->partitions[i]; + struct sun_info *info = &sunlabel->vtoc.infos[i]; + + if (part->num_sectors && + be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED && + be16_to_cpu(info->id) != SUN_TAG_WHOLEDISK) { + starts[i] = be32_to_cpu(part->start_cylinder) * + cxt->geom.heads * cxt->geom.sectors; + lens[i] = be32_to_cpu(part->num_sectors); + if (continuous) { + if (starts[i] == *start) + *start += lens[i]; + else if (starts[i] + lens[i] >= *stop) + *stop = starts[i]; + else + continuous = 0; + /* There will be probably more gaps + than one, so lets check afterwards */ + } + } else { + starts[i] = 0; + lens[i] = 0; + } + } +} + +#ifdef HAVE_QSORT_R +static int verify_sun_cmp(int *a, int *b, void *data) +{ + unsigned int *verify_sun_starts = (unsigned int *) data; + + if (*a == -1) + return 1; + if (*b == -1) + return -1; + if (verify_sun_starts[*a] > verify_sun_starts[*b]) + return 1; + return -1; +} +#endif + +static int sun_verify_disklabel(struct fdisk_context *cxt) +{ + uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS], start, stop; + uint32_t i,j,k,starto,endo; +#ifdef HAVE_QSORT_R + int array[SUN_MAXPARTITIONS]; + unsigned int *verify_sun_starts; +#endif + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + fetch_sun(cxt, starts, lens, &start, &stop); + + for (k = 0; k < 7; k++) { + for (i = 0; i < SUN_MAXPARTITIONS; i++) { + if (k && (lens[i] % (cxt->geom.heads * cxt->geom.sectors))) { + fdisk_warnx(cxt, _("Partition %d doesn't end on cylinder boundary."), i+1); + } + if (lens[i]) { + for (j = 0; j < i; j++) + if (lens[j]) { + if (starts[j] == starts[i]+lens[i]) { + starts[j] = starts[i]; lens[j] += lens[i]; + lens[i] = 0; + } else if (starts[i] == starts[j]+lens[j]){ + lens[j] += lens[i]; + lens[i] = 0; + } else if (!k) { + if (starts[i] < starts[j]+lens[j] && + starts[j] < starts[i]+lens[i]) { + starto = starts[i]; + if (starts[j] > starto) + starto = starts[j]; + endo = starts[i]+lens[i]; + if (starts[j]+lens[j] < endo) + endo = starts[j]+lens[j]; + fdisk_warnx(cxt, _("Partition %d overlaps with others in " + "sectors %d-%d."), i+1, starto, endo); + } + } + } + } + } + } + +#ifdef HAVE_QSORT_R + for (i = 0; i < SUN_MAXPARTITIONS; i++) { + if (lens[i]) + array[i] = i; + else + array[i] = -1; + } + verify_sun_starts = starts; + + qsort_r(array,ARRAY_SIZE(array),sizeof(array[0]), + (int (*)(const void *,const void *,void *)) verify_sun_cmp, + verify_sun_starts); + + if (array[0] == -1) { + fdisk_info(cxt, _("No partitions defined.")); + return 0; + } + stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors; + if (starts[array[0]]) + fdisk_warnx(cxt, _("Unused gap - sectors 0-%d."), starts[array[0]]); + for (i = 0; i < 7 && array[i+1] != -1; i++) { + fdisk_warnx(cxt, _("Unused gap - sectors %d-%d."), + (starts[array[i]] + lens[array[i]]), + starts[array[i+1]]); + } + start = (starts[array[i]] + lens[array[i]]); + if (start < stop) + fdisk_warnx(cxt, _("Unused gap - sectors %d-%d."), start, stop); +#endif + return 0; +} + +static int sun_add_partition( + struct fdisk_context *cxt, + size_t n, + struct fdisk_parttype *t) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS]; + struct sun_partition *part = &sunlabel->partitions[n]; + struct sun_info *info = &sunlabel->vtoc.infos[n]; + uint32_t start, stop, stop2; + int whole_disk = 0, sys = t ? t->type : SUN_TAG_LINUX_NATIVE; + struct fdisk_ask *ask; + int rc; + + char mesg[256]; + size_t i; + unsigned int first, last; + + if (part->num_sectors && be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED) { + fdisk_info(cxt, _("Partition %zd is already defined. Delete " + "it before re-adding it."), n + 1); + return -EINVAL; + } + + fetch_sun(cxt, starts, lens, &start, &stop); + + if (stop <= start) { + if (n == 2) + whole_disk = 1; + else { + fdisk_info(cxt, _("Other partitions already cover the " + "whole disk. Delete some/shrink them before retry.")); + return -EINVAL; + } + } + snprintf(mesg, sizeof(mesg), _("First %s"), + fdisk_context_get_unit(cxt, SINGULAR)); + for (;;) { + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + fdisk_ask_set_query(ask, mesg); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + + if (whole_disk) { + fdisk_ask_number_set_low(ask, 0); /* minimal */ + fdisk_ask_number_set_default(ask, 0); /* default */ + fdisk_ask_number_set_high(ask, 0); /* maximal */ + } else { + fdisk_ask_number_set_low(ask, fdisk_scround(cxt, start)); /* minimal */ + fdisk_ask_number_set_default(ask, fdisk_scround(cxt, start)); /* default */ + fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop)); /* maximal */ + } + rc = fdisk_do_ask(cxt, ask); + first = fdisk_ask_number_get_result(ask); + fdisk_free_ask(ask); + + if (rc) + return rc; + + if (fdisk_context_use_cylinders(cxt)) + first *= fdisk_context_get_units_per_sector(cxt); + else { + /* Starting sector has to be properly aligned */ + int cs = cxt->geom.heads * cxt->geom.sectors; + int x = first % cs; + + if (x) { + fdisk_info(cxt, _("Aligning the first sector from %u to %u " + "to be on cylinder boundary."), + first, first + cs - x); + first += cs - x; + } + } + if (n == 2 && first != 0) + fdisk_warnx(cxt, _("It is highly recommended that the " + "third partition covers the whole disk " + "and is of type `Whole disk'")); + /* ewt asks to add: "don't start a partition at cyl 0" + However, edmundo@rano.demon.co.uk writes: + "In addition to having a Sun partition table, to be able to + boot from the disc, the first partition, /dev/sdX1, must + start at cylinder 0. This means that /dev/sdX1 contains + the partition table and the boot block, as these are the + first two sectors of the disc. Therefore you must be + careful what you use /dev/sdX1 for. In particular, you must + not use a partition starting at cylinder 0 for Linux swap, + as that would overwrite the partition table and the boot + block. You may, however, use such a partition for a UFS + or EXT2 file system, as these file systems leave the first + 1024 bytes undisturbed. */ + /* On the other hand, one should not use partitions + starting at block 0 in an md, or the label will + be trashed. */ + for (i = 0; i < cxt->label->nparts_max; i++) + if (lens[i] && starts[i] <= first + && starts[i] + lens[i] > first) + break; + if (i < cxt->label->nparts_max && !whole_disk) { + if (n == 2 && !first) { + whole_disk = 1; + break; + } + fdisk_warnx(cxt, _("Sector %d is already allocated"), first); + } else + break; + } + stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors; /* ancient */ + stop2 = stop; + for (i = 0; i < cxt->label->nparts_max; i++) { + if (starts[i] > first && starts[i] < stop) + stop = starts[i]; + } + snprintf(mesg, sizeof(mesg), + _("Last %s or +%s or +size{K,M,G,T,P}"), + fdisk_context_get_unit(cxt, SINGULAR), + fdisk_context_get_unit(cxt, PLURAL)); + + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + fdisk_ask_set_query(ask, mesg); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET); + + if (whole_disk) { + fdisk_ask_number_set_low(ask, fdisk_scround(cxt, stop2)); /* minimal */ + fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2)); /* default */ + fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop2)); /* maximal */ + fdisk_ask_number_set_base(ask, 0); + } else if (n == 2 && !first) { + fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */ + fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2)); /* default */ + fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop2)); /* maximal */ + fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first)); + } else { + fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */ + fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop)); /* default */ + fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop)); /* maximal */ + fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first)); + } + + if (fdisk_context_use_cylinders(cxt)) + fdisk_ask_number_set_unit(ask, + cxt->sector_size * + fdisk_context_get_units_per_sector(cxt)); + else + fdisk_ask_number_set_unit(ask, cxt->sector_size); + + rc = fdisk_do_ask(cxt, ask); + last = fdisk_ask_number_get_result(ask); + + fdisk_free_ask(ask); + if (rc) + return rc; + + if (fdisk_context_use_cylinders(cxt)) + last *= fdisk_context_get_units_per_sector(cxt); + + if (n == 2 && !first) { + if (last >= stop2) { + whole_disk = 1; + last = stop2; + } else if (last > stop) { + fdisk_warnx(cxt, + _("You haven't covered the whole disk with the 3rd partition, but your value\n" + "%d %s covers some other partition. Your entry has been changed\n" + "to %d %s"), + (int) fdisk_scround(cxt, last), fdisk_context_get_unit(cxt, SINGULAR), + (int) fdisk_scround(cxt, stop), fdisk_context_get_unit(cxt, SINGULAR)); + last = stop; + } + } else if (!whole_disk && last > stop) + last = stop; + + if (whole_disk) + sys = SUN_TAG_WHOLEDISK; + + set_sun_partition(cxt, n, first, last, sys); + cxt->label->nparts_cur = count_used_partitions(cxt); + return 0; +} + +static int sun_delete_partition(struct fdisk_context *cxt, + size_t partnum) +{ + struct sun_disklabel *sunlabel; + struct sun_partition *part; + struct sun_info *info; + unsigned int nsec; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + sunlabel = self_disklabel(cxt); + part = &sunlabel->partitions[partnum]; + info = &sunlabel->vtoc.infos[partnum]; + + if (partnum == 2 && + be16_to_cpu(info->id) == SUN_TAG_WHOLEDISK && + !part->start_cylinder && + (nsec = be32_to_cpu(part->num_sectors)) + == cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders) + fdisk_info(cxt, _("If you want to maintain SunOS/Solaris compatibility, " + "consider leaving this " + "partition as Whole disk (5), starting at 0, with %u " + "sectors"), nsec); + info->id = cpu_to_be16(SUN_TAG_UNASSIGNED); + part->num_sectors = 0; + cxt->label->nparts_cur = count_used_partitions(cxt); + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + + +static int sun_list_disklabel(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel; + struct tt *tb = NULL; + size_t i; + int rc; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + sunlabel = self_disklabel(cxt); + + if (fdisk_context_display_details(cxt)) { + fdisk_colon(cxt, + _("Label geometry: %d rpm, %d alternate and %d physical cylinders,\n" + " %d extra sects/cyl, interleave %d:1"), + be16_to_cpu(sunlabel->rpm), + be16_to_cpu(sunlabel->acyl), + be16_to_cpu(sunlabel->pcyl), + be16_to_cpu(sunlabel->apc), + be16_to_cpu(sunlabel->intrlv)); + fdisk_colon(cxt, _("Label ID: %s"), sunlabel->label_id); + fdisk_colon(cxt, _("Volume ID: %s"), + *sunlabel->vtoc.volume_id ? sunlabel->vtoc.volume_id : _("<none>")); + } + + tb = tt_new_table(TT_FL_FREEDATA); + if (!tb) + return -ENOMEM; + + tt_define_column(tb, _("Device"), 0.2, 0); + tt_define_column(tb, _("Flag"), 2, TT_FL_RIGHT); + tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT); + tt_define_column(tb, _("End"), 9, TT_FL_RIGHT); + /* TRANSLATORS: keep one blank space behind 'Blocks' */ + tt_define_column(tb, _("Blocks "), 9, TT_FL_RIGHT); + tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT); + tt_define_column(tb, _("System"), 0.2, TT_FL_TRUNC); + + for (i = 0 ; i < cxt->label->nparts_max; i++) { + struct sun_partition *part = &sunlabel->partitions[i]; + uint16_t flags = be16_to_cpu(sunlabel->vtoc.infos[i].flags); + uint32_t start, len; + struct fdisk_parttype *t; + struct tt_line *ln; + char *p; + + if (!part->num_sectors) + continue; + ln = tt_add_line(tb, NULL); + if (!ln) + continue; + + start = be32_to_cpu(part->start_cylinder) + * cxt->geom.heads + * cxt->geom.sectors; + + len = be32_to_cpu(part->num_sectors); + t = fdisk_get_partition_type(cxt, i); + + p = fdisk_partname(cxt->dev_path, i + 1); + if (p) + tt_line_set_data(ln, 0, p); /* devname */ + if ((flags & SUN_FLAG_UNMNT || flags & SUN_FLAG_RONLY) + && asprintf(&p, "%c%c", + flags & SUN_FLAG_UNMNT ? 'u' : ' ', + flags & SUN_FLAG_RONLY ? 'r' : ' ') > 0) + tt_line_set_data(ln, 1, p); /* flags */ + if (asprintf(&p, "%ju", (uintmax_t) fdisk_scround(cxt, start)) > 0) + tt_line_set_data(ln, 2, p); /* start */ + if (asprintf(&p, "%ju", (uintmax_t) fdisk_scround(cxt, start + len - 1)) > 0) + tt_line_set_data(ln, 3, p); /* end */ + if (asprintf(&p, "%lu%c", + (unsigned long) len / 2, + len & 1 ? '+' : ' ') > 0) + tt_line_set_data(ln, 4, p); /* blocks + flag */ + if (asprintf(&p, "%2x", t->type) > 0) + tt_line_set_data(ln, 5, p); /* type ID */ + if (t->name) + tt_line_set_data(ln, 6, strdup(t->name)); /* type Name */ + + fdisk_free_parttype(t); + } + + rc = fdisk_print_table(cxt, tb); + tt_free_table(tb); + + return rc; +} + + +int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + uintmax_t res; + int rc = fdisk_ask_number(cxt, 0, /* low */ + be16_to_cpu(sunlabel->acyl), /* default */ + 65535, /* high */ + _("Number of alternate cylinders"), /* query */ + &res); /* result */ + if (rc) + return rc; + + sunlabel->acyl = cpu_to_be16(res); + return 0; +} + +int fdisk_sun_set_xcyl(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + uintmax_t res; + int rc = fdisk_ask_number(cxt, 0, /* low */ + be16_to_cpu(sunlabel->apc), /* default */ + cxt->geom.sectors, /* high */ + _("Extra sectors per cylinder"), /* query */ + &res); /* result */ + if (rc) + return rc; + sunlabel->apc = cpu_to_be16(res); + return 0; +} + +int fdisk_sun_set_ilfact(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + uintmax_t res; + int rc = fdisk_ask_number(cxt, 1, /* low */ + be16_to_cpu(sunlabel->intrlv), /* default */ + 32, /* high */ + _("Interleave factor"), /* query */ + &res); /* result */ + if (rc) + return rc; + sunlabel->intrlv = cpu_to_be16(res); + return 0; +} + +int fdisk_sun_set_rspeed(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + uintmax_t res; + int rc = fdisk_ask_number(cxt, 1, /* low */ + be16_to_cpu(sunlabel->rpm), /* default */ + USHRT_MAX, /* high */ + _("Rotation speed (rpm)"), /* query */ + &res); /* result */ + if (rc) + return rc; + sunlabel->rpm = cpu_to_be16(res); + return 0; +} + +int fdisk_sun_set_pcylcount(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + uintmax_t res; + int rc = fdisk_ask_number(cxt, 0, /* low */ + be16_to_cpu(sunlabel->pcyl), /* default */ + USHRT_MAX, /* high */ + _("Number of physical cylinders"), /* query */ + &res); /* result */ + if (!rc) + return rc; + sunlabel->pcyl = cpu_to_be16(res); + return 0; +} + +static int sun_write_disklabel(struct fdisk_context *cxt) +{ + struct sun_disklabel *sunlabel; + unsigned short *ush; + unsigned short csum = 0; + const size_t sz = sizeof(struct sun_disklabel); + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + sunlabel = self_disklabel(cxt); + + /* Maybe geometry has been modified */ + sunlabel->nhead = cpu_to_be16(cxt->geom.heads); + sunlabel->nsect = cpu_to_be16(cxt->geom.sectors); + + if (cxt->geom.cylinders != be16_to_cpu(sunlabel->ncyl)) + sunlabel->ncyl = cpu_to_be16( cxt->geom.cylinders + - be16_to_cpu(sunlabel->acyl) ); + + ush = (unsigned short *) sunlabel; + + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0) + return -errno; + if (write_all(cxt->dev_fd, sunlabel, sz) != 0) + return -errno; + + return 0; +} + +static struct fdisk_parttype *sun_get_parttype( + struct fdisk_context *cxt, + size_t n) +{ + struct sun_disklabel *sunlabel = self_disklabel(cxt); + struct fdisk_parttype *t; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + if (n >= cxt->label->nparts_max) + return NULL; + + t = fdisk_get_parttype_from_code(cxt, be16_to_cpu(sunlabel->vtoc.infos[n].id)); + if (!t) + t = fdisk_new_unknown_parttype(be16_to_cpu(sunlabel->vtoc.infos[n].id), NULL); + return t; +} + +static int sun_set_parttype( + struct fdisk_context *cxt, + size_t i, + struct fdisk_parttype *t) +{ + struct sun_disklabel *sunlabel; + struct sun_partition *part; + struct sun_info *info; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + sunlabel = self_disklabel(cxt); + + if (i >= cxt->label->nparts_max || !t || t->type > UINT16_MAX) + return -EINVAL; + + if (i == 2 && t->type != SUN_TAG_WHOLEDISK) + fdisk_info(cxt, _("Consider leaving partition 3 as Whole disk (5),\n" + "as SunOS/Solaris expects it and even Linux likes it.\n")); + + part = &sunlabel->partitions[i]; + info = &sunlabel->vtoc.infos[i]; + + if (t->type == SUN_TAG_LINUX_SWAP && !part->start_cylinder) { + int yes, rc; + rc = fdisk_ask_yesno(cxt, + _("It is highly recommended that the partition at offset 0\n" + "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n" + "there may destroy your partition table and bootblock.\n" + "Are you sure you want to tag the partition as Linux swap?"), &yes); + if (rc) + return rc; + if (!yes) + return 1; + } + + switch (t->type) { + case SUN_TAG_SWAP: + case SUN_TAG_LINUX_SWAP: + /* swaps are not mountable by default */ + info->flags |= cpu_to_be16(SUN_FLAG_UNMNT); + break; + default: + /* assume other types are mountable; + user can change it anyway */ + info->flags &= ~cpu_to_be16(SUN_FLAG_UNMNT); + break; + } + info->id = cpu_to_be16(t->type); + return 0; +} + + +static int sun_reset_alignment(struct fdisk_context *cxt __attribute__((__unused__))) +{ + return 0; +} + + +static int sun_get_partition_status( + struct fdisk_context *cxt, + size_t i, + int *status) +{ + struct sun_disklabel *sunlabel; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_disklabel(cxt, SUN)); + + if (!status || i >= cxt->label->nparts_max) + return -EINVAL; + + sunlabel = self_disklabel(cxt); + *status = FDISK_PARTSTAT_NONE; + + if (sunlabel->partitions[i].num_sectors) + *status = FDISK_PARTSTAT_USED; + + return 0; +} + + +const struct fdisk_label_operations sun_operations = +{ + .probe = sun_probe_label, + .write = sun_write_disklabel, + .verify = sun_verify_disklabel, + .create = sun_create_disklabel, + .list = sun_list_disklabel, + .part_add = sun_add_partition, + .part_delete = sun_delete_partition, + .part_get_type = sun_get_parttype, + .part_set_type = sun_set_parttype, + + .part_get_status = sun_get_partition_status, + .part_toggle_flag = sun_toggle_partition_flag, + + .reset_alignment = sun_reset_alignment, +}; + +/* + * allocates SUN label driver + */ +struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt) +{ + struct fdisk_label *lb; + struct fdisk_sun_label *sun; + + assert(cxt); + + sun = calloc(1, sizeof(*sun)); + if (!sun) + return NULL; + + /* initialize generic part of the driver */ + lb = (struct fdisk_label *) sun; + lb->name = "sun"; + lb->id = FDISK_DISKLABEL_SUN; + lb->op = &sun_operations; + lb->parttypes = sun_parttypes; + lb->nparttypes = ARRAY_SIZE(sun_parttypes); + + lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY; + + return lb; +} diff --git a/libfdisk/src/utils.c b/libfdisk/src/utils.c index 1360e8467..20b8cf7bb 100644 --- a/libfdisk/src/utils.c +++ b/libfdisk/src/utils.c @@ -1,5 +1,9 @@ #include "fdiskP.h" +#include "pathnames.h" + +#include <ctype.h> + /* * Zeros in-memory first sector buffer @@ -41,3 +45,73 @@ int fdisk_read_firstsector(struct fdisk_context *cxt) return 0; } + +/* + * Return allocated buffer with partition name + */ +char *fdisk_partname(const char *dev, size_t partno) +{ + char *res = NULL; + const char *p = ""; + int w = 0; + + if (!dev || !*dev) { + if (asprintf(&res, "%zd", partno) > 0) + return res; + return NULL; + } + + w = strlen(dev); + if (isdigit(dev[w - 1])) + p = "p"; + + /* devfs kludge - note: fdisk partition names are not supposed + to equal kernel names, so there is no reason to do this */ + if (strcmp(dev + w - 4, "disc") == 0) { + w -= 4; + p = "part"; + } + + /* udev names partitions by appending -partN + e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */ + if ((strncmp(dev, _PATH_DEV_BYID, sizeof(_PATH_DEV_BYID) - 1) == 0) || + strncmp(dev, _PATH_DEV_BYPATH, sizeof(_PATH_DEV_BYPATH) - 1) == 0) { + p = "-part"; + } + + if (asprintf(&res, "%.*s%s%zu", w, dev, p, partno) > 0) + return res; + + return NULL; +} + +#ifdef TEST_PROGRAM +struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; } + +int test_partnames(struct fdisk_test *ts, int argc, char *argv[]) +{ + size_t i; + const char *disk = argv[1]; + + for (i = 0; i < 5; i++) { + char *p = fdisk_partname(disk, i + 1); + if (p) + printf("%zu: '%s'\n", i + 1, p); + free(p); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct fdisk_test tss[] = { + { "--partnames", test_partnames, "<diskname>" }, + { NULL } + }; + + return fdisk_run_test(tss, argc, argv); +} + +#endif |
