/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- libparted - a library for manipulating disk partitions Copyright (C) 1998-2000 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contributor: Ben Collins */ #include "config.h" #include #include #include #include #include #if ENABLE_NLS # define _(String) dgettext (PACKAGE, String) #else # define _(String) (String) #endif /* ENABLE_NLS */ /* Most of this came from util-linux's sun support, which was mostly done by Jakub Jelinek. */ #define SUN_DISK_MAGIC 0xDABE /* Disk magic number */ #define SUN_DISK_MAXPARTITIONS 8 #define WHOLE_DISK_ID 5 #define WHOLE_DISK_PART 2 /* as in 0, 1, 2 (3rd partition) */ typedef struct { u_int32_t start_cylinder; /* where the part starts... */ u_int32_t num_sectors; /* ...and it's length */ } __attribute__ ((packed)) SUNRawPartition; typedef struct { u_int8_t spare1; u_int8_t id; /* Partition type */ u_int8_t spare2; u_int8_t flags; /* Partition flags */ } __attribute__ ((packed)) SUNPartitionInfo; typedef struct { u_int8_t info[128]; /* Informative text string */ u_int8_t spare0[14]; SUNPartitionInfo infos[SUN_DISK_MAXPARTITIONS]; u_int8_t spare1[246]; /* Boot information etc. */ u_int16_t rspeed; /* Disk rotational speed */ u_int16_t pcylcount; /* Physical cylinder count */ u_int16_t sparecyl; /* extra sects per cylinder */ u_int8_t spare2[4]; /* More magic... */ u_int16_t ilfact; /* Interleave factor */ u_int16_t ncyl; /* Data cylinder count */ u_int16_t nacyl; /* Alt. cylinder count */ u_int16_t ntrks; /* Tracks per cylinder */ u_int16_t nsect; /* Sectors per track */ u_int8_t spare3[4]; /* Even more magic... */ SUNRawPartition partitions[SUN_DISK_MAXPARTITIONS]; u_int16_t magic; /* Magic number */ u_int16_t csum; /* Label xor'd checksum */ } __attribute__ ((packed)) SUNRawLabel; static int sun_probe (const PedDevice *dev); static PedDisk* sun_open (PedDevice* dev); static PedDisk* sun_create (PedDevice* dev); static int sun_clobber (PedDevice* dev); static int sun_close (PedDisk* disk); static int sun_read (PedDisk* disk); static int sun_write (PedDisk* disk); static PedPartition* sun_partition_new ( const PedDisk* disk, PedPartitionType part_type, const PedFileSystemType* fs_type, PedSector start, PedSector end); static void sun_partition_destroy (PedPartition* part); static int sun_partition_set_flag ( PedPartition* part, PedPartitionFlag flag, int state); static int sun_partition_get_flag ( const PedPartition* part, PedPartitionFlag flag); static int sun_partition_is_flag_available ( const PedPartition* part, PedPartitionFlag flag); static int sun_partition_align (PedPartition* part, const PedConstraint* constraint); static int sun_partition_enumerate (PedPartition* part); static int sun_get_max_primary_partition_count (const PedDisk* disk); static int sun_alloc_metadata (PedDisk* disk); static PedDiskOps sun_disk_ops = { probe: sun_probe, open: sun_open, close: sun_close, read: sun_read, write: sun_write, create: sun_create, clobber: sun_clobber, partition_new: sun_partition_new, partition_destroy: sun_partition_destroy, partition_set_flag: sun_partition_set_flag, partition_get_flag: sun_partition_get_flag, partition_is_flag_available: sun_partition_is_flag_available, partition_align: sun_partition_align, partition_enumerate: sun_partition_enumerate, alloc_metadata: sun_alloc_metadata, get_max_primary_partition_count: sun_get_max_primary_partition_count, partition_set_name: NULL, partition_get_name: NULL, partition_set_extended_system: NULL, }; static PedDiskType sun_disk_type = { next: NULL, name: "sun", ops: &sun_disk_ops, features: 0 }; void ped_disk_sun_init () { PED_ASSERT (sizeof (SUNRawLabel) == 512, return); ped_register_disk_type (&sun_disk_type); } void ped_disk_sun_done () { ped_unregister_disk_type (&sun_disk_type); } /* Checksum computation */ static void sun_compute_checksum (SUNRawLabel *label) { u_int16_t *ush = (u_int16_t *)label; u_int16_t csum = 0; while(ush < (u_int16_t *)(&label->csum)) csum ^= *ush++; label->csum = csum; } /* Checksum Verification */ static int sun_verify_checksum (SUNRawLabel *label) { u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1; u_int16_t csum = 0; while (ush >= (u_int16_t *)label) csum ^= *ush--; return !csum; } static PedDisk* sun_alloc (PedDevice* dev) { PedDisk* disk; PED_ASSERT (dev != NULL, return NULL); if (!ped_device_open ((PedDevice*) dev)) goto error; if (dev->length < 512) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("%s is too small for a Sun disk label!"), dev->path); goto error_close_dev; } disk = ped_disk_alloc (dev, &sun_disk_type); if (!disk) goto error_close_dev; disk->disk_specific = (SUNDiskData*) ped_malloc (sizeof (SUNDiskData)); if (!disk->disk_specific) goto error_free_disk; return disk; error_free_disk: ped_disk_free (disk); error_close_dev: ped_device_close ((PedDevice*) dev); error: return NULL; } static int sun_free (PedDisk *disk) { ped_device_close (disk->dev); ped_free (disk->disk_specific); ped_disk_free (disk); return 1; } static int sun_probe (const PedDevice *dev) { PedDiskType* disk_type; SUNRawLabel label; int i; PED_ASSERT (dev != NULL, return 0); if (!ped_device_open ((PedDevice*) dev)) return 0; if (!ped_device_read (dev, &label, 0, 1)) { ped_device_close ((PedDevice*) dev); return 0; } ped_device_close ((PedDevice*) dev); /* check magic */ if (PED_BE16_TO_CPU (label.magic) != SUN_DISK_MAGIC) return 0; if (!sun_verify_checksum(&label)) { return ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, _("Corrupted Sun disk label detected.")); } return 1; } static PedDisk* sun_open (PedDevice* dev) { PedDisk* disk; disk = sun_alloc(dev); if (!disk) goto error; if (!sun_read (disk)) goto error_free_disk; return disk; error_free_disk: sun_free (disk); error: return NULL; } static int sun_close (PedDisk* disk) { PED_ASSERT (disk != NULL, return 0); sun_free(disk); return 1; } static PedDisk* sun_create (PedDevice* dev) { SUNRawLabel label; PED_ASSERT (dev != NULL, return 0); if (!ped_device_open ((PedDevice*) dev)) goto error; memset(&label, 0, sizeof(SUNRawLabel)); /* util-linux's fdisk asks about these...I'm just going to use defaults since most people do anyway...sue me. */ label.magic = PED_CPU_TO_BE16 (SUN_DISK_MAGIC); label.nacyl = PED_CPU_TO_BE16 (2); label.pcylcount = PED_CPU_TO_BE16 (dev->cylinders); label.rspeed = PED_CPU_TO_BE16 (5400); label.ilfact = PED_CPU_TO_BE16 (1); label.sparecyl = PED_CPU_TO_BE16 (dev->sectors); label.ntrks = PED_CPU_TO_BE16 (dev->heads); label.nsect = PED_CPU_TO_BE16 (dev->sectors); label.ncyl = PED_CPU_TO_BE16 (dev->cylinders - 2); /* Add a whole disk partition at a minimum */ label.infos[WHOLE_DISK_PART].id = WHOLE_DISK_ID; label.partitions[WHOLE_DISK_PART].start_cylinder = 0; label.partitions[WHOLE_DISK_PART].num_sectors = PED_CPU_TO_BE32(label.ntrks * label.nsect * label.ncyl); /* Now a neato string to describe this label */ snprintf(label.info, sizeof(label.info) - 1, "GNU Parted Custom cyl %d alt 2 hd %d sec %d", PED_BE16_TO_CPU(label.ncyl), PED_BE16_TO_CPU(label.ntrks), PED_BE16_TO_CPU(label.nsect)); sun_compute_checksum(&label); if (!ped_device_write (dev, &label, 0, 1)) goto error_close_dev; if (!ped_device_sync (dev)) goto error_close_dev; ped_device_close (dev); return sun_open (dev); error_close_dev: ped_device_close (dev); error: return 0; } static int sun_clobber (PedDevice* dev) { SUNRawLabel label; PED_ASSERT (dev != NULL, return 0); PED_ASSERT (sun_probe (dev), return 0); if (!ped_device_open ((PedDevice*) dev)) goto error; if (!ped_device_read (dev, &label, 0, 1)) goto error_close_dev; label.magic = 0; if (!ped_device_write (dev, &label, 0, 1)) goto error_close_dev; ped_device_close (dev); return 1; error_close_dev: ped_device_close (dev); error: return 0; } static int sun_read (PedDisk* disk) { SUNRawLabel label; SUNPartitionData* sun_data; SUNDiskData* disk_data; int i, s; PedPartition* part; PedSector cyl_size, end, start; PedConstraint* constraint_exact; PED_ASSERT (disk != NULL, return 0); PED_ASSERT (disk->dev != NULL, return 0); PED_ASSERT (disk->disk_specific != NULL, return 0); ped_disk_delete_all (disk); if (!ped_device_open ((PedDevice*) disk->dev)) goto error; if (!ped_device_read (disk->dev, &label, 0, 1)) goto error_close_dev; /* Unfortunately, this may not be the same as the physical settings. So we use the label's values. */ disk_data = disk->disk_specific; disk_data->block_size = PED_BE16_TO_CPU(label.ntrks) * PED_BE16_TO_CPU(label.nsect); disk_data->length = disk_data->block_size * PED_BE16_TO_CPU(label.ncyl); for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { if (PED_BE32_TO_CPU(label.partitions[i].num_sectors) && label.infos[i].id && label.infos[i].id != WHOLE_DISK_ID) { start = PED_BE32_TO_CPU(label.partitions[i].start_cylinder) * disk_data->block_size; end = start + PED_BE32_TO_CPU(label.partitions[i].num_sectors) - 1; part = ped_partition_new (disk, PED_PARTITION_PRIMARY, NULL, start, end); if (!part) goto error_close_dev; sun_data = part->disk_specific; sun_data->type = PED_BE32_TO_CPU(label.infos[i].id); part->num = i + 1; 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_close_dev; ped_constraint_destroy (constraint_exact); } } ped_device_close ((PedDevice*) disk->dev); return 1; error_close_dev: ped_device_close ((PedDevice*) disk->dev); error: return 0; } static int sun_write (PedDisk* disk) { SUNRawLabel label; SUNPartitionData* sun_data; SUNDiskData* disk_data; PedPartition* part; PedSector length; int i; PED_ASSERT (disk != NULL, return 0); PED_ASSERT (disk->dev != NULL, return 0); if (!ped_device_read (disk->dev, &label, 0, 1)) return 0; memset (label.partitions, 0, sizeof (SUNRawPartition) * SUN_DISK_MAXPARTITIONS); memset (label.infos, 0, sizeof (SUNPartitionInfo) * SUN_DISK_MAXPARTITIONS); disk_data = disk->disk_specific; for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { part = ped_disk_get_partition (disk, i + 1); if (!part) { if (i != WHOLE_DISK_PART) continue; /* Ok, nothing explicitly in the whole disk partition, so let's put it there for safety sake. */ label.infos[i].id = WHOLE_DISK_ID; label.partitions[i].start_cylinder = 0; label.partitions[i].num_sectors = PED_CPU_TO_BE32(disk_data->length); continue; } sun_data = part->disk_specific; label.infos[i].id = sun_data->type; length = part->geom.end - part->geom.start + 1; label.partitions[i].start_cylinder = PED_CPU_TO_BE32 (part->geom.start) / disk_data->block_size; label.partitions[i].num_sectors = PED_CPU_TO_BE32 (length); } sun_compute_checksum(&label); if (!ped_device_write (disk->dev, &label, 0, 1)) return 0; if (!ped_device_sync (disk->dev)) return 0; return 1; } static PedPartition* sun_partition_new (const PedDisk* disk, PedPartitionType part_type, const PedFileSystemType* fs_type, PedSector start, PedSector end) { PedPartition* part; SUNPartitionData* sun_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 = sun_data = ped_malloc (sizeof (SUNPartitionData)); if (!sun_data) goto error_free_part; sun_data->type = 0; } else { part->disk_specific = NULL; } return part; error_free_sun_data: ped_free (sun_data); error_free_part: ped_free (part); error: return NULL; } static void sun_partition_destroy (PedPartition* part) { PED_ASSERT (part != NULL, return); if (ped_partition_is_active (part)) ped_free (part->disk_specific); ped_free (part); } static int sun_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) { /* no flags for sun */ return 0; } static int sun_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) { /* no flags for sun */ return 0; } static int sun_partition_is_flag_available (const PedPartition* part, PedPartitionFlag flag) { /* no flags for sun */ return 0; } static int sun_get_max_primary_partition_count (const PedDisk* disk) { return SUN_DISK_MAXPARTITIONS; } static PedConstraint* _primary_constraint (PedDisk* disk) { PedAlignment start_align; PedAlignment end_align; PedGeometry max_geom; SUNDiskData* disk_data = disk->disk_specific; if (!ped_alignment_init (&start_align, 0, disk_data->block_size)) return NULL; if (!ped_alignment_init (&end_align, -1, disk_data->block_size)) return NULL; if (!ped_geometry_init (&max_geom, disk, 0, disk_data->length)) return NULL; return ped_constraint_new (&start_align, &end_align, &max_geom, &max_geom, 1); } /* argghh! this should be somewhere else... but where?! */ static int _try_constraint (PedPartition* part, const PedConstraint* external, PedConstraint* internal) { PedConstraint* intersection; PedGeometry* solution; intersection = ped_constraint_intersect (external, internal); ped_constraint_destroy (internal); if (!intersection) goto fail; solution = ped_constraint_solve_nearest (intersection, &part->geom); if (!solution) goto fail_free_intersection; ped_geometry_set (&part->geom, solution->start, solution->length); ped_geometry_destroy (solution); ped_constraint_destroy (intersection); return 1; fail_free_intersection: ped_constraint_destroy (intersection); fail: return 0; } static int sun_partition_align (PedPartition* part, const PedConstraint* constraint) { PED_ASSERT (part != NULL, return 0); if (!_try_constraint (part, constraint, _primary_constraint (part->geom.disk))) { ped_exception_throw ( PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Unable to align partition.")); return 0; } return 1; } static int sun_partition_enumerate (PedPartition* part) { int i; PedPartition* p; /* never change the partition numbers */ if (part->num != -1) return 1; for (i = 1; i <= SUN_DISK_MAXPARTITIONS; i++) { /* skip the Whole Disk partition for now */ if (i == WHOLE_DISK_PART + 1) continue; p = ped_disk_get_partition (part->geom.disk, i); if (!p) { part->num = i; return 1; } } /* Ok, now allocate the Whole disk if it isn't already */ p = ped_disk_get_partition (part->geom.disk, WHOLE_DISK_PART + 1); if (!p) { int i = ped_exception_throw(PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL, _("The Whole Disk partion is the only available one " "left. Generally it is not a good " "to overwrite this partition with a real one. For " "starters, Solaris may not be able to boot without " "it.")); if (i == PED_EXCEPTION_IGNORE) { /* bad bad bad...you will suffer your own fate */ part->num = WHOLE_DISK_PART + 1; return 1; } } /* failed to allocate a number */ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, _("Unable to allocate a sun disklabel slot")); return 0; } static int sun_alloc_metadata (PedDisk* disk) { PedPartition* new_part; PedConstraint* constraint_any = ped_constraint_any (disk); PED_ASSERT (disk != NULL, goto error); PED_ASSERT (disk->disk_specific != NULL, return 0); PED_ASSERT (disk->dev != NULL, goto error); #if 0 /* allocate 1 sector for the disk label at the start */ new_part = ped_partition_new (disk, PED_PARTITION_PRIMARY | 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; } #endif ped_constraint_destroy (constraint_any); return 1; error: ped_constraint_destroy (constraint_any); return 0; }