/* -*- 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;
}