| /* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- |
| |
| libparted - a library for manipulating disk partitions |
| Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| |
| Contributor: Matt Wilson <msw@redhat.com> |
| */ |
| |
| #include <config.h> |
| |
| #include <parted/parted.h> |
| #include <parted/debug.h> |
| #include <parted/endian.h> |
| |
| #if ENABLE_NLS |
| # include <libintl.h> |
| # define _(String) dgettext (PACKAGE, String) |
| #else |
| # define _(String) (String) |
| #endif /* ENABLE_NLS */ |
| |
| /* struct's & #define's stolen from libfdisk, which probably came from |
| * Linux... |
| */ |
| |
| #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ |
| #define BSD_MAXPARTITIONS 8 |
| #define BSD_FS_UNUSED 0 /* disklabel unused partition entry ID */ |
| #define BSD_LABEL_OFFSET 64 |
| |
| #define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ |
| #define BSD_DTYPE_MSCP 2 /* MSCP */ |
| #define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */ |
| #define BSD_DTYPE_SCSI 4 /* SCSI */ |
| #define BSD_DTYPE_ESDI 5 /* ESDI interface */ |
| #define BSD_DTYPE_ST506 6 /* ST506 etc. */ |
| #define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */ |
| #define BSD_DTYPE_HPFL 8 /* HP Fiber-link */ |
| #define BSD_DTYPE_FLOPPY 10 /* floppy */ |
| |
| #define BSD_BBSIZE 8192 /* size of boot area, with label */ |
| #define BSD_SBSIZE 8192 /* max size of fs superblock */ |
| |
| typedef struct _BSDRawPartition BSDRawPartition; |
| typedef struct _BSDRawLabel BSDRawLabel; |
| |
| struct _BSDRawPartition { /* the partition table */ |
| uint32_t p_size; /* number of sectors in partition */ |
| uint32_t p_offset; /* starting sector */ |
| uint32_t p_fsize; /* file system basic fragment size */ |
| uint8_t p_fstype; /* file system type, see below */ |
| uint8_t p_frag; /* file system fragments per block */ |
| uint16_t p_cpg; /* file system cylinders per group */ |
| } __attribute__((packed)); |
| |
| struct _BSDRawLabel { |
| uint32_t d_magic; /* the magic number */ |
| int16_t d_type; /* drive type */ |
| int16_t d_subtype; /* controller/d_type specific */ |
| int8_t d_typename[16]; /* type name, e.g. "eagle" */ |
| int8_t d_packname[16]; /* pack identifier */ |
| uint32_t d_secsize; /* # of bytes per sector */ |
| uint32_t d_nsectors; /* # of data sectors per track */ |
| uint32_t d_ntracks; /* # of tracks per cylinder */ |
| uint32_t d_ncylinders; /* # of data cylinders per unit */ |
| uint32_t d_secpercyl; /* # of data sectors per cylinder */ |
| uint32_t d_secperunit; /* # of data sectors per unit */ |
| uint16_t d_sparespertrack; /* # of spare sectors per track */ |
| uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ |
| uint32_t d_acylinders; /* # of alt. cylinders per unit */ |
| uint16_t d_rpm; /* rotational speed */ |
| uint16_t d_interleave; /* hardware sector interleave */ |
| uint16_t d_trackskew; /* sector 0 skew, per track */ |
| uint16_t d_cylskew; /* sector 0 skew, per cylinder */ |
| uint32_t d_headswitch; /* head switch time, usec */ |
| uint32_t d_trkseek; /* track-to-track seek, usec */ |
| uint32_t d_flags; /* generic flags */ |
| #define NDDATA 5 |
| uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ |
| #define NSPARE 5 |
| uint32_t d_spare[NSPARE]; /* reserved for future use */ |
| uint32_t d_magic2; /* the magic number (again) */ |
| uint16_t d_checksum; /* xor of data incl. partitions */ |
| |
| /* file system and partition information: */ |
| uint16_t d_npartitions; /* number of partitions in following */ |
| uint32_t d_bbsize; /* size of boot area at sn0, bytes */ |
| uint32_t d_sbsize; /* max size of fs superblock, bytes */ |
| BSDRawPartition d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ |
| } __attribute__((packed)); |
| |
| typedef struct { |
| char boot_code [512]; |
| } BSDDiskData; |
| |
| typedef struct { |
| uint8_t type; |
| } BSDPartitionData; |
| |
| static PedDiskType bsd_disk_type; |
| |
| /* XXX fixme: endian? */ |
| static unsigned short |
| xbsd_dkcksum (BSDRawLabel *lp) { |
| unsigned short *start, *end; |
| unsigned short sum = 0; |
| |
| lp->d_checksum = 0; |
| start = (u_short*) lp; |
| end = (u_short*) &lp->d_partitions [ |
| PED_LE16_TO_CPU (lp->d_npartitions)]; |
| while (start < end) |
| sum ^= *start++; |
| return sum; |
| } |
| |
| /* XXX fixme: endian? */ |
| static void |
| alpha_bootblock_checksum (char *boot) { |
| uint64_t *dp, sum; |
| int i; |
| |
| dp = (uint64_t *)boot; |
| sum = 0; |
| for (i = 0; i < 63; i++) |
| sum += dp[i]; |
| dp[63] = sum; |
| } |
| |
| |
| static int |
| bsd_probe (const PedDevice *dev) |
| { |
| char boot[512]; |
| BSDRawLabel *label; |
| |
| PED_ASSERT (dev != NULL, return 0); |
| |
| if (dev->sector_size != 512) |
| return 0; |
| |
| if (!ped_device_read (dev, boot, 0, 1)) |
| return 0; |
| |
| label = (BSDRawLabel *) (boot + BSD_LABEL_OFFSET); |
| |
| alpha_bootblock_checksum(boot); |
| |
| /* check magic */ |
| if (PED_LE32_TO_CPU (label->d_magic) != BSD_DISKMAGIC) |
| return 0; |
| |
| return 1; |
| } |
| |
| static PedDisk* |
| bsd_alloc (const PedDevice* dev) |
| { |
| PedDisk* disk; |
| BSDDiskData* bsd_specific; |
| BSDRawLabel* label; |
| |
| PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return 0); |
| |
| disk = _ped_disk_alloc ((PedDevice*)dev, &bsd_disk_type); |
| if (!disk) |
| goto error; |
| disk->disk_specific = bsd_specific = ped_malloc (sizeof (BSDDiskData)); |
| if (!bsd_specific) |
| goto error_free_disk; |
| |
| label = (BSDRawLabel*) (bsd_specific->boot_code + BSD_LABEL_OFFSET); |
| memset(label, 0, sizeof(BSDRawLabel)); |
| |
| label->d_magic = PED_CPU_TO_LE32 (BSD_DISKMAGIC); |
| label->d_type = PED_CPU_TO_LE16 (BSD_DTYPE_SCSI); |
| label->d_flags = 0; |
| label->d_secsize = PED_CPU_TO_LE16 (dev->sector_size); |
| label->d_nsectors = PED_CPU_TO_LE32 (dev->bios_geom.sectors); |
| label->d_ntracks = PED_CPU_TO_LE32 (dev->bios_geom.heads); |
| label->d_ncylinders = PED_CPU_TO_LE32 (dev->bios_geom.cylinders); |
| label->d_secpercyl = PED_CPU_TO_LE32 (dev->bios_geom.sectors |
| * dev->bios_geom.heads); |
| label->d_secperunit |
| = PED_CPU_TO_LE32 (dev->bios_geom.sectors |
| * dev->bios_geom.heads |
| * dev->bios_geom.cylinders); |
| |
| label->d_rpm = PED_CPU_TO_LE16 (3600); |
| label->d_interleave = PED_CPU_TO_LE16 (1);; |
| label->d_trackskew = 0; |
| label->d_cylskew = 0; |
| label->d_headswitch = 0; |
| label->d_trkseek = 0; |
| |
| label->d_magic2 = PED_CPU_TO_LE32 (BSD_DISKMAGIC); |
| label->d_bbsize = PED_CPU_TO_LE32 (BSD_BBSIZE); |
| label->d_sbsize = PED_CPU_TO_LE32 (BSD_SBSIZE); |
| |
| label->d_npartitions = 0; |
| label->d_checksum = xbsd_dkcksum (label); |
| return disk; |
| |
| error_free_disk: |
| ped_free (disk); |
| error: |
| return NULL; |
| } |
| |
| static PedDisk* |
| bsd_duplicate (const PedDisk* disk) |
| { |
| PedDisk* new_disk; |
| BSDDiskData* new_bsd_data; |
| BSDDiskData* old_bsd_data = (BSDDiskData*) disk->disk_specific; |
| |
| new_disk = ped_disk_new_fresh (disk->dev, &bsd_disk_type); |
| if (!new_disk) |
| return NULL; |
| |
| new_bsd_data = (BSDDiskData*) new_disk->disk_specific; |
| memcpy (new_bsd_data->boot_code, old_bsd_data->boot_code, 512); |
| return new_disk; |
| } |
| |
| static void |
| bsd_free (PedDisk* disk) |
| { |
| ped_free (disk->disk_specific); |
| _ped_disk_free (disk); |
| } |
| |
| #ifndef DISCOVER_ONLY |
| static int |
| bsd_clobber (PedDevice* dev) |
| { |
| char boot [512]; |
| BSDRawLabel* label = (BSDRawLabel *) (boot + BSD_LABEL_OFFSET); |
| |
| if (!ped_device_read (dev, boot, 0, 1)) |
| return 0; |
| label->d_magic = 0; |
| return ped_device_write (dev, (void*) boot, 0, 1); |
| } |
| #endif /* !DISCOVER_ONLY */ |
| |
| static int |
| bsd_read (PedDisk* disk) |
| { |
| BSDDiskData* bsd_specific = (BSDDiskData*) disk->disk_specific; |
| BSDRawLabel* label; |
| int i; |
| |
| ped_disk_delete_all (disk); |
| |
| if (!ped_device_read (disk->dev, bsd_specific->boot_code, 0, 1)) |
| goto error; |
| label = (BSDRawLabel *) (bsd_specific->boot_code + BSD_LABEL_OFFSET); |
| |
| for (i = 1; i <= BSD_MAXPARTITIONS; i++) { |
| PedPartition* part; |
| BSDPartitionData* bsd_part_data; |
| PedSector start; |
| PedSector end; |
| PedConstraint* constraint_exact; |
| |
| if (!label->d_partitions[i - 1].p_size |
| || !label->d_partitions[i - 1].p_fstype) |
| continue; |
| start = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset); |
| end = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset) |
| + PED_LE32_TO_CPU(label->d_partitions[i - 1].p_size) - 1; |
| part = ped_partition_new (disk, 0, NULL, start, end); |
| if (!part) |
| goto error; |
| bsd_part_data = part->disk_specific; |
| bsd_part_data->type = label->d_partitions[i - 1].p_fstype; |
| part->num = i; |
| part->fs_type = ped_file_system_probe (&part->geom); |
| |
| constraint_exact = ped_constraint_exact (&part->geom); |
| if (!ped_disk_add_partition (disk, part, constraint_exact)) |
| goto error; |
| ped_constraint_destroy (constraint_exact); |
| } |
| |
| return 1; |
| |
| error: |
| return 0; |
| } |
| |
| static void |
| _probe_and_add_boot_code (const PedDisk* disk) |
| { |
| BSDDiskData* bsd_specific; |
| BSDRawLabel* old_label; |
| char old_boot_code [512]; |
| |
| bsd_specific = (BSDDiskData*) disk->disk_specific; |
| old_label = (BSDRawLabel*) (old_boot_code + BSD_LABEL_OFFSET); |
| |
| if (!ped_device_read (disk->dev, old_boot_code, 0, 1)) |
| return; |
| if (old_boot_code [0] |
| && old_label->d_magic == PED_CPU_TO_LE32 (BSD_DISKMAGIC)) |
| memcpy (bsd_specific->boot_code, old_boot_code, 512); |
| } |
| |
| #ifndef DISCOVER_ONLY |
| static int |
| bsd_write (const PedDisk* disk) |
| { |
| BSDDiskData* bsd_specific; |
| BSDRawLabel* label; |
| BSDPartitionData* bsd_data; |
| PedPartition* part; |
| int i; |
| int max_part = 0; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (disk->dev != NULL, return 0); |
| |
| bsd_specific = (BSDDiskData*) disk->disk_specific; |
| label = (BSDRawLabel *) (bsd_specific->boot_code + BSD_LABEL_OFFSET); |
| |
| if (!bsd_specific->boot_code [0]) |
| _probe_and_add_boot_code (disk); |
| |
| memset (label->d_partitions, 0, |
| sizeof (BSDRawPartition) * BSD_MAXPARTITIONS); |
| |
| for (i = 1; i <= BSD_MAXPARTITIONS; i++) { |
| part = ped_disk_get_partition (disk, i); |
| if (!part) |
| continue; |
| bsd_data = part->disk_specific; |
| label->d_partitions[i - 1].p_fstype = bsd_data->type; |
| label->d_partitions[i - 1].p_offset |
| = PED_CPU_TO_LE32 (part->geom.start); |
| label->d_partitions[i - 1].p_size |
| = PED_CPU_TO_LE32 (part->geom.length); |
| max_part = i; |
| } |
| |
| label->d_npartitions = PED_CPU_TO_LE16 (max_part) + 1; |
| label->d_checksum = xbsd_dkcksum (label); |
| |
| alpha_bootblock_checksum (bsd_specific->boot_code); |
| |
| if (!ped_device_write (disk->dev, (void*) bsd_specific->boot_code, |
| 0, 1)) |
| goto error; |
| return ped_device_sync (disk->dev); |
| |
| error: |
| return 0; |
| } |
| #endif /* !DISCOVER_ONLY */ |
| |
| static PedPartition* |
| bsd_partition_new (const PedDisk* disk, PedPartitionType part_type, |
| const PedFileSystemType* fs_type, |
| PedSector start, PedSector end) |
| { |
| PedPartition* part; |
| BSDPartitionData* bsd_data; |
| |
| part = _ped_partition_alloc (disk, part_type, fs_type, start, end); |
| if (!part) |
| goto error; |
| |
| if (ped_partition_is_active (part)) { |
| part->disk_specific |
| = bsd_data = ped_malloc (sizeof (BSDPartitionData)); |
| if (!bsd_data) |
| goto error_free_part; |
| bsd_data->type = 0; |
| } else { |
| part->disk_specific = NULL; |
| } |
| return part; |
| |
| ped_free (bsd_data); |
| error_free_part: |
| ped_free (part); |
| error: |
| return 0; |
| } |
| |
| static PedPartition* |
| bsd_partition_duplicate (const PedPartition* part) |
| { |
| PedPartition* new_part; |
| BSDPartitionData* new_bsd_data; |
| BSDPartitionData* old_bsd_data; |
| |
| new_part = ped_partition_new (part->disk, part->type, |
| part->fs_type, part->geom.start, |
| part->geom.end); |
| if (!new_part) |
| return NULL; |
| new_part->num = part->num; |
| |
| old_bsd_data = (BSDPartitionData*) part->disk_specific; |
| new_bsd_data = (BSDPartitionData*) new_part->disk_specific; |
| new_bsd_data->type = old_bsd_data->type; |
| return new_part; |
| } |
| |
| static void |
| bsd_partition_destroy (PedPartition* part) |
| { |
| PED_ASSERT (part != NULL, return); |
| |
| if (ped_partition_is_active (part)) |
| ped_free (part->disk_specific); |
| _ped_partition_free (part); |
| } |
| |
| static int |
| bsd_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) |
| { |
| BSDPartitionData* bsd_data = part->disk_specific; |
| |
| part->fs_type = fs_type; |
| |
| if (!fs_type) |
| bsd_data->type = 0x8; |
| else if (!strcmp (fs_type->name, "linux-swap")) |
| bsd_data->type = 0x1; |
| else |
| bsd_data->type = 0x8; |
| |
| return 1; |
| } |
| |
| static int |
| bsd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) |
| { |
| /* no flags for bsd */ |
| return 0; |
| } |
| |
| static int |
| bsd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) |
| { |
| /* no flags for bsd */ |
| return 0; |
| } |
| |
| static int |
| bsd_partition_is_flag_available (const PedPartition* part, |
| PedPartitionFlag flag) |
| { |
| /* no flags for bsd */ |
| return 0; |
| } |
| |
| |
| static int |
| bsd_get_max_primary_partition_count (const PedDisk* disk) |
| { |
| return BSD_MAXPARTITIONS; |
| } |
| |
| static PedConstraint* |
| _get_constraint (const PedDevice* dev) |
| { |
| PedGeometry max; |
| |
| ped_geometry_init (&max, dev, 1, dev->length - 1); |
| return ped_constraint_new_from_max (&max); |
| } |
| |
| static int |
| bsd_partition_align (PedPartition* part, const PedConstraint* constraint) |
| { |
| if (_ped_partition_attempt_align (part, constraint, |
| _get_constraint (part->disk->dev))) |
| return 1; |
| |
| #ifndef DISCOVER_ONLY |
| ped_exception_throw ( |
| PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("Unable to satisfy all constraints on the partition.")); |
| #endif |
| return 0; |
| } |
| |
| static int |
| bsd_partition_enumerate (PedPartition* part) |
| { |
| int i; |
| PedPartition* p; |
| |
| /* never change the partition numbers */ |
| if (part->num != -1) |
| return 1; |
| for (i = 1; i <= BSD_MAXPARTITIONS; i++) { |
| p = ped_disk_get_partition (part->disk, i); |
| if (!p) { |
| part->num = i; |
| return 1; |
| } |
| } |
| |
| /* failed to allocate a number */ |
| #ifndef DISCOVER_ONLY |
| ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, |
| _("Unable to allocate a bsd disklabel slot.")); |
| #endif |
| return 0; |
| } |
| |
| static int |
| bsd_alloc_metadata (PedDisk* disk) |
| { |
| PedPartition* new_part; |
| PedConstraint* constraint_any = NULL; |
| |
| PED_ASSERT (disk != NULL, goto error); |
| PED_ASSERT (disk->dev != NULL, goto error); |
| |
| constraint_any = ped_constraint_any (disk->dev); |
| |
| /* allocate 1 sector for the disk label at the start */ |
| new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, 0, 0); |
| if (!new_part) |
| goto error; |
| |
| if (!ped_disk_add_partition (disk, new_part, constraint_any)) { |
| ped_partition_destroy (new_part); |
| goto error; |
| } |
| |
| ped_constraint_destroy (constraint_any); |
| return 1; |
| error: |
| ped_constraint_destroy (constraint_any); |
| return 0; |
| } |
| |
| static PedDiskOps bsd_disk_ops = { |
| probe: bsd_probe, |
| #ifndef DISCOVER_ONLY |
| clobber: bsd_clobber, |
| #else |
| clobber: NULL, |
| #endif |
| alloc: bsd_alloc, |
| duplicate: bsd_duplicate, |
| free: bsd_free, |
| read: bsd_read, |
| #ifndef DISCOVER_ONLY |
| write: bsd_write, |
| #else |
| write: NULL, |
| #endif |
| |
| partition_new: bsd_partition_new, |
| partition_duplicate: bsd_partition_duplicate, |
| partition_destroy: bsd_partition_destroy, |
| partition_set_system: bsd_partition_set_system, |
| partition_set_flag: bsd_partition_set_flag, |
| partition_get_flag: bsd_partition_get_flag, |
| partition_is_flag_available: bsd_partition_is_flag_available, |
| partition_set_name: NULL, |
| partition_get_name: NULL, |
| partition_align: bsd_partition_align, |
| partition_enumerate: bsd_partition_enumerate, |
| |
| alloc_metadata: bsd_alloc_metadata, |
| get_max_primary_partition_count: |
| bsd_get_max_primary_partition_count |
| }; |
| |
| static PedDiskType bsd_disk_type = { |
| next: NULL, |
| name: "bsd", |
| ops: &bsd_disk_ops, |
| features: 0 |
| }; |
| |
| void |
| ped_disk_bsd_init () |
| { |
| PED_ASSERT (sizeof (BSDRawPartition) == 16, return); |
| PED_ASSERT (sizeof (BSDRawLabel) == 276, return); |
| |
| ped_disk_type_register (&bsd_disk_type); |
| } |
| |
| void |
| ped_disk_bsd_done () |
| { |
| ped_disk_type_unregister (&bsd_disk_type); |
| } |