blob: 747a9c58864056dd1b8d5a59b29ce4c58dde18e9 [file] [log] [blame]
/* -*- 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);
}