summaryrefslogtreecommitdiff
path: root/libfdisk/src
diff options
context:
space:
mode:
Diffstat (limited to 'libfdisk/src')
-rw-r--r--libfdisk/src/Makemodule.am19
-rw-r--r--libfdisk/src/alignment.c241
-rw-r--r--libfdisk/src/ask.c230
-rw-r--r--libfdisk/src/bsd.c943
-rw-r--r--libfdisk/src/context.c152
-rw-r--r--libfdisk/src/dos.c2086
-rw-r--r--libfdisk/src/fdiskP.h69
-rw-r--r--libfdisk/src/gpt.c498
-rw-r--r--libfdisk/src/label.c143
-rw-r--r--libfdisk/src/libfdisk.h118
-rw-r--r--libfdisk/src/parttype.c15
-rw-r--r--libfdisk/src/sgi.c1189
-rw-r--r--libfdisk/src/sun.c1024
-rw-r--r--libfdisk/src/utils.c74
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