summaryrefslogtreecommitdiffstats
path: root/libfdisk/src/sgi.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfdisk/src/sgi.c')
-rw-r--r--libfdisk/src/sgi.c1204
1 files changed, 1204 insertions, 0 deletions
diff --git a/libfdisk/src/sgi.c b/libfdisk/src/sgi.c
new file mode 100644
index 0000000..f38e9a0
--- /dev/null
+++ b/libfdisk/src/sgi.c
@@ -0,0 +1,1204 @@
+/*
+ *
+ * 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 "all-io.h"
+
+#include "blkdev.h"
+
+#include "bitops.h"
+#include "pt-sgi.h"
+#include "pt-mbr.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: sgi
+ * @title: SGI
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * 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_label(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_label(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);
+}
+
+/**
+ * fdisk_sgi_create_info:
+ * @cxt: context
+ *
+ * This function add hint about SGI label (e.g. set "sgilabel" as volume name)
+ * to the first SGI volume. This is probably old SGI convention without any
+ * effect to the device partitioning.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+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));
+ memcpy((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_label(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_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
+{
+ struct sgi_disklabel *sgilabel;
+ struct sgi_device_parameter *sgiparam;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ sgilabel = self_disklabel(cxt);
+ sgiparam = &sgilabel->devparam;
+
+ switch (item->id) {
+ case SGI_LABELITEM_PCYLCOUNT:
+ item->name = _("Physical cylinders");
+ item->type = 'j';
+ item->data.num64 = (uint64_t) be16_to_cpu(sgiparam->pcylcount);
+ break;
+ case SGI_LABELITEM_SPARECYL:
+ item->name = _("Extra sects/cyl");
+ item->type = 'j';
+ item->data.num64 = (uint64_t) sgiparam->sparecyl;
+ break;
+ case SGI_LABELITEM_ILFACT:
+ item->name = _("Interleave");
+ item->type = 'j';
+ item->data.num64 = (uint64_t) be16_to_cpu(sgiparam->ilfact);
+ break;
+ case SGI_LABELITEM_BOOTFILE:
+ item->name = _("Bootfile");
+ item->type = 's';
+ item->data.str = *sgilabel->boot_file ? strdup((char *) sgilabel->boot_file) : NULL;
+ break;
+ default:
+ if (item->id < __FDISK_NLABELITEMS)
+ rc = 1; /* unsupported generic item */
+ else
+ rc = 2; /* out of range */
+ break;
+ }
+
+ 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 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_label_get_parttype_from_code(cxt->label, sgi_get_sysid(cxt, n));
+ return t ? : fdisk_new_unknown_parttype(sgi_get_sysid(cxt, n), NULL);
+}
+
+/* fdisk_get_partition() backend */
+static int sgi_get_partition(struct fdisk_context *cxt, size_t n, struct fdisk_partition *pa)
+{
+ fdisk_sector_t start, len;
+
+ pa->used = sgi_get_num_sectors(cxt, n) > 0;
+ if (!pa->used)
+ return 0;
+
+ start = sgi_get_start_sector(cxt, n);
+ len = sgi_get_num_sectors(cxt, n);
+
+ pa->type = sgi_get_parttype(cxt, n);
+ pa->size = len;
+ pa->start = start;
+
+ if (pa->type && pa->type->code == SGI_TYPE_ENTIRE_DISK)
+ pa->wholedisk = 1;
+
+ pa->attrs = sgi_get_swappartition(cxt) == (int) n ? "swap" :
+ sgi_get_bootpartition(cxt) == (int) n ? "boot" : NULL;
+ if (pa->attrs)
+ pa->attrs = strdup(pa->attrs);
+
+ return 0;
+}
+
+
+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 */
+}
+
+/**
+ * fdisk_sgi_set_bootfile:
+ * @cxt: context
+ *
+ * Allows to set SGI boot file. The function uses Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+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_info(cxt, _("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_label(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.
+ */
+ const unsigned int i = *(const int *) x;
+ const unsigned int j = *(const 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_label(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 && verbose) {
+ fdisk_info(cxt, _("More than one entire "
+ "disk entry present."));
+ }
+ }
+ }
+ if (sortcount == 0) {
+ if (verbose)
+ fdisk_info(cxt, _("No partitions defined."));
+ if (lastblock)
+ add_to_freelist(cxt, 0, lastblock);
+ 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, ul_debug(
+ "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, ul_debug("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, ul_debug("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, ul_debug("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, ul_debug("%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 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_label(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_label_get_parttype_from_code(cxt->label, 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)) {
+ 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))
+ 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 = 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,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ struct fdisk_sgi_label *sgi;
+ char mesg[256];
+ unsigned int first = 0, last = 0;
+ struct fdisk_ask *ask;
+ int sys = pa && pa->type ? pa->type->code : SGI_TYPE_XFS;
+ int rc;
+ size_t n;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ rc = fdisk_partition_next_partno(pa, cxt, &n);
+ if (rc)
+ return rc;
+ 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 %zu is already defined. "
+ "Delete it before re-adding it."), n + 1);
+ return -EINVAL;
+ }
+ if (!cxt->script && 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;
+ }
+
+ if (sys == SGI_TYPE_ENTIRE_DISK) {
+ first = 0;
+ last = sgi_get_lastblock(cxt);
+ } else {
+ first = sgi->freelist[0].first;
+ last = sgi->freelist[0].last;
+ }
+
+ /* first sector */
+ if (pa && pa->start_follow_default)
+ ;
+ else if (pa && fdisk_partition_has_start(pa)) {
+ first = pa->start;
+ last = is_in_freelist(cxt, first);
+
+ if (sys != SGI_TYPE_ENTIRE_DISK && !last)
+ return -ERANGE;
+ } else {
+ snprintf(mesg, sizeof(mesg), _("First %s"),
+ fdisk_get_unit(cxt, FDISK_SINGULAR));
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ fdisk_ask_set_query(ask, mesg);
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+ 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_unref_ask(ask);
+
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ first *= fdisk_get_units_per_sector(cxt);
+ }
+
+ 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 (!last)
+ last = is_in_freelist(cxt, first);
+
+ /* last sector */
+ if (pa && pa->end_follow_default)
+ last -= 1ULL;
+ else if (pa && fdisk_partition_has_size(pa)) {
+ if (first + pa->size - 1ULL > last)
+ return -ERANGE;
+ last = first + pa->size - 1ULL;
+ } else {
+ snprintf(mesg, sizeof(mesg),
+ _("Last %s or +%s or +size{K,M,G,T,P}"),
+ fdisk_get_unit(cxt, FDISK_SINGULAR),
+ fdisk_get_unit(cxt, FDISK_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));
+ fdisk_ask_number_set_wrap_negative(ask, 1); /* wrap negative around high */
+
+ if (fdisk_use_cylinders(cxt))
+ fdisk_ask_number_set_unit(ask,
+ cxt->sector_size *
+ fdisk_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_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ last *= fdisk_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'."));
+
+ set_partition(cxt, n, first, last - first, sys);
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+ if (partno)
+ *partno = n;
+ return 0;
+}
+
+static int sgi_create_disklabel(struct fdisk_context *cxt)
+{
+ struct fdisk_sgi_label *sgi;
+ struct sgi_disklabel *sgilabel;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ if (cxt->geom.heads && cxt->geom.sectors) {
+ fdisk_sector_t llsectors;
+
+ if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+ /* the get device size ioctl was successful */
+ fdisk_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);
+ }
+ }
+
+ rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
+ if (rc)
+ return rc;
+
+ 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;
+
+ /* don't create default layout when a script defined */
+ if (!cxt->script) {
+ sgi_set_entire(cxt);
+ sgi_set_volhdr(cxt);
+ }
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+
+ fdisk_info(cxt, _("Created a new SGI disklabel."));
+ return 0;
+}
+
+static int sgi_set_partition(struct fdisk_context *cxt,
+ size_t i,
+ struct fdisk_partition *pa)
+{
+ struct sgi_disklabel *sgilabel;
+
+ if (i >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ sgilabel = self_disklabel(cxt);
+
+ if (pa->type) {
+ struct fdisk_parttype *t = pa->type;
+
+ 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->code != SGI_TYPE_ENTIRE_DISK)
+ || (i == 8 && t->code != 0))
+ fdisk_info(cxt, _("Consider leaving partition 9 as volume header (0), "
+ "and partition 11 as entire volume (6), "
+ "as IRIX expects it."));
+
+ if (cxt->script == NULL
+ && ((t->code != SGI_TYPE_ENTIRE_DISK) && (t->code != 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->partitions[i].type = cpu_to_be32(t->code);
+ }
+
+ if (fdisk_partition_has_start(pa))
+ sgilabel->partitions[i].first_block = cpu_to_be32(pa->start);
+ if (fdisk_partition_has_size(pa))
+ sgilabel->partitions[i].num_blocks = cpu_to_be32(pa->size);
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+
+static int sgi_partition_is_used(
+ struct fdisk_context *cxt,
+ size_t i)
+{
+ assert(cxt);
+ assert(fdisk_is_label(cxt, SGI));
+
+ if (i >= cxt->label->nparts_max)
+ return 0;
+ return sgi_get_num_sectors(cxt, i) ? 1 : 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_label(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_field sgi_fields[] =
+{
+ { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
+ { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+ { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_TYPE, N_("Type"), 0.1, FDISK_FIELDFL_EYECANDY },
+ { FDISK_FIELD_ATTR, N_("Attrs"), 0, FDISK_FIELDFL_NUMBER }
+};
+
+static const struct fdisk_label_operations sgi_operations =
+{
+ .probe = sgi_probe_label,
+ .write = sgi_write_disklabel,
+ .verify = sgi_verify_disklabel,
+ .get_item = sgi_get_disklabel_item,
+ .create = sgi_create_disklabel,
+
+ .get_part = sgi_get_partition,
+ .set_part = sgi_set_partition,
+ .add_part = sgi_add_partition,
+ .del_part = sgi_delete_partition,
+
+ .part_is_used = sgi_partition_is_used,
+ .part_toggle_flag = sgi_toggle_partition_flag
+};
+
+/* Allocates an SGI label driver. */
+struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt __attribute__ ((__unused__)))
+{
+ struct fdisk_label *lb;
+ struct fdisk_sgi_label *sgi;
+
+ 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) - 1;
+ lb->fields = sgi_fields;
+ lb->nfields = ARRAY_SIZE(sgi_fields);
+
+ lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+ return lb;
+}