| /* |
| * |
| * 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" |
| |
| /** |
| * 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)); |
| 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_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_list_table(struct fdisk_context *cxt) |
| { |
| struct sgi_disklabel *sgilabel = self_disklabel(cxt); |
| struct sgi_device_parameter *sgiparam = &sgilabel->devparam; |
| int rc = 0; |
| |
| if (fdisk_is_details(cxt)) |
| fdisk_info(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)); |
| |
| fdisk_info(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 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. |
| */ |
| 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_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) { |
| if (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)); |
| |
| 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)); |
| |
| #ifdef HDIO_GETGEO |
| 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); |
| } |
| } |
| #endif |
| rc = fdisk_init_firstsector_buffer(cxt); |
| 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 (t->code > 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->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, |
| .create = sgi_create_disklabel, |
| .list = sgi_list_table, |
| |
| .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) |
| { |
| 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) - 1; |
| lb->fields = sgi_fields; |
| lb->nfields = ARRAY_SIZE(sgi_fields); |
| |
| lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY; |
| |
| return lb; |
| } |