+From 5750e235a3cb27dc94aae0ca0e7d946c9319cc9d Mon Sep 17 00:00:00 2001
+From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
+Date: Sun, 4 Dec 2016 17:12:46 +0100
+Subject: [PATCH 65/75] libparted: Add support for atari partition tables
+
+Signed-off-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
+Signed-off-by: Brian C. Lane <bcl@redhat.com>
+---
+ libparted/labels/Makefile.am | 1 +
+ libparted/labels/atari.c | 1969 +++++++++++++++++++++++++++++++++++++++
+ libparted/labels/pt-limit.gperf | 1 +
+ libparted/libparted.c | 4 +
+ libparted/tests/common.c | 3 +
+ po/POTFILES.in | 1 +
+ tests/t3310-flags.sh | 6 +-
+ tests/t9021-maxima.sh | 5 +-
+ 8 files changed, 1988 insertions(+), 2 deletions(-)
+ create mode 100644 libparted/labels/atari.c
+
+diff --git a/libparted/labels/Makefile.am b/libparted/labels/Makefile.am
+index c996f81..3327c8c 100644
+--- a/libparted/labels/Makefile.am
++++ b/libparted/labels/Makefile.am
+@@ -19,6 +19,7 @@ noinst_LTLIBRARIES = liblabels.la
+ liblabels_la_SOURCES = \
+ $(S390_SRCS) \
+ aix.c \
++ atari.c \
+ bsd.c \
+ dos.c \
+ dvh.c \
+diff --git a/libparted/labels/atari.c b/libparted/labels/atari.c
+new file mode 100644
+index 0000000..36e68b9
+--- /dev/null
++++ b/libparted/labels/atari.c
+@@ -0,0 +1,1969 @@
++/* -*- 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 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 <xlocale.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 */
++ 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)) _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->id, 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->id, 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);
++
++ /* 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);
++}
+diff --git a/libparted/labels/pt-limit.gperf b/libparted/labels/pt-limit.gperf
+index 3d764ae..d5a580d 100644
+--- a/libparted/labels/pt-limit.gperf
++++ b/libparted/labels/pt-limit.gperf
+@@ -25,3 +25,4 @@ pc98,UINT32_MAX,UINT32_MAX
+ #
+ # FIXME: not verified. looks like these are cylinder aligned, too
+ amiga,UINT32_MAX,UINT32_MAX
++atari,INT32_MAX,INT32_MAX
+diff --git a/libparted/libparted.c b/libparted/libparted.c
+index d5cbb3a..d855d0e 100644
+--- a/libparted/libparted.c
++++ b/libparted/libparted.c
+@@ -75,6 +75,7 @@ extern void ped_disk_pc98_init ();
+ extern void ped_disk_sun_init ();
+ extern void ped_disk_amiga_init ();
+ extern void ped_disk_dasd_init ();
++extern void ped_disk_atari_init ();
+
+ static void
+ init_disk_types ()
+@@ -96,6 +97,7 @@ init_disk_types ()
+ ped_disk_bsd_init ();
+ ped_disk_amiga_init ();
+ ped_disk_aix_init ();
++ ped_disk_atari_init ();
+ }
+
+ extern void ped_file_system_amiga_init (void);
+@@ -139,6 +141,7 @@ extern void ped_disk_pc98_done ();
+ extern void ped_disk_sun_done ();
+ extern void ped_disk_amiga_done ();
+ extern void ped_disk_dasd_done ();
++extern void ped_disk_atari_done ();
+
+ static void
+ done_disk_types ()
+@@ -158,6 +161,7 @@ done_disk_types ()
+ ped_disk_bsd_done ();
+ ped_disk_amiga_done ();
+ ped_disk_aix_done ();
++ ped_disk_atari_done ();
+ }
+
+ static void _init() __attribute__ ((constructor));
+diff --git a/libparted/tests/common.c b/libparted/tests/common.c
+index 9115686..2be0e3a 100644
+--- a/libparted/tests/common.c
++++ b/libparted/tests/common.c
+@@ -83,6 +83,9 @@ _implemented_disk_label (const char *label)
+ /* FIXME: these have minor problems, so skip them, temporarily. */
+ if (STREQ (label, "amiga")) return 0;
+
++ if (STREQ (label, "atari") && get_sector_size() != 512)
++ return 0;
++
+ /* Not implemented yet */
+ if (STREQ (label, "aix")) return 0;
+ if (STREQ (label, "pc98")) return 0;
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index a8e5994..7a196f7 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -27,6 +27,7 @@ libparted/debug.c
+ libparted/disk.c
+ libparted/exception.c
+ libparted/labels/aix.c
++libparted/labels/atari.c
+ libparted/labels/bsd.c
+ libparted/labels/dasd.c
+ libparted/labels/dos.c
+diff --git a/tests/t3310-flags.sh b/tests/t3310-flags.sh
+index 10ac50d..2da72d0 100644
+--- a/tests/t3310-flags.sh
++++ b/tests/t3310-flags.sh
+@@ -25,7 +25,7 @@ extract_flags()
+ perl -nle '/^[^:]*:4096s:6143s:2048s::[^:]*:(.+);$/ and print $1' "$@"
+ }
+
+-for table_type in aix amiga bsd dvh gpt mac msdos pc98 sun loop; do
++for table_type in aix amiga atari bsd dvh gpt mac msdos pc98 sun loop; do
+ ptn_num=1
+
+ case $table_type in
+@@ -35,6 +35,10 @@ for table_type in aix amiga bsd dvh gpt mac msdos pc98 sun loop; do
+ ;;
+ amiga) primary_or_name='PTNNAME'
+ ;;
++ atari) primary_or_name='primary'
++ # atari only supports 512b sectors
++ [ $ss -ne 512 ] && continue
++ ;;
+ bsd) primary_or_name=''
+ ;;
+ dvh) primary_or_name='primary'
+diff --git a/tests/t9021-maxima.sh b/tests/t9021-maxima.sh
+index 510e6c2..5320a6e 100755
+--- a/tests/t9021-maxima.sh
++++ b/tests/t9021-maxima.sh
+@@ -37,6 +37,7 @@ max_n_partitions()
+ mac) m=65536;;
+ bsd) m=8;;
+ amiga) m=128;;
++ atari) m=12;;
+ loop) m=1;;
+ pc98) case $ss in 512) m=16;; *) m=64;; esac;;
+ *) warn_ invalid partition table type: $1 1>&2; exit 1;;
+@@ -45,8 +46,9 @@ max_n_partitions()
+ }
+
+ # FIXME: add aix when/if it's supported again
+-for t in msdos gpt dvh sun mac bsd amiga loop pc98; do
++for t in msdos gpt dvh sun mac bsd amiga atari loop pc98; do
+ echo $t
++ [ $t == 'atari' ] && [ $ss != 512 ] && continue
+ rm -f $dev
+ dd if=/dev/zero of=$dev bs=$ss count=1 seek=10000 || { fail=1; continue; }
+ parted -s $dev mklabel $t || { fail=1; continue; }
+@@ -56,6 +58,7 @@ for t in msdos gpt dvh sun mac bsd amiga loop pc98; do
+ max_start=4294967295
+ max_len=4294967295
+ case $t in
++ atari) max_start=2147483647; max_len=$max_start;;
+ gpt|loop) max_start=18446744073709551615; max_len=$max_start;;
+ sun) max_start=549755813760;; # 128 * (2^32-1)
+ esac
+--
+2.9.3
+