blob: f11bc5289f939d6ce5c62a7c35218b2cf9f16213 [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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contributor: Phil Knirsch <phil@redhat.de>
Harald Hoyer <harald@redhat.de>
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <parted/vtoc.h>
#include <parted/fdasd.h>
#include <parted/linux.h>
#include <libintl.h>
#if ENABLE_NLS
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#define PARTITION_LINUX_SWAP 0x82
#define PARTITION_LINUX 0x83
#define PARTITION_LINUX_EXT 0x85
#define PARTITION_LINUX_LVM 0x8e
#define PARTITION_LINUX_RAID 0xfd
#define PARTITION_LINUX_LVM_OLD 0xfe
extern void ped_disk_dasd_init ();
extern void ped_disk_dasd_done ();
#define DASD_NAME "dasd"
typedef struct {
int type;
int system;
int raid;
int lvm;
void *part_info;
} DasdPartitionData;
typedef struct {
unsigned int real_sector_size;
unsigned int format_type;
/* IBM internal dasd structure (i guess ;), required. */
struct fdasd_anchor *anchor;
} DasdDiskSpecific;
static int dasd_probe (const PedDevice *dev);
static int dasd_clobber (PedDevice* dev);
static int dasd_read (PedDisk* disk);
static int dasd_write (const PedDisk* disk);
static PedPartition* dasd_partition_new (const PedDisk* disk,
PedPartitionType part_type,
const PedFileSystemType* fs_type,
PedSector start,
PedSector end);
static void dasd_partition_destroy (PedPartition* part);
static int dasd_partition_set_flag (PedPartition* part,
PedPartitionFlag flag,
int state);
static int dasd_partition_get_flag (const PedPartition* part,
PedPartitionFlag flag);
static int dasd_partition_is_flag_available (const PedPartition* part,
PedPartitionFlag flag);
static int dasd_partition_align (PedPartition* part,
const PedConstraint* constraint);
static int dasd_partition_enumerate (PedPartition* part);
static int dasd_get_max_primary_partition_count (const PedDisk* disk);
static PedDisk* dasd_alloc (const PedDevice* dev);
static PedDisk* dasd_duplicate (const PedDisk* disk);
static void dasd_free (PedDisk* disk);
static int dasd_partition_set_system (PedPartition* part,
const PedFileSystemType* fs_type);
static int dasd_alloc_metadata (PedDisk* disk);
static PedDiskOps dasd_disk_ops = {
probe: dasd_probe,
clobber: dasd_clobber,
read: dasd_read,
write: dasd_write,
alloc: dasd_alloc,
duplicate: dasd_duplicate,
free: dasd_free,
partition_set_system: dasd_partition_set_system,
partition_new: dasd_partition_new,
partition_destroy: dasd_partition_destroy,
partition_set_flag: dasd_partition_set_flag,
partition_get_flag: dasd_partition_get_flag,
partition_is_flag_available: dasd_partition_is_flag_available,
partition_set_name: NULL,
partition_get_name: NULL,
partition_align: dasd_partition_align,
partition_enumerate: dasd_partition_enumerate,
alloc_metadata: dasd_alloc_metadata,
get_max_primary_partition_count: dasd_get_max_primary_partition_count,
partition_duplicate: NULL
};
static PedDiskType dasd_disk_type = {
next: NULL,
name: "dasd",
ops: &dasd_disk_ops,
features: 0
};
static PedDisk*
dasd_alloc (const PedDevice* dev)
{
PedDisk* disk;
LinuxSpecific* arch_specific;
DasdDiskSpecific *disk_specific;
PED_ASSERT (dev != NULL, return NULL);
arch_specific = LINUX_SPECIFIC (dev);
disk = _ped_disk_alloc (dev, &dasd_disk_type);
if (!disk)
return NULL;
disk->disk_specific = disk_specific = ped_malloc(sizeof(DasdDiskSpecific));
if (!disk->disk_specific) {
ped_free (disk);
return NULL;
}
/* because we lie to parted we have to compensate with the
real sector size. Record that now. */
if (ioctl(arch_specific->fd, BLKSSZGET,
&disk_specific->real_sector_size) == -1) {
ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Unable to determine the block "
"size of this dasd"));
ped_free(disk_specific);
ped_free(disk);
return NULL;
}
return disk;
}
static PedDisk*
dasd_duplicate (const PedDisk* disk)
{
PedDisk* new_disk;
new_disk = ped_disk_new_fresh(disk->dev, &dasd_disk_type);
if (!new_disk)
return NULL;
new_disk->disk_specific = NULL;
return new_disk;
}
static void
dasd_free (PedDisk* disk)
{
PED_ASSERT(disk != NULL, return);
_ped_disk_free(disk);
}
void
ped_disk_dasd_init ()
{
ped_disk_type_register(&dasd_disk_type);
}
void
ped_disk_dasd_done ()
{
ped_disk_type_unregister(&dasd_disk_type);
}
static int
dasd_probe (const PedDevice *dev)
{
char *errstr = 0;
LinuxSpecific* arch_specific;
struct fdasd_anchor anchor;
PED_ASSERT(dev != NULL, return 0);
PED_ASSERT((dev->type == PED_DEVICE_DASD
|| dev->type == PED_DEVICE_VIODASD), return 0);
arch_specific = LINUX_SPECIFIC(dev);
/* add partition test here */
fdasd_initialize_anchor(&anchor);
fdasd_get_geometry(&anchor, arch_specific->fd);
fdasd_check_api_version(&anchor, arch_specific->fd);
if (fdasd_check_volume(&anchor, arch_specific->fd))
goto error_cleanup;
fdasd_cleanup(&anchor);
return 1;
error_cleanup:
fdasd_cleanup(&anchor);
ped_exception_throw(PED_EXCEPTION_ERROR,PED_EXCEPTION_IGNORE_CANCEL,errstr);
return 0;
}
static int
dasd_clobber (PedDevice* dev)
{
LinuxSpecific* arch_specific;
struct fdasd_anchor anchor;
PED_ASSERT(dev != NULL, return 0);
arch_specific = LINUX_SPECIFIC(dev);
fdasd_initialize_anchor(&anchor);
fdasd_get_geometry(&anchor, arch_specific->fd);
fdasd_recreate_vtoc(&anchor);
fdasd_write_labels(&anchor, arch_specific->fd);
return 1;
}
static int
dasd_read (PedDisk* disk)
{
int i;
char str[20];
PedDevice* dev;
PedPartition* part;
PedSector start, end;
PedConstraint* constraint_exact;
partition_info_t *p;
LinuxSpecific* arch_specific;
DasdDiskSpecific* disk_specific;
PDEBUG;
PED_ASSERT (disk != NULL, return 0);
PDEBUG;
PED_ASSERT (disk->dev != NULL, return 0);
PDEBUG;
dev = disk->dev;
arch_specific = LINUX_SPECIFIC(dev);
disk_specific = disk->disk_specific;
disk_specific->anchor = ped_malloc(sizeof(fdasd_anchor_t));
PDEBUG;
fdasd_initialize_anchor(disk_specific->anchor);
fdasd_get_geometry(disk_specific->anchor, arch_specific->fd);
/* check dasd for labels and vtoc */
if (fdasd_check_volume(disk_specific->anchor, arch_specific->fd))
goto error_close_dev;
if ((disk_specific->anchor->geo.cylinders
* disk_specific->anchor->geo.heads) > BIG_DISK_SIZE)
disk_specific->anchor->big_disk++;
ped_disk_delete_all (disk);
if (strncmp(disk_specific->anchor->vlabel->volkey,
vtoc_ebcdic_enc ("LNX1", str, 4), 4) == 0) {
DasdPartitionData* dasd_data;
/* LDL format, old one */
disk_specific->format_type = 1;
start = 24;
end = (long long)(long long) disk_specific->anchor->geo.cylinders
* (long long)disk_specific->anchor->geo.heads
* (long long)disk->dev->hw_geom.sectors
* (long long)disk_specific->real_sector_size
/ (long long)disk->dev->sector_size - 1;
part = ped_partition_new (disk, PED_PARTITION_PROTECTED, NULL, start, end);
if (!part)
goto error_close_dev;
part->num = 1;
part->fs_type = ped_file_system_probe (&part->geom);
dasd_data = part->disk_specific;
dasd_data->raid = 0;
dasd_data->lvm = 0;
dasd_data->type = 0;
if (!ped_disk_add_partition (disk, part, NULL))
goto error_close_dev;
return 1;
}
/* CDL format, newer */
disk_specific->format_type = 2;
p = disk_specific->anchor->first;
PDEBUG;
for (i = 1 ; i <= USABLE_PARTITIONS; i++) {
char *ch = p->f1->DS1DSNAM;
DasdPartitionData* dasd_data;
if (p->used != 0x01)
continue;
PDEBUG;
start = (long long)(long long) p->start_trk
* (long long) disk->dev->hw_geom.sectors
* (long long) disk_specific->real_sector_size
/ (long long) disk->dev->sector_size;
end = (long long)((long long) p->end_trk + 1)
* (long long) disk->dev->hw_geom.sectors
* (long long) disk_specific->real_sector_size
/ (long long) disk->dev->sector_size - 1;
part = ped_partition_new(disk, 0, NULL, start, end);
PDEBUG;
if (!part)
goto error_close_dev;
PDEBUG;
part->num = i;
part->fs_type = ped_file_system_probe(&part->geom);
vtoc_ebcdic_dec(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44);
ch = strstr(p->f1->DS1DSNAM, "PART");
if (ch != NULL) {
strncpy(str, ch+9, 6);
str[6] = '\0';
}
dasd_data = part->disk_specific;
if (strncmp(PART_TYPE_RAID, str, 6) == 0)
ped_partition_set_flag(part, PED_PARTITION_RAID, 1);
else
ped_partition_set_flag(part, PED_PARTITION_RAID, 0);
if (strncmp(PART_TYPE_LVM, str, 6) == 0)
ped_partition_set_flag(part, PED_PARTITION_LVM, 1);
else
ped_partition_set_flag(part, PED_PARTITION_LVM, 0);
if (strncmp(PART_TYPE_SWAP, str, 6) == 0) {
dasd_data->system = PARTITION_LINUX_SWAP;
PDEBUG;
}
vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44);
dasd_data->part_info = (void *) p;
dasd_data->type = 0;
constraint_exact = ped_constraint_exact (&part->geom);
if (!constraint_exact)
goto error_close_dev;
if (!ped_disk_add_partition(disk, part, constraint_exact))
goto error_close_dev;
ped_constraint_destroy(constraint_exact);
if (p->fspace_trk > 0) {
start = (long long)((long long) p->end_trk + 1)
* (long long) disk->dev->hw_geom.sectors
* (long long) disk_specific->real_sector_size
/ (long long) disk->dev->sector_size;
end = (long long)((long long) p->end_trk + 1 + p->fspace_trk)
* (long long) disk->dev->hw_geom.sectors
* (long long) disk_specific->real_sector_size
/ (long long) disk->dev->sector_size - 1;
part = ped_partition_new (disk, 0, NULL, start, end);
if (!part)
goto error_close_dev;
part->type = PED_PARTITION_FREESPACE;
constraint_exact = ped_constraint_exact(&part->geom);
if (!constraint_exact)
goto error_close_dev;
if (!ped_disk_add_partition(disk, part, constraint_exact))
goto error_close_dev;
ped_constraint_destroy (constraint_exact);
}
p = p->next;
}
PDEBUG;
return 1;
error_close_dev:
PDEBUG;
return 0;
}
static int
dasd_update_type (const PedDisk* disk)
{
PedPartition* part;
LinuxSpecific* arch_specific;
DasdDiskSpecific* disk_specific;
arch_specific = LINUX_SPECIFIC(disk->dev);
disk_specific = disk->disk_specific;
PDEBUG;
for (part = ped_disk_next_partition(disk, NULL); part;
part = ped_disk_next_partition(disk, part)) {
partition_info_t *p;
char *ch = NULL;
DasdPartitionData* dasd_data;
PDEBUG;
if (part->type & PED_PARTITION_FREESPACE
|| part->type & PED_PARTITION_METADATA)
continue;
PDEBUG;
dasd_data = part->disk_specific;
p = dasd_data->part_info;
if (!p ) {
PDEBUG;
continue;
}
vtoc_ebcdic_dec(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44);
ch = strstr(p->f1->DS1DSNAM, "PART");
PDEBUG;
if (ch == NULL) {
vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44);
PDEBUG;
continue;
}
ch += 9;
switch (dasd_data->system) {
case PARTITION_LINUX_LVM:
PDEBUG;
strncpy(ch, PART_TYPE_LVM, 6);
break;
case PARTITION_LINUX_RAID:
PDEBUG;
strncpy(ch, PART_TYPE_RAID, 6);
break;
case PARTITION_LINUX:
PDEBUG;
strncpy(ch, PART_TYPE_NATIVE, 6);
break;
case PARTITION_LINUX_SWAP:
PDEBUG;
strncpy(ch, PART_TYPE_SWAP, 6);
break;
default:
PDEBUG;
strncpy(ch, PART_TYPE_NATIVE, 6);
break;
}
disk_specific->anchor->vtoc_changed++;
vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44);
}
return 1;
}
static int
dasd_write (const PedDisk* disk)
{
DasdPartitionData* dasd_data;
PedPartition* part;
int i;
partition_info_t *p;
LinuxSpecific* arch_specific;
DasdDiskSpecific* disk_specific;
PED_ASSERT(disk != NULL, return 0);
PED_ASSERT(disk->dev != NULL, return 0);
arch_specific = LINUX_SPECIFIC (disk->dev);
disk_specific = disk->disk_specific;
PDEBUG;
/* If formated in LDL, don't write anything. */
if (disk_specific->format_type == 1)
return 1;
/* XXX re-initialize anchor? */
fdasd_initialize_anchor(disk_specific->anchor);
fdasd_get_geometry(disk_specific->anchor, arch_specific->fd);
/* check dasd for labels and vtoc */
if (fdasd_check_volume(disk_specific->anchor, arch_specific->fd))
goto error;
if ((disk_specific->anchor->geo.cylinders
* disk_specific->anchor->geo.heads) > BIG_DISK_SIZE)
disk_specific->anchor->big_disk++;
fdasd_recreate_vtoc(disk_specific->anchor);
for (i = 1; i <= USABLE_PARTITIONS; i++) {
unsigned int start, stop;
int type;
PDEBUG;
part = ped_disk_get_partition(disk, i);
if (!part)
continue;
PDEBUG;
start = part->geom.start * disk->dev->sector_size
/ disk_specific->real_sector_size / disk->dev->hw_geom.sectors;
stop = (part->geom.end + 1)
* disk->dev->sector_size / disk_specific->real_sector_size
/ disk->dev->hw_geom.sectors - 1;
PDEBUG;
dasd_data = part->disk_specific;
type = dasd_data->type;
PDEBUG;
p = fdasd_add_partition(disk_specific->anchor, start, stop);
if (!p) {
PDEBUG;
return 0;
}
dasd_data->part_info = (void *) p;
p->type = dasd_data->system;
}
PDEBUG;
if (!fdasd_prepare_labels(disk_specific->anchor, arch_specific->fd))
return 0;
dasd_update_type(disk);
PDEBUG;
if (!fdasd_write_labels(disk_specific->anchor, arch_specific->fd))
return 0;
return 1;
error:
PDEBUG;
return 0;
}
static PedPartition*
dasd_partition_new (const PedDisk* disk, PedPartitionType part_type,
const PedFileSystemType* fs_type,
PedSector start, PedSector end)
{
PedPartition* part;
part = _ped_partition_alloc(disk, part_type, fs_type, start, end);
if (!part)
goto error;
part->disk_specific = ped_malloc (sizeof (DasdPartitionData));
return part;
error:
return 0;
}
static void
dasd_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
dasd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
{
DasdPartitionData* dasd_data;
PED_ASSERT(part != NULL, return 0);
PED_ASSERT(part->disk_specific != NULL, return 0);
dasd_data = part->disk_specific;
switch (flag) {
case PED_PARTITION_RAID:
if (state)
dasd_data->lvm = 0;
dasd_data->raid = state;
return ped_partition_set_system(part, part->fs_type);
case PED_PARTITION_LVM:
if (state)
dasd_data->raid = 0;
dasd_data->lvm = state;
return ped_partition_set_system(part, part->fs_type);
default:
return 0;
}
}
static int
dasd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
{
DasdPartitionData* dasd_data;
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk_specific != NULL, return 0);
dasd_data = part->disk_specific;
switch (flag) {
case PED_PARTITION_RAID:
return dasd_data->raid;
case PED_PARTITION_LVM:
return dasd_data->lvm;
default:
return 0;
}
}
static int
dasd_partition_is_flag_available (const PedPartition* part,
PedPartitionFlag flag)
{
switch (flag) {
case PED_PARTITION_RAID:
return 1;
case PED_PARTITION_LVM:
return 1;
default:
return 0;
}
}
static int
dasd_get_max_primary_partition_count (const PedDisk* disk)
{
DasdDiskSpecific* disk_specific;
disk_specific = disk->disk_specific;
/* If formated in LDL, maximum partition number is 1 */
if (disk_specific->format_type == 1)
return 1;
return USABLE_PARTITIONS;
}
static PedConstraint*
_primary_constraint (PedDisk* disk)
{
PedAlignment start_align;
PedAlignment end_align;
PedGeometry max_geom;
PedSector sector_size;
LinuxSpecific* arch_specific;
DasdDiskSpecific* disk_specific;
PDEBUG;
arch_specific = LINUX_SPECIFIC (disk->dev);
disk_specific = disk->disk_specific;
sector_size = disk_specific->real_sector_size / disk->dev->sector_size;
if (!ped_alignment_init (&start_align, 0,
disk->dev->hw_geom.sectors * sector_size))
return NULL;
if (!ped_alignment_init (&end_align, -1,
disk->dev->hw_geom.sectors * sector_size))
return NULL;
if (!ped_geometry_init (&max_geom, disk->dev, 0, disk->dev->length))
return NULL;
return ped_constraint_new(&start_align, &end_align, &max_geom,
&max_geom, 1, disk->dev->length);
}
static int
dasd_partition_align (PedPartition* part, const PedConstraint* constraint)
{
DasdDiskSpecific* disk_specific;
PED_ASSERT (part != NULL, return 0);
disk_specific = part->disk->disk_specific;
/* If formated in LDL, ignore metadata partition */
if (disk_specific->format_type == 1)
return 1;
if (_ped_partition_attempt_align(part, constraint,
_primary_constraint(part->disk)))
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
dasd_partition_enumerate (PedPartition* part)
{
int i;
PedPartition* p;
/* never change the partition numbers */
if (part->num != -1)
return 1;
for (i = 1; i <= USABLE_PARTITIONS; i++) {
p = ped_disk_get_partition (part->disk, i);
if (!p) {
part->num = i;
return 1;
}
}
/* failed to allocate a number */
ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Unable to allocate a dasd disklabel slot"));
return 0;
}
static int
dasd_partition_set_system (PedPartition* part,
const PedFileSystemType* fs_type)
{
DasdPartitionData* dasd_data = part->disk_specific;
PedSector cyl_size;
cyl_size=part->disk->dev->hw_geom.sectors * part->disk->dev->hw_geom.heads;
PDEBUG;
part->fs_type = fs_type;
if (dasd_data->lvm) {
dasd_data->system = PARTITION_LINUX_LVM;
PDEBUG;
return 1;
}
if (dasd_data->raid) {
dasd_data->system = PARTITION_LINUX_RAID;
PDEBUG;
return 1;
}
if (!fs_type) {
dasd_data->system = PARTITION_LINUX;
PDEBUG;
} else if (!strcmp (fs_type->name, "linux-swap")) {
dasd_data->system = PARTITION_LINUX_SWAP;
PDEBUG;
} else {
dasd_data->system = PARTITION_LINUX;
PDEBUG;
}
return 1;
}
static int
dasd_alloc_metadata (PedDisk* disk)
{
PedPartition* new_part;
PedConstraint* constraint_any = NULL;
PedSector vtoc_end;
LinuxSpecific* arch_specific;
DasdDiskSpecific* disk_specific;
PED_ASSERT (disk != NULL, goto error);
PED_ASSERT (disk->dev != NULL, goto error);
arch_specific = LINUX_SPECIFIC (disk->dev);
disk_specific = disk->disk_specific;
constraint_any = ped_constraint_any (disk->dev);
/* If formated in LDL, the real partition starts at sector 24. */
if (disk_specific->format_type == 1)
vtoc_end = 23;
else
/* Mark the start of the disk as metadata. */
vtoc_end = (FIRST_USABLE_TRK * (long long) disk->dev->hw_geom.sectors
* (long long) disk_specific->real_sector_size
/ (long long) disk->dev->sector_size) - 1;
new_part = ped_partition_new (disk,PED_PARTITION_METADATA,NULL,0,vtoc_end);
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;
}