Hi!

I just stumbled into this bug again since I wanted to use parted today to
create a disk image that would be recognized by ARAnyM with a kernel with
just Atari partition support.

I pulled the old patch from Stephen's git repository, applied it to the
current git version of parted which surprisingly still worked, albeit
the patches applied with some fuzz.

However, the code did not build anymore, so I fixed all compiler errors
and warnings and was now able to run parted with the patch merged.

Creating a partition table with "mklabel atari" worked, but now libparted
crashed when running "mkpart". After refactoring the Atari code to make
use of pt-common.h and pt-tools.h, this problem was fixed, too.

I then tried the image on Aranym but to my disappointment, the kernel did
not recognize the partition table, so there is still something wrong with
the patch as-is.

I'm attaching my current patch for parted. Maybe someone else has an idea
what I might be missing here to get the kernel recognize the partition
table created with the patched version of parted.

Once we have sorted the remaining issues out, I will take care of adding
the necessary tests and get the changes merged upstream.

Thanks,
Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer - glaub...@debian.org
`. `'   Freie Universitaet Berlin - glaub...@physik.fu-berlin.de
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913
>From c965b4144784341003d9b1000df96da1a1ac1aff Mon Sep 17 00:00:00 2001
From: John Paul Adrian Glaubitz <glaub...@physik.fu-berlin.de>
Date: Sat, 15 Oct 2016 14:52:17 +0200
Subject: [PATCH] Add support for Atari partition labels

---
 libparted/labels/Makefile.am |    1 +
 libparted/labels/atari.c     | 1969 ++++++++++++++++++++++++++++++++++++++++++
 libparted/libparted.c        |    4 +
 po/POTFILES.in               |    1 +
 4 files changed, 1975 insertions(+)
 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..8f85ccd
--- /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
+    disk_amiga.c - libparted module to manipulate amiga RDB 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_guilla...@libertysurf.fr>
+                  John Paul Adrian Glaubitz <glaub...@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/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/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
-- 
2.9.3

Reply via email to