diff options
Diffstat (limited to 'libfdisk/src/bsd.c')
-rw-r--r-- | libfdisk/src/bsd.c | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/libfdisk/src/bsd.c b/libfdisk/src/bsd.c new file mode 100644 index 0000000..a7e2791 --- /dev/null +++ b/libfdisk/src/bsd.c @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com> + * + * Based on the original code from fdisk + * written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de) + * with code from the NetBSD disklabel command. + * + * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, March 1999 + * David Huggins-Daines <dhuggins@linuxcare.com>, January 2000 + */ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/param.h> + +#include "blkdev.h" +#include "fdiskP.h" +#include "pt-mbr.h" +#include "pt-bsd.h" +#include "all-io.h" + + +/** + * SECTION: bsd + * @title: BSD + * @short_description: disk label specific functions + * + */ + +static const char *bsd_dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + NULL +}; +#define BSD_DKMAXTYPES (ARRAY_SIZE(bsd_dktypenames) - 1) + +static struct fdisk_parttype bsd_fstypes[] = { + {BSD_FS_UNUSED, "unused"}, + {BSD_FS_SWAP, "swap"}, + {BSD_FS_V6, "Version 6"}, + {BSD_FS_V7, "Version 7"}, + {BSD_FS_SYSV, "System V"}, + {BSD_FS_V71K, "4.1BSD"}, + {BSD_FS_V8, "Eighth Edition"}, + {BSD_FS_BSDFFS, "4.2BSD"}, +#ifdef __alpha__ + {BSD_FS_EXT2, "ext2"}, +#else + {BSD_FS_MSDOS, "MS-DOS"}, +#endif + {BSD_FS_BSDLFS, "4.4LFS"}, + {BSD_FS_OTHER, "unknown"}, + {BSD_FS_HPFS, "HPFS"}, + {BSD_FS_ISO9660,"ISO-9660"}, + {BSD_FS_BOOT, "boot"}, + {BSD_FS_ADOS, "ADOS"}, + {BSD_FS_HFS, "HFS"}, + {BSD_FS_ADVFS, "AdvFS"}, + { 0, NULL } +}; +#define BSD_FSMAXTYPES (ARRAY_SIZE(bsd_fstypes)-1) + +/* + * in-memory fdisk BSD stuff + */ +struct fdisk_bsd_label { + struct fdisk_label head; /* generic part */ + + struct dos_partition *dos_part; /* parent */ + struct bsd_disklabel bsd; /* on disk label */ +#if defined (__alpha__) + /* We access this through a u_int64_t * when checksumming */ + char bsdbuffer[BSD_BBSIZE] __attribute__((aligned(8))); +#else + char bsdbuffer[BSD_BBSIZE]; +#endif +}; + +static int bsd_initlabel(struct fdisk_context *cxt); +static int bsd_readlabel(struct fdisk_context *cxt); +static void sync_disks(struct fdisk_context *cxt); + +static inline struct fdisk_bsd_label *self_label(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_label(cxt, BSD)); + + return (struct fdisk_bsd_label *) cxt->label; +} + +static inline struct bsd_disklabel *self_disklabel(struct fdisk_context *cxt) +{ + assert(cxt); + assert(cxt->label); + assert(fdisk_is_label(cxt, BSD)); + + return &((struct fdisk_bsd_label *) cxt->label)->bsd; +} + +static struct fdisk_parttype *bsd_partition_parttype( + struct fdisk_context *cxt, + struct bsd_partition *p) +{ + struct fdisk_parttype *t + = fdisk_label_get_parttype_from_code(cxt->label, p->p_fstype); + return t ? : fdisk_new_unknown_parttype(p->p_fstype, NULL); +} + + +#if defined (__alpha__) +static void alpha_bootblock_checksum (char *boot) +{ + uint64_t *dp = (uint64_t *) boot, sum = 0; + int i; + + for (i = 0; i < 63; i++) + sum += dp[i]; + dp[63] = sum; +} +#endif /* __alpha__ */ + +#define HIDDEN_MASK 0x10 + +static int is_bsd_partition_type(int type) +{ + return (type == MBR_FREEBSD_PARTITION || + type == (MBR_FREEBSD_PARTITION ^ HIDDEN_MASK) || + type == MBR_NETBSD_PARTITION || + type == (MBR_NETBSD_PARTITION ^ HIDDEN_MASK) || + type == MBR_OPENBSD_PARTITION || + type == (MBR_OPENBSD_PARTITION ^ HIDDEN_MASK)); +} + +/* + * look for DOS partition usable for nested BSD partition table + */ +static int bsd_assign_dos_partition(struct fdisk_context *cxt) +{ + struct fdisk_bsd_label *l = self_label(cxt); + size_t i; + + for (i = 0; i < 4; i++) { + fdisk_sector_t ss; + + l->dos_part = fdisk_dos_get_partition(cxt->parent, i); + + if (!l->dos_part || !is_bsd_partition_type(l->dos_part->sys_ind)) + continue; + + ss = dos_partition_get_start(l->dos_part); + if (!ss) { + fdisk_warnx(cxt, _("Partition %zd: has invalid starting " + "sector 0."), i + 1); + return -1; + } + + if (cxt->parent->dev_path) { + free(cxt->dev_path); + cxt->dev_path = fdisk_partname( + cxt->parent->dev_path, i + 1); + } + + DBG(LABEL, ul_debug("partition %zu assigned to BSD", i + 1)); + return 0; + } + + fdisk_warnx(cxt, _("There is no *BSD partition on %s."), + cxt->parent->dev_path); + free(cxt->dev_path); + cxt->dev_path = NULL; + l->dos_part = NULL; + return 1; +} + +static int bsd_probe_label(struct fdisk_context *cxt) +{ + int rc = 0; + + if (cxt->parent) + rc = bsd_assign_dos_partition(cxt); /* nested BSD partition table */ + if (!rc) + rc = bsd_readlabel(cxt); + if (!rc) + return 1; /* found BSD */ + return 0; /* not found */ +} + +static int set_parttype( + struct fdisk_context *cxt, + size_t partnum, + struct fdisk_parttype *t) +{ + struct bsd_partition *p; + struct bsd_disklabel *d = self_disklabel(cxt); + + if (partnum >= d->d_npartitions || !t || t->code > UINT8_MAX) + return -EINVAL; + + p = &d->d_partitions[partnum]; + if (t->code == p->p_fstype) + return 0; + + p->p_fstype = t->code; + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +static int bsd_add_partition(struct fdisk_context *cxt, + struct fdisk_partition *pa, + size_t *partno) +{ + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + size_t i; + unsigned int begin = 0, end; + int rc = 0; + + rc = fdisk_partition_next_partno(pa, cxt, &i); + if (rc) + return rc; + if (i >= BSD_MAXPARTITIONS) + return -ERANGE; + if (l->dos_part) { + begin = dos_partition_get_start(l->dos_part); + end = begin + dos_partition_get_size(l->dos_part) - 1; + } else + end = d->d_secperunit - 1; + + /* + * First sector + */ + if (pa && pa->start_follow_default) + ; + else if (pa && fdisk_partition_has_start(pa)) { + if (pa->start < begin || pa->start > end) + return -ERANGE; + begin = pa->start; + } else { + struct fdisk_ask *ask = fdisk_new_ask(); + + if (!ask) + return -ENOMEM; + fdisk_ask_set_query(ask, + fdisk_use_cylinders(cxt) ? + _("First cylinder") : _("First sector")); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin)); + fdisk_ask_number_set_default(ask, fdisk_cround(cxt, begin)); + fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end)); + + rc = fdisk_do_ask(cxt, ask); + begin = fdisk_ask_number_get_result(ask); + fdisk_unref_ask(ask); + if (rc) + return rc; + if (fdisk_use_cylinders(cxt)) + begin = (begin - 1) * d->d_secpercyl; + } + + /* + * Last sector + */ + if (pa && pa->end_follow_default) + ; + else if (pa && fdisk_partition_has_size(pa)) { + if (begin + pa->size > end) + return -ERANGE; + end = begin + pa->size - 1ULL; + } else { + /* ask user by dialog */ + struct fdisk_ask *ask = fdisk_new_ask(); + + if (!ask) + return -ENOMEM; + fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET); + + if (fdisk_use_cylinders(cxt)) { + fdisk_ask_set_query(ask, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}")); + fdisk_ask_number_set_unit(ask, + cxt->sector_size * + fdisk_get_units_per_sector(cxt)); + } else { + fdisk_ask_set_query(ask, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}")); + fdisk_ask_number_set_unit(ask,cxt->sector_size); + } + + fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin)); + fdisk_ask_number_set_default(ask, fdisk_cround(cxt, end)); + fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end)); + fdisk_ask_number_set_base(ask, fdisk_cround(cxt, begin)); + fdisk_ask_number_set_wrap_negative(ask, 1); /* wrap negative around high */ + + rc = fdisk_do_ask(cxt, ask); + end = fdisk_ask_number_get_result(ask); + fdisk_unref_ask(ask); + if (rc) + return rc; + if (fdisk_use_cylinders(cxt)) + end = end * d->d_secpercyl - 1; + } + + d->d_partitions[i].p_size = end - begin + 1; + d->d_partitions[i].p_offset = begin; + d->d_partitions[i].p_fstype = BSD_FS_UNUSED; + + if (i >= d->d_npartitions) + d->d_npartitions = i + 1; + cxt->label->nparts_cur = d->d_npartitions; + + if (pa && pa->type) + set_parttype(cxt, i, pa->type); + + fdisk_label_set_changed(cxt->label, 1); + if (partno) + *partno = i; + return 0; +} + +static int bsd_set_partition(struct fdisk_context *cxt, size_t n, + struct fdisk_partition *pa) +{ + struct bsd_partition *p; + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + + if (n >= d->d_npartitions) + return -EINVAL; + + p = &d->d_partitions[n]; + + /* we have to stay within parental DOS partition */ + if (l->dos_part && (fdisk_partition_has_start(pa) || + fdisk_partition_has_size(pa))) { + + fdisk_sector_t dosbegin = dos_partition_get_start(l->dos_part); + fdisk_sector_t dosend = dosbegin + dos_partition_get_size(l->dos_part) - 1; + fdisk_sector_t begin = fdisk_partition_has_start(pa) ? pa->start : p->p_offset; + fdisk_sector_t end = begin + (fdisk_partition_has_size(pa) ? pa->size : p->p_size) - 1; + + if (begin < dosbegin || begin > dosend) + return -ERANGE; + if (end < dosbegin || end > dosend) + return -ERANGE; + } + + if (pa->type) { + int rc = set_parttype(cxt, n, pa->type); + if (rc) + return rc; + } + + if (fdisk_partition_has_start(pa)) + d->d_partitions[n].p_offset = pa->start; + if (fdisk_partition_has_size(pa)) + d->d_partitions[n].p_size = pa->size; + + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + + +/* Returns 0 on success, < 0 on error. */ +static int bsd_create_disklabel(struct fdisk_context *cxt) +{ + int rc, yes = 0; + struct bsd_disklabel *d = self_disklabel(cxt); + + fdisk_info(cxt, _("The device %s does not contain BSD disklabel."), cxt->dev_path); + rc = fdisk_ask_yesno(cxt, + _("Do you want to create a BSD disklabel?"), + &yes); + if (rc) + return rc; + if (!yes) + return 1; + if (cxt->parent) { + rc = bsd_assign_dos_partition(cxt); + if (rc == 1) + /* not found DOS partition usable for BSD label */ + rc = -EINVAL; + } + if (rc) + return rc; + + rc = bsd_initlabel(cxt); + if (!rc) { + cxt->label->nparts_cur = d->d_npartitions; + cxt->label->nparts_max = BSD_MAXPARTITIONS; + } + + return rc; +} + +static int bsd_delete_part( + struct fdisk_context *cxt, + size_t partnum) +{ + struct bsd_disklabel *d = self_disklabel(cxt); + + d->d_partitions[partnum].p_size = 0; + d->d_partitions[partnum].p_offset = 0; + d->d_partitions[partnum].p_fstype = BSD_FS_UNUSED; + + if (d->d_npartitions == partnum + 1) + while (!d->d_partitions[d->d_npartitions - 1].p_size) + d->d_npartitions--; + + cxt->label->nparts_cur = d->d_npartitions; + fdisk_label_set_changed(cxt->label, 1); + return 0; +} + +static int bsd_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item) +{ + struct bsd_disklabel *d; + int rc = 0; + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_label(cxt, BSD)); + + d = self_disklabel(cxt); + + switch (item->id) { + case BSD_LABELITEM_TYPE: + item->name = _("Type"); + item->type = 's'; + if ((unsigned) d->d_type < BSD_DKMAXTYPES) { + item->data.str = strdup(bsd_dktypenames[d->d_type]); + if (!item->data.str) + rc = -ENOMEM; + } else if (asprintf(&item->data.str, "%d", d->d_type) < 0) + rc = -ENOMEM; + break; + case BSD_LABELITEM_DISK: + item->name = _("Disk"); + item->type = 's'; + item->data.str = strndup(d->d_typename, sizeof(d->d_typename)); + if (!item->data.str) + rc = -ENOMEM; + break; + case BSD_LABELITEM_PACKNAME: + item->name = _("Packname"); + item->type = 's'; + item->data.str = strndup(d->d_packname, sizeof(d->d_packname)); + if (!item->data.str) + rc = -ENOMEM; + break; + case BSD_LABELITEM_FLAGS: + item->name = _("Flags"); + item->type = 's'; + item->data.str = strdup( + d->d_flags & BSD_D_REMOVABLE ? _(" removable") : + d->d_flags & BSD_D_ECC ? _(" ecc") : + d->d_flags & BSD_D_BADSECT ? _(" badsect") : ""); + if (!item->data.str) + rc = -ENOMEM; + break; + + /* On various machines the fields of *lp are short/int/long */ + /* In order to avoid problems, we cast them all uint64. */ + case BSD_LABELITEM_SECSIZE: + item->name = _("Bytes/Sector"); + item->type = 'j'; + item->data.num64 = (uint64_t) d->d_secsize; + break; + case BSD_LABELITEM_NTRACKS: + item->name = _("Tracks/Cylinder"); + item->type = 'j'; + item->data.num64 = (uint64_t) d->d_ntracks; + break; + case BSD_LABELITEM_SECPERCYL: + item->name = _("Sectors/Cylinder"); + item->data.num64 = (uint64_t) d->d_secpercyl; + item->type = 'j'; + break; + case BSD_LABELITEM_CYLINDERS: + item->name = _("Cylinders"); + item->data.num64 = (uint64_t) d->d_ncylinders; + item->type = 'j'; + break; + case BSD_LABELITEM_RPM: + item->name = _("Rpm"); + item->data.num64 = (uint64_t) d->d_rpm; + item->type = 'j'; + break; + case BSD_LABELITEM_INTERLEAVE: + item->name = _("Interleave"); + item->data.num64 = (uint64_t) d->d_interleave; + item->type = 'j'; + break; + case BSD_LABELITEM_TRACKSKEW: + item->name = _("Trackskew"); + item->data.num64 = (uint64_t) d->d_trackskew; + item->type = 'j'; + break; + case BSD_LABELITEM_CYLINDERSKEW: + item->name = _("Cylinderskew"); + item->data.num64 = (uint64_t) d->d_cylskew; + item->type = 'j'; + break; + case BSD_LABELITEM_HEADSWITCH: + item->name = _("Headswitch"); + item->data.num64 = (uint64_t) d->d_headswitch; + item->type = 'j'; + break; + case BSD_LABELITEM_TRKSEEK: + item->name = _("Track-to-track seek"); + item->data.num64 = (uint64_t) d->d_trkseek; + item->type = 'j'; + break; + default: + if (item->id < __FDISK_NLABELITEMS) + rc = 1; /* unsupported generic item */ + else + rc = 2; /* out of range */ + break; + } + + return rc; +} + +static int bsd_get_partition(struct fdisk_context *cxt, size_t n, + struct fdisk_partition *pa) +{ + struct bsd_partition *p; + struct bsd_disklabel *d = self_disklabel(cxt); + + assert(cxt); + assert(cxt->label); + assert(fdisk_is_label(cxt, BSD)); + + if (n >= d->d_npartitions) + return -EINVAL; + + p = &d->d_partitions[n]; + + pa->used = p->p_size ? 1 : 0; + if (!pa->used) + return 0; + + if (fdisk_use_cylinders(cxt) && d->d_secpercyl) { + pa->start_post = p->p_offset % d->d_secpercyl ? '*' : ' '; + pa->end_post = (p->p_offset + p->p_size) % d->d_secpercyl ? '*' : ' '; + } + + pa->start = p->p_offset; + pa->size = p->p_size; + pa->type = bsd_partition_parttype(cxt, p); + + if (p->p_fstype == BSD_FS_UNUSED || p->p_fstype == BSD_FS_BSDFFS) { + pa->fsize = p->p_fsize; + pa->bsize = p->p_fsize * p->p_frag; + } + if (p->p_fstype == BSD_FS_BSDFFS) + pa->cpg = p->p_cpg; + + return 0; +} + +static uint32_t ask_uint32(struct fdisk_context *cxt, + uint32_t dflt, char *mesg) +{ + uintmax_t res; + + if (fdisk_ask_number(cxt, min(dflt, (uint32_t) 1), dflt, + UINT32_MAX, mesg, &res) == 0) + return res; + return dflt; +} + +static uint16_t ask_uint16(struct fdisk_context *cxt, + uint16_t dflt, char *mesg) +{ + uintmax_t res; + + if (fdisk_ask_number(cxt, min(dflt, (uint16_t) 1), + dflt, UINT16_MAX, mesg, &res) == 0) + return res; + return dflt; +} + +/** + * fdisk_bsd_edit_disklabel: + * @cxt: context + * + * Edits fields in BSD disk label. + * + * Returns: 0 on success, <0 on error + */ +int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt) +{ + struct bsd_disklabel *d = self_disklabel(cxt); + uintmax_t res; + +#if defined (__alpha__) || defined (__ia64__) + if (fdisk_ask_number(cxt, DEFAULT_SECTOR_SIZE, d->d_secsize, + UINT32_MAX, _("bytes/sector"), &res) == 0) + d->d_secsize = res; + + d->d_nsectors = ask_uint32(cxt, d->d_nsectors, _("sectors/track")); + d->d_ntracks = ask_uint32(cxt, d->d_ntracks, _("tracks/cylinder")); + d->d_ncylinders = ask_uint32(cxt, d->d_ncylinders ,_("cylinders")); +#endif + if (fdisk_ask_number(cxt, 1, + (uintmax_t) d->d_nsectors * d->d_ntracks, + (uintmax_t) d->d_nsectors * d->d_ntracks, + _("sectors/cylinder"), &res) == 0) + d->d_secpercyl = res; + + d->d_rpm = ask_uint16(cxt, d->d_rpm, _("rpm")); + d->d_interleave = ask_uint16(cxt, d->d_interleave, _("interleave")); + d->d_trackskew = ask_uint16(cxt, d->d_trackskew, _("trackskew")); + d->d_cylskew = ask_uint16(cxt, d->d_cylskew, _("cylinderskew")); + + d->d_headswitch = ask_uint32(cxt, d->d_headswitch, _("headswitch")); + d->d_trkseek = ask_uint32(cxt, d->d_trkseek, _("track-to-track seek")); + + d->d_secperunit = d->d_secpercyl * d->d_ncylinders; + return 0; +} + +static int bsd_get_bootstrap(struct fdisk_context *cxt, + char *path, void *ptr, int size) +{ + int fd; + + if ((fd = open(path, O_RDONLY)) < 0) { + fdisk_warn(cxt, _("cannot open %s"), path); + return -errno; + } + + if (read_all(fd, ptr, size) != size) { + fdisk_warn(cxt, _("cannot read %s"), path); + close(fd); + return -errno; + } + + fdisk_info(cxt, _("The bootstrap file %s successfully loaded."), path); + close (fd); + return 0; +} + +/** + * fdisk_bsd_write_bootstrap: + * @cxt: context + * + * Install bootstrap file to the BSD device + */ +int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt) +{ + struct bsd_disklabel dl, *d = self_disklabel(cxt); + struct fdisk_bsd_label *l = self_label(cxt); + char *name = d->d_type == BSD_DTYPE_SCSI ? "sd" : "wd"; + char buf[BUFSIZ]; + char *res, *dp, *p; + int rc; + fdisk_sector_t sector; + + snprintf(buf, sizeof(buf), + _("Bootstrap: %1$sboot -> boot%1$s (default %1$s)"), + name); + rc = fdisk_ask_string(cxt, buf, &res); + if (rc) + goto done; + if (res && *res) + name = res; + + snprintf(buf, sizeof(buf), "%s/%sboot", BSD_LINUX_BOOTDIR, name); + rc = bsd_get_bootstrap(cxt, buf, l->bsdbuffer, (int) d->d_secsize); + if (rc) + goto done; + + /* We need a backup of the disklabel (might have changed). */ + dp = &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE]; + memmove(&dl, dp, sizeof(struct bsd_disklabel)); + + /* The disklabel will be overwritten by 0's from bootxx anyway */ + memset(dp, 0, sizeof(struct bsd_disklabel)); + + snprintf(buf, sizeof(buf), "%s/boot%s", BSD_LINUX_BOOTDIR, name); + rc = bsd_get_bootstrap(cxt, buf, + &l->bsdbuffer[d->d_secsize], + (int) d->d_bbsize - d->d_secsize); + if (rc) + goto done; + + /* check end of the bootstrap */ + for (p = dp; p < dp + sizeof(struct bsd_disklabel); p++) { + if (!*p) + continue; + fdisk_warnx(cxt, _("Bootstrap overlaps with disklabel!")); + return -EINVAL; + } + + /* move disklabel back */ + memmove(dp, &dl, sizeof(struct bsd_disklabel)); + + sector = 0; + if (l->dos_part) + sector = dos_partition_get_start(l->dos_part); +#if defined (__alpha__) + alpha_bootblock_checksum(l->bsdbuffer); +#endif + if (lseek(cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) { + fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path); + rc = -errno; + goto done; + } + if (write_all(cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) { + fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path); + rc = -errno; + goto done; + } + + fdisk_info(cxt, _("Bootstrap installed on %s."), cxt->dev_path); + sync_disks(cxt); + + rc = 0; +done: + free(res); + return rc; +} + +static unsigned short bsd_dkcksum (struct bsd_disklabel *lp) +{ + unsigned char *ptr, *end; + unsigned short sum = 0; + + ptr = (unsigned char *) lp; + end = (unsigned char *) &lp->d_partitions[lp->d_npartitions]; + + while (ptr < end) { + unsigned short val; + + memcpy(&val, ptr, sizeof(unsigned short)); + sum ^= val; + + ptr += sizeof(unsigned short); + } + return sum; +} + +static int bsd_initlabel (struct fdisk_context *cxt) +{ + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + struct bsd_partition *pp; + + memset (d, 0, sizeof (struct bsd_disklabel)); + + d -> d_magic = BSD_DISKMAGIC; + + if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0) + d -> d_type = BSD_DTYPE_SCSI; + else + d -> d_type = BSD_DTYPE_ST506; + +#if !defined (__alpha__) + d -> d_flags = BSD_D_DOSPART; +#else + d -> d_flags = 0; +#endif + d -> d_secsize = DEFAULT_SECTOR_SIZE; /* bytes/sector */ + d -> d_nsectors = cxt->geom.sectors; /* sectors/track */ + d -> d_ntracks = cxt->geom.heads; /* tracks/cylinder (heads) */ + d -> d_ncylinders = cxt->geom.cylinders; + d -> d_secpercyl = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */ + if (d -> d_secpercyl == 0) + d -> d_secpercyl = 1; /* avoid segfaults */ + d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders; + + d -> d_rpm = 3600; + d -> d_interleave = 1; + d -> d_trackskew = 0; + d -> d_cylskew = 0; + d -> d_headswitch = 0; + d -> d_trkseek = 0; + + d -> d_magic2 = BSD_DISKMAGIC; + d -> d_bbsize = BSD_BBSIZE; + d -> d_sbsize = BSD_SBSIZE; + + if (l->dos_part) { + d->d_npartitions = 4; + + pp = &d->d_partitions[2]; /* Partition C should be the NetBSD partition */ + pp->p_offset = dos_partition_get_start(l->dos_part); + pp->p_size = dos_partition_get_size(l->dos_part); + pp->p_fstype = BSD_FS_UNUSED; + + pp = &d -> d_partitions[3]; /* Partition D should be the whole disk */ + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; + } else { + d->d_npartitions = 3; + + pp = &d->d_partitions[2]; /* Partition C should be the whole disk */ + pp->p_offset = 0; + pp->p_size = d->d_secperunit; + pp->p_fstype = BSD_FS_UNUSED; + } + + return 0; +} + +/* + * Read a bsd_disklabel from sector 0 or from the starting sector of p. + * If it has the right magic, return 0. + */ +static int bsd_readlabel(struct fdisk_context *cxt) +{ + struct fdisk_bsd_label *l; + struct bsd_disklabel *d; + int t; + off_t offset = 0; + + l = self_label(cxt); + d = self_disklabel(cxt); + + if (l->dos_part) + /* BSD is nested within DOS partition, get the begin of the + * partition. Note that DOS uses native sector size. */ + offset = dos_partition_get_start(l->dos_part) * cxt->sector_size; + + if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) + return -1; + if (read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0) + return errno ? -errno : -1; + + /* The offset to begin of the disk label. Note that BSD uses + * 512-byte (default) sectors. */ + memmove(d, &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE + + BSD_LABELOFFSET], sizeof(*d)); + + if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) { + DBG(LABEL, ul_debug("not found magic")); + return -1; + } + + for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) { + d->d_partitions[t].p_size = 0; + d->d_partitions[t].p_offset = 0; + d->d_partitions[t].p_fstype = BSD_FS_UNUSED; + } + + if (d->d_npartitions > BSD_MAXPARTITIONS) + fdisk_warnx(cxt, ("Too many partitions (%d, maximum is %d)."), + d->d_npartitions, BSD_MAXPARTITIONS); + + /* let's follow in-PT geometry */ + cxt->geom.sectors = d->d_nsectors; + cxt->geom.heads = d->d_ntracks; + cxt->geom.cylinders = d->d_ncylinders; + + if (fdisk_has_user_device_geometry(cxt)) + fdisk_apply_user_device_properties(cxt); + + cxt->label->nparts_cur = d->d_npartitions; + cxt->label->nparts_max = BSD_MAXPARTITIONS; + DBG(LABEL, ul_debug("read BSD label")); + return 0; +} + +static int bsd_write_disklabel(struct fdisk_context *cxt) +{ + off_t offset = 0; + struct fdisk_bsd_label *l = self_label(cxt); + struct bsd_disklabel *d = self_disklabel(cxt); + + + if (l->dos_part) + offset = dos_partition_get_start(l->dos_part) * cxt->sector_size; + + d->d_checksum = 0; + d->d_checksum = bsd_dkcksum(d); + + /* Update label within boot block. */ + memmove(&l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE + + BSD_LABELOFFSET], d, sizeof(*d)); + +#if defined (__alpha__) && BSD_LABELSECTOR == 0 + /* Write the checksum to the end of the first sector. */ + alpha_bootblock_checksum(l->bsdbuffer); +#endif + if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) { + fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path); + return -errno; + } + if (write_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) { + fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path); + return -errno; + } + sync_disks(cxt); + + if (cxt->parent && fdisk_label_is_changed(cxt->parent->label)) + fdisk_info(cxt, _("Disklabel written to %s. (Don't forget to write the %s disklabel too.)"), + cxt->dev_path, cxt->parent->dev_path); + else + fdisk_info(cxt, _("Disklabel written to %s."), cxt->dev_path); + return 0; +} + +static void sync_disks(struct fdisk_context *cxt) +{ + fdisk_info(cxt, _("Syncing disks.")); + sync(); +} + +static int bsd_translate_fstype (int linux_type) +{ + switch (linux_type) { + case 0x01: /* DOS 12-bit FAT */ + case 0x04: /* DOS 16-bit <32M */ + case 0x06: /* DOS 16-bit >=32M */ + case 0xe1: /* DOS access */ + case 0xe3: /* DOS R/O */ +#if !defined (__alpha__) + case 0xf2: /* DOS secondary */ + return BSD_FS_MSDOS; +#endif + case 0x07: /* OS/2 HPFS */ + return BSD_FS_HPFS; + default: + break; + } + + return BSD_FS_OTHER; +} + +/** + * fdisk_bsd_link_partition: + * @cxt: context + * + * Links partition from parent (DOS) to nested BSD partition table. + * + * Returns: 0 on success, <0 on error + */ +int fdisk_bsd_link_partition(struct fdisk_context *cxt) +{ + size_t k, i; + int rc; + struct dos_partition *p; + struct bsd_disklabel *d = self_disklabel(cxt); + + if (!cxt->parent || !fdisk_is_label(cxt->parent, DOS)) { + fdisk_warnx(cxt, _("BSD label is not nested within a DOS partition.")); + return -EINVAL; + } + + /* ask for DOS partition */ + rc = fdisk_ask_partnum(cxt->parent, &k, FALSE); + if (rc) + return rc; + /* ask for BSD partition */ + rc = fdisk_ask_partnum(cxt, &i, TRUE); + if (rc) + return rc; + + if (i >= BSD_MAXPARTITIONS) + return -EINVAL; + + p = fdisk_dos_get_partition(cxt->parent, k); + + d->d_partitions[i].p_size = dos_partition_get_size(p); + d->d_partitions[i].p_offset = dos_partition_get_start(p); + d->d_partitions[i].p_fstype = bsd_translate_fstype(p->sys_ind); + + if (i >= d->d_npartitions) + d->d_npartitions = i + 1; + + cxt->label->nparts_cur = d->d_npartitions; + fdisk_label_set_changed(cxt->label, 1); + + fdisk_info(cxt, _("BSD partition '%c' linked to DOS partition %zu."), + 'a' + (int) i, k + 1); + return 0; +} + + +static int bsd_partition_is_used( + struct fdisk_context *cxt, + size_t partnum) +{ + struct bsd_disklabel *d = self_disklabel(cxt); + + if (partnum >= BSD_MAXPARTITIONS) + return 0; + + return d->d_partitions[partnum].p_size ? 1 : 0; +} + + +static const struct fdisk_label_operations bsd_operations = +{ + .probe = bsd_probe_label, + .get_item = bsd_get_disklabel_item, + .write = bsd_write_disklabel, + .create = bsd_create_disklabel, + + .del_part = bsd_delete_part, + .get_part = bsd_get_partition, + .set_part = bsd_set_partition, + .add_part = bsd_add_partition, + + .part_is_used = bsd_partition_is_used, +}; + +static const struct fdisk_field bsd_fields[] = +{ + { FDISK_FIELD_DEVICE, N_("Slice"), 1, 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_FIELD_TYPE, N_("Type"), 8, 0 }, + { FDISK_FIELD_FSIZE, N_("Fsize"), 5, FDISK_FIELDFL_NUMBER }, + { FDISK_FIELD_BSIZE, N_("Bsize"), 5, FDISK_FIELDFL_NUMBER }, + { FDISK_FIELD_CPG, N_("Cpg"), 5, FDISK_FIELDFL_NUMBER } +}; + +/* + * allocates BSD label driver + */ +struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt __attribute__ ((__unused__))) +{ + struct fdisk_label *lb; + struct fdisk_bsd_label *bsd; + + bsd = calloc(1, sizeof(*bsd)); + if (!bsd) + return NULL; + + /* initialize generic part of the driver */ + lb = (struct fdisk_label *) bsd; + lb->name = "bsd"; + lb->id = FDISK_DISKLABEL_BSD; + lb->op = &bsd_operations; + lb->parttypes = bsd_fstypes; + lb->nparttypes = ARRAY_SIZE(bsd_fstypes) - 1; + + lb->fields = bsd_fields; + lb->nfields = ARRAY_SIZE(bsd_fields); + + lb->flags |= FDISK_LABEL_FL_INCHARS_PARTNO; + lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY; + + return lb; +} |