/* * * Copyright (C) 2012 Davidlohr Bueso * 2013 Karel Zak * * 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 , Mar 1999, * Phillip Kesling , 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 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 arithmetic. 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; }