diff options
Diffstat (limited to '')
-rw-r--r-- | libparted/labels/atari.c | 1975 |
1 files changed, 1975 insertions, 0 deletions
diff --git a/libparted/labels/atari.c b/libparted/labels/atari.c new file mode 100644 index 0000000..8ab3720 --- /dev/null +++ b/libparted/labels/atari.c @@ -0,0 +1,1975 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + atari.c - libparted module to manipulate Atari partition tables. + Copyright (C) 2000-2001, 2004, 2007-2014, 2019-2023 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 3 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, see <http://www.gnu.org/licenses/>. + + Contributor: Guillaume Knispel <k_guillaume@libertysurf.fr> + John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> +*/ + +/* + Documentation : + README file of atari-fdisk + atari-fdisk source code + Linux atari partitions parser source code + ( fs/partitions/atari.[ch] ) +*/ + +#include <config.h> + +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <string.h> +#include <locale.h> +#include <stdint.h> +#include <ctype.h> +#include <stddef.h> + +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + + +/********************** Atari data and structure stuff **********************/ + +#define BOOTABLE_CKSUM 0x1234 +#define NONBOOT_CKSUM 0x4321 + +#define GEM_MAX ((32*1024*1024)/PED_SECTOR_SIZE_DEFAULT) + +#define PART_FLAG_USED 0x01 +#define PART_FLAG_BOOT_GEM 0x80 /* GEMDOS */ +#define PART_FLAG_BOOT_ASV 0x40 /* Atari System V */ +#define PART_FLAG_BOOT_BSD 0x20 /* Net(?)BSD */ +#define PART_FLAG_BOOT_LNX 0x10 /* Linux */ +#define PART_FLAG_BOOT_UNK 0x08 /* unknown / other */ + +#define N_AHDI 4 +#define N_ICD 8 + +#define MAXIMUM_PARTS 64 + +/* what we put instead of id, start and size in empty */ +/* partition tables, to be able to detect it */ +#define SIGNATURE_EMPTY_TABLE "PARTEDATARI" +#define SIGNATURE_EMPTY_SIZE 11 + +/* to be compared to the last two bytes of 1st sector (Big Endian) */ +static const uint16_t atr_forbidden_sign[] = { + 0x55AA, + 0 +}; + +static const char *atr_known_icd_pid[] = { + "BGM", "GEM", "LNX", "SWP", "RAW", NULL +}; + +/* static const char *atr_known_pid[] = { */ +/* "BGM", "GEM", "LNX", "MAC", "MIX", "MNX", "RAW", "SWP", "UNX", */ +/* "F32", "SV4", NULL */ +/* }; */ + +struct _AtariPartID2BootFlag { + const char pid[4]; + uint8_t flag; +}; +typedef struct _AtariPartID2BootFlag AtariPartID2BootFlag; + +static AtariPartID2BootFlag atr_pid2bf[] = { + { "GEM", PART_FLAG_BOOT_GEM }, + { "BGM", PART_FLAG_BOOT_GEM }, + { "UNX", PART_FLAG_BOOT_ASV }, + { "LNX", PART_FLAG_BOOT_LNX }, + { "", PART_FLAG_BOOT_UNK }, +}; + +struct _AtariFS2PartId { + const char* fs; + const char pid[4]; + PedSector max_sectors; +}; +typedef struct _AtariFS2PartId AtariFS2PartId; + +static AtariFS2PartId atr_fs2pid[] = { +/* Other ID are available : MIX MNX <= minix + UNX <= Atari SysV Unix + SV4 <= Univ System 4 */ + { "ext2", "LNX", INT32_MAX }, + { "ext3", "LNX", INT32_MAX }, + { "fat16", "GEM", GEM_MAX }, /* small partitions */ + { "fat16", "BGM", INT32_MAX }, /* big partitions */ + { "fat32", "F32", INT32_MAX }, + { "hfs", "MAC", INT32_MAX }, + { "hfs+", "MAC", INT32_MAX }, + { "hfsx", "MAC", INT32_MAX }, + { "jfs", "LNX", INT32_MAX }, + { "linux-swap", "SWP", INT32_MAX }, + { "reiserfs", "LNX", INT32_MAX }, + { "hp-ufs", "LNX", INT32_MAX }, + { "sun-ufs", "LNX", INT32_MAX }, + { "xfs", "LNX", INT32_MAX }, + { "ntfs", "RAW", INT32_MAX }, + { "", "RAW", INT32_MAX }, /* default entry */ + { NULL, "" , 0 } /* end of list */ +}; + +struct __attribute__ ((packed)) _AtariRawPartition { + uint8_t flag; /* bit 0: active; bit 7: bootable */ + union { + uint8_t empty[11]; /* Empty table */ + struct __attribute__ ((packed)) { + uint8_t id[3]; /* "GEM", "BGM", "XGM", ... */ + uint32_t start; /* start of partition */ + uint32_t size; /* length of partition */ + }; + }; +}; +typedef struct _AtariRawPartition AtariRawPartition; + +struct __attribute__ ((packed,aligned(2))) _AtariRawTable { + uint8_t boot_code[0x156]; /* room for boot code */ + AtariRawPartition icd_part[N_ICD]; /* info for ICD-partitions 5..12 */ + uint8_t unused[0xc]; + uint32_t hd_size; /* size of disk in blocks */ + AtariRawPartition part[N_AHDI]; /* the four primary partitions */ + uint32_t bsl_start; /* start of bad sector list */ + uint32_t bsl_count; /* length of bad sector list */ + uint16_t checksum; /* checksum for bootable disks */ +}; +typedef struct _AtariRawTable AtariRawTable; + +typedef enum { + FMT_AHDI = 0, /* AHDI v1 compatible, no ICD and no XGM */ + FMT_XGM = 1, /* AHDI v3 with XGM / this disable ICD */ + FMT_ICD = 2 /* ICD detected / requested because more than 4 prim */ + /* no XGM allowed */ +} AtrFmt; + +struct _AtariDisk { + AtrFmt format; + int has_been_read; /* actually means has been read or written... */ + uint32_t bsl_start; /* first sector of the Bad Sectors List */ + uint32_t bsl_count; /* number of sectors of the BSL */ + uint8_t HDX_comp; /* if set to one, atari_write will initialize */ + /* the bsl area */ +}; +typedef struct _AtariDisk AtariDisk; + +struct _AtariPart { + char part_id[4]; /* ASCIIZ */ + char icd_id[4]; /* Linux only parse a limited set of ID */ + /* in ICD (why???), so everything else */ + /* is translated to RAW. */ + uint8_t flag; /* without bit 0 (entry used) */ +}; +typedef struct _AtariPart AtariPart; + +/* set by initialisation code to C locale */ +static locale_t atr_c_locale; + +static PedDiskType atari_disk_type; + + + +/******************************** Atari Code ********************************/ + +#define ATARI_DISK(disk) ((AtariDisk*)((disk)->disk_specific)) +#define ATARI_PART(part) ((AtariPart*)((part)->disk_specific)) + +#define atr_pid_eq(a,b) (!memcmp( (a), (b), 3 )) + +#define atr_pid_assign(a, b) (memcpy ( (a), (b), 3 )) + +#define atr_part_used(part) (((part)->flag) & PART_FLAG_USED) + +static int +atr_start_size_correct (uint32_t start, uint32_t size, uint32_t hd_size) +{ + uint32_t end = start + size; + + return end >= start + && 0 < start && start <= hd_size + && 0 < size && size <= hd_size + && 0 < end && end <= hd_size; +} + +static int +atr_part_correct (AtariRawPartition* part, uint32_t hd_size) +{ + uint32_t start, size; + + start = PED_BE32_TO_CPU (part->start); + size = PED_BE32_TO_CPU (part->size); + + return isalnum_l(part->id[0], atr_c_locale) + && isalnum_l(part->id[1], atr_c_locale) + && isalnum_l(part->id[2], atr_c_locale) + && atr_start_size_correct (start, size, hd_size); +} + +static int _GL_ATTRIBUTE_PURE +atr_pid_known (const char* pid, const char** pid_list) +{ + for (; *pid_list; pid_list++) { + if (atr_pid_eq(pid, *pid_list)) + return 1; + } + + return 0; +} + +/* Recognize Parted signature in an AHDI entry, used to + * identify empty Atari partition tables */ +static int +atr_is_signature_entry (AtariRawPartition* part) +{ + return part->flag == 0 + && !memcmp (part->empty, SIGNATURE_EMPTY_TABLE, + SIGNATURE_EMPTY_SIZE ); +} + +/* Set Parted signature in an AHDI entry */ +static void +atr_put_signature_entry (AtariRawPartition* part) +{ + part->flag = 0; + memcpy (part->empty, SIGNATURE_EMPTY_TABLE, SIGNATURE_EMPTY_SIZE); +} + +#define atr_part_known(part, pid_list) (atr_pid_known ((part)->id, pid_list)) + +#define atr_part_valid(part, sz) (atr_part_used(part)\ + && atr_part_correct((part), (sz))) +#define atr_part_trash(part, sz) (atr_part_used(part)\ + && !atr_part_correct((part), (sz))) + +/* Check if this device can be used with an Atari label */ +static int +atr_can_use_dev (const PedDevice *dev) +{ + /* i really don't know how atari behave with non 512 bytes */ + /* sectors... */ + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't use Atari partition tables on disks with a " + "sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + /* the format isn't well defined enough to support > 0x7FFFFFFF */ + /* sectors */ + if (dev->length > INT32_MAX) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't use Atari partition tables on disks with more " + "than %d sectors."), + INT32_MAX ); + return 0; + } + + return 1; +} + +/* + * The Atari disk label doesn't have any magic id + * so we must completely parse the layout to be sure + * we are really dealing with it. + */ +static int +atari_probe (const PedDevice *dev) +{ + AtariRawTable table; + uint32_t rs_hd_size, parts, exts; + int valid_count, xgm_part, xgm_num, i; + int num_sign, total_count = 0; + + PED_ASSERT (dev != NULL); + PED_ASSERT (sizeof(table) == 512); + + /* Device Spec ok for Atari label? */ + if (!atr_can_use_dev (dev)) + return 0; + + /* read the root sector */ + if (!ped_device_read (dev, &table, 0, 1)) + return 0; + + /* number of sectors stored in the root sector > device length ? */ + /* => just reject the Atari disk label */ + rs_hd_size = PED_BE32_TO_CPU (table.hd_size); + if (rs_hd_size > dev->length + || rs_hd_size < 2) + return 0; + + /* check the BSL fields */ + if ((table.bsl_start || table.bsl_count) + && !atr_start_size_correct (PED_BE32_TO_CPU (table.bsl_start), + PED_BE32_TO_CPU (table.bsl_count), + rs_hd_size ) ) + return 0; + + /* scan the main AHDI fields */ + num_sign = 0; xgm_num = 0; + valid_count = 0; xgm_part = 0; + for (i = 0; i < N_AHDI; i++) { + if (atr_part_valid (&table.part[i], rs_hd_size)) { + valid_count++; + total_count++; + if (atr_pid_eq(table.part[i].id, "XGM")) { + xgm_part++; + xgm_num = i; + } + } else if (atr_part_trash (&table.part[i], rs_hd_size)) { + return 0; + } + if (atr_is_signature_entry (&table.part[i])) + num_sign++; + } + + /* no way to reliably detect empty Atari disk labels if + * they aren't using parted signature in 4 prim fields + * && reject multi XGM labels because Parted can't handle + * multiple extended partitions + * && reject if xgm partition in slot 0 because not allowed */ + if ((!valid_count && num_sign != N_AHDI) + || xgm_part > 1 + || (xgm_part == 1 && xgm_num == 0) ) + return 0; + + /* check coherency of each logical partitions and ARS */ + if (xgm_part) { /* ! WARNING ! reuses "table" */ + /* we must allow empty ext partition even if they're */ + /* not valid because parted write the layout to the HD */ + /* at each operation, and we can't create ext and log */ + /* at the same time */ + int empty_ars_allowed = 1; + + parts = exts = PED_BE32_TO_CPU (table.part[xgm_num].start); + while (1) { + if (!ped_device_read (dev, &table, parts, 1)) + return 0; + + for (i = 0; i < N_AHDI-1; ++i) { + if (atr_part_used (&table.part[i])) + break; + } + + /* we allow the ext part to be empty (see above) */ + if (i == N_AHDI-1 && empty_ars_allowed) + break; + + /* data partition must be in slot 0, 1 or 2 */ + if (i == N_AHDI-1 + || !atr_part_correct (&table.part[i], rs_hd_size + - parts ) + || atr_pid_eq (table.part[i].id, "XGM")) + return 0; + + /* If there is at least one logical partition */ + /* then next ARS should not be empty */ + empty_ars_allowed = 0; + + total_count++; + if (total_count > MAXIMUM_PARTS) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many Atari partitions detected. " + " Maybe there is a loop in the XGM " + "linked list. Aborting.") ); + return 0; + } + + /* end of logical partitions? */ + if (!atr_part_used (&table.part[i+1])) + break; + + /* is this really the descriptor of the next ARS? */ + if (!atr_part_correct (&table.part[i+1], rs_hd_size + - exts ) + || !atr_pid_eq (table.part[i+1].id, "XGM")) + return 0; + + parts = exts + PED_BE32_TO_CPU (table.part[i+1].start); + } + } /* no XGM so try ICD */ + else if (atr_part_valid (&table.icd_part[0], rs_hd_size) + && atr_part_known (&table.icd_part[0], atr_known_icd_pid)) { + for (i = 1; i < N_ICD; i++) { + if (atr_part_trash (&table.icd_part[i], rs_hd_size)) + return 0; + } + } + + return 1; +} + +static void +atr_disk_reset (AtariDisk* atr_disk) +{ + /* Empty partition table => only AHDI needed right now */ + atr_disk->format = FMT_AHDI; + /* The disk is not in sync with the actual content of the label */ + atr_disk->has_been_read = 0; + /* Create an empty BSL for HDX compatibility */ + atr_disk->bsl_start = 1; + atr_disk->bsl_count = 1; + atr_disk->HDX_comp = 1; +} + +/* + * Must set up the PedDisk and the associated AtariDisk as if + * the user is doing mklabel, since in this case atari_alloc + * is called alone whereas when reading an existing partition + * table atari_read is called after atari_alloc and can overwrite + * the settings. + */ +static PedDisk* +atari_alloc (const PedDevice* dev) +{ + PedDisk* disk; + AtariDisk* atr_disk; + + PED_ASSERT (dev != NULL); + + if (!atr_can_use_dev (dev) + || !(disk = _ped_disk_alloc (dev, &atari_disk_type))) + return NULL; + + if (!(disk->disk_specific = atr_disk = ped_malloc (sizeof (AtariDisk)))) + goto error_free_disk; + + atr_disk_reset (atr_disk); + + return disk; + +error_free_disk: + free (disk); + return NULL; +} + +static PedDisk* +atari_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + AtariDisk* old_atr_dsk; + AtariDisk* new_atr_dsk; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + + old_atr_dsk = ATARI_DISK (disk); + if (!(new_disk = ped_disk_new_fresh (disk->dev, &atari_disk_type))) + return NULL; + new_atr_dsk = ATARI_DISK (new_disk); + + memcpy (new_atr_dsk, old_atr_dsk, sizeof(*old_atr_dsk)); + + return new_disk; +} + +static void +atari_free (PedDisk* disk) +{ + AtariDisk* atr_disk; + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + atr_disk = ATARI_DISK (disk); + + _ped_disk_free (disk); + free (atr_disk); +} + +/* Warning : ID not ASCIIZ but 3 chars long */ +static void +atr_part_sysraw (PedPartition* part, const char* id, uint8_t flag) +{ + AtariPart* atr_part = ATARI_PART (part); + + atr_part->flag = flag & ~PART_FLAG_USED; + + atr_pid_assign (atr_part->part_id, id); + atr_part->part_id[3] = 0; + + if (atr_pid_known (id, atr_known_icd_pid)) { + atr_pid_assign (atr_part->icd_id, id); + atr_part->icd_id[3] = 0; + } else { + atr_pid_assign (atr_part->icd_id, "RAW"); + atr_part->icd_id[3] = 0; + } +} + +static int +atr_parse_add_rawpart (PedDisk* disk, PedPartitionType type, PedSector st_off, + int num, const AtariRawPartition* rawpart ) +{ + PedSector start, end; + PedPartition* part; + PedConstraint* const_exact; + int added; + + start = st_off + PED_BE32_TO_CPU (rawpart->start); + end = start + PED_BE32_TO_CPU (rawpart->size) - 1; + + part = ped_partition_new (disk, type, NULL, start, end); + if (!part) + return 0; + + /*part->num = num;*/ /* Enumeration will take care of that */ + part->num = -1; /* Indeed we can't enumerate here + * because the enumerate function uses + * -1 do detect new partition being + * inserted and update the atrdisk->format */ + if (type != PED_PARTITION_EXTENDED) + part->fs_type = ped_file_system_probe (&part->geom); + else + part->fs_type = NULL; + atr_part_sysraw (part, rawpart->id, rawpart->flag); + + const_exact = ped_constraint_exact (&part->geom); + added = ped_disk_add_partition (disk, part, const_exact); + ped_constraint_destroy (const_exact); + if (!added) { + ped_partition_destroy (part); + return 0; + } + + PED_ASSERT (part->num == num); + return 1; +} + +/* + * Read the chained list of logical partitions. + * exts points to the first Auxiliary Root Sector, at the start + * of the extended partition. + * In each ARS one partition entry describes to the logical partition + * (start relative to the ARS position) and the next entry with ID "XGM" + * points to the next ARS (start relative to exts). + */ +static int +atr_read_logicals (PedDisk* disk, PedSector exts, int* pnum) +{ + AtariRawTable table; + PedSector parts = exts; + int i, empty_ars_allowed = 1; + + while (1) { + if (!ped_device_read (disk->dev, &table, parts, 1)) + return 0; + + for (i = 0; i < N_AHDI-1; ++i) + if (atr_part_used (&table.part[i])) + break; + + if (i == N_AHDI-1 && empty_ars_allowed) + break; + + /* data partition must be in slot 0, 1 or 2 */ + if (i == N_AHDI-1 + || atr_pid_eq (table.part[i].id, "XGM")) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No data partition found in the ARS at " + "sector %lli."), parts ); + return 0; + } + + empty_ars_allowed = 0; + + if (!atr_parse_add_rawpart (disk, PED_PARTITION_LOGICAL, + parts, *pnum, &table.part[i] ) ) + return 0; + + (*pnum)++; + + /* end of logical partitions? */ + if (!atr_part_used (&table.part[i+1])) + break; + + if (!atr_pid_eq (table.part[i+1].id, "XGM")) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The entry of the next logical ARS is not of " + "type XGM in ARS at sector %lli."), parts ); + return 0; + } + + parts = exts + PED_BE32_TO_CPU (table.part[i+1].start); + } + + return 1; +} + +static int +atari_read (PedDisk* disk) +{ + AtariRawTable table; + AtariDisk* atr_disk; + uint32_t rs_hd_size; + int i, pnum, xgm, pcount; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + atr_disk = ATARI_DISK (disk); + + ped_disk_delete_all (disk); + atr_disk_reset (atr_disk); + + if (!atari_probe (disk->dev)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("There doesn't seem to be an Atari partition table " + "on this disk (%s), or it is corrupted."), + disk->dev->path ) + != PED_EXCEPTION_IGNORE) + return 0; + } + + if (!ped_device_read (disk->dev, (void*) &table, 0, 1)) + goto error; + + /* We are sure that the layout looks coherent so we + don't need to check too much */ + + rs_hd_size = PED_BE32_TO_CPU (table.hd_size); + atr_disk->bsl_start = PED_BE32_TO_CPU (table.bsl_start); + atr_disk->bsl_count = PED_BE32_TO_CPU (table.bsl_count); + atr_disk->HDX_comp = 0; + + /* AHDI primary partitions */ + pnum = 1; xgm = 0; pcount = 0; + for (i = 0; i < N_AHDI; i++) { + if (!atr_part_used (&table.part[i])) + continue; + + pcount++; + + if (atr_pid_eq (table.part[i].id, "XGM")) { + + atr_disk->format = FMT_XGM; + xgm = 1; + if (!atr_parse_add_rawpart(disk, PED_PARTITION_EXTENDED, + 0, 0, &table.part[i] ) + || !atr_read_logicals ( + disk, + PED_BE32_TO_CPU (table.part[i].start), + &pnum ) ) + goto error; + + } else { + + if (!atr_parse_add_rawpart (disk, PED_PARTITION_NORMAL, + 0, pnum, &table.part[i] ) ) + goto error; + pnum++; + } + } + + /* If no XGM partition has been found, the AHDI table is not empty, */ + /* the first entry is valid and its ID ok for ICD, then we parse the */ + /* ICD table. */ + if (!xgm && pcount != 0 + && atr_part_valid (&table.icd_part[0], rs_hd_size) + && atr_part_known (&table.icd_part[0], atr_known_icd_pid)) + for (i = 0; i < N_ICD; i++) { + + if (!atr_part_known (&table.icd_part[i], atr_known_icd_pid) + || !atr_part_used (&table.icd_part[i])) + continue; + atr_disk->format = FMT_ICD; + + if (!atr_parse_add_rawpart (disk, PED_PARTITION_NORMAL, + 0, pnum, &table.icd_part[i] ) ) + goto error; + pnum++; + } + + atr_disk->has_been_read = 1; + return 1; + +error: + ped_disk_delete_all (disk); + atr_disk_reset (atr_disk); + return 0; +} + +/* Returns the number of the first logical partition or -1 if not found */ +static int +atr_find_first_log (const PedDisk* disk) +{ + PedPartition* part; + int first_log, last; + + last = ped_disk_get_last_partition_num (disk); + + for (first_log = 1; first_log <= last; first_log++) { + if ((part = ped_disk_get_partition (disk, first_log)) + && (part->type & PED_PARTITION_LOGICAL)) + break; + } + + return first_log > last ? -1 : first_log; +} + +#ifndef DISCOVER_ONLY +static int +atari_clobber (PedDevice* dev) +{ + AtariRawTable table; + + PED_ASSERT (dev != NULL); + PED_ASSERT (atari_probe (dev)); + + if (!ped_device_read (dev, &table, 0, 1)) + return 0; + + /* clear anything but the boot code and the optional ICD table */ + memset (table.boot_code + offsetof (AtariRawTable, hd_size), + 0, + PED_SECTOR_SIZE_DEFAULT - offsetof (AtariRawTable, hd_size)); + + return ped_device_write (dev, &table, 0, 1); +} + +/* Computes the checksum of the root sector */ +static uint16_t +atr_calc_rs_sum (const AtariRawTable* table) +{ + const uint16_t* word = (uint16_t*)(table); + const uint16_t* end = (uint16_t*)(table + 1); + uint16_t sum; + + for (sum = 0; word < end; word++) + sum += PED_BE16_TO_CPU(*word); + + return sum; +} + +/* Returns 1 if the root sector is bootable, else returns 0 */ +static int +atr_is_boot_table (const AtariRawTable* table) +{ + return atr_calc_rs_sum (table) == BOOTABLE_CKSUM; +} + +/* + * Returns 1 if sign belongs to a set of `forbidden' signatures. + * (e.g.: 55AA which is the MSDOS siganture...) + * Only used for non bootable root sector since the signature of + * a bootable one is unique. + */ +static int _GL_ATTRIBUTE_PURE +atr_sign_is_forbidden (uint16_t sign) +{ + const uint16_t* forbidden; + + for (forbidden = atr_forbidden_sign; *forbidden; forbidden++) { + if (sign == *forbidden) + return 1; + } + + return 0; +} + +/* Updates table->checksum so the RS will be considered bootable (or not) */ +static void +atr_table_set_boot (AtariRawTable* table, int boot) +{ + uint16_t boot_cksum, noboot_cksum; + uint16_t sum; + + table->checksum = 0; + sum = atr_calc_rs_sum (table); + boot_cksum = BOOTABLE_CKSUM - sum; + + if (boot) { + table->checksum = PED_CPU_TO_BE16 (boot_cksum); + return; + } + + noboot_cksum = NONBOOT_CKSUM - sum; + + while (atr_sign_is_forbidden (noboot_cksum) + || noboot_cksum == boot_cksum) + noboot_cksum++; + + table->checksum = PED_CPU_TO_BE16 (noboot_cksum); +} + +/* Fill an used partition entry */ +static void +atr_fill_raw_entry (AtariRawPartition* rawpart, uint8_t flag, const char* id, + uint32_t start, uint32_t size ) +{ + rawpart->flag = PART_FLAG_USED | flag; + atr_pid_assign (rawpart->id, id); + rawpart->start = PED_CPU_TO_BE32 (start); + rawpart->size = PED_CPU_TO_BE32 (size); +} + +static int +atr_write_logicals (const PedDisk* disk) +{ + AtariRawTable table; + PedPartition* log_curr; + PedPartition* log_next; + PedPartition* ext; + PedPartition* part; + PedSector exts; + PedSector parts; + AtariPart* atr_part; + int first_log, pnum, i; + + PED_ASSERT (disk != NULL); + + ext = ped_disk_extended_partition (disk); + exts = parts = ext->geom.start; + + pnum = first_log = atr_find_first_log (disk); + + while (1) { + if (pnum != -1) { + log_curr = ped_disk_get_partition (disk, pnum); + log_next = ped_disk_get_partition (disk, pnum + 1); + } else { + log_curr = log_next = NULL; + } + + if (log_curr && !(log_curr->type & PED_PARTITION_LOGICAL)) + log_curr = NULL; + if (log_next && !(log_next->type & PED_PARTITION_LOGICAL)) + log_next = NULL; + + PED_ASSERT (pnum == first_log || log_curr); + + part = ped_disk_get_partition_by_sector (disk, parts); + if (part && ped_partition_is_active (part)) { + if (log_curr) + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No room at sector %lli to store ARS " + "of logical partition %d."), + parts, pnum ); + else + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No room at sector %lli to store ARS."), + parts ); + return 0; + } + + if (!ped_device_read (disk->dev, &table, parts, 1)) + return 0; + + if (!log_curr) { + PED_ASSERT (!log_next); + + for (i = 0; i < N_AHDI; i++) + table.part[i].flag &= ~PART_FLAG_USED; + } else { + atr_part = ATARI_PART (log_curr); + atr_fill_raw_entry (&table.part[0], atr_part->flag, + atr_part->part_id, + log_curr->geom.start - parts, + log_curr->geom.length ); + + for (i = 1; i < N_AHDI; i++) + table.part[i].flag &= ~PART_FLAG_USED; + + if (log_next) { + atr_fill_raw_entry (&table.part[1], 0, "XGM", + log_next->geom.start - 1 - exts, + log_next->geom.length + 1 ); + } + } + + /* TODO: check if we can set that bootable, and when */ + atr_table_set_boot (&table, 0); + + if (!ped_device_write (disk->dev, &table, parts, 1)) + return 0; + + if (!log_next) + break; + + parts = log_next->geom.start - 1; + pnum++; + } + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +_disk_logical_partition_count (const PedDisk* disk) +{ + PedPartition* walk; + + int count = 0; + + PED_ASSERT (disk != NULL); + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) + && (walk->type & PED_PARTITION_LOGICAL)) + count++; + } + + return count; +} + +/* Load the HD size from the table and ask to fix it if != device size. */ +static int +atr_load_fix_hdsize (const PedDisk* disk, uint32_t* rs_hd_size, AtariRawTable* table) +{ + AtariDisk* atr_disk = ATARI_DISK (disk); + int result = PED_EXCEPTION_UNHANDLED; + + *rs_hd_size = PED_BE32_TO_CPU (table->hd_size); + if (*rs_hd_size != disk->dev->length) { + if (atr_disk->has_been_read) { + result = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE_CANCEL, + _("The sector count that is stored in the " + "partition table does not correspond " + "to the size of your device. Do you " + "want to fix the partition table?") ); + if (result == PED_EXCEPTION_CANCEL) + return 0; + } + + if (result == PED_EXCEPTION_UNHANDLED) + result = PED_EXCEPTION_FIX; + + if (result == PED_EXCEPTION_FIX) { + *rs_hd_size = disk->dev->length; + table->hd_size = PED_CPU_TO_BE32(*rs_hd_size); + } + } + return 1; +} + +/* Try to init the HDX compatibility Bad Sectors List. */ +static int +atr_empty_init_bsl (const PedDisk* disk) +{ + uint8_t zeros[PED_SECTOR_SIZE_DEFAULT]; + PedSector sec; + PedPartition* part; + AtariDisk* atr_disk = ATARI_DISK (disk); + + memset (zeros, 0, PED_SECTOR_SIZE_DEFAULT); + for (sec = atr_disk->bsl_start; + sec < atr_disk->bsl_start + atr_disk->bsl_count; + sec++ ) { + if (sec == atr_disk->bsl_start) + zeros[3] = 0xA5; + else + zeros[3] = 0; + part = ped_disk_get_partition_by_sector (disk, sec); + if (part && ped_partition_is_active (part)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No room at sector %lli to store BSL."), + sec ); + return 0; + } + ped_device_write (disk->dev, zeros, sec, 1); + } + atr_disk->HDX_comp = 0; + return 1; +} + +static int +atari_write (const PedDisk* disk) +{ + AtariRawTable table; + AtariDisk* atr_disk; + AtariPart* atr_part; + PedPartition* log; + PedPartition* ext_part; + PedPartition* part = NULL; + uint32_t rs_hd_size; + int i, xgm_begin, pnum, append_ext; + int put_sign, boot, prim_count, last_num; + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + prim_count = ped_disk_get_primary_partition_count (disk); + last_num = ped_disk_get_last_partition_num (disk); + ext_part = ped_disk_extended_partition (disk); + + /* WARNING: similar/related code in atari_enumerate */ + xgm_begin = ((log = ped_disk_get_partition (disk, 1)) + && (log->type & PED_PARTITION_LOGICAL)); + PED_ASSERT (atr_disk->format != FMT_ICD || ext_part == NULL); + PED_ASSERT (atr_disk->format != FMT_XGM || prim_count + xgm_begin <= N_AHDI); + PED_ASSERT (atr_disk->format != FMT_AHDI || (ext_part == NULL && prim_count + xgm_begin <= N_AHDI)); + + /* Device Spec ok for Atari label? */ + if (!atr_can_use_dev (disk->dev)) + goto error; + + if (!ped_device_read (disk->dev, (void*) &table, 0, 1)) + goto error; + + boot = atr_is_boot_table (&table); + + table.bsl_start = PED_CPU_TO_BE32 (atr_disk->bsl_start); + table.bsl_count = PED_CPU_TO_BE32 (atr_disk->bsl_count); + + /* Before anything else check the sector count and */ + /* fix it if necessary */ + if (!atr_load_fix_hdsize (disk, &rs_hd_size, &table)) + goto error; + + append_ext = (ext_part != NULL) + && (_disk_logical_partition_count (disk) == 0); + + /* Fill the AHDI table */ + put_sign = (prim_count == 0); + pnum = 1; + for (i = 0; i < N_AHDI; i++) { + if (pnum > last_num) + part = NULL; + else while (pnum <= last_num + && !(part = ped_disk_get_partition (disk, pnum))) + pnum++; + + if (put_sign) { + atr_put_signature_entry (&table.part[i]); + continue; + } + + if (!part && i != 0 && append_ext) { + part = ext_part; + append_ext = 0; + } + + if (!part || (i == 0 && xgm_begin)) { + table.part[i].flag &= ~PART_FLAG_USED; + continue; + } + + if (part->type & PED_PARTITION_LOGICAL) + part = ext_part; + + PED_ASSERT (part != NULL); + + atr_part = ATARI_PART (part); + atr_fill_raw_entry (&table.part[i], atr_part->flag, + atr_part->part_id, part->geom.start, + part->geom.length ); + + if (part->type & PED_PARTITION_EXTENDED) { + while (pnum <= last_num) { + part = ped_disk_get_partition (disk, pnum); + if (part && + !(part->type & PED_PARTITION_LOGICAL)) + break; + pnum++; + } + } else + pnum++; + } + + if ((ext_part != NULL || atr_disk->format == FMT_AHDI) + && pnum <= last_num) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("There were remaining partitions after filling " + "the main AHDI table.") ); + goto error; + } + + /* Leave XGM or ICD mode if uneeded */ + if (pnum > last_num + && (atr_disk->format == FMT_ICD || ext_part == NULL)) + atr_disk->format = FMT_AHDI; + + /* If AHDI mode, check that no ICD will be detected */ + /* and propose to fix */ + if (atr_disk->format == FMT_AHDI + && atr_part_valid (&table.icd_part[0], rs_hd_size) + && atr_part_known (&table.icd_part[0], atr_known_icd_pid)) { + int result = PED_EXCEPTION_UNHANDLED; + result = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_YES_NO_CANCEL, + _("The main AHDI table has been filled with all " + "partitions but the ICD table is not empty " + "so more partitions of unknown size and position " + "will be detected by ICD compatible software. Do " + "you want to invalidate the ICD table?") ); + if (result == PED_EXCEPTION_YES + || result == PED_EXCEPTION_UNHANDLED) + table.icd_part[0].flag &= ~PART_FLAG_USED; + else if (result == PED_EXCEPTION_CANCEL) + goto error; + } + + if (put_sign) + goto write_to_dev; + + /* Fill the ICD table */ + if (atr_disk->format == FMT_ICD) + for (i = 0; i < N_ICD; i++) { + if (pnum > last_num) + part = NULL; + else while (pnum <= last_num + && !(part = ped_disk_get_partition (disk, pnum))) + pnum++; + + if (!part) { + table.icd_part[i].flag &= ~PART_FLAG_USED; + continue; + } + + if (part->type & PED_PARTITION_EXTENDED + || part->type & PED_PARTITION_LOGICAL) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("ICD entries can't contain extended or " + "logical partitions.") ); + goto error; + } + + atr_part = ATARI_PART (part); + atr_fill_raw_entry (&table.icd_part[i], atr_part->flag, + atr_part->icd_id, part->geom.start, + part->geom.length ); + + pnum++; + } + + /* Write the chained list of logical partitions */ + if (atr_disk->format == FMT_XGM) { + if (!atr_write_logicals (disk)) + goto error; + } + +write_to_dev: + if (pnum <= last_num) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("There were remaining partitions after filling " + "the tables.") ); + goto error; + } + + /* Do we need to do that in case of failure too??? */ + atr_table_set_boot (&table, boot); + + /* Commit the root sector... */ + if (!ped_device_write (disk->dev, (void*) &table, 0, 1) + || !ped_device_sync (disk->dev)) + goto error; + + /* Try to init the HDX compatibility Bad Sectors List if needed. */ + if (atr_disk->HDX_comp && !atr_empty_init_bsl (disk)) + goto error; + + atr_disk->has_been_read = 1; + return ped_device_sync (disk->dev); + +error: + atr_disk->has_been_read = 0; + return 0; +} +#endif + +/* If extended partition in ICD mode, generate an error and returns 1 */ +/* else returns 0 */ +static int +atr_xgm_in_icd (const PedDisk* disk, PedPartitionType part_type) +{ + AtariDisk* atrdisk; + + PED_ASSERT (disk != NULL); + + if (part_type & PED_PARTITION_EXTENDED) { + atrdisk = ATARI_DISK (disk); + if (atrdisk->format == FMT_ICD) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("You can't use an extended XGM partition in " + "ICD mode (more than %d primary partitions, if " + "XGM is the first one it counts for two)."), + N_AHDI ); + return 1; + } + } + + return 0; +} + +static PedPartition* +atari_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + AtariPart* atrpart; + + if (atr_xgm_in_icd(disk, part_type)) + return 0; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + if (ped_partition_is_active (part)) { + part->disk_specific = atrpart = ped_malloc (sizeof (AtariPart)); + if (!atrpart) + goto error_free_part; + memset (atrpart, 0, sizeof (AtariPart)); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + _ped_partition_free (part); +error: + return NULL; +} + +static PedPartition* +atari_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + + 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; + if (ped_partition_is_active (part)) + memcpy (new_part->disk_specific, part->disk_specific, + sizeof (AtariPart)); + + return new_part; +} + +static void +atari_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) { + PED_ASSERT (part->disk_specific != NULL); + free (part->disk_specific); + } + _ped_partition_free (part); +} + +/* Note: fs_type is NULL for extended partitions */ +static int +atari_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + AtariPart* atrpart; + AtariFS2PartId* fs2id; + PED_ASSERT (part != NULL); + atrpart = ATARI_PART (part); + PED_ASSERT (atrpart != NULL); + + part->fs_type = fs_type; + + if (atr_xgm_in_icd(part->disk, part->type)) + return 0; + + if (part->type & PED_PARTITION_EXTENDED) { + strcpy (atrpart->part_id, "XGM"); + strcpy (atrpart->icd_id, "XGM"); + return 1; + } + + if (!fs_type) { + strcpy (atrpart->part_id, "RAW"); + strcpy (atrpart->icd_id, "RAW"); + return 1; + } + + for (fs2id = atr_fs2pid; fs2id->fs; fs2id++) { + if (!*fs2id->fs /* default entry */ + || ((!strcmp (fs_type->name, fs2id->fs) + && part->geom.length < fs2id->max_sectors))) { + + strcpy (atrpart->part_id, fs2id->pid); + if (atr_pid_known (fs2id->pid, atr_known_icd_pid)) + strcpy (atrpart->icd_id, fs2id->pid); + else + strcpy (atrpart->icd_id, "RAW"); + + break; + } + } + PED_ASSERT (fs2id->fs != NULL); + + return 1; +} + +static int +atari_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + AtariPart* atr_part; + AtariPartID2BootFlag* bf; + + PED_ASSERT (part != NULL); + atr_part = ATARI_PART (part); + PED_ASSERT (atr_part != NULL); + + if (flag != PED_PARTITION_BOOT) + return 0; + + if (state == 0) { + atr_part->flag = 0; + } else { + for (bf = atr_pid2bf; *bf->pid; bf++) { + if (atr_pid_eq (bf->pid, atr_part->part_id)) + break; + } + atr_part->flag = bf->flag; + } + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +atari_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + AtariPart* atr_part; + + PED_ASSERT (part != NULL); + atr_part = ATARI_PART (part); + PED_ASSERT (atr_part != NULL); + + if (flag != PED_PARTITION_BOOT) + return 0; + + return (atr_part->flag != 0); +} + +static int +atari_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + if (flag == PED_PARTITION_BOOT) + return 1; + + return 0; +} + +/* Adapted from disk_dos */ +static PedConstraint* +atr_log_constraint (const PedPartition* part) +{ + const PedGeometry* geom = &part->geom; + PedGeometry safe_space; + PedSector min_start; + PedSector max_end; + PedDisk* disk; + PedDevice* dev; + PedPartition* ext_part; + PedPartition* walk; + int first_log, not_first; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL); + + dev = (disk = part->disk) -> dev; + + first_log = atr_find_first_log (disk); + if (first_log == -1) + first_log = part->num; + + not_first = (part->num != first_log); + + walk = ext_part->part_list; + + min_start = ext_part->geom.start + 1 + not_first; + max_end = ext_part->geom.end; + + while (walk != NULL + && ( walk->geom.start - (walk->num != first_log) + < geom->start - not_first + || walk->geom.start - (walk->num != first_log) + < min_start ) ) { + if (walk != part && ped_partition_is_active (walk)) + min_start = walk->geom.end + 1 + not_first; + walk = walk->next; + } + + while (walk && (walk == part || !ped_partition_is_active (walk))) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1 - (walk->num != first_log); + + if (min_start >= max_end) + return NULL; + + ped_geometry_init (&safe_space, dev, min_start, + max_end - min_start + 1); + return ped_constraint_new_from_max (&safe_space); +} + +/* Adapted from disk_dos */ +static PedGeometry* +art_min_extended_geom (const PedPartition* ext_part) +{ + PedDisk* disk = ext_part->disk; + PedPartition* walk; + PedGeometry* min_geom; + int first_log; + + first_log = atr_find_first_log (disk); + if (first_log == -1) + return NULL; + + walk = ped_disk_get_partition (disk, first_log); + PED_ASSERT (walk->type & PED_PARTITION_LOGICAL); + min_geom = ped_geometry_duplicate (&walk->geom); + if (!min_geom) + return NULL; + ped_geometry_set_start (min_geom, walk->geom.start - 1); + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_partition_is_active (walk) || walk->num == first_log) + continue; + if (walk->geom.start < min_geom->start) + ped_geometry_set_start (min_geom, walk->geom.start - 2); + if (walk->geom.end > min_geom->end) + ped_geometry_set_end (min_geom, walk->geom.end); + } + + return min_geom; +} + +/* Adapted from disk_dos */ +static PedConstraint* +atr_ext_constraint (const PedPartition* part) +{ + PedGeometry start_range; + PedGeometry end_range; + PedConstraint* constraint; + PedDevice* dev; + PedDisk* disk; + PedGeometry* min; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + + dev = (disk = part->disk) -> dev; + min = art_min_extended_geom (part); + + if (min) { + ped_geometry_init (&start_range, dev, 1, min->start); + ped_geometry_init (&end_range, dev, min->end, + dev->length - min->end); + ped_geometry_destroy (min); + } else { + ped_geometry_init (&start_range, dev, 1, dev->length - 1); + ped_geometry_init (&end_range, dev, 1, dev->length - 1); + } + + constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any, + &start_range, &end_range, 1, dev->length); + return constraint; +} + +static PedConstraint* +atr_prim_constraint (const PedPartition* part) +{ + PedDevice* dev; + PedGeometry max; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + + dev = part->disk->dev; + + ped_geometry_init (&max, dev, 1, dev->length - 1); + return ped_constraint_new_from_max (&max); +} + +/* inspiration from disk_dos */ +static PedGeometry* +_best_solution (PedGeometry* a, PedGeometry* b) +{ + if (!a) + return b; + if (!b) + return a; + + if (a->length < b->length) + goto choose_b; + + ped_geometry_destroy (b); + return a; + +choose_b: + ped_geometry_destroy (a); + return b; +} + +/* copied from disk_dos */ +static PedGeometry* +_try_constraint (const PedPartition* part, const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + return NULL; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + ped_constraint_destroy (intersection); + return solution; +} + +/* + * internal is either the primary or extented constraint. + * If there's no BSL, the is the only internal constraint considered. + * If there's a BSL, try to fit the partition before or after (and + * choose the best fit, the one which results in the greatest size...) + */ +static int +atr_prim_align (PedPartition* part, const PedConstraint* constraint, + PedConstraint* internal) +{ + PedDevice* dev; + AtariDisk* atr_disk; + PedConstraint* cut; + PedGeometry* solution = NULL; + PedGeometry max; + PedSector bsl_end; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + dev = part->disk->dev; + atr_disk = ATARI_DISK (part->disk); + PED_ASSERT (atr_disk != NULL); + + /* No BSL */ + if (!atr_disk->bsl_start && !atr_disk->bsl_count) { + /* Note: _ped_partition_attempt_align will destroy internal */ + return _ped_partition_attempt_align(part, constraint, internal); + } + + /* BSL, try to fit before */ + if (atr_disk->bsl_start > 1) { + ped_geometry_init (&max, dev, 1, atr_disk->bsl_start - 1); + cut = ped_constraint_new_from_max (&max); + solution = _best_solution (solution, + _try_constraint (part, constraint, + ped_constraint_intersect (internal, cut))); + ped_constraint_destroy (cut); + } + + /* BSL, try to fit after, take the best solution */ + bsl_end = atr_disk->bsl_start + atr_disk->bsl_count; + if (bsl_end < dev->length) { + ped_geometry_init (&max, dev, bsl_end, dev->length - bsl_end); + cut = ped_constraint_new_from_max (&max); + solution = _best_solution (solution, + _try_constraint (part, constraint, + ped_constraint_intersect (internal, cut))); + ped_constraint_destroy (cut); + } + + ped_constraint_destroy (internal); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + + return 0; +} + +static int +atari_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + switch (part->type) { + case PED_PARTITION_LOGICAL: + if (_ped_partition_attempt_align (part, constraint, + atr_log_constraint (part) ) ) + return 1; + break; + case PED_PARTITION_EXTENDED: + if (atr_prim_align (part, constraint, + atr_ext_constraint (part) ) ) + return 1; + break; + default: + if (atr_prim_align (part, constraint, + atr_prim_constraint (part) ) ) + return 1; + break; + } + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +/* increment numbers of any non logical partition found after the last */ +/* logical one, to make room for a new logical partition */ +static int +art_room_for_logic (PedDisk* disk) +{ + PedPartition* part; + int num, last_logic, last; + + /* too many partitions ? */ + last = ped_disk_get_last_partition_num (disk); + if (last >= MAXIMUM_PARTS) + return 0; + + /* find the last logical partition */ + last_logic = 0; + for (num = 1; num <= last; num++) { + part = ped_disk_get_partition (disk, num); + if (part && ped_partition_is_active (part) + && (part->type & PED_PARTITION_LOGICAL)) + last_logic = num; + } + + if (!last_logic) + return 1; + + /* increment */ + for (num = last; num > last_logic; num--) { + part = ped_disk_get_partition (disk, num); + if (part && ped_partition_is_active (part) + && !(part->type & ( PED_PARTITION_LOGICAL + | PED_PARTITION_EXTENDED)) + && part->num > 0 ) + part->num++; + } + + return 1; +} + +static int +atari_partition_enumerate (PedPartition* part) +{ + AtariDisk* atrdisk; + PedPartition* ext_part; + PedPartition* log; + int i, want_icd, want_xgm, num_max, xgm_begin, prim_count; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + atrdisk = ATARI_DISK (part->disk); + PED_ASSERT (atrdisk != NULL); + + /* WARNING: some similar/related code in atari_write */ + /* This is quite a <hack> : this function is probably the only way */ + /* to know something has been / is going to be modified in the table.*/ + /* So we detect the current operation mode (AHDI/XGM/ICD) and report */ + /* errors (in which case we refuse to operate...) */ + + prim_count = ped_disk_get_primary_partition_count (part->disk); + ext_part = ped_disk_extended_partition (part->disk); + + /* <hack in the hack> : we can't reorder (yet) , so if we begin with */ + /* XGM the first slot must be empty */ + xgm_begin = ((log = ped_disk_get_partition (part->disk, 1)) + && (log->type & PED_PARTITION_LOGICAL)) + || ((part->num == -1) + && (part->type & PED_PARTITION_LOGICAL) + && !ped_disk_get_partition (part->disk, 1)); + /* </hack in the hack> */ + + PED_ASSERT (atrdisk->format != FMT_ICD || ext_part == NULL); + PED_ASSERT (atrdisk->format != FMT_XGM + || prim_count + xgm_begin <= N_AHDI); + PED_ASSERT (atrdisk->format != FMT_AHDI + || (ext_part == NULL && prim_count + xgm_begin <= N_AHDI)); + + want_icd = ( ( prim_count + + xgm_begin + + ( (part->num == -1) + && !(part->type & PED_PARTITION_LOGICAL) ) ) + > N_AHDI ); + want_xgm = ( (part->type & PED_PARTITION_EXTENDED) + || ext_part != NULL ); + + if (!want_xgm && !want_icd) + atrdisk->format = FMT_AHDI; + else if (want_xgm && !want_icd) + atrdisk->format = FMT_XGM; + else if (!want_xgm && want_icd) + atrdisk->format = FMT_ICD; + else { + if (atr_xgm_in_icd (part->disk, PED_PARTITION_EXTENDED)) + return 0; + else { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("You can't use more than %d primary partitions " + "(ICD mode) if you use an extended XGM " + "partition. If XGM is the first partition " + "it counts for two."), + N_AHDI ); + return 0; + } + } + /* End of </hack> */ + + + /* Ext will be numbered 0 and will stay 0... */ + if (part->num == 0) + return 1; + + if (part->num == -1) { + + /* Linux don't show the ext part itself for Atari disk labels */ + /* so we use number 0 (could use a big number too, but that */ + /* would be less cute ;) */ + if (part->type & PED_PARTITION_EXTENDED) { + part->num = 0; + return 1; + } + + switch (atrdisk->format) { + case FMT_AHDI: + case FMT_ICD: + num_max = N_ICD + N_AHDI; + break; + case FMT_XGM: + num_max = MAXIMUM_PARTS; + break; + default: + num_max = 0; + PED_ASSERT (0); + } + + /* make room for logical partitions */ + if (part->type & PED_PARTITION_LOGICAL) { + if (!art_room_for_logic (part->disk)) + goto error_alloc_failed; + } + + /* find an unused number */ + for (i = 1; i <= num_max; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + + } else { + /* find an unused number before or don't re-number */ + for (i = 1; i < part->num; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + } + } + return 1; + } + + /* failed to allocate a number */ +error_alloc_failed: +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a partition number.")); +#endif + return 0; +} + +static int +atr_creat_add_metadata (PedDisk* disk, PedSector start, PedSector end, + PedPartitionType type ) +{ + PedPartition* new_part; + PedConstraint* const_exact; + int added; + + type |= PED_PARTITION_METADATA; + new_part = ped_partition_new (disk, type, NULL, start, end); + if (!new_part) + goto error; + + const_exact = ped_constraint_exact (&new_part->geom); + added = ped_disk_add_partition (disk, new_part, const_exact); + ped_constraint_destroy (const_exact); + if (!added) + goto error_destroy_part; + + return 1; + +error_destroy_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +static int +atari_alloc_metadata (PedDisk* disk) +{ + PedPartition* ext; + PedPartition* log; + AtariDisk* atr_disk; + int i; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + /* allocate 1 sector for the disk label at the start */ + if (!atr_creat_add_metadata (disk, 0, 0, 0)) + return 0; + + /* allocate the sectors containing the BSL */ + if (atr_disk->bsl_start || atr_disk->bsl_count) { + if (!atr_creat_add_metadata (disk, atr_disk->bsl_start, + atr_disk->bsl_start + + atr_disk->bsl_count - 1, 0 ) ) + return 0; + } + + ext = ped_disk_extended_partition (disk); + if (ext) { + if (!atr_creat_add_metadata (disk, ext->geom.start, + ext->geom.start, + PED_PARTITION_LOGICAL ) ) + return 0; + + /* Find the first logical part */ + for (i = 1; i <= ped_disk_get_last_partition_num (disk); i++) + if ((log = ped_disk_get_partition (disk, i)) + && (log->type & PED_PARTITION_LOGICAL)) + break; + + for (log = ext->part_list; log; log = log->next) { + if ((log->type & ( PED_PARTITION_METADATA + | PED_PARTITION_FREESPACE)) + || log->num == i) + continue; + + if (!atr_creat_add_metadata (disk, log->geom.start-1, + log->geom.start-1, + PED_PARTITION_LOGICAL ) ) + return 0; + } + } + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +atari_get_max_primary_partition_count (const PedDisk* disk) +{ + AtariDisk* atr_disk; + + PED_ASSERT (disk != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + return atr_disk->format == FMT_XGM ? N_AHDI : N_AHDI + N_ICD; +} + +static bool +atari_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + AtariDisk* atr_disk; + + PED_ASSERT (disk != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + *max_n = atr_disk->format == FMT_XGM ? N_AHDI : N_AHDI + N_ICD; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions(atari) + +static PedDiskOps atari_disk_ops = { + clobber: NULL_IF_DISCOVER_ONLY (atari_clobber), + write: NULL_IF_DISCOVER_ONLY (atari_write), + + partition_set_name: NULL, + partition_get_name: NULL, + + PT_op_function_initializers (atari) +}; + +static PedDiskType atari_disk_type = { + next: NULL, + name: "atari", + ops: &atari_disk_ops, + features: PED_DISK_TYPE_EXTENDED +}; + +void +ped_disk_atari_init () +{ + PED_ASSERT (sizeof (AtariRawPartition) == 12); + PED_ASSERT (sizeof (AtariRawTable) == 512); + /* GNU Libc doesn't support NULL instead of the locale name */ + PED_ASSERT ((atr_c_locale = newlocale(LC_ALL_MASK, "C", NULL)) != NULL); + + ped_disk_type_register (&atari_disk_type); +} + +void +ped_disk_atari_done () +{ + ped_disk_type_unregister (&atari_disk_type); + freelocale(atr_c_locale); +} |