| /* |
| * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com> |
| * |
| * Based on the original code from fdisk |
| * written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de) |
| * with code from the NetBSD disklabel command. |
| * |
| * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, March 1999 |
| * David Huggins-Daines <dhuggins@linuxcare.com>, January 2000 |
| */ |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/param.h> |
| |
| #include "nls.h" |
| #include "blkdev.h" |
| #include "fdiskP.h" |
| #include "pt-mbr.h" |
| #include "pt-bsd.h" |
| #include "all-io.h" |
| |
| |
| /** |
| * 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", |
| 0 |
| }; |
| #define BSD_DKMAXTYPES (ARRAY_SIZE(bsd_dktypenames) - 1) |
| |
| static struct fdisk_parttype bsd_fstypes[] = { |
| {BSD_FS_UNUSED, "unused"}, |
| {BSD_FS_SWAP, "swap"}, |
| {BSD_FS_V6, "Version 6"}, |
| {BSD_FS_V7, "Version 7"}, |
| {BSD_FS_SYSV, "System V"}, |
| {BSD_FS_V71K, "4.1BSD"}, |
| {BSD_FS_V8, "Eighth Edition"}, |
| {BSD_FS_BSDFFS, "4.2BSD"}, |
| #ifdef __alpha__ |
| {BSD_FS_EXT2, "ext2"}, |
| #else |
| {BSD_FS_MSDOS, "MS-DOS"}, |
| #endif |
| {BSD_FS_BSDLFS, "4.4LFS"}, |
| {BSD_FS_OTHER, "unknown"}, |
| {BSD_FS_HPFS, "HPFS"}, |
| {BSD_FS_ISO9660,"ISO-9660"}, |
| {BSD_FS_BOOT, "boot"}, |
| {BSD_FS_ADOS, "ADOS"}, |
| {BSD_FS_HFS, "HFS"}, |
| {BSD_FS_ADVFS, "AdvFS"}, |
| { 0, NULL } |
| }; |
| #define BSD_FSMAXTYPES (ARRAY_SIZE(bsd_fstypes)-1) |
| |
| /* |
| * in-memory fdisk BSD stuff |
| */ |
| struct fdisk_bsd_label { |
| struct fdisk_label head; /* generic part */ |
| |
| struct dos_partition *dos_part; /* parent */ |
| struct bsd_disklabel bsd; /* on disk label */ |
| #if defined (__alpha__) |
| /* We access this through a u_int64_t * when checksumming */ |
| char bsdbuffer[BSD_BBSIZE] __attribute__((aligned(8))); |
| #else |
| char bsdbuffer[BSD_BBSIZE]; |
| #endif |
| }; |
| |
| static int bsd_list_disklabel(struct fdisk_context *cxt); |
| static int bsd_initlabel(struct fdisk_context *cxt); |
| static int bsd_readlabel(struct fdisk_context *cxt); |
| static void sync_disks(struct fdisk_context *cxt); |
| |
| 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 partiotn 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)); |
| |
| 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) { |
| int org = fdisk_is_details(cxt); |
| |
| cxt->label->nparts_cur = d->d_npartitions; |
| cxt->label->nparts_max = BSD_MAXPARTITIONS; |
| |
| fdisk_enable_details(cxt, 1); |
| bsd_list_disklabel(cxt); |
| fdisk_enable_details(cxt, org); |
| } |
| |
| return rc; |
| } |
| |
| static int bsd_delete_part( |
| struct fdisk_context *cxt, |
| size_t partnum) |
| { |
| struct bsd_disklabel *d = self_disklabel(cxt); |
| |
| d->d_partitions[partnum].p_size = 0; |
| d->d_partitions[partnum].p_offset = 0; |
| d->d_partitions[partnum].p_fstype = BSD_FS_UNUSED; |
| |
| if (d->d_npartitions == partnum + 1) |
| while (!d->d_partitions[d->d_npartitions - 1].p_size) |
| d->d_npartitions--; |
| |
| cxt->label->nparts_cur = d->d_npartitions; |
| fdisk_label_set_changed(cxt->label, 1); |
| return 0; |
| } |
| |
| static int bsd_list_disklabel(struct fdisk_context *cxt) |
| { |
| struct bsd_disklabel *d = self_disklabel(cxt); |
| |
| assert(cxt); |
| assert(cxt->label); |
| assert(fdisk_is_label(cxt, BSD)); |
| |
| if (fdisk_is_details(cxt)) { |
| fdisk_info(cxt, "# %s:", cxt->dev_path); |
| |
| if ((unsigned) d->d_type < BSD_DKMAXTYPES) |
| fdisk_info(cxt, _("type: %s"), bsd_dktypenames[d->d_type]); |
| else |
| fdisk_info(cxt, _("type: %d"), d->d_type); |
| |
| fdisk_info(cxt, _("disk: %.*s"), (int) sizeof(d->d_typename), d->d_typename); |
| fdisk_info(cxt, _("label: %.*s"), (int) sizeof(d->d_packname), d->d_packname); |
| |
| fdisk_info(cxt, _("flags: %s"), |
| d->d_flags & BSD_D_REMOVABLE ? _(" removable") : |
| d->d_flags & BSD_D_ECC ? _(" ecc") : |
| d->d_flags & BSD_D_BADSECT ? _(" badsect") : ""); |
| |
| /* On various machines the fields of *lp are short/int/long */ |
| /* In order to avoid problems, we cast them all to long. */ |
| fdisk_info(cxt, _("bytes/sector: %ld"), (long) d->d_secsize); |
| fdisk_info(cxt, _("sectors/track: %ld"), (long) d->d_nsectors); |
| fdisk_info(cxt, _("tracks/cylinder: %ld"), (long) d->d_ntracks); |
| fdisk_info(cxt, _("sectors/cylinder: %ld"), (long) d->d_secpercyl); |
| fdisk_info(cxt, _("cylinders: %ld"), (long) d->d_ncylinders); |
| fdisk_info(cxt, _("rpm: %d"), d->d_rpm); |
| fdisk_info(cxt, _("interleave: %d"), d->d_interleave); |
| fdisk_info(cxt, _("trackskew: %d"), d->d_trackskew); |
| fdisk_info(cxt, _("cylinderskew: %d"), d->d_cylskew); |
| fdisk_info(cxt, _("headswitch: %ld (milliseconds)"), (long) d->d_headswitch); |
| fdisk_info(cxt, _("track-to-track seek: %ld (milliseconds)"), (long) d->d_trkseek); |
| } |
| |
| fdisk_info(cxt, _("partitions: %d"), d->d_npartitions); |
| |
| return 0; |
| } |
| |
| 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, d->d_nsectors * d->d_ntracks, |
| d->d_nsectors * d->d_ntracks, |
| _("sectors/cylinder"), &res) == 0) |
| d->d_secpercyl = res; |
| |
| d->d_rpm = ask_uint16(cxt, d->d_rpm, _("rpm")); |
| d->d_interleave = ask_uint16(cxt, d->d_interleave, _("interleave")); |
| d->d_trackskew = ask_uint16(cxt, d->d_trackskew, _("trackskew")); |
| d->d_cylskew = ask_uint16(cxt, d->d_cylskew, _("cylinderskew")); |
| |
| d->d_headswitch = ask_uint32(cxt, d->d_headswitch, _("headswitch")); |
| d->d_trkseek = ask_uint32(cxt, d->d_trkseek, _("track-to-track seek")); |
| |
| d->d_secperunit = d->d_secpercyl * d->d_ncylinders; |
| return 0; |
| } |
| |
| static int bsd_get_bootstrap(struct fdisk_context *cxt, |
| char *path, void *ptr, int size) |
| { |
| int fd; |
| |
| if ((fd = open(path, O_RDONLY)) < 0) { |
| fdisk_warn(cxt, _("cannot open %s"), path); |
| return -errno; |
| } |
| |
| if (read_all(fd, ptr, size) != size) { |
| fdisk_warn(cxt, _("cannot read %s"), path); |
| close(fd); |
| return -errno; |
| } |
| |
| fdisk_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 short *start, *end; |
| unsigned short sum = 0; |
| |
| start = (unsigned short *) lp; |
| end = (unsigned short *) &lp->d_partitions[lp->d_npartitions]; |
| while (start < end) |
| sum ^= *start++; |
| return sum; |
| } |
| |
| static int bsd_initlabel (struct fdisk_context *cxt) |
| { |
| struct fdisk_bsd_label *l = self_label(cxt); |
| struct bsd_disklabel *d = self_disklabel(cxt); |
| struct bsd_partition *pp; |
| |
| memset (d, 0, sizeof (struct bsd_disklabel)); |
| |
| d -> d_magic = BSD_DISKMAGIC; |
| |
| if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0) |
| d -> d_type = BSD_DTYPE_SCSI; |
| else |
| d -> d_type = BSD_DTYPE_ST506; |
| |
| #if !defined (__alpha__) |
| d -> d_flags = BSD_D_DOSPART; |
| #else |
| d -> d_flags = 0; |
| #endif |
| d -> d_secsize = DEFAULT_SECTOR_SIZE; /* bytes/sector */ |
| d -> d_nsectors = cxt->geom.sectors; /* sectors/track */ |
| d -> d_ntracks = cxt->geom.heads; /* tracks/cylinder (heads) */ |
| d -> d_ncylinders = cxt->geom.cylinders; |
| d -> d_secpercyl = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */ |
| if (d -> d_secpercyl == 0) |
| d -> d_secpercyl = 1; /* avoid segfaults */ |
| d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders; |
| |
| d -> d_rpm = 3600; |
| d -> d_interleave = 1; |
| d -> d_trackskew = 0; |
| d -> d_cylskew = 0; |
| d -> d_headswitch = 0; |
| d -> d_trkseek = 0; |
| |
| d -> d_magic2 = BSD_DISKMAGIC; |
| d -> d_bbsize = BSD_BBSIZE; |
| d -> d_sbsize = BSD_SBSIZE; |
| |
| if (l->dos_part) { |
| d->d_npartitions = 4; |
| |
| pp = &d->d_partitions[2]; /* Partition C should be the NetBSD partition */ |
| pp->p_offset = dos_partition_get_start(l->dos_part); |
| pp->p_size = dos_partition_get_size(l->dos_part); |
| pp->p_fstype = BSD_FS_UNUSED; |
| |
| pp = &d -> d_partitions[3]; /* Partition D should be the whole disk */ |
| pp->p_offset = 0; |
| pp->p_size = d->d_secperunit; |
| pp->p_fstype = BSD_FS_UNUSED; |
| } else { |
| d->d_npartitions = 3; |
| |
| pp = &d->d_partitions[2]; /* Partition C should be the whole disk */ |
| pp->p_offset = 0; |
| pp->p_size = d->d_secperunit; |
| pp->p_fstype = BSD_FS_UNUSED; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Read a bsd_disklabel from sector 0 or from the starting sector of p. |
| * If it has the right magic, return 0. |
| */ |
| static int bsd_readlabel(struct fdisk_context *cxt) |
| { |
| struct fdisk_bsd_label *l; |
| struct bsd_disklabel *d; |
| int t; |
| off_t offset = 0; |
| |
| l = self_label(cxt); |
| d = self_disklabel(cxt); |
| |
| if (l->dos_part) |
| /* BSD is nested within DOS partition, get the begin of the |
| * partition. Note that DOS uses native sector size. */ |
| offset = dos_partition_get_start(l->dos_part) * cxt->sector_size; |
| |
| if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) |
| return -1; |
| if (read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0) |
| return errno ? -errno : -1; |
| |
| /* The offset to begin of the disk label. Note that BSD uses |
| * 512-byte (default) sectors. */ |
| memmove(d, &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE |
| + BSD_LABELOFFSET], sizeof(*d)); |
| |
| if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) { |
| DBG(LABEL, 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; |
| |
| 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); |
| |
| 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, |
| .list = bsd_list_disklabel, |
| .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) |
| { |
| struct fdisk_label *lb; |
| struct fdisk_bsd_label *bsd; |
| |
| assert(cxt); |
| |
| bsd = calloc(1, sizeof(*bsd)); |
| if (!bsd) |
| return NULL; |
| |
| /* initialize generic part of the driver */ |
| lb = (struct fdisk_label *) bsd; |
| lb->name = "bsd"; |
| lb->id = FDISK_DISKLABEL_BSD; |
| lb->op = &bsd_operations; |
| lb->parttypes = bsd_fstypes; |
| lb->nparttypes = ARRAY_SIZE(bsd_fstypes) - 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; |
| } |