bug-parted
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 02/10] lib-fs-resize: re-add HFS and FAT file-system-related code


From: Jim Meyering
Subject: [PATCH 02/10] lib-fs-resize: re-add HFS and FAT file-system-related code
Date: Fri, 3 Feb 2012 22:46:51 +0100

From: Jim Meyering <address@hidden>

This just adds the code, without hooking it up yet.
* po/POTFILES.in: Add all libparted/fs/r/**.c files.
---
 include/parted/filesys.in.h     |    5 +
 libparted/fs/Makefile.am        |   54 ++-
 libparted/fs/r/fat/bootsector.c |  452 +++++++++++++
 libparted/fs/r/fat/bootsector.h |  131 ++++
 libparted/fs/r/fat/calc.c       |  433 +++++++++++++
 libparted/fs/r/fat/calc.h       |   76 +++
 libparted/fs/r/fat/clstdup.c    |  422 ++++++++++++
 libparted/fs/r/fat/clstdup.h    |   27 +
 libparted/fs/r/fat/context.c    |  260 ++++++++
 libparted/fs/r/fat/context.h    |   69 ++
 libparted/fs/r/fat/count.c      |  401 ++++++++++++
 libparted/fs/r/fat/count.h      |   45 ++
 libparted/fs/r/fat/fat.c        |  892 +++++++++++++++++++++++++
 libparted/fs/r/fat/fat.h        |  158 +++++
 libparted/fs/r/fat/fatio.c      |  149 +++++
 libparted/fs/r/fat/fatio.h      |   48 ++
 libparted/fs/r/fat/resize.c     |  877 +++++++++++++++++++++++++
 libparted/fs/r/fat/table.c      |  480 ++++++++++++++
 libparted/fs/r/fat/table.h      |   73 +++
 libparted/fs/r/fat/traverse.c   |  367 +++++++++++
 libparted/fs/r/fat/traverse.h   |   74 +++
 libparted/fs/r/filesys.c        |  164 +++++
 libparted/fs/r/hfs/DOC          |   92 +++
 libparted/fs/r/hfs/HISTORY      |  115 ++++
 libparted/fs/r/hfs/TODO         |   27 +
 libparted/fs/r/hfs/advfs.c      |  328 ++++++++++
 libparted/fs/r/hfs/advfs.h      |   48 ++
 libparted/fs/r/hfs/advfs_plus.c |  382 +++++++++++
 libparted/fs/r/hfs/advfs_plus.h |   51 ++
 libparted/fs/r/hfs/cache.c      |  238 +++++++
 libparted/fs/r/hfs/cache.h      |  117 ++++
 libparted/fs/r/hfs/file.c       |  228 +++++++
 libparted/fs/r/hfs/file.h       |   41 ++
 libparted/fs/r/hfs/file_plus.c  |  273 ++++++++
 libparted/fs/r/hfs/file_plus.h  |   60 ++
 libparted/fs/r/hfs/hfs.c        | 1356 +++++++++++++++++++++++++++++++++++++++
 libparted/fs/r/hfs/hfs.h        |  647 +++++++++++++++++++
 libparted/fs/r/hfs/journal.c    |  389 +++++++++++
 libparted/fs/r/hfs/journal.h    |   44 ++
 libparted/fs/r/hfs/probe.c      |  230 +++++++
 libparted/fs/r/hfs/probe.h      |   43 ++
 libparted/fs/r/hfs/reloc.c      |  669 +++++++++++++++++++
 libparted/fs/r/hfs/reloc.h      |   35 +
 libparted/fs/r/hfs/reloc_plus.c |  942 +++++++++++++++++++++++++++
 libparted/fs/r/hfs/reloc_plus.h |   36 +
 po/POTFILES.in                  |   19 +
 46 files changed, 12066 insertions(+), 1 deletions(-)
 create mode 100644 libparted/fs/r/fat/bootsector.c
 create mode 100644 libparted/fs/r/fat/bootsector.h
 create mode 100644 libparted/fs/r/fat/calc.c
 create mode 100644 libparted/fs/r/fat/calc.h
 create mode 100644 libparted/fs/r/fat/clstdup.c
 create mode 100644 libparted/fs/r/fat/clstdup.h
 create mode 100644 libparted/fs/r/fat/context.c
 create mode 100644 libparted/fs/r/fat/context.h
 create mode 100644 libparted/fs/r/fat/count.c
 create mode 100644 libparted/fs/r/fat/count.h
 create mode 100644 libparted/fs/r/fat/fat.c
 create mode 100644 libparted/fs/r/fat/fat.h
 create mode 100644 libparted/fs/r/fat/fatio.c
 create mode 100644 libparted/fs/r/fat/fatio.h
 create mode 100644 libparted/fs/r/fat/resize.c
 create mode 100644 libparted/fs/r/fat/table.c
 create mode 100644 libparted/fs/r/fat/table.h
 create mode 100644 libparted/fs/r/fat/traverse.c
 create mode 100644 libparted/fs/r/fat/traverse.h
 create mode 100644 libparted/fs/r/filesys.c
 create mode 100644 libparted/fs/r/hfs/DOC
 create mode 100644 libparted/fs/r/hfs/HISTORY
 create mode 100644 libparted/fs/r/hfs/TODO
 create mode 100644 libparted/fs/r/hfs/advfs.c
 create mode 100644 libparted/fs/r/hfs/advfs.h
 create mode 100644 libparted/fs/r/hfs/advfs_plus.c
 create mode 100644 libparted/fs/r/hfs/advfs_plus.h
 create mode 100644 libparted/fs/r/hfs/cache.c
 create mode 100644 libparted/fs/r/hfs/cache.h
 create mode 100644 libparted/fs/r/hfs/file.c
 create mode 100644 libparted/fs/r/hfs/file.h
 create mode 100644 libparted/fs/r/hfs/file_plus.c
 create mode 100644 libparted/fs/r/hfs/file_plus.h
 create mode 100644 libparted/fs/r/hfs/hfs.c
 create mode 100644 libparted/fs/r/hfs/hfs.h
 create mode 100644 libparted/fs/r/hfs/journal.c
 create mode 100644 libparted/fs/r/hfs/journal.h
 create mode 100644 libparted/fs/r/hfs/probe.c
 create mode 100644 libparted/fs/r/hfs/probe.h
 create mode 100644 libparted/fs/r/hfs/reloc.c
 create mode 100644 libparted/fs/r/hfs/reloc.h
 create mode 100644 libparted/fs/r/hfs/reloc_plus.c
 create mode 100644 libparted/fs/r/hfs/reloc_plus.h

diff --git a/include/parted/filesys.in.h b/include/parted/filesys.in.h
index 2f74c91..7d493d6 100644
--- a/include/parted/filesys.in.h
+++ b/include/parted/filesys.in.h
@@ -98,6 +98,11 @@ extern PedGeometry* ped_file_system_probe_specific (
                        const PedFileSystemType* fs_type,
                        PedGeometry* geom);

+PedFileSystem *ped_file_system_open (PedGeometry *geom);
+int ped_file_system_close (PedFileSystem *fs);
+int ped_file_system_resize (PedFileSystem *fs, PedGeometry *geom,
+                           PedTimer *timer);
+
 #endif /* PED_FILESYS_H_INCLUDED */

 /** @} */
diff --git a/libparted/fs/Makefile.am b/libparted/fs/Makefile.am
index 6339cfd..947af0e 100644
--- a/libparted/fs/Makefile.am
+++ b/libparted/fs/Makefile.am
@@ -4,7 +4,10 @@
 # This file may be modified and/or distributed without restriction.

 partedincludedir = -I$(top_builddir)/include -I$(top_srcdir)/include
-noinst_LTLIBRARIES =   libfs.la
+
+AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+
+noinst_LTLIBRARIES    =        libfs.la

 libfs_la_LIBADD   = $(UUID_LIBS)               \
                    $(INTLLIBS)                 \
@@ -48,4 +51,53 @@ libfs_la_SOURCES =           \

 EXTRA_DIST = hfs/DOC hfs/HISTORY

+lib_LTLIBRARIES = libparted-fs-resize.la
+
+EXTRA_DIST =                   \
+  r/hfs/DOC                    \
+  r/hfs/HISTORY                        \
+  r/hfs/TODO
+
+libparted_fs_resize_la_SOURCES = \
+  r/filesys.c                  \
+  r/fat/bootsector.c           \
+  r/fat/bootsector.h           \
+  r/fat/calc.c                 \
+  r/fat/calc.h                 \
+  r/fat/clstdup.c              \
+  r/fat/clstdup.h              \
+  r/fat/context.c              \
+  r/fat/context.h              \
+  r/fat/count.c                        \
+  r/fat/count.h                        \
+  r/fat/fat.c                  \
+  r/fat/fat.h                  \
+  r/fat/fatio.c                        \
+  r/fat/fatio.h                        \
+  r/fat/resize.c               \
+  r/fat/table.c                        \
+  r/fat/table.h                        \
+  r/fat/traverse.c             \
+  r/fat/traverse.h             \
+  r/hfs/advfs.c                        \
+  r/hfs/advfs.h                        \
+  r/hfs/advfs_plus.c           \
+  r/hfs/advfs_plus.h           \
+  r/hfs/cache.c                        \
+  r/hfs/cache.h                        \
+  r/hfs/file.c                 \
+  r/hfs/file.h                 \
+  r/hfs/file_plus.c            \
+  r/hfs/file_plus.h            \
+  r/hfs/hfs.c                  \
+  r/hfs/hfs.h                  \
+  r/hfs/journal.c              \
+  r/hfs/journal.h              \
+  r/hfs/probe.c                        \
+  r/hfs/probe.h                        \
+  r/hfs/reloc.c                        \
+  r/hfs/reloc.h                        \
+  r/hfs/reloc_plus.c           \
+  r/hfs/reloc_plus.h
+
 INCLUDES = $(partedincludedir) $(INTLINCS)
diff --git a/libparted/fs/r/fat/bootsector.c b/libparted/fs/r/fat/bootsector.c
new file mode 100644
index 0000000..a5d69c3
--- /dev/null
+++ b/libparted/fs/r/fat/bootsector.c
@@ -0,0 +1,452 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2002, 2004, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Reads in the boot sector (superblock), and does a minimum of sanity
+ * checking.  The goals are:
+ *     - to detect fat file systems, even if they are damaged [i.e. not
+ * return an error / throw an exception]
+ *     - to fail detection if there's not enough information for
+ * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero)
+ */
+int
+fat_boot_sector_read (FatBootSector* bs, const PedGeometry *geom)
+{
+       PED_ASSERT (bs != NULL);
+       PED_ASSERT (geom != NULL);
+
+       if (!ped_geometry_read (geom, bs, 0, 1))
+               return 0;
+
+       if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("File system has an invalid signature for a FAT "
+                         "file system."));
+               return 0;
+       }
+
+       if (!bs->system_id[0]) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("File system has an invalid signature for a FAT "
+                         "file system."));
+               return 0;
+       }
+
+       if (!bs->sector_size
+            || PED_LE16_TO_CPU (bs->sector_size) % PED_SECTOR_SIZE_DEFAULT) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("File system has an invalid sector size for a FAT "
+                         "file system."));
+               return 0;
+       }
+
+       if (!bs->cluster_size) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("File system has an invalid cluster size for a FAT "
+                         "file system."));
+               return 0;
+       }
+
+       if (!bs->reserved) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("File system has an invalid number of reserved "
+                         "sectors for a FAT file system."));
+               return 0;
+       }
+
+       if (bs->fats < 1 || bs->fats > 4) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("File system has an invalid number of FATs."));
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+    Don't trust the FAT12, FAT16 or FAT32 label string.
+ */
+FatType
+fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom)
+{
+       PedSector       logical_sector_size;
+       PedSector       first_cluster_sector;
+       FatCluster      cluster_count;
+
+       if (!PED_LE16_TO_CPU (bs->dir_entries))
+               return FAT_TYPE_FAT32;
+
+       logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512;
+
+       first_cluster_sector
+               = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size
+                 + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size
+                 + PED_LE16_TO_CPU (bs->dir_entries)
+                       / (512 / sizeof (FatDirEntry));
+       cluster_count = (geom->length - first_cluster_sector)
+                       / bs->cluster_size / logical_sector_size;
+       if (cluster_count > MAX_FAT12_CLUSTERS)
+               return FAT_TYPE_FAT16;
+       else
+               return FAT_TYPE_FAT12;
+}
+
+/* Analyses the boot sector, and sticks appropriate numbers in
+   fs->type_specific.
+
+   Note: you need to subtract (2 * cluster_sectors) off cluster offset,
+   because the first cluster is number 2.  (0 and 1 are not real clusters,
+   and referencing them is a bug)
+ */
+int
+fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       int                     fat_entry_size;
+
+       PED_ASSERT (bs != NULL);
+
+       if (PED_LE16_TO_CPU (bs->sector_size) != 512) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_BUG,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("This file system has a logical sector size of %d.  "
+                       "GNU Parted is known not to work properly with sector "
+                       "sizes other than 512 bytes."),
+                       (int) PED_LE16_TO_CPU (bs->sector_size))
+                               != PED_EXCEPTION_IGNORE)
+                       return 0;
+       }
+
+       fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512;
+
+       fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track);
+       fs_info->heads = PED_LE16_TO_CPU (bs->heads);
+       if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63
+           || fs_info->heads < 1 || fs_info->heads > 255) {
+               PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom;
+               int cyl_count = 0;
+
+               if (fs_info->heads > 0 && fs_info->sectors_per_track > 0)
+                       cyl_count = fs->geom->dev->length / fs_info->heads
+                                       / fs_info->sectors_per_track;
+
+               switch (ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_FIX + PED_EXCEPTION_IGNORE
+                       + PED_EXCEPTION_CANCEL,
+                       _("The file system's CHS geometry is (%d, %d, %d), "
+                         "which is invalid.  The partition table's CHS "
+                         "geometry is (%d, %d, %d).  If you select Ignore, "
+                         "the file system's CHS geometry will be left "
+                         "unchanged.  If you select Fix, the file system's "
+                         "CHS geometry will be set to match the partition "
+                         "table's CHS geometry."),
+                        cyl_count, fs_info->heads, fs_info->sectors_per_track,
+                        bios_geom->cylinders, bios_geom->heads,
+                        bios_geom->sectors)) {
+
+               case PED_EXCEPTION_FIX:
+                       fs_info->sectors_per_track = bios_geom->sectors;
+                       fs_info->heads = bios_geom->heads;
+                       bs->secs_track
+                               = PED_CPU_TO_LE16 (fs_info->sectors_per_track);
+                       bs->heads = PED_CPU_TO_LE16 (fs_info->heads);
+                       if (!fat_boot_sector_write (bs, fs))
+                               return 0;
+                       break;
+
+               case PED_EXCEPTION_CANCEL:
+                       return 0;
+
+               case PED_EXCEPTION_IGNORE:
+                       break;
+
+                default:
+                        break;
+               }
+       }
+
+       if (bs->sectors)
+               fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors)
+                                               * fs_info->logical_sector_size;
+       else
+               fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count)
+                                               * fs_info->logical_sector_size;
+
+       fs_info->fat_table_count = bs->fats;
+       fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries);
+       fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved)
+                                       * fs_info->logical_sector_size;
+       fs_info->cluster_sectors = bs->cluster_size
+                                  * fs_info->logical_sector_size;
+       fs_info->cluster_size = fs_info->cluster_sectors * 512;
+
+       if (fs_info->logical_sector_size == 0) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("FAT boot sector says logical sector size is 0.  "
+                         "This is weird. "));
+               return 0;
+       }
+       if (fs_info->fat_table_count == 0) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("FAT boot sector says there are no FAT tables.  This "
+                         "is weird. "));
+               return 0;
+       }
+       if (fs_info->cluster_sectors == 0) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                       _("FAT boot sector says clusters are 0 sectors.  This "
+                         "is weird. "));
+               return 0;
+       }
+
+       fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom);
+       if (fs_info->fat_type == FAT_TYPE_FAT12) {
+               ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_CANCEL,
+                       _("File system is FAT12, which is unsupported."));
+               return 0;
+       }
+       if (fs_info->fat_type == FAT_TYPE_FAT16) {
+               fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length)
+                                      * fs_info->logical_sector_size;
+               fs_info->serial_number
+                       = PED_LE32_TO_CPU (bs->u.fat16.serial_number);
+               fs_info->root_cluster = 0;
+               fs_info->root_dir_offset
+                       = fs_info->fat_offset
+                         + fs_info->fat_sectors * fs_info->fat_table_count;
+               fs_info->root_dir_sector_count
+                       = fs_info->root_dir_entry_count * sizeof (FatDirEntry)
+                         / (512 * fs_info->logical_sector_size);
+               fs_info->cluster_offset
+                       = fs_info->root_dir_offset
+                         + fs_info->root_dir_sector_count;
+       }
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length)
+                                       * fs_info->logical_sector_size;
+               fs_info->serial_number
+                       = PED_LE32_TO_CPU (bs->u.fat32.serial_number);
+               fs_info->info_sector_offset
+                   = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.info_sector)
+                         * fs_info->logical_sector_size;
+               fs_info->boot_sector_backup_offset
+                 = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.backup_sector)
+                         * fs_info->logical_sector_size;
+               fs_info->root_cluster
+                       = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster);
+               fs_info->root_dir_offset = 0;
+               fs_info->root_dir_sector_count = 0;
+               fs_info->cluster_offset
+                       = fs_info->fat_offset
+                         + fs_info->fat_sectors * fs_info->fat_table_count;
+       }
+
+       fs_info->cluster_count
+               = (fs_info->sector_count - fs_info->cluster_offset)
+                 / fs_info->cluster_sectors;
+
+       fat_entry_size = fat_table_entry_size (fs_info->fat_type);
+       if (fs_info->cluster_count + 2
+                       > fs_info->fat_sectors * 512 / fat_entry_size)
+               fs_info->cluster_count
+                       = fs_info->fat_sectors * 512 / fat_entry_size - 2;
+
+       fs_info->dir_entries_per_cluster
+               = fs_info->cluster_size / sizeof (FatDirEntry);
+       return 1;
+}
+
+#ifndef DISCOVER_ONLY
+int
+fat_boot_sector_set_boot_code (FatBootSector* bs)
+{
+       PED_ASSERT (bs != NULL);
+
+       memset (bs, 0, 512);
+       memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3);
+       memcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE, FAT_BOOT_CODE_LENGTH);
+       return 1;
+}
+
+int
+fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (bs != NULL);
+
+       memcpy (bs->system_id, "MSWIN4.1", 8);
+       bs->sector_size = PED_CPU_TO_LE16 (fs_info->logical_sector_size * 512);
+       bs->cluster_size = fs_info->cluster_sectors
+                               / fs_info->logical_sector_size;
+       bs->reserved = PED_CPU_TO_LE16 (fs_info->fat_offset
+                                       / fs_info->logical_sector_size);
+       bs->fats = fs_info->fat_table_count;
+
+       bs->dir_entries = (fs_info->fat_type == FAT_TYPE_FAT16)
+                         ? PED_CPU_TO_LE16 (fs_info->root_dir_entry_count)
+                         : 0;
+
+       if (fs_info->sector_count / fs_info->logical_sector_size > 0xffff
+               || fs_info->fat_type == FAT_TYPE_FAT32) {
+               bs->sectors = 0;
+               bs->sector_count = PED_CPU_TO_LE32 (fs_info->sector_count
+                                               / fs_info->logical_sector_size);
+       } else {
+               bs->sectors = PED_CPU_TO_LE16 (fs_info->sector_count
+                                              / fs_info->logical_sector_size);
+               bs->sector_count = 0;
+       }
+
+       bs->media = 0xf8;
+
+       bs->secs_track = PED_CPU_TO_LE16 (fs_info->sectors_per_track);
+       bs->heads = PED_CPU_TO_LE16 (fs_info->heads);
+       bs->hidden = PED_CPU_TO_LE32 (fs->geom->start);
+
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               bs->fat_length = 0;
+               bs->u.fat32.fat_length = PED_CPU_TO_LE32 (fs_info->fat_sectors
+                                               / fs_info->logical_sector_size);
+               bs->u.fat32.flags = 0;  /* FIXME: what the hell are these? */
+               bs->u.fat32.version = 0;  /* must be 0, for Win98 bootstrap */
+               bs->u.fat32.root_dir_cluster
+                       = PED_CPU_TO_LE32 (fs_info->root_cluster);
+               bs->u.fat32.info_sector
+                       = PED_CPU_TO_LE16 (fs_info->info_sector_offset
+                                          / fs_info->logical_sector_size);
+               bs->u.fat32.backup_sector
+                       = PED_CPU_TO_LE16 (fs_info->boot_sector_backup_offset
+                                          / fs_info->logical_sector_size);
+
+               bs->u.fat32.drive_num = 0x80;   /* _ALWAYS_ 0x80.  silly DOS */
+
+               memset (bs->u.fat32.empty_1, 0, 12);
+
+               bs->u.fat32.ext_signature = 0x29;
+               bs->u.fat32.serial_number
+                       = PED_CPU_TO_LE32 (fs_info->serial_number);
+               memcpy (bs->u.fat32.volume_name, "NO NAME    ", 11);
+               memcpy (bs->u.fat32.fat_name, "FAT32   ", 8);
+       } else {
+               bs->fat_length
+                       = PED_CPU_TO_LE16 (fs_info->fat_sectors
+                                          / fs_info->logical_sector_size);
+
+               bs->u.fat16.drive_num = 0x80;   /* _ALWAYS_ 0x80.  silly DOS */
+
+               bs->u.fat16.ext_signature = 0x29;
+               bs->u.fat16.serial_number
+                       = PED_CPU_TO_LE32 (fs_info->serial_number);
+               memcpy (bs->u.fat16.volume_name, "NO NAME    ", 11);
+               memcpy (bs->u.fat16.fat_name, "FAT16   ", 8);
+       }
+
+       bs->boot_sign = PED_CPU_TO_LE16 (0xaa55);
+
+       return 1;
+}
+
+int
+fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (bs != NULL);
+
+       if (!ped_geometry_write (fs->geom, bs, 0, 1))
+               return 0;
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               if (!ped_geometry_write (fs->geom, bs,
+                                        fs_info->boot_sector_backup_offset, 1))
+                       return 0;
+       }
+       return ped_geometry_sync (fs->geom);
+}
+
+int
+fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       int             status;
+
+       PED_ASSERT (is != NULL);
+
+       if (!ped_geometry_read (fs->geom, is, fs_info->info_sector_offset, 1))
+               return 0;
+
+       if (PED_LE32_TO_CPU (is->signature_2) != FAT32_INFO_MAGIC2) {
+               status = ped_exception_throw (PED_EXCEPTION_WARNING,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("The information sector has the wrong "
+                               "signature (%x).  Select cancel for now, "
+                               "and send in a bug report.  If you're "
+                               "desperate, it's probably safe to ignore."),
+                               PED_LE32_TO_CPU (is->signature_2));
+               if (status == PED_EXCEPTION_CANCEL) return 0;
+       }
+       return 1;
+}
+
+int
+fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (is != NULL);
+
+       fat_table_count_stats (fs_info->fat);
+
+       memset (is, 0, 512);
+
+       is->signature_1 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC1);
+       is->signature_2 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC2);
+       is->free_clusters = PED_CPU_TO_LE32 (fs_info->fat->free_cluster_count);
+       is->next_cluster = PED_CPU_TO_LE32 (fs_info->fat->last_alloc);
+       is->signature_3 = PED_CPU_TO_LE16 (FAT32_INFO_MAGIC3);
+
+       return 1;
+}
+
+int
+fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (is != NULL);
+
+       if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1))
+               return 0;
+       return ped_geometry_sync (fs->geom);
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/bootsector.h b/libparted/fs/r/fat/bootsector.h
new file mode 100644
index 0000000..d049ed7
--- /dev/null
+++ b/libparted/fs/r/fat/bootsector.h
@@ -0,0 +1,131 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#ifndef PED_FAT_BOOTSECTOR_H
+#define PED_FAT_BOOTSECTOR_H
+
+typedef struct _FatBootSector  FatBootSector;
+typedef struct _FatInfoSector  FatInfoSector;
+
+#include "fat.h"
+
+#define FAT32_INFO_MAGIC1      0x41615252
+#define FAT32_INFO_MAGIC2      0x61417272
+#define FAT32_INFO_MAGIC3      0xaa55
+
+/* stolen from mkdosfs, by Dave Hudson */
+
+#define FAT_BOOT_MESSAGE       \
+"This partition does not have an operating system loader installed on it.\n\r"\
+"Press a key to reboot..."
+
+#define FAT_BOOT_JUMP  "\xeb\x58\x90"          /* jmp  +5a */
+
+#define FAT_BOOT_CODE  "\x0e"                  /* push cs */           \
+                       "\x1f"                  /* pop ds */            \
+                       "\xbe\x74\x7e"          /* mov si, offset message */ \
+                                       /* write_msg_loop: */           \
+                       "\xac"                  /* lodsb */             \
+                       "\x22\xc0"              /* and al, al */        \
+                       "\x74\x06"              /* jz done (+8) */      \
+                       "\xb4\x0e"              /* mov ah, 0x0e */      \
+                       "\xcd\x10"              /* int 0x10 */          \
+                       "\xeb\xf5"              /* jmp write_msg_loop */ \
+                                       /* done: */                     \
+                       "\xb4\x00"              /* mov ah, 0x00 */      \
+                       "\xcd\x16"              /* int 0x16 */          \
+                       "\xb4\x00"              /* mov ah, 0x00 */      \
+                       "\xcd\x19"              /* int 0x19 */          \
+                       "\xeb\xfe"              /* jmp +0 - in case int 0x19 */ 
\
+                                               /* doesn't work */      \
+                                       /* message: */                  \
+                       FAT_BOOT_MESSAGE
+
+#define FAT_BOOT_CODE_LENGTH 128
+
+struct __attribute__ ((packed)) _FatBootSector {
+        uint8_t                boot_jump[3];   /* 00: Boot strap short or near 
jump */
+        uint8_t                system_id[8];   /* 03: system name */
+        uint16_t       sector_size;    /* 0b: bytes per logical sector */
+        uint8_t                cluster_size;   /* 0d: sectors/cluster */
+        uint16_t       reserved;       /* 0e: reserved sectors */
+        uint8_t                fats;           /* 10: number of FATs */
+        uint16_t       dir_entries;    /* 11: number of root directory entries 
*/
+        uint16_t       sectors;        /* 13: if 0, total_sect supersedes */
+        uint8_t                media;          /* 15: media code */
+        uint16_t       fat_length;     /* 16: sectors/FAT for FAT12/16 */
+        uint16_t       secs_track;     /* 18: sectors per track */
+        uint16_t       heads;          /* 1a: number of heads */
+        uint32_t       hidden;         /* 1c: hidden sectors (partition start) 
*/
+        uint32_t       sector_count;   /* 20: no. of sectors (if sectors == 0) 
*/
+
+        union __attribute__ ((packed)) {
+                /* FAT16 fields */
+                struct __attribute__ ((packed)) {
+                        uint8_t                drive_num;      /* 24: */
+                        uint8_t                empty_1;        /* 25: */
+                        uint8_t                ext_signature;  /* 26: always 
0x29 */
+                        uint32_t       serial_number;  /* 27: */
+                        uint8_t                volume_name [11];       /* 2b: 
*/
+                        uint8_t                fat_name [8];   /* 36: */
+                        uint8_t                boot_code[448]; /* 3f: Boot 
code (or message) */
+                } fat16;
+                /* FAT32 fields */
+                struct __attribute__ ((packed)) {
+                        uint32_t       fat_length;     /* 24: size of FAT in 
sectors */
+                        uint16_t       flags;          /* 28: bit8: fat 
mirroring, low4: active fat */
+                        uint16_t       version;        /* 2a: minor * 256 + 
major */
+                        uint32_t       root_dir_cluster;       /* 2c: */
+                        uint16_t       info_sector;    /* 30: */
+                        uint16_t       backup_sector;  /* 32: */
+                        uint8_t                empty_1 [12];   /* 34: */
+                        uint16_t       drive_num;      /* 40: */
+                        uint8_t                ext_signature;  /* 42: always 
0x29 */
+                        uint32_t       serial_number;  /* 43: */
+                        uint8_t                volume_name [11];       /* 47: 
*/
+                        uint8_t                fat_name [8];   /* 52: */
+                        uint8_t                boot_code[420]; /* 5a: Boot 
code (or message) */
+                } fat32;
+        } u;
+
+       uint16_t        boot_sign;      /* 1fe: always 0xAA55 */
+};
+
+struct __attribute__ ((packed)) _FatInfoSector {
+        uint32_t       signature_1;    /* should be 0x41615252 */
+        uint8_t                unused [480];
+        uint32_t       signature_2;    /* should be 0x61417272 */
+        uint32_t       free_clusters;
+        uint32_t       next_cluster;   /* most recently allocated cluster */
+        uint8_t                unused2 [0xe];
+        uint16_t       signature_3;    /* should be 0xaa55 */
+};
+
+int fat_boot_sector_read (FatBootSector* bs, const PedGeometry* geom);
+FatType fat_boot_sector_probe_type (const FatBootSector* bs,
+                                   const PedGeometry* geom);
+int fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs);
+int fat_boot_sector_set_boot_code (FatBootSector* bs);
+int fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs);
+int fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs);
+
+int fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs);
+int fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs);
+int fat_info_sector_write (const FatInfoSector* is, PedFileSystem* fs);
+
+#endif /* PED_FAT_BOOTSECTOR_H */
diff --git a/libparted/fs/r/fat/calc.c b/libparted/fs/r/fat/calc.c
new file mode 100644
index 0000000..dddd84b
--- /dev/null
+++ b/libparted/fs/r/fat/calc.c
@@ -0,0 +1,433 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2002, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+/* returns the minimum size of clusters for a given file system type */
+PedSector
+fat_min_cluster_size (FatType fat_type) {
+       switch (fat_type) {
+               case FAT_TYPE_FAT12: return 1;
+               case FAT_TYPE_FAT16: return 1024/512;
+               case FAT_TYPE_FAT32: return 4096/512;
+       }
+       return 0;
+}
+
+static PedSector
+_smallest_power2_over (PedSector ceiling)
+{
+       PedSector       result = 1;
+
+       while (result < ceiling)
+               result *= 2;
+
+       return result;
+}
+
+/* returns the minimum size of clusters for a given file system type */
+PedSector
+fat_recommend_min_cluster_size (FatType fat_type, PedSector size) {
+       switch (fat_type) {
+               case FAT_TYPE_FAT12: return 1;
+               case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type);
+               case FAT_TYPE_FAT32:
+                       return PED_MAX(_smallest_power2_over(size
+                                               / MAX_FAT32_CLUSTERS),
+                                      fat_min_cluster_size (fat_type));
+       }
+       return 0;
+}
+
+/* returns the maxmimum size of clusters for a given file system type */
+PedSector
+fat_max_cluster_size (FatType fat_type) {
+       switch (fat_type) {
+               case FAT_TYPE_FAT12: return 1;  /* dunno... who cares? */
+               case FAT_TYPE_FAT16: return 65536/512;
+               case FAT_TYPE_FAT32: return 65536/512;
+       }
+       return 0;
+}
+
+/* returns the minimum number of clusters for a given file system type */
+FatCluster
+fat_min_cluster_count (FatType fat_type) {
+       switch (fat_type) {
+               case FAT_TYPE_FAT12:
+               case FAT_TYPE_FAT16:
+                       return fat_max_cluster_count (fat_type) / 2;
+
+               case FAT_TYPE_FAT32: return 0xfff0;
+       }
+       return 0;
+}
+
+/* returns the maximum number of clusters for a given file system type */
+FatCluster
+fat_max_cluster_count (FatType fat_type) {
+       switch (fat_type) {
+               case FAT_TYPE_FAT12: return 0xff0;
+               case FAT_TYPE_FAT16: return 0xfff0;
+               case FAT_TYPE_FAT32: return 0x0ffffff0;
+       }
+       return 0;
+}
+
+/* what is this supposed to be?  What drugs are M$ on?  (Can I have some? :-) 
*/
+PedSector
+fat_min_reserved_sector_count (FatType fat_type)
+{
+       return (fat_type == FAT_TYPE_FAT32) ? 32 : 1;
+}
+
+int
+fat_check_resize_geometry (const PedFileSystem* fs,
+                          const PedGeometry* geom,
+                          PedSector new_cluster_sectors,
+                          FatCluster new_cluster_count)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedSector       free_space;
+       PedSector       min_free_space;
+       PedSector       total_space;
+       PedSector       new_total_space;
+       PedSector       dir_space;
+
+       PED_ASSERT (geom != NULL);
+
+       dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors;
+       free_space = fs_info->fat->free_cluster_count
+                       * fs_info->cluster_sectors;
+       total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors;
+       new_total_space = new_cluster_count * new_cluster_sectors;
+       min_free_space = total_space - new_total_space + dir_space;
+
+       PED_ASSERT (new_cluster_count
+                   <= fat_max_cluster_count (FAT_TYPE_FAT32));
+
+       if (free_space < min_free_space) {
+               char* needed = ped_unit_format (geom->dev, min_free_space);
+               char* have = ped_unit_format (geom->dev, free_space);
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("You need %s of free disk space to shrink this "
+                         "partition to this size.  Currently, only %s is "
+                         "free."),
+                       needed, have);
+               free (needed);
+               free (have);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/******************************************************************************/
+
+/* DO NOT EDIT THIS ALGORITHM!
+ * As far as I can tell, this is the same algorithm used by Microsoft to
+ * calculate the size of the file allocaion tables, and the number of clusters.
+ * I have not verified this by dissassembling Microsoft code - I came to this
+ * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE).
+ *
+ * If you think this code makes no sense, then you are right.  I will restrain
+ * the urge to inflict serious bodily harm on Microsoft people.
+ */
+
+static int
+entries_per_sector (FatType fat_type)
+{
+       switch (fat_type) {
+               case FAT_TYPE_FAT12:
+                       return 512 * 3 / 2;
+               case FAT_TYPE_FAT16:
+                       return 512 / 2;
+               case FAT_TYPE_FAT32:
+                       return 512 / 4;
+       }
+       return 0;
+}
+
+static int
+calc_sizes (PedSector size, PedSector align, FatType fat_type,
+           PedSector root_dir_sectors, PedSector cluster_sectors,
+           FatCluster* out_cluster_count, PedSector* out_fat_size)
+{
+       PedSector       data_fat_space; /* space available to clusters + FAT */
+       PedSector       fat_space;      /* space taken by each FAT */
+       PedSector       cluster_space;  /* space taken by clusters */
+       FatCluster      cluster_count;
+       int             i;
+
+       PED_ASSERT (out_cluster_count != NULL);
+       PED_ASSERT (out_fat_size != NULL);
+
+       data_fat_space = size - fat_min_reserved_sector_count (fat_type)
+                        - align;
+       if (fat_type == FAT_TYPE_FAT16)
+               data_fat_space -= root_dir_sectors;
+
+       fat_space = 0;
+       for (i = 0; i < 2; i++) {
+               if (fat_type == FAT_TYPE_FAT32)
+                       cluster_space = data_fat_space - fat_space;
+               else
+                       cluster_space = data_fat_space - 2 * fat_space;
+
+               cluster_count = cluster_space / cluster_sectors;
+               fat_space = ped_div_round_up (cluster_count + 2,
+                                             entries_per_sector (fat_type));
+       }
+
+       cluster_space = data_fat_space - 2 * fat_space;
+       cluster_count = cluster_space / cluster_sectors;
+
+       /* looks like this should be part of the loop condition?
+        * Need to build the Big Table TM again to check
+        */
+       if (fat_space < ped_div_round_up (cluster_count + 2,
+                                         entries_per_sector (fat_type))) {
+               fat_space = ped_div_round_up (cluster_count + 2,
+                                             entries_per_sector (fat_type));
+       }
+
+       if (cluster_count > fat_max_cluster_count (fat_type)
+           || cluster_count < fat_min_cluster_count (fat_type))
+               return 0;
+
+       *out_cluster_count = cluster_count;
+       *out_fat_size = fat_space;
+
+       return 1;
+}
+
+/****************************************************************************/
+
+int
+fat_calc_sizes (PedSector size, PedSector align, FatType fat_type,
+               PedSector root_dir_sectors,
+               PedSector* out_cluster_sectors, FatCluster* out_cluster_count,
+               PedSector* out_fat_size)
+{
+       PedSector       cluster_sectors;
+
+       PED_ASSERT (out_cluster_sectors != NULL);
+       PED_ASSERT (out_cluster_count != NULL);
+       PED_ASSERT (out_fat_size != NULL);
+
+       for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
+            cluster_sectors <= fat_max_cluster_size (fat_type);
+            cluster_sectors *= 2) {
+               if (calc_sizes (size, align, fat_type, root_dir_sectors,
+                               cluster_sectors,
+                               out_cluster_count, out_fat_size)) {
+                       *out_cluster_sectors = cluster_sectors;
+                       return 1;
+               }
+       }
+
+       for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
+            cluster_sectors >= fat_min_cluster_size (fat_type);
+            cluster_sectors /= 2) {
+               if (calc_sizes (size, align, fat_type, root_dir_sectors,
+                               cluster_sectors,
+                               out_cluster_count, out_fat_size)) {
+                       *out_cluster_sectors = cluster_sectors;
+                       return 1;
+               }
+       }
+
+       /* only make the cluster size really small (<4k) if a bigger one is
+        * isn't possible.  Windows never makes FS's like this, but it
+        * seems to work...  (do more tests!)
+        */
+       for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) {
+               if (calc_sizes (size, align, fat_type, root_dir_sectors,
+                               cluster_sectors,
+                               out_cluster_count, out_fat_size)) {
+                       *out_cluster_sectors = cluster_sectors;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/* Same as fat_calc_sizes, except it only attempts to match a particular
+ * cluster size.  This is useful, because the FAT resizer can only shrink the
+ * cluster size.
+ */
+int
+fat_calc_resize_sizes (
+       const PedGeometry* geom,
+       PedSector align,
+       FatType fat_type,
+       PedSector root_dir_sectors,
+       PedSector cluster_sectors,
+       PedSector* out_cluster_sectors,
+       FatCluster* out_cluster_count,
+       PedSector* out_fat_size)
+{
+       PED_ASSERT (geom != NULL);
+       PED_ASSERT (out_cluster_sectors != NULL);
+       PED_ASSERT (out_cluster_count != NULL);
+       PED_ASSERT (out_fat_size != NULL);
+
+/* libparted can only reduce the cluster size at this point */
+       for (*out_cluster_sectors = cluster_sectors;
+            *out_cluster_sectors >= fat_min_cluster_size (fat_type);
+            *out_cluster_sectors /= 2) {
+               if (calc_sizes (geom->length, align, fat_type, root_dir_sectors,
+                               *out_cluster_sectors,
+                               out_cluster_count, out_fat_size))
+                       return 1;
+       }
+       return 0;
+}
+
+/*  Calculates the number of sectors needed to be added to cluster_offset,
+    to make the cluster on the new file system match up with the ones
+    on the old file system.
+       However, some space is reserved by fat_calc_resize_sizes() and
+    friends, to allow room for this space.  If too much of this space is left
+    over, everyone will complain, so we have to be greedy, and use it all up...
+ */
+PedSector
+fat_calc_align_sectors (const PedFileSystem* new_fs,
+                       const PedFileSystem* old_fs)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (new_fs);
+       PedSector       raw_old_meta_data_end;
+       PedSector       new_meta_data_size;
+       PedSector       min_new_meta_data_end;
+       PedSector       new_data_size;
+       PedSector       new_clusters_size;
+       PedSector       align;
+
+       new_meta_data_size
+               = fat_min_reserved_sector_count (new_fs_info->fat_type)
+                 + new_fs_info->fat_sectors * 2;
+
+       if (new_fs_info->fat_type == FAT_TYPE_FAT16)
+               new_meta_data_size += new_fs_info->root_dir_sector_count;
+
+       raw_old_meta_data_end = old_fs->geom->start
+                                + old_fs_info->cluster_offset;
+
+       min_new_meta_data_end = new_fs->geom->start + new_meta_data_size;
+
+       if (raw_old_meta_data_end > min_new_meta_data_end)
+               align = (raw_old_meta_data_end - min_new_meta_data_end)
+                       % new_fs_info->cluster_sectors;
+       else
+               align = (new_fs_info->cluster_sectors
+                        - (   (min_new_meta_data_end - raw_old_meta_data_end)
+                               % new_fs_info->cluster_sectors   ))
+                       % new_fs_info->cluster_sectors;
+
+       new_data_size = new_fs->geom->length - new_meta_data_size;
+       new_clusters_size = new_fs_info->cluster_count
+                               * new_fs_info->cluster_sectors;
+
+       while (new_clusters_size + align + new_fs_info->cluster_sectors
+                       <= new_data_size)
+               align += new_fs_info->cluster_sectors;
+
+       return align;
+}
+
+int
+fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       return sector >= fs_info->cluster_offset
+              && sector < fs_info->cluster_offset
+                          + fs_info->cluster_sectors * fs_info->cluster_count;
+}
+
+FatFragment
+fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2);
+
+       return (cluster - 2) * fs_info->cluster_frags;
+}
+
+FatCluster
+fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (frag >= 0 && frag < fs_info->frag_count);
+
+       return frag / fs_info->cluster_frags + 2;
+}
+
+PedSector
+fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (frag >= 0 && frag < fs_info->frag_count);
+
+       return frag * fs_info->frag_sectors + fs_info->cluster_offset;
+}
+
+FatFragment
+fat_sector_to_frag (const PedFileSystem* fs, PedSector sector)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (sector >= fs_info->cluster_offset);
+
+       return (sector - fs_info->cluster_offset) / fs_info->frag_sectors;
+}
+
+PedSector
+fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2);
+
+       return (cluster - 2) * fs_info->cluster_sectors
+               + fs_info->cluster_offset;
+}
+
+FatCluster
+fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (sector >= fs_info->cluster_offset);
+
+       return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors
+               + 2;
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/calc.h b/libparted/fs/r/fat/calc.h
new file mode 100644
index 0000000..d5ab8a3
--- /dev/null
+++ b/libparted/fs/r/fat/calc.h
@@ -0,0 +1,76 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#ifndef PED_FAT_CALC_H
+#define PED_FAT_CALC_H
+
+extern PedSector fat_min_cluster_size (FatType fat_type);
+extern PedSector fat_max_cluster_size (FatType fat_type);
+extern FatCluster fat_min_cluster_count (FatType fat_type);
+extern FatCluster fat_max_cluster_count (FatType fat_type);
+
+extern PedSector fat_min_reserved_sector_count (FatType fat_type);
+
+extern int fat_check_resize_geometry (const PedFileSystem* fs,
+                                     const PedGeometry* geom,
+                                     PedSector new_cluster_sectors,
+                                     FatCluster new_cluster_count);
+
+extern int fat_calc_sizes (PedSector size,
+                          PedSector align,
+                          FatType fat_type,
+                          PedSector root_dir_sectors,
+                          PedSector* out_cluster_sectors,
+                          FatCluster* out_cluster_count,
+                          PedSector* out_fat_size);
+
+extern int fat_calc_resize_sizes (const PedGeometry* geom,
+                                 PedSector align,
+                                 FatType fat_type,
+                                 PedSector root_dir_sectors,
+                                 PedSector cluster_sectors,
+                                 PedSector* out_cluster_sectors,
+                                 FatCluster* out_cluster_count,
+                                 PedSector* out_fat_size);
+
+extern PedSector
+fat_calc_align_sectors (const PedFileSystem* new_fs,
+                       const PedFileSystem* old_fs);
+
+extern int
+fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector);
+
+extern FatFragment
+fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster);
+
+extern FatCluster
+fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag);
+
+extern PedSector
+fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag);
+
+extern FatFragment
+fat_sector_to_frag (const PedFileSystem* fs, PedSector sector);
+
+extern PedSector
+fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster);
+
+extern FatCluster
+fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector);
+
+#endif /* PED_FAT_CALC_H */
diff --git a/libparted/fs/r/fat/clstdup.c b/libparted/fs/r/fat/clstdup.c
new file mode 100644
index 0000000..3b8549e
--- /dev/null
+++ b/libparted/fs/r/fat/clstdup.c
@@ -0,0 +1,422 @@
+/*
+    libparted
+    Copyright (C) 1998-2001, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+#include <string.h>
+
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+static int
+needs_duplicating (const FatOpContext* ctx, FatFragment frag)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatCluster      cluster = fat_frag_to_cluster (ctx->old_fs, frag);
+       FatClusterFlag  flag;
+
+       PED_ASSERT (cluster >= 2 && cluster < old_fs_info->cluster_count + 2);
+
+       flag = fat_get_fragment_flag (ctx->old_fs, frag);
+       switch (flag) {
+       case FAT_FLAG_FREE:
+               return 0;
+
+       case FAT_FLAG_DIRECTORY:
+               return 1;
+
+       case FAT_FLAG_FILE:
+               return fat_op_context_map_static_fragment (ctx, frag) == -1;
+
+       case FAT_FLAG_BAD:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int
+search_next_fragment (FatOpContext* ctx)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (ctx->old_fs);
+
+       for (; ctx->buffer_offset < fs_info->frag_count; ctx->buffer_offset++) {
+               if (needs_duplicating (ctx, ctx->buffer_offset))
+                       return 1;
+       }
+       return 0;       /* all done! */
+}
+
+static int
+read_marked_fragments (FatOpContext* ctx, FatFragment length)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (ctx->old_fs);
+       int                     status;
+       FatFragment             i;
+
+       ped_exception_fetch_all ();
+       status = fat_read_fragments (ctx->old_fs, fs_info->buffer,
+                                    ctx->buffer_offset, length);
+       ped_exception_leave_all ();
+       if (status)
+               return 1;
+
+       ped_exception_catch ();
+
+/* something bad happened, so read fragments one by one.  (The error may
+   have occurred on an unused fragment: who cares) */
+       for (i = 0; i < length; i++) {
+               if (ctx->buffer_map [i]) {
+                       if (!fat_read_fragment (ctx->old_fs,
+                             fs_info->buffer + i * fs_info->frag_size,
+                             ctx->buffer_offset + i))
+                               return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int
+fetch_fragments (FatOpContext* ctx)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatFragment     fetch_length = 0;
+       FatFragment     frag;
+
+       for (frag = 0; frag < ctx->buffer_frags; frag++)
+               ctx->buffer_map [frag] = -1;
+
+       for (frag = 0;
+            frag < ctx->buffer_frags
+               && ctx->buffer_offset + frag < old_fs_info->frag_count;
+            frag++) {
+               if (needs_duplicating (ctx, ctx->buffer_offset + frag)) {
+                       ctx->buffer_map [frag] = 1;
+                       fetch_length = frag + 1;
+               }
+       }
+
+       if (!read_marked_fragments (ctx, fetch_length))
+               return 0;
+
+       return 1;
+}
+
+/*****************************************************************************
+ * here starts the write code.  All assumes that ctx->buffer_map [first] and
+ * ctx->buffer_map [last] are occupied by fragments that need to be duplicated.
+ *****************************************************************************/
+
+/* finds the first fragment that is not going to get overwritten (that needs to
+   get read in) */
+static FatFragment
+get_first_underlay (const FatOpContext* ctx, int first, int last)
+{
+       int             old;
+       FatFragment     new;
+
+       PED_ASSERT (first <= last);
+
+       new = ctx->buffer_map [first];
+       for (old = first + 1; old <= last; old++) {
+               if (ctx->buffer_map [old] == -1)
+                       continue;
+               new++;
+               if (ctx->buffer_map [old] != new)
+                       return new;
+       }
+       return -1;
+}
+
+/* finds the last fragment that is not going to get overwritten (that needs to
+   get read in) */
+static FatFragment
+get_last_underlay (const FatOpContext* ctx, int first, int last)
+{
+       int             old;
+       FatFragment     new;
+
+       PED_ASSERT (first <= last);
+
+       new = ctx->buffer_map [last];
+       for (old = last - 1; old >= first; old--) {
+               if (ctx->buffer_map [old] == -1)
+                       continue;
+               new--;
+               if (ctx->buffer_map [old] != new)
+                       return new;
+       }
+       return -1;
+}
+
+/* "underlay" refers to the "static" fragments, that remain unchanged.
+ * when writing large chunks at a time, we don't want to clobber these,
+ * so we read them in, and write them back again.  MUCH quicker that way.
+ */
+static int
+quick_group_write_read_underlay (FatOpContext* ctx, int first, int last)
+{
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatFragment     first_underlay;
+       FatFragment     last_underlay;
+       FatFragment     underlay_length;
+
+       PED_ASSERT (first <= last);
+
+       first_underlay = get_first_underlay (ctx, first, last);
+       if (first_underlay == -1)
+               return 1;
+       last_underlay = get_last_underlay (ctx, first, last);
+
+       PED_ASSERT (first_underlay <= last_underlay);
+
+       underlay_length = last_underlay - first_underlay + 1;
+       if (!fat_read_fragments (ctx->new_fs,
+                               new_fs_info->buffer
+                                  + (first_underlay - ctx->buffer_map [first])
+                                       * new_fs_info->frag_size,
+                               first_underlay,
+                               underlay_length))
+               return 0;
+       return 1;
+}
+
+/* quick_group_write() makes no attempt to recover from errors - just
+ * does things fast.  If there is an error, slow_group_write() is
+ * called.
+ *    Note: we do syncing writes, to make sure there isn't any
+ * error writing out.  It's rather difficult recovering from errors
+ * further on.
+ */
+static int
+quick_group_write (FatOpContext* ctx, int first, int last)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       int                     active_length;
+       int                     i;
+       int                     offset;
+
+       PED_ASSERT (first <= last);
+
+       ped_exception_fetch_all ();
+       if (!quick_group_write_read_underlay (ctx, first, last))
+               goto error;
+
+       for (i = first; i <= last; i++) {
+               if (ctx->buffer_map [i] == -1)
+                       continue;
+
+               offset = ctx->buffer_map [i] - ctx->buffer_map [first];
+               memcpy (new_fs_info->buffer + offset * new_fs_info->frag_size,
+                       old_fs_info->buffer + i * new_fs_info->frag_size,
+                       new_fs_info->frag_size);
+       }
+
+       active_length = ctx->buffer_map [last] - ctx->buffer_map [first] + 1;
+       if (!fat_write_sync_fragments (ctx->new_fs, new_fs_info->buffer,
+                                      ctx->buffer_map [first], active_length))
+               goto error;
+
+       ped_exception_leave_all ();
+       return 1;
+
+error:
+       ped_exception_catch ();
+       ped_exception_leave_all ();
+       return 0;
+}
+
+/* Writes fragments out, one at a time, avoiding errors on redundant writes
+ * on damaged parts of the disk we already know about.  If there's an error
+ * on one of the required fragments, it gets marked as bad, and a replacement
+ * is found.
+ */
+static int
+slow_group_write (FatOpContext* ctx, int first, int last)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       int                     i;
+
+       PED_ASSERT (first <= last);
+
+       for (i = first; i <= last; i++) {
+               if (ctx->buffer_map [i] == -1)
+                       continue;
+
+               while (!fat_write_sync_fragment (ctx->new_fs,
+                             old_fs_info->buffer + i * old_fs_info->frag_size,
+                             ctx->buffer_map [i])) {
+                       fat_table_set_bad (new_fs_info->fat,
+                                          ctx->buffer_map [i]);
+                       ctx->buffer_map [i] = fat_table_alloc_cluster
+                                               (new_fs_info->fat);
+                       if (ctx->buffer_map [i] == 0)
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+static int
+update_remap (FatOpContext* ctx, int first, int last)
+{
+       int             i;
+
+       PED_ASSERT (first <= last);
+
+       for (i = first; i <= last; i++) {
+               if (ctx->buffer_map [i] == -1)
+                       continue;
+               ctx->remap [ctx->buffer_offset + i] = ctx->buffer_map [i];
+       }
+
+       return 1;
+}
+
+static int
+group_write (FatOpContext* ctx, int first, int last)
+{
+       PED_ASSERT (first <= last);
+
+       if (!quick_group_write (ctx, first, last)) {
+               if (!slow_group_write (ctx, first, last))
+                       return 0;
+       }
+       if (!update_remap (ctx, first, last))
+               return 0;
+       return 1;
+}
+
+/* assumes fragment size and new_fs's cluster size are equal */
+static int
+write_fragments (FatOpContext* ctx)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       int                     group_start;
+       int                     group_end = -1; /* shut gcc up! */
+       FatFragment             mapped_length;
+       FatFragment             i;
+       FatCluster              new_cluster;
+
+       PED_ASSERT (ctx->buffer_offset < old_fs_info->frag_count);
+
+       group_start = -1;
+       for (i = 0; i < ctx->buffer_frags; i++) {
+               if (ctx->buffer_map [i] == -1)
+                       continue;
+
+               ctx->frags_duped++;
+
+               new_cluster = fat_table_alloc_cluster (new_fs_info->fat);
+               if (!new_cluster)
+                       return 0;
+               fat_table_set_eof (new_fs_info->fat, new_cluster);
+               ctx->buffer_map [i] = fat_cluster_to_frag (ctx->new_fs,
+                                                          new_cluster);
+
+               if (group_start == -1)
+                       group_start = group_end = i;
+
+               PED_ASSERT (ctx->buffer_map [i]
+                               >= ctx->buffer_map [group_start]);
+
+               mapped_length = ctx->buffer_map [i]
+                               - ctx->buffer_map [group_start] + 1;
+               if (mapped_length <= ctx->buffer_frags) {
+                       group_end = i;
+               } else {
+                       /* ran out of room in the buffer, so write this group,
+                        * and start a new one...
+                        */
+                       if (!group_write (ctx, group_start, group_end))
+                               return 0;
+                       group_start = group_end = i;
+               }
+       }
+
+       PED_ASSERT (group_start != -1);
+
+       if (!group_write (ctx, group_start, group_end))
+               return 0;
+       return 1;
+}
+
+/*  default all fragments to unmoved
+ */
+static void
+init_remap (FatOpContext* ctx)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatFragment             i;
+
+       for (i = 0; i < old_fs_info->frag_count; i++)
+               ctx->remap[i] = fat_op_context_map_static_fragment (ctx, i);
+}
+
+static FatFragment
+count_frags_to_dup (FatOpContext* ctx)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatFragment     i;
+       FatFragment     total;
+
+       total = 0;
+
+       for (i = 0; i < fs_info->frag_count; i++) {
+               if (needs_duplicating (ctx, i))
+                       total++;
+       }
+
+       return total;
+}
+
+/*  duplicates unreachable file clusters, and all directory clusters
+ */
+int
+fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer)
+{
+       FatFragment     total_frags_to_dup;
+
+       init_remap (ctx);
+       total_frags_to_dup = count_frags_to_dup (ctx);
+
+       ped_timer_reset (timer);
+       ped_timer_set_state_name (timer, "moving data");
+
+       ctx->buffer_offset = 0;
+       ctx->frags_duped = 0;
+       while (search_next_fragment (ctx)) {
+               ped_timer_update (
+                       timer, 1.0 * ctx->frags_duped / total_frags_to_dup);
+
+               if (!fetch_fragments (ctx))
+                       return 0;
+               if (!write_fragments (ctx))
+                       return 0;
+               ctx->buffer_offset += ctx->buffer_frags;
+       }
+
+       ped_timer_update (timer, 1.0);
+       return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/clstdup.h b/libparted/fs/r/fat/clstdup.h
new file mode 100644
index 0000000..77ef089
--- /dev/null
+++ b/libparted/fs/r/fat/clstdup.h
@@ -0,0 +1,27 @@
+/*
+    libparted
+    Copyright (C) 1999, 2007, 2009-2011 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/>.
+*/
+
+#ifndef PED_FAT_CLSTDUP_H_INCLUDED
+#define PED_FAT_CLSTDUP_H_INCLUDED
+
+#include "context.h"
+
+/* the big important one :-) */
+extern int fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer);
+
+#endif /* PED_FAT_CLSTDUP_H_INCLUDED */
diff --git a/libparted/fs/r/fat/context.c b/libparted/fs/r/fat/context.c
new file mode 100644
index 0000000..4176b17
--- /dev/null
+++ b/libparted/fs/r/fat/context.c
@@ -0,0 +1,260 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+#include <string.h>
+
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+/* Note: this deals with file system start and end sectors, even if the 
physical
+ * devices are different (eg for fat_copy())  Perhaps this is a hack, but it
+ * works ;-)
+ */
+static int
+calc_deltas (FatOpContext* ctx)
+{
+       PedFileSystem*  old_fs = ctx->old_fs;
+       PedFileSystem*  new_fs = ctx->new_fs;
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (new_fs);
+       PedSector       old_cluster_ofs;
+       PedSector       new_cluster_ofs;
+       PedSector       sector_delta;
+
+       old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset;
+       new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset;
+
+       if (new_cluster_ofs > old_cluster_ofs) {
+               ctx->start_move_dir = FAT_DIR_FORWARD;
+               sector_delta = new_cluster_ofs - old_cluster_ofs;
+       } else {
+               ctx->start_move_dir = FAT_DIR_BACKWARD;
+               sector_delta = old_cluster_ofs - new_cluster_ofs;
+       }
+
+       if (sector_delta % new_fs_info->cluster_sectors) {
+               ped_exception_throw (
+                       PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+                       _("Cluster start delta = %d, which is not a multiple "
+                         "of the cluster size %d."),
+                       (int) sector_delta,
+                       (int) new_fs_info->cluster_sectors);
+               return 0;
+       }
+
+       ctx->start_move_delta = sector_delta / ctx->frag_sectors;
+
+#ifdef PED_VERBOSE
+       printf ("Start move delta is: %d %s.\n",
+               (int) ctx->start_move_delta,
+               (ctx->start_move_dir == FAT_DIR_FORWARD)?
+                       "forwards" : "backwards");
+#endif
+
+       return 1;
+}
+
+FatOpContext*
+fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (new_fs);
+       FatOpContext*   ctx;
+
+       ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext));
+       if (!ctx)
+               goto error;
+
+       ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors,
+                                    new_fs_info->cluster_sectors);
+       if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors))
+               goto error;
+       if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors))
+               goto error;
+
+       ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors;
+       ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment)
+                                                    * ctx->buffer_frags);
+       if (!ctx->buffer_map)
+               goto error_free_ctx;
+
+       ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment)
+                                                  * old_fs_info->frag_count);
+       if (!ctx->remap)
+               goto error_free_buffer_map;
+
+       ctx->new_fs = new_fs;
+       ctx->old_fs = old_fs;
+       if (!calc_deltas (ctx))
+               goto error_free_buffer_map;
+
+       return ctx;
+
+error_free_buffer_map:
+       free (ctx->buffer_map);
+error_free_ctx:
+       free (ctx);
+error:
+       return NULL;
+}
+
+void
+fat_op_context_destroy (FatOpContext* ctx)
+{
+       free (ctx->buffer_map);
+       free (ctx->remap);
+       free (ctx);
+}
+
+FatFragment
+fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag)
+{
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatFragment     result;
+
+       if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev)
+               return -1;
+
+       if (ctx->start_move_dir == FAT_DIR_FORWARD) {
+               if (frag < ctx->start_move_delta)
+                       return -1;
+               result = frag - ctx->start_move_delta;
+       } else {
+               result = frag + ctx->start_move_delta;
+       }
+
+       if (result >= new_fs_info->frag_count)
+               return -1;
+
+       return result;
+}
+
+FatCluster
+fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst)
+{
+       FatFragment     mapped_frag;
+
+       mapped_frag = fat_op_context_map_static_fragment (ctx,
+                               fat_cluster_to_frag (ctx->old_fs, clst));
+       if (mapped_frag != -1)
+               return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
+       else
+               return 0;
+}
+
+FatFragment
+fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag)
+{
+       return ctx->remap [frag];
+}
+
+FatCluster
+fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst)
+{
+       FatFragment     mapped_frag;
+
+       mapped_frag = fat_op_context_map_fragment (ctx,
+                               fat_cluster_to_frag (ctx->old_fs, clst));
+       if (mapped_frag != -1)
+               return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
+       else
+               return 0;
+}
+
+/* This function sets the initial fat for the new resized file system.
+   This is in *NO WAY* a proper FAT table - all it does is:
+       a) mark bad clusters as bad.
+       b) mark used clusters (that is, clusters from the original FS that are
+          reachable from the resized one).  Marks as EOF (i.e. used, end of
+          file chain).
+       c) mark original file system metadata as EOF (i.e. used), to prevent
+          it from being clobbered.  This will leave the original file system
+          intact, until the partition table is modified, if the start of
+          the partition is moved.
+
+   The FATs are rebuilt *properly* after cluster relocation.  This here is
+   only to mark clusters as used, so when cluster relocation occurs, clusters
+   aren't relocated on top of ones marked in a, b or c.
+*/
+int
+fat_op_context_create_initial_fat (FatOpContext* ctx)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatCluster      clst;
+       FatCluster      new_clst;
+       PedSector       sect;
+       PedSector       new_sect;
+       FatFragment     frag;
+       FatFragment     new_frag;
+       FatClusterFlag  frag_flag;
+
+       new_fs_info->fat = fat_table_new (
+               new_fs_info->fat_type,
+               new_fs_info->fat_sectors * 512
+                       / fat_table_entry_size (new_fs_info->fat_type));
+       if (!new_fs_info->fat)
+               return 0;
+
+       if (!fat_table_set_cluster_count (new_fs_info->fat,
+                                         new_fs_info->cluster_count))
+               return 0;
+
+/* mark bad and used clusters */
+       for (frag = 0; frag < old_fs_info->frag_count; frag++) {
+               frag_flag = fat_get_fragment_flag (ctx->old_fs, frag);
+               if (frag_flag == FAT_FLAG_FREE)
+                       continue;
+
+               new_frag = fat_op_context_map_static_fragment (ctx, frag);
+               if (new_frag == -1)
+                       continue;
+
+               new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag);
+               PED_ASSERT (new_clst != 0);
+
+               if (frag_flag == FAT_FLAG_BAD) {
+                       if (!fat_table_set_bad (new_fs_info->fat, new_clst))
+                               return 0;
+               } else {
+                       if (!fat_table_set_eof (new_fs_info->fat, new_clst))
+                               return 0;
+               }
+       }
+
+/* mark metadata regions that map to clusters on the new FS */
+       for (sect = 0; sect < old_fs_info->cluster_offset; sect++) {
+               new_sect = ped_geometry_map (ctx->new_fs->geom,
+                                            ctx->old_fs->geom, sect);
+               if (new_sect == -1
+                   || !fat_is_sector_in_clusters (ctx->new_fs, new_sect))
+                       continue;
+
+               clst = fat_sector_to_cluster (ctx->new_fs, new_sect);
+               PED_ASSERT (clst != 0);
+
+               if (!fat_table_set_eof (new_fs_info->fat, clst))
+                       return 0;
+       }
+
+       return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/context.h b/libparted/fs/r/fat/context.h
new file mode 100644
index 0000000..704db90
--- /dev/null
+++ b/libparted/fs/r/fat/context.h
@@ -0,0 +1,69 @@
+/*
+    libparted
+    Copyright (C) 1999-2000, 2007, 2009-2011 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/>.
+*/
+
+#ifndef PED_FAT_CONTEXT_H_INCLUDED
+#define PED_FAT_CONTEXT_H_INCLUDED
+
+#include "count.h"
+
+enum _FatDirection {
+       FAT_DIR_FORWARD,
+       FAT_DIR_BACKWARD
+};
+typedef enum _FatDirection FatDirection;
+
+struct _FatOpContext {
+       PedFileSystem*          old_fs;
+       PedFileSystem*          new_fs;
+
+       PedSector               frag_sectors;   /* should equal old_fs and
+                                                  new_fs's frag_sectors */
+
+       FatDirection            start_move_dir;
+       FatFragment             start_move_delta;
+
+       FatFragment             buffer_offset;
+       FatFragment             buffer_frags;
+       FatFragment*            buffer_map;
+
+       FatFragment             frags_duped;
+
+       FatFragment*            remap;
+
+       FatCluster              new_root_dir [32];
+};
+typedef struct _FatOpContext FatOpContext;
+
+extern FatOpContext* fat_op_context_new (PedFileSystem* new_fs,
+                                        PedFileSystem* old_fs);
+
+extern void fat_op_context_destroy (FatOpContext* ctx);
+
+extern FatFragment fat_op_context_map_static_fragment (const FatOpContext* ctx,
+                                                      FatFragment frag);
+extern FatCluster fat_op_context_map_static_cluster (const FatOpContext* ctx,
+                                                    FatCluster clst);
+
+extern FatFragment fat_op_context_map_fragment (const FatOpContext* ctx,
+                                               FatFragment frag);
+extern FatCluster fat_op_context_map_cluster (const FatOpContext* ctx,
+                                             FatCluster clst);
+
+extern int fat_op_context_create_initial_fat (FatOpContext* ctx);
+
+#endif /* PED_FAT_CONTEXT_H_INCLUDED */
diff --git a/libparted/fs/r/fat/count.c b/libparted/fs/r/fat/count.c
new file mode 100644
index 0000000..97ed2ab
--- /dev/null
+++ b/libparted/fs/r/fat/count.c
@@ -0,0 +1,401 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+#if 0
+/* extremely ugly hack: stick everything that obviously isn't an unmovable file
+ * in here.  Note: DAT is a bit dubious.  Unfortunately, it's used by the
+ * registry, so it'll be all over the place :-(
+ */
+static char*   movable_extensions[] = {
+       "",
+       "1ST",
+       "AVI",
+       "BAK", "BAT", "BMP",
+       "CFG", "COM", "CSS",
+       "DAT", "DLL", "DOC", "DRV",
+       "EXE",
+       "FAQ", "FLT", "FON",
+       "GID", "GIF",
+       "HLP", "HTT", "HTM",
+       "ICO", "INI",
+       "JPG",
+       "LNK", "LOG",
+       "KBD",
+       "ME", "MID", "MSG",
+       "OCX", "OLD",
+       "PIF", "PNG", "PRV",
+       "RTF",
+       "SCR", "SYS",
+       "TMP", "TTF", "TXT",
+       "URL",
+       "WAV",
+       "VBX", "VOC", "VXD",
+       NULL
+};
+
+static char*
+get_extension (char* file_name)
+{
+       char*           ext;
+
+       ext = strrchr (file_name, '.');
+       if (!ext)
+               return "";
+       if (strchr (ext, '\\'))
+               return "";
+       return ext + 1;
+}
+
+static int
+is_movable_system_file (char* file_name)
+{
+       char*           ext = get_extension (file_name);
+       int             i;
+
+       for (i = 0; movable_extensions [i]; i++) {
+               if (strcasecmp (ext, movable_extensions [i]) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+#endif /* 0 */
+
+/*
+    prints out the sequence of clusters for a given file chain, beginning
+    at start_cluster.
+*/
+#ifdef PED_VERBOSE
+static void
+print_chain (PedFileSystem* fs, FatCluster start)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      clst;
+       int             this_row;
+
+       this_row = 0;
+       for (clst = start; !fat_table_is_eof (fs_info->fat, clst);
+            clst = fat_table_get (fs_info->fat, clst)) {
+               printf ("  %d", (int) clst);
+               if (++this_row == 7) {
+                        putchar ('\n');
+                       this_row = 0;
+               }
+       }
+       putchar ('\n');
+}
+#endif /* PED_VERBOSE */
+
+static PedSector
+remainder_round_up (PedSector a, PedSector b)
+{
+       PedSector       result;
+
+       result = a % b;
+       if (!result)
+               result = b;
+       return result;
+}
+
+/*
+    traverse the FAT for a file/directory, marking each entry's flag
+    to "flag".
+*/
+static int
+flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start,
+                  FatClusterFlag flag, PedSector size)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      clst;
+       FatCluster      prev_clst;
+       int             last_cluster_usage;
+       FatCluster      chain_length = 0;
+
+       if (fat_table_is_eof (fs_info->fat, start)) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("Bad directory entry for %s: first cluster is the "
+                         "end of file marker."),
+                       chain_name)
+                               != PED_EXCEPTION_IGNORE)
+                       return 0;
+       }
+
+       for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst);
+            prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) {
+               chain_length++;
+               if (!clst) {
+                       ped_exception_throw (PED_EXCEPTION_FATAL,
+                               PED_EXCEPTION_CANCEL,
+                               _("Bad FAT: unterminated chain for %s.  You "
+                                 "should run dosfsck or scandisk."),
+                               chain_name);
+                       return 0;
+               }
+
+               if (clst >= fs_info->fat->cluster_count + 2) {
+                       ped_exception_throw (PED_EXCEPTION_FATAL,
+                               PED_EXCEPTION_CANCEL,
+                               _("Bad FAT: cluster %d outside file system "
+                                 "in chain for %s.  You should run dosfsck "
+                                 "or scandisk."),
+                               (int) clst, chain_name);
+                       return 0;
+               }
+
+               if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) {
+                       ped_exception_throw (PED_EXCEPTION_FATAL,
+                               PED_EXCEPTION_CANCEL,
+                               _("Bad FAT: cluster %d is cross-linked for "
+                                 "%s.  You should run dosfsck or scandisk."),
+                               (int) clst, chain_name);
+                       return 0;
+               }
+
+               if (flag == FAT_FLAG_DIRECTORY)
+                       fs_info->total_dir_clusters++;
+
+               fs_info->cluster_info [clst].flag = flag;
+               fs_info->cluster_info [clst].units_used = 0;    /* 0 == 64 */
+       }
+
+       if (size
+           && chain_length
+                       != ped_div_round_up (size, fs_info->cluster_sectors)) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("%s is %dk, but it has %d clusters (%dk)."),
+                       chain_name,
+                       (int) size / 2,
+                       (int) chain_length,
+                       (int) chain_length * fs_info->cluster_sectors / 2)
+                               != PED_EXCEPTION_IGNORE)
+                       return 0;
+       }
+
+       last_cluster_usage
+               = ped_div_round_up (64 * remainder_round_up (size,
+                                               fs_info->cluster_sectors),
+                               fs_info->cluster_sectors);
+
+       fs_info->cluster_info [prev_clst].units_used = last_cluster_usage;
+
+       return 1;
+}
+
+/*
+    recursively traverses a directory, flagging all clusters in the process.
+    It frees the traverse_info structure before returning.
+*/
+static int
+flag_traverse_dir (FatTraverseInfo* trav_info) {
+       PedFileSystem*          fs = trav_info->fs;
+       FatDirEntry*            this_entry;
+       FatTraverseInfo*        subdir_trav_info;
+       char                    file_name [512];
+       char*                   file_name_start;
+       FatCluster              first_cluster;
+       PedSector               size;
+
+       PED_ASSERT (trav_info != NULL);
+
+       strcpy (file_name, trav_info->dir_name);
+       file_name_start = file_name + strlen (file_name);
+
+       while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) {
+               if (fat_dir_entry_is_null_term (this_entry))
+                       break;
+               if (!fat_dir_entry_has_first_cluster (this_entry, fs))
+                       continue;
+               if (this_entry->name [0] == '.')
+                       continue;       /* skip . and .. entries */
+
+               fat_dir_entry_get_name (this_entry, file_name_start);
+               first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs);
+               size = ped_div_round_up (fat_dir_entry_get_length (this_entry),
+                                        512);
+
+#ifdef PED_VERBOSE
+               printf ("%s: ", file_name);
+               print_chain (fs, first_cluster);
+#endif
+
+#if 0
+               if (fat_dir_entry_is_system_file (this_entry)
+                   && !is_movable_system_file (file_name)) {
+                        PedExceptionOption ex_status;
+                       ex_status = ped_exception_throw (
+                               PED_EXCEPTION_WARNING,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("The file %s is marked as a system file.  "
+                               "This means moving it could cause some "
+                               "programs to stop working."),
+                               file_name);
+
+                       switch (ex_status) {
+                               case PED_EXCEPTION_CANCEL:
+                                       return 0;
+
+                               case PED_EXCEPTION_UNHANDLED:
+                                       ped_exception_catch ();
+                               case PED_EXCEPTION_IGNORE:
+                       }
+               }
+#endif /* 0 */
+
+               if (fat_dir_entry_is_directory (this_entry)) {
+                       if (!flag_traverse_fat (fs, file_name, first_cluster,
+                                               FAT_FLAG_DIRECTORY, size))
+                               return 0;
+
+                       subdir_trav_info = fat_traverse_directory (trav_info,
+                                                                  this_entry);
+                       if (!subdir_trav_info)
+                               return 0;
+                       if (!flag_traverse_dir (subdir_trav_info))
+                               return 0;
+               } else if (fat_dir_entry_is_file (this_entry)) {
+                       if (!flag_traverse_fat (fs, file_name, first_cluster,
+                                               FAT_FLAG_FILE, size))
+                               return 0;
+               }
+       }
+
+       fat_traverse_complete (trav_info);
+       return 1;
+}
+
+static void
+_mark_bad_clusters (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      cluster;
+
+       for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) {
+               if (fat_table_is_bad (fs_info->fat, cluster))
+                       fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD;
+       }
+}
+
+/*
+    fills in cluster_info.  Each FAT entry (= cluster) is flagged as either
+    FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY.
+
+    Also, the fraction of each cluster (x/64) is recorded
+*/
+int
+fat_collect_cluster_info (PedFileSystem* fs) {
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       FatTraverseInfo*        trav_info;
+
+       /* set all clusters to unused as a default */
+       memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2);
+       fs_info->total_dir_clusters = 0;
+
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               trav_info = fat_traverse_begin (fs, fs_info->root_cluster,
+                                               "\\");
+               if (!flag_traverse_dir (trav_info))
+                       return 0;
+               if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster,
+                                        FAT_FLAG_DIRECTORY, 0))
+                       return 0;
+       } else {
+               trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\");
+               if (!flag_traverse_dir (trav_info))
+                       return 0;
+       }
+
+       _mark_bad_clusters (fs);
+       return 1;
+}
+
+FatClusterFlag
+fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+
+       return fs_info->cluster_info [cluster].flag;
+}
+
+PedSector
+fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       int                     fraction;
+
+       if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE)
+               return 0;
+
+       fraction = fs_info->cluster_info [cluster].units_used;
+       if (fraction == 0)
+               fraction = 64;
+
+       return fraction * fs_info->cluster_sectors / 64;
+}
+
+FatClusterFlag
+fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      cluster = fat_frag_to_cluster (fs, frag);
+       FatFragment     offset = frag % fs_info->cluster_frags;
+       FatFragment     last_frag_used;
+       FatClusterFlag  flag;
+
+       PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2);
+
+       flag = fat_get_cluster_flag (fs, cluster);
+       if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY)
+               return flag;
+       last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1)
+                               / fs_info->frag_sectors;
+       if (offset > last_frag_used)
+               return FAT_FLAG_FREE;
+       else
+               return flag;
+}
+
+int
+fat_is_fragment_active (PedFileSystem* fs, FatFragment frag)
+{
+       switch (fat_get_fragment_flag (fs, frag)) {
+               case FAT_FLAG_FREE:
+               case FAT_FLAG_BAD:
+                       return 0;
+
+               case FAT_FLAG_FILE:
+               case FAT_FLAG_DIRECTORY:
+                       return 1;
+       }
+       return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/count.h b/libparted/fs/r/fat/count.h
new file mode 100644
index 0000000..622e796
--- /dev/null
+++ b/libparted/fs/r/fat/count.h
@@ -0,0 +1,45 @@
+/*
+    libparted
+    Copyright (C) 1999-2000, 2007-2011 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/>.
+*/
+
+#ifndef COUNT_H_INCLUDED
+#define COUNT_H_INCLUDED
+
+typedef enum _FatClusterFlag FatClusterFlag;
+typedef struct _FatClusterInfo FatClusterInfo;
+
+enum _FatClusterFlag {
+       FAT_FLAG_FREE=0,
+       FAT_FLAG_FILE=1,
+       FAT_FLAG_DIRECTORY=2,
+       FAT_FLAG_BAD=3
+};
+
+struct __attribute__ ((packed)) _FatClusterInfo {
+       unsigned int    units_used:6;   /* 1 unit = cluster_size / 64 */
+       FatClusterFlag  flag:2;
+};
+
+extern int fat_collect_cluster_info (PedFileSystem *fs);
+extern FatClusterFlag fat_get_cluster_flag (PedFileSystem* fs,
+                                           FatCluster cluster);
+extern PedSector fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster);
+extern FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs,
+                                            FatFragment frag);
+extern int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag);
+
+#endif /* COUNT_H_INCLUDED */
diff --git a/libparted/fs/r/fat/fat.c b/libparted/fs/r/fat/fat.c
new file mode 100644
index 0000000..a51a91e
--- /dev/null
+++ b/libparted/fs/r/fat/fat.c
@@ -0,0 +1,892 @@
+/*
+    libparted
+    Copyright (C) 1998-2001, 2007-2011 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/>.
+*/
+
+#include <config.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include "fat.h"
+#include "calc.h"
+
+PedFileSystem*
+fat_alloc (const PedGeometry* geom)
+{
+       PedFileSystem*          fs;
+
+       fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+       if (!fs)
+               goto error;
+
+       fs->type_specific = (FatSpecific*) ped_malloc (sizeof (FatSpecific));
+       if (!fs->type_specific)
+               goto error_free_fs;
+
+       fs->geom = ped_geometry_duplicate (geom);
+       if (!fs->geom)
+               goto error_free_type_specific;
+
+       fs->checked = 0;
+       return fs;
+
+error_free_type_specific:
+       free (fs->type_specific);
+error_free_fs:
+       free (fs);
+error:
+       return NULL;
+}
+
+/* Requires the boot sector to be analysed */
+int
+fat_alloc_buffers (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       fs_info->buffer_sectors = BUFFER_SIZE;
+        fs_info->buffer = ped_malloc (fs_info->buffer_sectors * 512);
+        if (!fs_info->buffer)
+               goto error;
+
+       fs_info->cluster_info = ped_malloc (fs_info->cluster_count + 2);
+       if (!fs_info->cluster_info)
+               goto error_free_buffer;
+
+       return 1;
+
+error_free_buffer:
+       free (fs_info->buffer);
+error:
+       return 0;
+};
+
+void
+fat_free_buffers (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       free (fs_info->cluster_info);
+       free (fs_info->buffer);
+}
+
+void
+fat_free (PedFileSystem* fs)
+{
+       ped_geometry_destroy (fs->geom);
+       free (fs->type_specific);
+       free (fs);
+}
+
+int
+fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (fs_info->cluster_sectors % frag_sectors == 0
+                       && frag_sectors <= fs_info->cluster_sectors);
+
+       fs_info->frag_size = frag_sectors * 512;
+       fs_info->frag_sectors = frag_sectors;
+       fs_info->buffer_frags = fs_info->buffer_sectors / frag_sectors;
+       fs_info->cluster_frags = fs_info->cluster_sectors / frag_sectors;
+       fs_info->frag_count = fs_info->cluster_count * fs_info->cluster_frags;
+
+       return 1;
+}
+
+PedGeometry*
+fat_probe (PedGeometry* geom, FatType* fat_type)
+{
+       PedFileSystem*          fs;
+       FatSpecific*            fs_info;
+       PedGeometry*            result;
+
+       fs = fat_alloc (geom);
+       if (!fs)
+               goto error;
+       fs_info = (FatSpecific*) fs->type_specific;
+
+       if (!fat_boot_sector_read (&fs_info->boot_sector, geom))
+               goto error_free_fs;
+       if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs))
+               goto error_free_fs;
+
+       *fat_type = fs_info->fat_type;
+       result = ped_geometry_new (geom->dev, geom->start,
+                                  fs_info->sector_count);
+
+       fat_free (fs);
+       return result;
+
+error_free_fs:
+       fat_free (fs);
+error:
+       return NULL;
+}
+
+PedGeometry*
+fat_probe_fat16 (PedGeometry* geom)
+{
+       FatType         fat_type;
+       PedGeometry*    probed_geom = fat_probe (geom, &fat_type);
+
+       if (probed_geom) {
+               if (fat_type == FAT_TYPE_FAT16)
+                       return probed_geom;
+               ped_geometry_destroy (probed_geom);
+       }
+       return NULL;
+}
+
+PedGeometry*
+fat_probe_fat32 (PedGeometry* geom)
+{
+       FatType         fat_type;
+       PedGeometry*    probed_geom = fat_probe (geom, &fat_type);
+
+       if (probed_geom) {
+               if (fat_type == FAT_TYPE_FAT32)
+                       return probed_geom;
+               ped_geometry_destroy (probed_geom);
+       }
+       return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+int
+fat_clobber (PedGeometry* geom)
+{
+       FatBootSector           boot_sector;
+
+       if (!fat_boot_sector_read (&boot_sector, geom))
+               return 1;
+
+       boot_sector.system_id[0] = 0;
+       boot_sector.boot_sign = 0;
+       if (boot_sector.u.fat16.fat_name[0] == 'F')
+               boot_sector.u.fat16.fat_name[0] = 0;
+       if (boot_sector.u.fat32.fat_name[0] == 'F')
+               boot_sector.u.fat32.fat_name[0] = 0;
+
+        return ped_geometry_write (geom, &boot_sector, 0, 1);
+}
+
+static int
+_init_fats (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      table_size;
+
+       table_size = fs_info->fat_sectors * 512
+                    / fat_table_entry_size (fs_info->fat_type);
+       fs_info->fat = fat_table_new (fs_info->fat_type, table_size);
+       if (!fs_info->fat)
+               goto error;
+
+       if (!fat_table_read (fs_info->fat, fs, 0))
+               goto error_free_fat;
+
+       return 1;
+
+error_free_fat:
+       fat_table_destroy (fs_info->fat);
+error:
+       return 0;
+}
+
+PedFileSystem*
+fat_open (PedGeometry* geom)
+{
+       PedFileSystem*          fs;
+       FatSpecific*            fs_info;
+
+       fs = fat_alloc (geom);
+       if (!fs)
+               goto error;
+       fs_info = (FatSpecific*) fs->type_specific;
+
+       if (!fat_boot_sector_read (&fs_info->boot_sector, geom))
+               goto error_free_fs;
+       if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs))
+               goto error_free_fs;
+       fs->type = (fs_info->fat_type == FAT_TYPE_FAT16)
+                               ? &fat16_type
+                               : &fat32_type;
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               if (!fat_info_sector_read (&fs_info->info_sector, fs))
+                       goto error_free_fs;
+       }
+
+       if (!_init_fats (fs))
+               goto error_free_fs;
+       if (!fat_alloc_buffers (fs))
+               goto error_free_fat_table;
+       if (!fat_collect_cluster_info (fs))
+               goto error_free_buffers;
+
+       return fs;
+
+error_free_buffers:
+       fat_free_buffers (fs);
+error_free_fat_table:
+       fat_table_destroy (fs_info->fat);
+error_free_fs:
+       fat_free (fs);
+error:
+       return NULL;
+}
+
+static int
+fat_root_dir_clear (PedFileSystem* fs)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       memset (fs_info->buffer, 0, 512 * fs_info->root_dir_sector_count);
+       return ped_geometry_write (fs->geom, fs_info->buffer,
+                                  fs_info->root_dir_offset,
+                                  fs_info->root_dir_sector_count);
+}
+
+/* hack: use the ext2 uuid library to generate a reasonably random (hopefully
+ * with /dev/random) number.  Unfortunately, we can only use 4 bytes of it
+ */
+static uint32_t
+_gen_new_serial_number (void)
+{
+       union {
+               uuid_t uuid;
+               uint32_t i;
+       } uu32;
+
+       uuid_generate (uu32.uuid);
+       return uu32.i;
+}
+
+PedFileSystem*
+fat_create (PedGeometry* geom, FatType fat_type, PedTimer* timer)
+{
+       PedFileSystem*          fs;
+       FatSpecific*            fs_info;
+       FatCluster              table_size;
+
+       fs = fat_alloc (geom);
+       if (!fs)
+               goto error;
+       fs_info = (FatSpecific*) fs->type_specific;
+
+       fs_info->logical_sector_size = 1;
+       fs_info->sectors_per_track = geom->dev->bios_geom.sectors;
+       fs_info->heads = geom->dev->bios_geom.heads;
+       fs_info->sector_count = fs->geom->length;
+       fs_info->fat_table_count = 2;
+/* some initial values, to be changed later */
+       fs_info->root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT
+                                         / (512 / sizeof (FatDirEntry));
+       fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT;
+
+       fs_info->fat_type = fat_type;
+       if (!fat_calc_sizes (fs->geom->length, 0,
+                       fs_info->fat_type,
+                       fs_info->root_dir_sector_count,
+                       &fs_info->cluster_sectors,
+                       &fs_info->cluster_count,
+                       &fs_info->fat_sectors)) {
+               ped_exception_throw (PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Partition too big/small for a %s file system."),
+                       (fat_type == FAT_TYPE_FAT16)
+                               ? fat16_type.name
+                               : fat32_type.name);
+               goto error_free_fs;
+       }
+
+       fs_info->cluster_size = fs_info->cluster_sectors * 512;
+
+       fs_info->fat_offset = fat_min_reserved_sector_count (fs_info->fat_type);
+       fs_info->dir_entries_per_cluster
+               = fs_info->cluster_size / sizeof (FatDirEntry);
+
+       if (fs_info->fat_type == FAT_TYPE_FAT16) {
+               /* FAT16 */
+               fs->type = &fat16_type;
+
+               if (fs_info->cluster_count
+                       > fat_max_cluster_count (fs_info->fat_type)) {
+                       fs_info->cluster_count
+                               = fat_max_cluster_count (fs_info->fat_type);
+               }
+
+               fs_info->root_dir_sector_count
+                       = FAT_ROOT_DIR_ENTRY_COUNT
+                               / (512 / sizeof (FatDirEntry));
+               fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT;
+                fs_info->root_dir_offset
+                       = fs_info->fat_offset
+                       + fs_info->fat_sectors * fs_info->fat_table_count;
+               fs_info->cluster_offset
+                       = fs_info->root_dir_offset
+                         + fs_info->root_dir_sector_count;
+       } else {
+               /* FAT32 */
+               fs->type = &fat32_type;
+
+               fs_info->info_sector_offset = 1;
+               fs_info->boot_sector_backup_offset = 6;
+
+               fs_info->root_dir_sector_count = 0;
+               fs_info->root_dir_entry_count = 0;
+               fs_info->root_dir_offset = 0;
+
+               fs_info->cluster_offset
+                       = fs_info->fat_offset
+                         + fs_info->fat_sectors * fs_info->fat_table_count;
+       }
+
+       table_size = fs_info->fat_sectors * 512
+                    / fat_table_entry_size (fs_info->fat_type);
+       fs_info->fat = fat_table_new (fs_info->fat_type, table_size);
+       if (!fs_info->fat)
+               goto error_free_fs;
+       fat_table_set_cluster_count (fs_info->fat, fs_info->cluster_count);
+       if (!fat_alloc_buffers (fs))
+               goto error_free_fat_table;
+
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               fs_info->root_cluster
+                       = fat_table_alloc_cluster (fs_info->fat);
+               fat_table_set_eof (fs_info->fat, fs_info->root_cluster);
+               memset (fs_info->buffer, 0, fs_info->cluster_size);
+               if (!fat_write_cluster (fs, fs_info->buffer,
+                                       fs_info->root_cluster))
+                       return 0;
+       }
+
+       fs_info->serial_number = _gen_new_serial_number ();
+
+       if (!fat_boot_sector_set_boot_code (&fs_info->boot_sector))
+               goto error_free_buffers;
+       if (!fat_boot_sector_generate (&fs_info->boot_sector, fs))
+               goto error_free_buffers;
+       if (!fat_boot_sector_write (&fs_info->boot_sector, fs))
+               goto error_free_buffers;
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               if (!fat_info_sector_generate (&fs_info->info_sector, fs))
+                       goto error_free_buffers;
+               if (!fat_info_sector_write (&fs_info->info_sector, fs))
+                       goto error_free_buffers;
+       }
+
+       if (!fat_table_write_all (fs_info->fat, fs))
+               goto error_free_buffers;
+
+       if (fs_info->fat_type == FAT_TYPE_FAT16) {
+               if (!fat_root_dir_clear (fs))
+                       goto error_free_buffers;
+       }
+
+       return fs;
+
+error_free_buffers:
+       fat_free_buffers (fs);
+error_free_fat_table:
+       fat_table_destroy (fs_info->fat);
+error_free_fs:
+       fat_free (fs);
+error:
+       return NULL;
+}
+
+PedFileSystem*
+fat_create_fat16 (PedGeometry* geom, PedTimer* timer)
+{
+       return fat_create (geom, FAT_TYPE_FAT16, timer);
+}
+
+PedFileSystem*
+fat_create_fat32 (PedGeometry* geom, PedTimer* timer)
+{
+       return fat_create (geom, FAT_TYPE_FAT32, timer);
+}
+
+int
+fat_close (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       fat_free_buffers (fs);
+       fat_table_destroy (fs_info->fat);
+       fat_free (fs);
+       return 1;
+}
+
+/* Hack: just resize the file system outside of its boundaries! */
+PedFileSystem*
+fat_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+       PedFileSystem*          new_fs;
+
+       new_fs = ped_file_system_open (fs->geom);
+       if (!new_fs)
+               goto error;
+       if (!ped_file_system_resize (new_fs, geom, timer))
+               goto error_close_new_fs;
+       return new_fs;
+
+error_close_new_fs:
+       ped_file_system_close (new_fs);
+error:
+       return 0;
+}
+
+static int
+_compare_fats (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatTable*       table_copy;
+       FatCluster      table_size;
+       int             i;
+
+       table_size = fs_info->fat_sectors * 512
+                    / fat_table_entry_size (fs_info->fat_type);
+
+       table_copy = fat_table_new (fs_info->fat_type, table_size);
+       if (!table_copy)
+               goto error;
+
+       for (i = 1; i < fs_info->fat_table_count; i++) {
+               if (!fat_table_read (table_copy, fs, i))
+                       goto error_free_table_copy;
+               if (!fat_table_compare (fs_info->fat, table_copy)) {
+                       if (ped_exception_throw (PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("The FATs don't match.  If you don't know "
+                                 "what this means, then select cancel, run "
+                                 "scandisk on the file system, and then come "
+                                 "back."))
+                           != PED_EXCEPTION_IGNORE)
+                               goto error_free_table_copy;
+               }
+       }
+
+       fat_table_destroy (table_copy);
+       return 1;
+
+error_free_table_copy:
+       fat_table_destroy (table_copy);
+error:
+       return 0;
+}
+
+int
+fat_check (PedFileSystem* fs, PedTimer* timer)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedSector       cluster_sectors;
+       FatCluster      cluster_count;
+       PedSector       fat_sectors;
+       PedSector       align_sectors;
+       FatCluster      info_free_clusters;
+
+       align_sectors = fs_info->fat_offset
+                       - fat_min_reserved_sector_count (fs_info->fat_type);
+
+       if (!fat_calc_sizes (fs->geom->length,
+                            align_sectors,
+                            fs_info->fat_type,
+                            fs_info->root_dir_sector_count,
+                            &cluster_sectors,
+                            &cluster_count,
+                            &fat_sectors)) {
+               if (ped_exception_throw (PED_EXCEPTION_BUG,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("There are no possible configurations for this FAT "
+                         "type."))
+                               != PED_EXCEPTION_IGNORE)
+                       goto error;
+       }
+
+       if (fs_info->fat_type == FAT_TYPE_FAT16) {
+               if (cluster_sectors != fs_info->cluster_sectors
+                   || cluster_count != fs_info->cluster_count
+                   || fat_sectors != fs_info->fat_sectors) {
+                       if (ped_exception_throw (PED_EXCEPTION_WARNING,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("File system doesn't have expected sizes for "
+                                 "Windows to like it.  "
+                                 "Cluster size is %dk (%dk expected); "
+                                 "number of clusters is %d (%d expected); "
+                                 "size of FATs is %d sectors (%d expected)."),
+                               (int) fs_info->cluster_sectors / 2,
+                                       (int) cluster_sectors / 2,
+                               (int) fs_info->cluster_count,
+                                       (int) cluster_count,
+                               (int) fs_info->fat_sectors,
+                                       (int) fat_sectors)
+                                       != PED_EXCEPTION_IGNORE)
+                               goto error;
+               }
+       }
+
+       if (fs_info->fat_type == FAT_TYPE_FAT32) {
+               info_free_clusters
+                       = PED_LE32_TO_CPU (fs_info->info_sector.free_clusters);
+               if (info_free_clusters != (FatCluster) -1
+                   && info_free_clusters != fs_info->fat->free_cluster_count) {
+                       if (ped_exception_throw (PED_EXCEPTION_WARNING,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("File system is reporting the free space as "
+                                 "%d clusters, not %d clusters."),
+                               info_free_clusters,
+                               fs_info->fat->free_cluster_count)
+                                       != PED_EXCEPTION_IGNORE)
+                               goto error;
+               }
+       }
+
+       if (!_compare_fats (fs))
+               goto error;
+
+       fs->checked = 1;
+       return 1;       /* existence of fs implies consistency ;-) */
+
+error:
+       return 0;
+}
+
+/* Calculates how much space there will be in clusters in:
+ *     old_fs intersect the-new-fs
+ */
+static PedSector
+_calc_resize_data_size (
+       const PedFileSystem* old_fs,
+       PedSector new_cluster_sectors,
+       FatCluster new_cluster_count,
+       PedSector new_fat_size)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (old_fs);
+       PedSector       fat_size_delta;
+
+       fat_size_delta = old_fs_info->fat_sectors - new_fat_size;
+       return new_cluster_sectors * new_cluster_count - fat_size_delta * 2;
+}
+
+static int
+_test_resize_size (const PedFileSystem* fs,
+                  PedSector length, PedSector min_data_size)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedGeometry     geom;
+        PedSector      _cluster_sectors;
+       FatCluster      _cluster_count;
+       PedSector       _fat_size;
+
+       ped_geometry_init (&geom, fs->geom->dev, fs->geom->start, length);
+
+       if (fat_calc_resize_sizes (
+                               &geom,
+                               fs_info->cluster_sectors,
+                               FAT_TYPE_FAT16,
+                               fs_info->root_dir_sector_count,
+                               fs_info->cluster_sectors,
+                               &_cluster_sectors,
+                               &_cluster_count,
+                               &_fat_size)
+           && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count,
+                                      _fat_size)
+                       >= min_data_size)
+               return 1;
+
+       if (fat_calc_resize_sizes (
+                               &geom,
+                               fs_info->cluster_sectors,
+                               FAT_TYPE_FAT32,
+                               0,
+                               fs_info->cluster_sectors,
+                               &_cluster_sectors,
+                               &_cluster_count,
+                               &_fat_size)
+           && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count,
+                                      _fat_size)
+                       >= min_data_size)
+               return 1;
+
+       return 0;
+}
+
+/* does a binary search (!) for the mininum size.  Too hard to compute directly
+ * (see calc_sizes() for why!)
+ */
+static PedSector
+_get_min_resize_size (const PedFileSystem* fs, PedSector min_data_size)
+{
+       PedSector       min_length = 0;
+       PedSector       max_length = fs->geom->length;
+       PedSector       length;
+
+       while (min_length < max_length - 1) {
+               length = (min_length + max_length) / 2;
+               if (_test_resize_size (fs, length, min_data_size))
+                       max_length = length;
+               else
+                       min_length = length;
+       }
+
+/* adds a bit of leeway (64 sectors), for resolving extra issues, like root
+ * directory allocation, that aren't covered here.
+ */
+       return max_length + 64;
+}
+
+PedConstraint*
+fat_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedGeometry     full_dev;
+       PedSector       min_cluster_count;
+       FatCluster      used_clusters;
+       PedSector       min_data_size;
+
+       if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+               return NULL;
+
+       used_clusters = fs_info->fat->cluster_count
+                       - fs_info->fat->free_cluster_count;
+       min_cluster_count = used_clusters + fs_info->total_dir_clusters;
+       min_data_size = min_cluster_count * fs_info->cluster_sectors;
+
+       return ped_constraint_new (ped_alignment_any, ped_alignment_any,
+                                  &full_dev, &full_dev,
+                                  _get_min_resize_size (fs, min_data_size),
+                                  dev->length);
+}
+
+PedConstraint*
+fat_get_resize_constraint (const PedFileSystem* fs)
+{
+       return fat_get_copy_constraint (fs, fs->geom->dev);
+}
+
+/* FIXME: fat_calc_sizes() needs to say "too big" or "too small", or
+ * something.  This is a really difficult (maths) problem to do
+ * nicely...
+ *     So, this algorithm works if dev->length / 2 is a valid fat_type
+ * size.  (Which is how I got the magic numbers below)
+ */
+#if 0
+/* returns: -1 too small, 0 ok, 1 too big */
+static int
+_test_create_size (PedSector length, FatType fat_type,
+                  PedSector cluster_sectors, PedSector cluster_count)
+{
+       PedSector       rootdir_sectors;
+       PedSector       _cluster_sectors;
+       FatCluster      _cluster_count;
+       PedSector       _fat_size;
+
+       rootdir_sectors = (fat_type == FAT_TYPE_FAT16) ? 16 : 0;
+
+       if (!fat_calc_sizes (length, 0, fat_type, rootdir_sectors,
+                            &_cluster_sectors, &_cluster_count, &_fat_size))
+               return -1; // XXX: doesn't work... can't see a better way!
+
+       if (_cluster_sectors < cluster_sectors)
+               return -1;
+       if (_cluster_sectors > cluster_sectors)
+               return 1;
+
+       if (_cluster_count < cluster_count)
+               return -1;
+       if (_cluster_count > cluster_count)
+               return 1;
+
+       return 0;
+}
+
+static PedSector
+_get_create_size (PedSector upper_bound, FatType fat_type,
+                 PedSector cluster_sectors, FatCluster cluster_count)
+{
+       PedSector       min_length = 0;
+       PedSector       max_length = upper_bound;
+       PedSector       length;
+
+       while (1) {
+               length = (min_length + max_length) / 2;
+               switch (_test_create_size (length, fat_type, cluster_sectors,
+                                          cluster_count)) {
+                       case -1: min_length = length; break;
+                       case 0: return length;
+                       case 1: max_length = length; break;
+               }
+               /* hack... won't always be able to get max cluster count
+                * with max cluster size, etc. */
+               if (max_length - min_length == 1)
+                       return min_length;
+       }
+
+       return 0;       /* shut gcc up */
+}
+#endif
+
+PedConstraint*
+fat_get_create_constraint_fat16 (const PedDevice* dev)
+{
+       PedGeometry     full_dev;
+       PedSector       min_size;
+       PedSector       max_size;
+
+       if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+               return NULL;
+
+#if 0
+       min_size = _get_create_size (dev->length, FAT_TYPE_FAT16,
+                                    fat_min_cluster_size (FAT_TYPE_FAT16),
+                                    fat_min_cluster_count (FAT_TYPE_FAT16));
+       max_size = _get_create_size (dev->length, FAT_TYPE_FAT16,
+                                    fat_max_cluster_size (FAT_TYPE_FAT16),
+                                    fat_max_cluster_count (FAT_TYPE_FAT16));
+       if (!min_size)
+               return NULL;
+#else
+       min_size = 65794;
+       max_size = 2097153;
+#endif
+
+       return ped_constraint_new (
+                       ped_alignment_any, ped_alignment_any,
+                       &full_dev, &full_dev,
+                       min_size, max_size);
+}
+
+PedConstraint*
+fat_get_create_constraint_fat32 (const PedDevice* dev)
+{
+       PedGeometry     full_dev;
+       PedSector       min_size;
+
+       if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+               return NULL;
+
+#if 0
+       min_size = _get_create_size (dev->length, FAT_TYPE_FAT32,
+                                    fat_min_cluster_size (FAT_TYPE_FAT32),
+                                    fat_min_cluster_count (FAT_TYPE_FAT32));
+       if (!min_size)
+               return NULL;
+#else
+       min_size = 525224;
+#endif
+
+       return ped_constraint_new (
+                       ped_alignment_any, ped_alignment_any,
+                       &full_dev, &full_dev,
+                       min_size, dev->length);
+}
+#endif /* !DISCOVER_ONLY */
+
+#if 0
+
+static PedFileSystemOps fat16_ops = {
+       probe:          fat_probe_fat16,
+#ifndef DISCOVER_ONLY
+       clobber:        fat_clobber,
+       open:           fat_open,
+       create:         fat_create_fat16,
+       close:          fat_close,
+       check:          fat_check,
+       resize:         fat_resize,
+       copy:           fat_copy,
+       get_create_constraint:  fat_get_create_constraint_fat16,
+       get_resize_constraint:  fat_get_resize_constraint,
+       get_copy_constraint:    fat_get_copy_constraint,
+#else /* !DISCOVER_ONLY */
+       clobber:        NULL,
+       open:           NULL,
+       create:         NULL,
+       close:          NULL,
+       check:          NULL,
+       resize:         NULL,
+       copy:           NULL,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  NULL,
+       get_copy_constraint:    NULL,
+#endif /* !DISCOVER_ONLY */
+};
+
+static PedFileSystemOps fat32_ops = {
+       probe:          fat_probe_fat32,
+#ifndef DISCOVER_ONLY
+       clobber:        fat_clobber,
+       open:           fat_open,
+       create:         fat_create_fat32,
+       close:          fat_close,
+       check:          fat_check,
+       resize:         fat_resize,
+       copy:           fat_copy,
+       get_create_constraint:  fat_get_create_constraint_fat32,
+       get_resize_constraint:  fat_get_resize_constraint,
+       get_copy_constraint:    fat_get_copy_constraint,
+#else /* !DISCOVER_ONLY */
+       clobber:        NULL,
+       open:           NULL,
+       create:         NULL,
+       close:          NULL,
+       check:          NULL,
+       resize:         NULL,
+       copy:           NULL,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  NULL,
+       get_copy_constraint:    NULL,
+#endif /* !DISCOVER_ONLY */
+};
+
+#define FAT_BLOCK_SIZES ((int[2]){512, 0})
+
+PedFileSystemType fat16_type = {
+       next:           NULL,
+       ops:            &fat16_ops,
+       name:           "fat16",
+        block_sizes:    FAT_BLOCK_SIZES
+};
+
+PedFileSystemType fat32_type = {
+       next:           NULL,
+       ops:            &fat32_ops,
+       name:           "fat32",
+        block_sizes:    FAT_BLOCK_SIZES
+};
+
+void
+ped_file_system_fat_init ()
+{
+       if (sizeof (FatBootSector) != 512) {
+               ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+                       _("GNU Parted was miscompiled: the FAT boot sector "
+                       "should be 512 bytes.  FAT support will be disabled."));
+       } else {
+               ped_file_system_type_register (&fat16_type);
+               ped_file_system_type_register (&fat32_type);
+       }
+}
+
+void
+ped_file_system_fat_done ()
+{
+       ped_file_system_type_unregister (&fat16_type);
+       ped_file_system_type_unregister (&fat32_type);
+}
+
+#endif
diff --git a/libparted/fs/r/fat/fat.h b/libparted/fs/r/fat/fat.h
new file mode 100644
index 0000000..70355b6
--- /dev/null
+++ b/libparted/fs/r/fat/fat.h
@@ -0,0 +1,158 @@
+/*
+    libparted
+    Copyright (C) 1998-2001, 2007, 2009-2011 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/>.
+*/
+
+#ifndef FAT_H_INCLUDED
+#define FAT_H_INCLUDED
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define BUFFER_SIZE  1024      /* buffer size in sectors (512 bytes) */
+
+typedef uint32_t               FatCluster;
+typedef int32_t                        FatFragment;
+
+enum _FatType {
+       FAT_TYPE_FAT12,
+       FAT_TYPE_FAT16,
+       FAT_TYPE_FAT32
+};
+typedef enum _FatType          FatType;
+
+typedef struct _FatSpecific    FatSpecific;
+typedef struct _FatDirEntry    FatDirEntry;
+
+/* FIXME: YUCKY */
+#include "table.h"
+#include "bootsector.h"
+#include "context.h"
+#include "fatio.h"
+#include "traverse.h"
+#include "calc.h"
+#include "count.h"
+#include "clstdup.h"
+
+struct __attribute__ ((packed)) _FatDirEntry {
+       char            name[8];
+       uint8_t         extension[3];
+       uint8_t         attributes;
+       uint8_t         is_upper_case_name;
+       uint8_t         creation_time_low;      /* milliseconds */
+       uint16_t        creation_time_high;
+       uint16_t        creation_date;
+       uint16_t        access_date;
+       uint16_t        first_cluster_high;     /* for FAT32 */
+       uint16_t        time;
+       uint16_t        date;
+       uint16_t        first_cluster;
+       uint32_t        length;
+};
+
+struct _FatSpecific {
+       FatBootSector   boot_sector;    /* structure of boot sector */
+       FatInfoSector   info_sector;    /* fat32-only information sector */
+
+       int             logical_sector_size;    /* illogical sector size :-) */
+       PedSector       sector_count;
+
+       int             sectors_per_track;      /* BIOS CHS stuff (S) */
+       int             heads;                  /* BIOS CHS stuff (H) */
+
+       int             cluster_size;
+       PedSector       cluster_sectors;
+       FatCluster      cluster_count;
+       int             dir_entries_per_cluster;
+
+       FatType         fat_type;
+       int             fat_table_count;
+       PedSector       fat_sectors;
+
+       uint32_t        serial_number;
+
+       PedSector       info_sector_offset;     /* FAT32 only */
+       PedSector       fat_offset;
+       PedSector       root_dir_offset;        /* non-FAT32 */
+       PedSector       cluster_offset;
+       PedSector       boot_sector_backup_offset;
+
+       FatCluster      root_cluster;           /* FAT32 only */
+       int             root_dir_entry_count;   /* non-FAT32 */
+       PedSector       root_dir_sector_count;  /* non-FAT32 */
+       FatCluster      total_dir_clusters;
+
+       FatTable*       fat;
+       FatClusterInfo* cluster_info;
+
+       PedSector       buffer_sectors;
+       char*           buffer;
+
+       int             frag_size;
+       PedSector       frag_sectors;
+       FatFragment     frag_count;
+       FatFragment     buffer_frags;
+       FatFragment     cluster_frags;
+};
+
+#define FAT_SPECIFIC(fs)       ((FatSpecific*) fs->type_specific)
+
+#define FAT_ROOT               0
+
+#define DELETED_FLAG           0xe5
+
+#define READONLY_ATTR          0x01
+#define HIDDEN_ATTR            0x02
+#define SYSTEM_ATTR            0x04
+#define VOLUME_LABEL_ATTR      0x08
+#define VFAT_ATTR              0x0f
+#define DIRECTORY_ATTR         0x10
+#define ARCH_ATTR              0x20
+
+#define MAX_FAT12_CLUSTERS     4086
+#define MAX_FAT16_CLUSTERS     65526
+#define MAX_FAT32_CLUSTERS     2000000
+
+#define FAT_ROOT_DIR_ENTRY_COUNT       512
+
+extern PedFileSystemType fat16_type;
+extern PedFileSystemType fat32_type;
+
+extern void fat_print (const PedFileSystem* fs);
+
+extern PedFileSystem* fat_alloc (const PedGeometry* geom);
+extern void fat_free (PedFileSystem* fs);
+extern int fat_alloc_buffers (PedFileSystem* fs);
+extern void fat_free_buffers (PedFileSystem* fs);
+
+extern int fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer);
+
+extern int fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors);
+
+#endif /* FAT_H_INCLUDED */
diff --git a/libparted/fs/r/fat/fatio.c b/libparted/fs/r/fat/fatio.c
new file mode 100644
index 0000000..ecc2cd8
--- /dev/null
+++ b/libparted/fs/r/fat/fatio.c
@@ -0,0 +1,149 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "fatio.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifndef DISCOVER_ONLY
+
+int
+fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+                   FatFragment count)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedSector       sector = fat_frag_to_sector (fs, frag);
+       PedSector       sector_count = count * fs_info->frag_sectors;
+
+       PED_ASSERT (frag >= 0 && frag < fs_info->frag_count);
+
+       return ped_geometry_read (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag)
+{
+       return fat_read_fragments (fs, buf, frag, 1);
+}
+
+int
+fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+                    FatFragment count)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedSector       sector = fat_frag_to_sector (fs, frag);
+       PedSector       sector_count = count * fs_info->frag_sectors;
+
+       PED_ASSERT (frag >= 0 && frag < fs_info->frag_count);
+
+       return ped_geometry_write (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag)
+{
+       return fat_write_fragments (fs, buf, frag, 1);
+}
+
+int
+fat_write_sync_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+                         FatFragment count)
+{
+       if (!fat_write_fragments (fs, buf, frag, count))
+               return 0;
+       if (!ped_geometry_sync (fs->geom))
+               return 0;
+       return 1;
+}
+
+int
+fat_write_sync_fragment (PedFileSystem* fs, char* buf, FatFragment frag)
+{
+       return fat_write_sync_fragments (fs, buf, frag, 1);
+}
+
+int
+fat_read_clusters (PedFileSystem* fs, char *buf, FatCluster cluster,
+                  FatCluster count)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedSector       sector = fat_cluster_to_sector (fs, cluster);
+       PedSector       sector_count = count * fs_info->cluster_sectors;
+
+       PED_ASSERT (cluster >= 2
+                   && cluster + count - 1 < fs_info->cluster_count + 2);
+
+       return ped_geometry_read (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster)
+{
+       return fat_read_clusters (fs, buf, cluster, 1);
+}
+
+int
+fat_write_clusters (PedFileSystem* fs, char *buf, FatCluster cluster,
+                   FatCluster count)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       PedSector       sector = fat_cluster_to_sector (fs, cluster);
+       PedSector       sector_count = count * fs_info->cluster_sectors;
+
+       PED_ASSERT (cluster >= 2
+                   && cluster + count - 1 < fs_info->cluster_count + 2);
+
+       return ped_geometry_write (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster)
+{
+       return fat_write_clusters (fs, buf, cluster, 1);
+}
+
+int
+fat_write_sync_clusters (PedFileSystem* fs, char *buf, FatCluster cluster,
+                        FatCluster count)
+{
+       if (!fat_write_clusters (fs, buf, cluster, count))
+               return 0;
+       if (!ped_geometry_sync (fs->geom))
+               return 0;
+       return 1;
+}
+
+int
+fat_write_sync_cluster (PedFileSystem* fs, char *buf, FatCluster cluster)
+{
+       if (!fat_write_cluster (fs, buf, cluster))
+               return 0;
+       if (!ped_geometry_sync (fs->geom))
+               return 0;
+       return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/fatio.h b/libparted/fs/r/fat/fatio.h
new file mode 100644
index 0000000..ad236fa
--- /dev/null
+++ b/libparted/fs/r/fat/fatio.h
@@ -0,0 +1,48 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#ifndef FATIO_H_INCLUDED
+#define FATIO_H_INCLUDED
+
+#include "fat.h"
+
+extern int fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+                              FatFragment count);
+extern int fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+                               FatFragment count);
+extern int fat_write_sync_fragments (PedFileSystem* fs, char* buf,
+                                    FatFragment frag, FatFragment count);
+
+extern int fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag);
+extern int fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag);
+extern int fat_write_sync_fragment (PedFileSystem* fs, char* buf,
+                                   FatFragment frag);
+
+extern int fat_read_clusters (PedFileSystem* fs, char* buf, FatCluster cluster,
+                             FatCluster count);
+extern int fat_write_clusters (PedFileSystem* fs, char* buf, FatCluster 
cluster,
+                              FatCluster count);
+extern int fat_write_sync_clusters (PedFileSystem* fs, char* buf,
+                                   FatCluster cluster, FatCluster count);
+
+extern int fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster);
+extern int fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster 
cluster);
+extern int fat_write_sync_cluster (PedFileSystem* fs, char *buf,
+                                  FatCluster cluster);
+
+#endif /* FATIO_H_INCLUDED */
diff --git a/libparted/fs/r/fat/resize.c b/libparted/fs/r/fat/resize.c
new file mode 100644
index 0000000..2c0097a
--- /dev/null
+++ b/libparted/fs/r/fat/resize.c
@@ -0,0 +1,877 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007-2011 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/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+#include "count.h"
+#include "fatio.h"
+#include "calc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+/* Recursively builds (i.e. makes consistent) the duplicated directory tree
+ * (leaving the original directory tree in tact)
+ */
+static int
+fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info)
+{
+       FatTraverseInfo*        sub_dir_info;
+       FatDirEntry*            dir_entry;
+       FatCluster              old_first_cluster;
+
+       while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) {
+               if (fat_dir_entry_is_null_term (dir_entry))
+                       break;
+               if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs))
+                       continue;
+
+               fat_traverse_mark_dirty (trav_info);
+
+               old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry,
+                                               ctx->old_fs);
+               fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs,
+                       fat_op_context_map_cluster (ctx, old_first_cluster));
+
+               if (fat_dir_entry_is_directory (dir_entry)
+                               && dir_entry->name [0] != '.') {
+                       sub_dir_info
+                               = fat_traverse_directory (trav_info, dir_entry);
+                       if (!sub_dir_info)
+                               return 0;
+                       if (!fat_construct_directory (ctx, sub_dir_info))
+                               return 0;
+               }
+       }
+       /* remove "stale" entries at the end */
+       while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) {
+               memset (dir_entry, 0, sizeof (FatDirEntry));
+               fat_traverse_mark_dirty (trav_info);
+       }
+       fat_traverse_complete (trav_info);
+       return 1;
+}
+
+static int
+duplicate_legacy_root_dir (FatOpContext* ctx)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+
+       PED_ASSERT (old_fs_info->root_dir_sector_count
+                       == new_fs_info->root_dir_sector_count);
+
+       if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
+                               old_fs_info->root_dir_offset,
+                               old_fs_info->root_dir_sector_count))
+               return 0;
+
+       if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
+                                new_fs_info->root_dir_offset,
+                                new_fs_info->root_dir_sector_count))
+               return 0;
+
+       return 1;
+}
+
+/*
+    Constructs the new directory tree for legacy (FAT16) file systems.
+*/
+static int
+fat_construct_legacy_root (FatOpContext* ctx)
+{
+       FatTraverseInfo*        trav_info;
+
+       if (!duplicate_legacy_root_dir (ctx))
+               return 0;
+       trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\");
+       return fat_construct_directory (ctx, trav_info);
+}
+
+/*
+    Constructs the new directory tree for new (FAT32) file systems.
+*/
+static int
+fat_construct_root (FatOpContext* ctx)
+{
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatTraverseInfo*        trav_info;
+
+       trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster,
+                                       "\\");
+       fat_construct_directory (ctx, trav_info);
+       return 1;
+}
+
+/* Converts the root directory between FAT16 and FAT32.  NOTE: this code
+ * can also do no conversion.  I'm leaving fat_construct_directory(), because
+ * it's really pretty :-)  It also leaves a higher chance of deleted file
+ * recovery, because it doesn't remove redundant entries.  (We do this here,
+ * because brain-damaged FAT16 has an arbitary limit on root directory entries,
+ * so we save room)
+ */
+static int
+fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav,
+                      FatTraverseInfo* new_trav)
+{
+       FatTraverseInfo*        sub_old_dir_trav;
+       FatTraverseInfo*        sub_new_dir_trav;
+       FatDirEntry*            new_dir_entry;
+       FatDirEntry*            old_dir_entry;
+       FatCluster              old_first_cluster;
+
+       while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) {
+               if (fat_dir_entry_is_null_term (old_dir_entry))
+                       break;
+               if (!fat_dir_entry_is_active (old_dir_entry))
+                       continue;
+
+               new_dir_entry = fat_traverse_next_dir_entry (new_trav);
+               if (!new_dir_entry) {
+                       return ped_exception_throw (PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("There's not enough room in the root "
+                                 "directory for all of the files.  Either "
+                                 "cancel, or ignore to lose the files."))
+                                       == PED_EXCEPTION_IGNORE;
+               }
+
+               *new_dir_entry = *old_dir_entry;
+               fat_traverse_mark_dirty (new_trav);
+
+               if (!fat_dir_entry_has_first_cluster (old_dir_entry,
+                                                     ctx->old_fs))
+                       continue;
+
+               old_first_cluster = fat_dir_entry_get_first_cluster (
+                                               old_dir_entry, ctx->old_fs);
+               fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs,
+                       fat_op_context_map_cluster (ctx, old_first_cluster));
+
+               if (fat_dir_entry_is_directory (old_dir_entry)
+                               && old_dir_entry->name [0] != '.') {
+                       sub_old_dir_trav
+                           = fat_traverse_directory (old_trav, old_dir_entry);
+                       sub_new_dir_trav
+                           = fat_traverse_directory (new_trav, new_dir_entry);
+                       if (!sub_old_dir_trav || !sub_new_dir_trav)
+                               return 0;
+
+                       if (!fat_convert_directory (ctx, sub_old_dir_trav,
+                                                   sub_new_dir_trav))
+                               return 0;
+               }
+       }
+
+       /* remove "stale" entries at the end, just in case there is some
+        * overlap
+        */
+       while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) {
+               memset (new_dir_entry, 0, sizeof (FatDirEntry));
+               fat_traverse_mark_dirty (new_trav);
+       }
+
+       fat_traverse_complete (old_trav);
+       fat_traverse_complete (new_trav);
+       return 1;
+}
+
+static void
+clear_cluster (PedFileSystem* fs, FatCluster cluster)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+
+       memset (fs_info->buffer, 0, fs_info->cluster_size);
+       fat_write_cluster (fs, fs_info->buffer, cluster);
+}
+
+/* This MUST be called BEFORE the fat_construct_new_fat(), because cluster
+ * allocation depend on the old FAT.  The reason is, old clusters may
+ * still be needed during the resize, (particularly clusters in the directory
+ * tree) even if they will be discarded later.
+ */
+static int
+alloc_root_dir (FatOpContext* ctx)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatCluster              i;
+       FatCluster              cluster;
+       FatCluster              cluster_count;
+
+       PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32);
+
+       cluster_count = ped_div_round_up (
+                          PED_MAX (16, old_fs_info->root_dir_sector_count),
+                          new_fs_info->cluster_sectors);
+
+       for (i = 0; i < cluster_count; i++) {
+               cluster = fat_table_alloc_check_cluster (new_fs_info->fat,
+                                                        ctx->new_fs);
+               if (!cluster)
+                       return 0;
+               ctx->new_root_dir [i] = cluster;
+               clear_cluster (ctx->new_fs, cluster);
+       }
+       ctx->new_root_dir [i] = 0;
+       new_fs_info->root_cluster = ctx->new_root_dir [0];
+       return 1;
+}
+
+/* when converting FAT32 -> FAT16
+ * fat_duplicate clusters() duplicated the root directory unnecessarily.
+ * Let's free it.
+ *
+ * This must be called AFTER fat_construct_new_fat().  (otherwise, our
+ * changes just get overwritten)
+ */
+static int
+free_root_dir (FatOpContext* ctx)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatCluster              old_cluster;
+       FatFragment             i;
+
+       PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32);
+       PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16);
+
+       for (old_cluster = old_fs_info->root_cluster;
+            !fat_table_is_eof (old_fs_info->fat, old_cluster);
+            old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) {
+               FatFragment old_frag;
+               old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster);
+               for (i = 0; i < new_fs_info->cluster_frags; i++) {
+                       FatFragment new_frag;
+                       FatCluster new_clst;
+                       new_frag = fat_op_context_map_fragment (ctx,
+                                                               old_frag + i);
+                       new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag);
+                       if (!fat_table_set_avail (new_fs_info->fat, new_clst))
+                               return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int
+fat_clear_root_dir (PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       int             i;
+
+       PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16);
+       PED_ASSERT (fs_info->root_dir_sector_count);
+
+       memset (fs_info->buffer, 0, 512);
+
+       for (i = 0; i < fs_info->root_dir_sector_count; i++) {
+               if (!ped_geometry_write (fs->geom, fs_info->buffer,
+                                        fs_info->root_dir_offset + i, 1)) {
+                       if (ped_exception_throw (PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_IGNORE_CANCEL,
+                               _("Error writing to the root directory."))
+                                       == PED_EXCEPTION_CANCEL)
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+static int
+fat_construct_converted_tree (FatOpContext* ctx)
+{
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatTraverseInfo*        old_trav_info;
+       FatTraverseInfo*        new_trav_info;
+
+       if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
+               new_trav_info = fat_traverse_begin (ctx->new_fs,
+                                           new_fs_info->root_cluster, "\\");
+               old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT,
+                                                   "\\");
+       } else {
+               fat_clear_root_dir (ctx->new_fs);
+               new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT,
+                                                   "\\");
+               old_trav_info = fat_traverse_begin (ctx->old_fs,
+                                           old_fs_info->root_cluster, "\\");
+       }
+       if (!new_trav_info || !old_trav_info)
+               return 0;
+       if (!fat_convert_directory (ctx, old_trav_info, new_trav_info))
+               return 0;
+       return 1;
+}
+
+/*
+    Constructs the new directory tree to match the new file locations.
+*/
+static int
+fat_construct_dir_tree (FatOpContext* ctx)
+{
+       FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+
+       if (new_fs_info->fat_type == old_fs_info->fat_type) {
+               switch (old_fs_info->fat_type) {
+                        case FAT_TYPE_FAT12:
+                        PED_ASSERT (0);
+                        break;
+
+                       case FAT_TYPE_FAT16:
+                       return fat_construct_legacy_root (ctx);
+
+                       case FAT_TYPE_FAT32:
+                       return fat_construct_root (ctx);
+               }
+       } else {
+               return fat_construct_converted_tree (ctx);
+       }
+
+       return 0;
+}
+
+static FatFragment
+_get_next_old_frag (FatOpContext* ctx, FatFragment frag)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatCluster      cluster;
+       FatCluster      next_cluster;
+
+       if ((frag + 1) % old_fs_info->cluster_frags != 0) {
+               if (fat_is_fragment_active (ctx->old_fs, frag + 1))
+                       return frag + 1;
+               else
+                       return -1;
+       } else {
+               cluster = fat_frag_to_cluster (ctx->old_fs, frag);
+               next_cluster = fat_table_get (old_fs_info->fat, cluster);
+
+               if (fat_table_is_eof (old_fs_info->fat, next_cluster))
+                       return -1;
+               else
+                       return fat_cluster_to_frag (ctx->old_fs, next_cluster);
+       }
+}
+
+/*
+    Constructs the new fat for the resized file system.
+*/
+static int
+fat_construct_new_fat (FatOpContext* ctx)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       FatFragment     old_frag;
+       FatCluster      new_cluster;
+       FatFragment     new_frag;
+       FatFragment     old_next_frag;
+       FatFragment     new_next_frag;
+       FatCluster      new_next_cluster;
+       FatClusterFlag  flag;
+       int             i;
+
+       fat_table_clear (new_fs_info->fat);
+       if (!fat_table_set_cluster_count (new_fs_info->fat,
+                                         new_fs_info->cluster_count))
+               return 0;
+
+       for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) {
+               flag = fat_get_fragment_flag (ctx->old_fs, old_frag);
+               if (flag == FAT_FLAG_FREE)
+                       continue;
+               if (flag == FAT_FLAG_BAD) {
+                       new_frag = fat_op_context_map_static_fragment (
+                                               ctx, old_frag);
+                       if (new_frag == -1)
+                               continue;
+                       new_cluster = fat_frag_to_cluster (ctx->new_fs,
+                                                          new_frag);
+                       fat_table_set_bad (new_fs_info->fat, new_cluster);
+                       continue;
+               }
+
+               new_frag = fat_op_context_map_fragment (ctx, old_frag);
+               new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag);
+
+               old_next_frag = _get_next_old_frag (ctx, old_frag);
+               if (old_next_frag == -1) {
+                       fat_table_set_eof (new_fs_info->fat, new_cluster);
+                       continue;
+               }
+
+               new_next_frag = fat_op_context_map_fragment (ctx,
+                                                            old_next_frag);
+               PED_ASSERT (new_next_frag != -1);
+
+               new_next_cluster = fat_frag_to_cluster (ctx->new_fs,
+                                                       new_next_frag);
+               PED_ASSERT (new_next_cluster != new_cluster);
+
+               fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster);
+       }
+
+#if 0
+#ifdef PED_VERBOSE
+       for (old_cluster=2; old_cluster < old_fs_info->cluster_count+2;
+            old_cluster++) {
+               if (fat_table_is_available (old_fs_info->fat, old_cluster))
+                       continue;
+
+               printf ("%d->%d\t(next: %d->%d)\n",
+                       old_cluster,
+                       ctx->remap [old_cluster],
+                       fat_table_get (old_fs_info->fat, old_cluster),
+                       fat_table_get (new_fs_info->fat,
+                                      ctx->remap [old_cluster]));
+       }
+#endif /* PED_VERBOSE */
+#endif
+
+       if (old_fs_info->fat_type == FAT_TYPE_FAT32
+           && new_fs_info->fat_type == FAT_TYPE_FAT32) {
+               new_fs_info->root_cluster
+                       = fat_op_context_map_cluster (ctx,
+                                       old_fs_info->root_cluster);
+       }
+
+       if (old_fs_info->fat_type == FAT_TYPE_FAT16
+           && new_fs_info->fat_type == FAT_TYPE_FAT32) {
+               for (i=0; ctx->new_root_dir[i+1]; i++) {
+                       fat_table_set (new_fs_info->fat,
+                                      ctx->new_root_dir[i],
+                                      ctx->new_root_dir[i+1]);
+               }
+               fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]);
+       }
+
+       return 1;
+}
+
+static int
+ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       PedExceptionOption      status;
+       const char*             fat16_msg;
+       const char*             fat32_msg;
+
+       if (fs_info->fat_type == FAT_TYPE_FAT16)
+               fat16_msg = _("If you leave your file system as FAT16, "
+                             "then you will have no problems.");
+       else
+               fat16_msg = _("If you convert to FAT16, and MS Windows "
+                             "is installed on this partition, then "
+                             "you must re-install the MS Windows boot "
+                             "loader.  If you want to do this, you "
+                             "should consult the Parted manual (or "
+                             "your distribution's manual).");
+
+       if (fs_info->fat_type == FAT_TYPE_FAT32)
+               fat32_msg = _("If you leave your file system as FAT32, "
+                             "then you will not introduce any new "
+                             "problems.");
+       else
+               fat32_msg = _("If you convert to FAT32, and MS Windows "
+                             "is installed on this partition, then "
+                             "you must re-install the MS Windows boot "
+                             "loader.  If you want to do this, you "
+                             "should consult the Parted manual (or "
+                             "your distribution's manual).  Also, "
+                             "converting to FAT32 will make the file "
+                             "system unreadable by MS DOS, MS Windows "
+                             "95a, and MS Windows NT.");
+
+       if (fat16_ok && fat32_ok) {
+               status = ped_exception_throw (
+                        PED_EXCEPTION_INFORMATION,
+                        PED_EXCEPTION_YES_NO_CANCEL,
+                        _("%s  %s  %s"),
+                        _("Would you like to use FAT32?"),
+                        fat16_msg,
+                        fat32_msg);
+
+               switch (status) {
+               case PED_EXCEPTION_YES:
+                       *out_fat_type = FAT_TYPE_FAT32;
+                       return 1;
+
+               case PED_EXCEPTION_NO:
+                       *out_fat_type = FAT_TYPE_FAT16;
+                       return 1;
+
+               case PED_EXCEPTION_UNHANDLED:
+                       *out_fat_type = fs_info->fat_type;
+                       return 1;
+
+               case PED_EXCEPTION_CANCEL:
+                       return 0;
+
+                default:
+                        PED_ASSERT (0);
+                        break;
+               }
+       }
+
+       if (fat16_ok) {
+               if (fs_info->fat_type != FAT_TYPE_FAT16) {
+                       status = ped_exception_throw (
+                               PED_EXCEPTION_WARNING,
+                               PED_EXCEPTION_OK_CANCEL,
+                               _("%s  %s"),
+                               _("The file system can only be resized to this "
+                                 "size by converting to FAT16."),
+                               fat16_msg);
+                       if (status == PED_EXCEPTION_CANCEL)
+                               return 0;
+               }
+               *out_fat_type = FAT_TYPE_FAT16;
+               return 1;
+       }
+
+       if (fat32_ok) {
+               if (fs_info->fat_type != FAT_TYPE_FAT32) {
+                       status = ped_exception_throw (
+                               PED_EXCEPTION_WARNING,
+                               PED_EXCEPTION_OK_CANCEL,
+                               _("%s  %s"),
+                               _("The file system can only be resized to this "
+                                 "size by converting to FAT32."),
+                               fat32_msg);
+                       if (status == PED_EXCEPTION_CANCEL)
+                               return 0;
+               }
+               *out_fat_type = FAT_TYPE_FAT32;
+               return 1;
+       }
+
+       ped_exception_throw (
+               PED_EXCEPTION_NO_FEATURE,
+               PED_EXCEPTION_CANCEL,
+               _("GNU Parted cannot resize this partition to this size.  "
+                 "We're working on it!"));
+
+       return 0;
+}
+
+/*  For resize operations: determine if the file system must be FAT16 or FAT32,
+ *  or either.  If the new file system must be FAT32, then query for
+ *  confirmation.  If either file system can be used, query for which one.
+ */
+static int
+get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom,
+             FatType* out_fat_type)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       PedSector               fat16_cluster_sectors;
+       PedSector               fat32_cluster_sectors;
+       FatCluster              dummy_cluster_count;
+       PedSector               dummy_fat_sectors;
+       int                     fat16_ok;
+       int                     fat32_ok;
+
+       fat16_ok = fat_calc_resize_sizes (
+                                   new_geom,
+                                   fs_info->cluster_sectors,
+                                   FAT_TYPE_FAT16,
+                                   fs_info->root_dir_sector_count,
+                                   fs_info->cluster_sectors,
+                                   &fat16_cluster_sectors,
+                                   &dummy_cluster_count,
+                                   &dummy_fat_sectors);
+
+       fat32_ok = fat_calc_resize_sizes (
+                                   new_geom,
+                                   fs_info->cluster_sectors,
+                                   FAT_TYPE_FAT32,
+                                   fs_info->root_dir_sector_count,
+                                   fs_info->cluster_sectors,
+                                   &fat32_cluster_sectors,
+                                   &dummy_cluster_count,
+                                   &dummy_fat_sectors);
+
+       return ask_type (fs, fat16_ok, fat32_ok, out_fat_type);
+}
+
+/*  Creates the PedFileSystem struct for the new resized file system, and
+    sticks it in a FatOpContext.  At the end of the process, the original
+    (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs).
+ */
+static FatOpContext*
+create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatSpecific*    new_fs_info;
+       PedFileSystem*  new_fs;
+       PedSector       new_cluster_sectors;
+       FatCluster      new_cluster_count;
+       PedSector       new_fat_sectors;
+       FatType         new_fat_type;
+       PedSector       root_dir_sector_count;
+       FatOpContext*   context;
+
+       /* hypothetical number of root dir sectors, if we end up using
+        * FAT16
+        */
+       if (fs_info->root_dir_sector_count)
+               root_dir_sector_count = fs_info->root_dir_sector_count;
+       else
+               root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT
+                                               * sizeof (FatDirEntry) / 512;
+
+       if (!get_fat_type (fs, new_geom, &new_fat_type))
+               return 0;
+
+       fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type,
+               root_dir_sector_count, fs_info->cluster_sectors,
+               &new_cluster_sectors, &new_cluster_count, &new_fat_sectors);
+
+       if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors,
+                                       new_cluster_count))
+               goto error;
+
+       new_fs = fat_alloc (new_geom);
+       if (!new_fs)
+               goto error;
+
+       new_fs_info = FAT_SPECIFIC (new_fs);
+       if (!new_fs_info)
+               goto error_free_new_fs;
+
+/* preserve boot code, etc. */
+       memcpy (&new_fs_info->boot_sector, &fs_info->boot_sector,
+               sizeof (FatBootSector));
+       memcpy (&new_fs_info->info_sector, &fs_info->info_sector,
+               sizeof (FatInfoSector));
+
+       new_fs_info->logical_sector_size = fs_info->logical_sector_size;
+       new_fs_info->sector_count = new_geom->length;
+
+       new_fs_info->sectors_per_track = fs_info->sectors_per_track;
+       new_fs_info->heads = fs_info->heads;
+
+       new_fs_info->cluster_size = new_cluster_sectors * 512;
+       new_fs_info->cluster_sectors = new_cluster_sectors;
+       new_fs_info->cluster_count = new_cluster_count;
+       new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster;
+
+       new_fs_info->fat_type = new_fat_type;
+       new_fs_info->fat_table_count = 2;
+       new_fs_info->fat_sectors = new_fat_sectors;
+
+       /* what about copying? */
+       new_fs_info->serial_number = fs_info->serial_number;
+
+       if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
+               new_fs_info->info_sector_offset = 1;
+               new_fs_info->boot_sector_backup_offset = 6;
+
+               new_fs_info->root_dir_offset = 0;
+               new_fs_info->root_dir_entry_count = 0;
+               new_fs_info->root_dir_sector_count = 0;
+
+               /* we add calc_align_sectors to push the cluster_offset
+                  forward, to keep the clusters aligned between the new
+                  and old file systems
+                */
+               new_fs_info->fat_offset
+                       = fat_min_reserved_sector_count (FAT_TYPE_FAT32)
+                         + fat_calc_align_sectors (new_fs, fs);
+
+               new_fs_info->cluster_offset
+                       = new_fs_info->fat_offset
+                         + 2 * new_fs_info->fat_sectors;
+       } else {
+               new_fs_info->root_dir_sector_count = root_dir_sector_count;
+               new_fs_info->root_dir_entry_count
+                       = root_dir_sector_count * 512 / sizeof (FatDirEntry);
+
+               new_fs_info->fat_offset
+                       = fat_min_reserved_sector_count (FAT_TYPE_FAT16)
+                         + fat_calc_align_sectors (new_fs, fs);
+
+               new_fs_info->root_dir_offset = new_fs_info->fat_offset
+                                              + 2 * new_fs_info->fat_sectors;
+
+               new_fs_info->cluster_offset = new_fs_info->root_dir_offset
+                                         + new_fs_info->root_dir_sector_count;
+       }
+
+       new_fs_info->total_dir_clusters = fs_info->total_dir_clusters;
+
+       context = fat_op_context_new (new_fs, fs);
+       if (!context)
+               goto error_free_new_fs_info;
+
+       if (!fat_op_context_create_initial_fat (context))
+               goto error_free_context;
+
+       if (!fat_alloc_buffers (new_fs))
+               goto error_free_fat;
+
+       return context;
+
+error_free_fat:
+       fat_table_destroy (new_fs_info->fat);
+error_free_context:
+       free (context);
+error_free_new_fs_info:
+       free (new_fs_info);
+error_free_new_fs:
+       free (new_fs);
+error:
+       return NULL;
+}
+
+static int
+resize_context_assimilate (FatOpContext* ctx)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+
+       fat_free_buffers (ctx->old_fs);
+       fat_table_destroy (old_fs_info->fat);
+       free (old_fs_info);
+       ped_geometry_destroy (ctx->old_fs->geom);
+
+       ctx->old_fs->type_specific = ctx->new_fs->type_specific;
+       ctx->old_fs->geom = ctx->new_fs->geom;
+       ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16)
+                               ? &fat16_type
+                               : &fat32_type;
+
+       free (ctx->new_fs);
+
+       fat_op_context_destroy (ctx);
+
+       return 1;
+}
+
+static int
+resize_context_abort (FatOpContext* ctx)
+{
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+
+       fat_free_buffers (ctx->new_fs);
+       fat_table_destroy (new_fs_info->fat);
+       free (new_fs_info);
+       ped_geometry_destroy (ctx->new_fs->geom);
+       free (ctx->new_fs);
+
+       fat_op_context_destroy (ctx);
+
+       return 1;
+}
+
+/* copies the "hidden" sectors, between the boot sector and the FAT.  Required,
+ * for the Windows 98 FAT32 boot loader
+ */
+int
+_copy_hidden_sectors (FatOpContext* ctx)
+{
+       FatSpecific*    old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+       FatSpecific*    new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+       PedSector       first = 1;
+       PedSector       last;
+       PedSector       count;
+
+       /* nothing to copy for FAT16 */
+       if (old_fs_info->fat_type == FAT_TYPE_FAT16
+                       || new_fs_info->fat_type == FAT_TYPE_FAT16)
+               return 1;
+
+       last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1;
+       count = last - first + 1;
+
+       PED_ASSERT (count < BUFFER_SIZE);
+
+       if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
+                               first, count))
+               return 0;
+       if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
+                                first, count))
+               return 0;
+       return 1;
+}
+
+int
+fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatSpecific*    new_fs_info;
+       FatOpContext*   ctx;
+       PedFileSystem*  new_fs;
+
+       ctx = create_resize_context (fs, geom);
+       if (!ctx)
+               goto error;
+       new_fs = ctx->new_fs;
+       new_fs_info = FAT_SPECIFIC (new_fs);
+
+       if (!fat_duplicate_clusters (ctx, timer))
+               goto error_abort_ctx;
+       if (fs_info->fat_type == FAT_TYPE_FAT16
+                       && new_fs_info->fat_type == FAT_TYPE_FAT32) {
+               if (!alloc_root_dir (ctx))
+                       goto error_abort_ctx;
+       }
+       if (!fat_construct_new_fat (ctx))
+               goto error_abort_ctx;
+       if (fs_info->fat_type == FAT_TYPE_FAT32
+                       && new_fs_info->fat_type == FAT_TYPE_FAT16) {
+               if (!free_root_dir (ctx))
+                       goto error_abort_ctx;
+       }
+       if (!fat_construct_dir_tree (ctx))
+               goto error_abort_ctx;
+       if (!fat_table_write_all (new_fs_info->fat, new_fs))
+               goto error_abort_ctx;
+
+       _copy_hidden_sectors (ctx);
+       fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs);
+       fat_boot_sector_write (&new_fs_info->boot_sector, new_fs);
+       if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
+               fat_info_sector_generate (&new_fs_info->info_sector, new_fs);
+               fat_info_sector_write (&new_fs_info->info_sector, new_fs);
+       }
+
+       if (!resize_context_assimilate (ctx))
+               goto error;
+
+       return 1;
+
+error_abort_ctx:
+       resize_context_abort (ctx);
+error:
+       return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/table.c b/libparted/fs/r/fat/table.c
new file mode 100644
index 0000000..b77e372
--- /dev/null
+++ b/libparted/fs/r/fat/table.c
@@ -0,0 +1,480 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007-2011 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/>.
+*/
+
+#include <config.h>
+#include <parted/endian.h>
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+FatTable*
+fat_table_new (FatType fat_type, FatCluster size)
+{
+       FatTable*       ft;
+       int             entry_size = fat_table_entry_size (fat_type);
+
+       ft = (FatTable*) ped_malloc (sizeof (FatTable));
+       if (!ft) return NULL;
+
+       ft->cluster_count = ft->free_cluster_count = size - 2;
+
+/* ensure there's some free room on the end, to finish off the sector */
+       ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size;
+       ft->fat_type = fat_type;
+       ft->raw_size = ft->size * entry_size;
+
+       ft->table = ped_malloc (ft->raw_size);
+       if (!ft->table) {
+               free (ft);
+               return NULL;
+       }
+
+       fat_table_clear (ft);
+       return ft;
+}
+
+void
+fat_table_destroy (FatTable* ft)
+{
+       free (ft->table);
+       free (ft);
+}
+
+FatTable*
+fat_table_duplicate (const FatTable* ft)
+{
+       FatTable*       dup_ft;
+
+       dup_ft = fat_table_new (ft->fat_type, ft->size);
+       if (!dup_ft) return NULL;
+
+       dup_ft->cluster_count   = ft->cluster_count;
+       dup_ft->free_cluster_count      = ft->free_cluster_count;
+       dup_ft->bad_cluster_count       = ft->bad_cluster_count;
+       dup_ft->last_alloc              = ft->last_alloc;
+
+       memcpy (dup_ft->table, ft->table, ft->raw_size);
+
+       return dup_ft;
+}
+
+void
+fat_table_clear (FatTable* ft)
+{
+       memset (ft->table, 0, ft->raw_size);
+
+       fat_table_set (ft, 0, 0x0ffffff8);
+       fat_table_set (ft, 1, 0x0fffffff);
+
+       ft->free_cluster_count = ft->cluster_count;
+       ft->bad_cluster_count = 0;
+       ft->last_alloc = 1;
+}
+
+int
+fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count)
+{
+       PED_ASSERT (new_cluster_count + 2 <= ft->size);
+
+       ft->cluster_count = new_cluster_count;
+       return fat_table_count_stats (ft);
+}
+
+int
+fat_table_count_stats (FatTable* ft)
+{
+       FatCluster      i;
+
+       PED_ASSERT (ft->cluster_count + 2 <= ft->size);
+
+       ft->free_cluster_count = 0;
+       ft->bad_cluster_count = 0;
+
+       for (i=2; i < ft->cluster_count + 2; i++) {
+               if (fat_table_is_available (ft, i))
+                       ft->free_cluster_count++;
+               if (fat_table_is_bad (ft, i))
+                       ft->bad_cluster_count++;
+       }
+       return 1;
+}
+
+int
+fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512);
+
+       memset (ft->table, 0, ft->raw_size);
+
+        if (!ped_geometry_read (fs->geom, (void *) ft->table,
+                               fs_info->fat_offset
+                                       + table_num * fs_info->fat_sectors,
+                               fs_info->fat_sectors))
+               return 0;
+
+        if ( *((unsigned char*) ft->table) != fs_info->boot_sector.media) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("FAT %d media %x doesn't match the boot sector's "
+                         "media %x.  You should probably run scandisk."),
+                       (int) table_num + 1,
+                       (int) *((unsigned char*) ft->table),
+                       (int) fs_info->boot_sector.media)
+                               != PED_EXCEPTION_IGNORE)
+                       return 0;
+        }
+
+       ft->cluster_count = fs_info->cluster_count;
+
+       fat_table_count_stats (ft);
+
+       return 1;
+}
+
+int
+fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+
+       PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512);
+
+        if (!ped_geometry_write (fs->geom, (void *) ft->table,
+                                fs_info->fat_offset
+                                       + table_num * fs_info->fat_sectors,
+                                fs_info->fat_sectors))
+               return 0;
+       if (!ped_geometry_sync (fs->geom))
+               return 0;
+
+       return 1;
+}
+
+int
+fat_table_write_all (const FatTable* ft, PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       int             i;
+
+       for (i = 0; i < fs_info->fat_table_count; i++) {
+               if (!fat_table_write (ft, fs, i))
+                       return 0;
+       }
+
+       return 1;
+}
+
+int
+fat_table_compare (const FatTable* a, const FatTable* b)
+{
+       FatCluster      i;
+
+       if (a->cluster_count != b->cluster_count)
+               return 0;
+
+       for (i = 0; i < a->cluster_count + 2; i++) {
+               if (fat_table_get (a, i) != fat_table_get (b, i))
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int
+_test_code_available (const FatTable* ft, FatCluster code)
+{
+       return code == 0;
+}
+
+static int
+_test_code_bad (const FatTable* ft, FatCluster code)
+{
+       switch (ft->fat_type) {
+                case FAT_TYPE_FAT12:
+                if (code == 0xff7) return 1;
+                break;
+
+               case FAT_TYPE_FAT16:
+               if (code == 0xfff7) return 1;
+               break;
+
+               case FAT_TYPE_FAT32:
+               if (code == 0x0ffffff7) return 1;
+               break;
+       }
+       return 0;
+}
+
+static int
+_test_code_eof (const FatTable* ft, FatCluster code)
+{
+       switch (ft->fat_type) {
+                case FAT_TYPE_FAT12:
+                if (code >= 0xff7) return 1;
+                break;
+
+               case FAT_TYPE_FAT16:
+               if (code >= 0xfff7) return 1;
+               break;
+
+               case FAT_TYPE_FAT32:
+               if (code >= 0x0ffffff7) return 1;
+               break;
+       }
+       return 0;
+}
+
+void
+_update_stats (FatTable* ft, FatCluster cluster, FatCluster value)
+{
+       if (_test_code_available (ft, value)
+           && !fat_table_is_available (ft, cluster)) {
+               ft->free_cluster_count++;
+               if (fat_table_is_bad (ft, cluster))
+                       ft->bad_cluster_count--;
+       }
+
+       if (!_test_code_available (ft, value)
+           && fat_table_is_available (ft, cluster)) {
+               ft->free_cluster_count--;
+               if (_test_code_bad (ft, cluster))
+                       ft->bad_cluster_count--;
+       }
+}
+
+int
+fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value)
+{
+       if (cluster >= ft->cluster_count + 2) {
+               ped_exception_throw (PED_EXCEPTION_BUG,
+                                    PED_EXCEPTION_CANCEL,
+                                    _("fat_table_set: cluster %ld outside "
+                                      "file system"),
+                                    (long) cluster);
+               return 0;
+       }
+
+       _update_stats (ft, cluster, value);
+
+       switch (ft->fat_type) {
+                case FAT_TYPE_FAT12:
+                PED_ASSERT (0);
+                break;
+
+               case FAT_TYPE_FAT16:
+               ((unsigned short *) ft->table) [cluster]
+                       = PED_CPU_TO_LE16 (value);
+               break;
+
+               case FAT_TYPE_FAT32:
+               ((unsigned int *) ft->table) [cluster]
+                       = PED_CPU_TO_LE32 (value);
+               break;
+       }
+       return 1;
+}
+
+FatCluster
+fat_table_get (const FatTable* ft, FatCluster cluster)
+{
+       if (cluster >= ft->cluster_count + 2) {
+               ped_exception_throw (PED_EXCEPTION_BUG,
+                                    PED_EXCEPTION_CANCEL,
+                                    _("fat_table_get: cluster %ld outside "
+                                      "file system"),
+                                    (long) cluster);
+               exit (EXIT_FAILURE);    /* FIXME */
+       }
+
+       switch (ft->fat_type) {
+                case FAT_TYPE_FAT12:
+                PED_ASSERT (0);
+                break;
+
+               case FAT_TYPE_FAT16:
+               return PED_LE16_TO_CPU
+                       (((unsigned short *) ft->table) [cluster]);
+
+               case FAT_TYPE_FAT32:
+               return PED_LE32_TO_CPU
+                       (((unsigned int *) ft->table) [cluster]);
+       }
+
+       return 0;
+}
+
+FatCluster
+fat_table_alloc_cluster (FatTable* ft)
+{
+       FatCluster      i;
+       FatCluster      cluster;
+
+/* hack: assumes the first two FAT entries are marked as used (which they
+ * always should be)
+ */
+       for (i=1; i < ft->cluster_count + 1; i++) {
+               cluster = (i + ft->last_alloc) % ft->cluster_count;
+               if (fat_table_is_available (ft, cluster)) {
+                       ft->last_alloc = cluster;
+                       return cluster;
+               }
+       }
+
+       ped_exception_throw (PED_EXCEPTION_ERROR,
+                            PED_EXCEPTION_CANCEL,
+                            _("fat_table_alloc_cluster: no free clusters"));
+       return 0;
+}
+
+FatCluster
+fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      result;
+
+       while (1) {
+               result = fat_table_alloc_cluster (ft);
+               if (!result)
+                       return 0;
+               if (fat_read_cluster (fs, fs_info->buffer, result))
+                       return result;
+               fat_table_set_bad (ft, result);
+       }
+}
+
+/*
+    returns true if <cluster> is marked as bad
+*/
+int
+fat_table_is_bad (const FatTable* ft, FatCluster cluster)
+{
+       return _test_code_bad (ft, fat_table_get (ft, cluster));
+}
+
+/*
+    returns true if <cluster> represents an EOF marker
+*/
+int
+fat_table_is_eof (const FatTable* ft, FatCluster cluster)
+{
+       return _test_code_eof (ft, cluster);
+}
+
+/*
+    returns true if <cluster> is available.
+*/
+int
+fat_table_is_available (const FatTable* ft, FatCluster cluster)
+{
+       return _test_code_available (ft, fat_table_get (ft, cluster));
+}
+
+/*
+    returns true if <cluster> is empty.  Note that this includes bad clusters.
+*/
+int
+fat_table_is_empty (const FatTable* ft, FatCluster cluster)
+{
+       return fat_table_is_available (ft, cluster)
+               || fat_table_is_bad (ft, cluster);
+}
+
+/*
+    returns true if <cluster> is being used for something constructive.
+*/
+int
+fat_table_is_active (const FatTable* ft, FatCluster cluster)
+{
+       return !fat_table_is_bad (ft, cluster)
+               && !fat_table_is_available (ft, cluster);
+}
+
+/*
+    marks <cluster> as the last cluster in the chain
+*/
+int
+fat_table_set_eof (FatTable* ft, FatCluster cluster)
+{
+
+       switch (ft->fat_type) {
+                case FAT_TYPE_FAT12:
+                PED_ASSERT (0);
+                break;
+
+               case FAT_TYPE_FAT16:
+               return fat_table_set (ft, cluster, 0xfff8);
+
+               case FAT_TYPE_FAT32:
+               return fat_table_set (ft, cluster, 0x0fffffff);
+       }
+
+       return 0;
+}
+
+/*
+       Marks a clusters as unusable, due to physical disk damage.
+*/
+int
+fat_table_set_bad (FatTable* ft, FatCluster cluster)
+{
+       if (!fat_table_is_bad (ft, cluster))
+               ft->bad_cluster_count++;
+
+       switch (ft->fat_type) {
+                case FAT_TYPE_FAT12:
+               return fat_table_set (ft, cluster, 0xff7);
+
+               case FAT_TYPE_FAT16:
+               return fat_table_set (ft, cluster, 0xfff7);
+
+               case FAT_TYPE_FAT32:
+               return fat_table_set (ft, cluster, 0x0ffffff7);
+       }
+
+       return 0;
+}
+
+/*
+    marks <cluster> as unused/free/available
+*/
+int
+fat_table_set_avail (FatTable* ft, FatCluster cluster)
+{
+       return fat_table_set (ft, cluster, 0);
+}
+
+#endif /* !DISCOVER_ONLY */
+
+int
+fat_table_entry_size (FatType fat_type)
+{
+       switch (fat_type) {
+               case FAT_TYPE_FAT12:
+               return 2;               /* FIXME: how? */
+
+               case FAT_TYPE_FAT16:
+               return 2;
+
+               case FAT_TYPE_FAT32:
+               return 4;
+       }
+
+       return 0;
+}
diff --git a/libparted/fs/r/fat/table.h b/libparted/fs/r/fat/table.h
new file mode 100644
index 0000000..e25d82f
--- /dev/null
+++ b/libparted/fs/r/fat/table.h
@@ -0,0 +1,73 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007, 2009-2011 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/>.
+*/
+
+#ifndef PED_FAT_TABLE_H_INCLUDED
+#define PED_FAT_TABLE_H_INCLUDED
+
+typedef struct _FatTable       FatTable;
+
+#include "fat.h"
+
+struct _FatTable {
+       void*           table;
+       FatCluster      size;
+       int             raw_size;
+
+       FatType         fat_type;
+       FatCluster      cluster_count;
+       FatCluster      free_cluster_count;
+       FatCluster      bad_cluster_count;
+
+       FatCluster      last_alloc;
+};
+
+extern FatTable* fat_table_new (FatType fat_type, FatCluster size);
+extern FatTable* fat_table_duplicate (const FatTable* ft);
+extern void fat_table_destroy (FatTable* ft);
+extern void fat_table_clear (FatTable* ft);
+extern int fat_table_set_cluster_count (FatTable* ft,
+                                       FatCluster new_cluster_count);
+
+extern int fat_table_read (FatTable* ft, const PedFileSystem* fs,
+                          int table_num);
+extern int fat_table_write (const FatTable* ft, PedFileSystem* fs,
+                           int table_num);
+extern int fat_table_write_all (const FatTable* ft, PedFileSystem* fs);
+extern int fat_table_compare (const FatTable* a, const FatTable* b);
+extern int fat_table_count_stats (FatTable* ft);
+
+extern FatCluster fat_table_get (const FatTable* ft, FatCluster cluster);
+extern int fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value);
+
+extern FatCluster fat_table_alloc_cluster (FatTable* ft);
+extern FatCluster fat_table_alloc_check_cluster (FatTable* ft,
+                                                PedFileSystem* fs);
+
+extern int fat_table_is_bad (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_eof (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_empty (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_available (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_active (const FatTable* ft, FatCluster cluster);
+
+extern int fat_table_set_eof (FatTable* ft, FatCluster cluster);
+extern int fat_table_set_avail (FatTable* ft, FatCluster cluster);
+extern int fat_table_set_bad (FatTable* ft, FatCluster cluster);
+
+extern int fat_table_entry_size (FatType fat_type);
+
+#endif /* PED_FAT_TABLE_H_INCLUDED */
diff --git a/libparted/fs/r/fat/traverse.c b/libparted/fs/r/fat/traverse.c
new file mode 100644
index 0000000..7cecfe3
--- /dev/null
+++ b/libparted/fs/r/fat/traverse.c
@@ -0,0 +1,367 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2005, 2007-2011 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/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+#define NO_CLUSTER -1
+
+static char tmp_buffer [4096];
+
+int
+fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info)
+{
+       return trav_info->buffer_size / sizeof (FatDirEntry);
+}
+
+/* returns 1 if there are no more directory entries in the directory being
+ * traversed, 0 otherwise.
+ */
+static int
+is_last_buffer (FatTraverseInfo* trav_info) {
+       FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
+
+       if (trav_info->is_legacy_root_dir)
+               return 1;
+       else
+               return fat_table_is_eof (fs_info->fat, trav_info->next_buffer);
+}
+
+static int
+write_root_dir (FatTraverseInfo* trav_info)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
+
+       if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries,
+                                fs_info->root_dir_offset,
+                                fs_info->root_dir_sector_count))
+               return 0;
+       if (!ped_geometry_sync (trav_info->fs->geom))
+               return 0;
+       trav_info->dirty = 0;
+       return 1;
+}
+
+static int
+write_dir_cluster (FatTraverseInfo* trav_info)
+{
+       if (!fat_write_sync_cluster (trav_info->fs,
+                                    (void*) trav_info->dir_entries,
+                                    trav_info->this_buffer))
+               return 0;
+       trav_info->dirty = 0;
+       return 1;
+}
+
+static int
+write_dir_buffer (FatTraverseInfo* trav_info)
+{
+       if (trav_info->is_legacy_root_dir)
+               return write_root_dir (trav_info);
+       else
+               return write_dir_cluster (trav_info);
+}
+
+static int
+read_next_dir_buffer (FatTraverseInfo* trav_info)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
+
+       PED_ASSERT (!trav_info->is_legacy_root_dir);
+
+       trav_info->this_buffer = trav_info->next_buffer;
+
+       if (trav_info->this_buffer < 2
+           || trav_info->this_buffer >= fs_info->cluster_count + 2) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       "Cluster %ld in directory %s is outside file system!",
+                       (long) trav_info->this_buffer,
+                       trav_info->dir_name);
+               return 0;
+       }
+
+       trav_info->next_buffer
+               = fat_table_get (fs_info->fat, trav_info->this_buffer);
+
+       return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries,
+                                trav_info->this_buffer);
+}
+
+/* FIXME: put into fat_dir_entry_* operations */
+void
+fat_traverse_mark_dirty (FatTraverseInfo* trav_info)
+{
+       trav_info->dirty = 1;
+}
+
+FatTraverseInfo*
+fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster,
+                   const char* dir_name)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+       FatTraverseInfo*        trav_info;
+
+       trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo));
+       if (!trav_info)
+               goto error;
+
+       trav_info->dir_name = strdup (dir_name);
+       if (!trav_info->dir_name)
+               goto error_free_trav_info;
+
+       trav_info->fs = fs;
+       trav_info->is_legacy_root_dir
+               = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0);
+       trav_info->dirty = 0;
+       trav_info->eof = 0;
+       trav_info->current_entry = -1;
+
+       if (trav_info->is_legacy_root_dir) {
+               trav_info->buffer_size = 512 * fs_info->root_dir_sector_count;
+       } else {
+               trav_info->next_buffer = start_cluster;
+               trav_info->buffer_size = fs_info->cluster_size;
+       }
+
+       trav_info->dir_entries
+               = (FatDirEntry*) ped_malloc (trav_info->buffer_size);
+       if (!trav_info->dir_entries)
+               goto error_free_dir_name;
+
+       if (trav_info->is_legacy_root_dir) {
+               if (!ped_geometry_read (fs->geom, trav_info->dir_entries,
+                                       fs_info->root_dir_offset,
+                                       fs_info->root_dir_sector_count))
+                       goto error_free_dir_entries;
+       } else {
+               if (!read_next_dir_buffer (trav_info))
+                       goto error_free_dir_entries;
+       }
+
+       return trav_info;
+
+error_free_dir_entries:
+       free (trav_info->dir_entries);
+error_free_dir_name:
+       free (trav_info->dir_name);
+error_free_trav_info:
+       free (trav_info);
+error:
+       return NULL;
+}
+
+int
+fat_traverse_complete (FatTraverseInfo* trav_info)
+{
+       if (trav_info->dirty) {
+               if (!write_dir_buffer (trav_info))
+                       return 0;
+       }
+       free (trav_info->dir_entries);
+       free (trav_info->dir_name);
+       free (trav_info);
+       return 1;
+}
+
+FatTraverseInfo*
+fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent)
+{
+       strcpy (tmp_buffer, trav_info->dir_name);
+       fat_dir_entry_get_name (parent,
+                               tmp_buffer + strlen (trav_info->dir_name));
+       strcat (tmp_buffer, "\\");
+
+       return fat_traverse_begin (trav_info->fs,
+                       fat_dir_entry_get_first_cluster (parent, trav_info->fs),
+                       tmp_buffer);
+}
+
+FatDirEntry*
+fat_traverse_next_dir_entry (FatTraverseInfo *trav_info)
+{
+       if (trav_info->eof)
+               return NULL;
+
+       trav_info->current_entry++;
+       if (trav_info->current_entry
+                       >= fat_traverse_entries_per_buffer (trav_info)) {
+               if (trav_info->dirty) {
+                       if (!write_dir_buffer (trav_info))
+                               return NULL;
+               }
+
+               trav_info->current_entry = 0;
+               if (is_last_buffer (trav_info)) {
+                       trav_info->eof = 1;
+                       return NULL;
+               }
+               if (!read_next_dir_buffer (trav_info))
+                       return NULL;
+       }
+       return trav_info->dir_entries + trav_info->current_entry;
+}
+
+FatCluster
+fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+
+       switch (fs_info->fat_type) {
+       case FAT_TYPE_FAT12:
+       case FAT_TYPE_FAT16:
+               return PED_LE16_TO_CPU (dir_entry->first_cluster);
+
+        case FAT_TYPE_FAT32:
+               return PED_LE16_TO_CPU (dir_entry->first_cluster_high)
+                               * 65536L
+                         + PED_LE16_TO_CPU (dir_entry->first_cluster);
+       }
+
+       return 0;
+}
+
+void
+fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs,
+                                FatCluster cluster)
+{
+       FatSpecific*            fs_info = FAT_SPECIFIC (fs);
+
+       switch (fs_info->fat_type) {
+                case FAT_TYPE_FAT12:
+                PED_ASSERT (0);
+                break;
+
+               case FAT_TYPE_FAT16:
+               dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster);
+               break;
+
+               case FAT_TYPE_FAT32:
+               dir_entry->first_cluster
+                       = PED_CPU_TO_LE16 (cluster & 0xffff);
+               dir_entry->first_cluster_high
+                       = PED_CPU_TO_LE16 (cluster / 0x10000);
+               break;
+       }
+}
+
+uint32_t
+fat_dir_entry_get_length (FatDirEntry* dir_entry)
+{
+       return PED_LE32_TO_CPU (dir_entry->length);
+}
+
+int
+fat_dir_entry_is_null_term (const FatDirEntry* dir_entry)
+{
+       FatDirEntry     null_entry;
+
+       memset (&null_entry, 0, sizeof (null_entry));
+       return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0;
+}
+
+int
+fat_dir_entry_is_active (FatDirEntry* dir_entry)
+{
+       if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0;
+       if ((unsigned char) dir_entry->name[0] == 0) return 0;
+       if ((unsigned char) dir_entry->name[0] == 0xF6) return 0;
+       return 1;
+}
+
+int
+fat_dir_entry_is_file (FatDirEntry* dir_entry) {
+       if (dir_entry->attributes == VFAT_ATTR) return 0;
+       if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
+       if (!fat_dir_entry_is_active (dir_entry)) return 0;
+       if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 
0;
+       return 1;
+}
+
+int
+fat_dir_entry_is_system_file (FatDirEntry* dir_entry)
+{
+       if (!fat_dir_entry_is_file (dir_entry)) return 0;
+       return (dir_entry->attributes & SYSTEM_ATTR)
+               || (dir_entry->attributes & HIDDEN_ATTR);
+}
+
+int
+fat_dir_entry_is_directory (FatDirEntry* dir_entry)
+{
+       if (dir_entry->attributes == VFAT_ATTR) return 0;
+       if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
+       if (!fat_dir_entry_is_active (dir_entry)) return 0;
+       return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR;
+}
+
+int
+fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs)
+{
+       FatSpecific*    fs_info = FAT_SPECIFIC (fs);
+       FatCluster      first_cluster;
+
+       if (!fat_dir_entry_is_file (dir_entry)
+               && !fat_dir_entry_is_directory (dir_entry))
+               return 0;
+
+       first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs);
+       if (first_cluster == 0
+               || fat_table_is_eof (fs_info->fat, first_cluster))
+               return 0;
+
+       return 1;
+}
+
+/*
+    decrypts silly DOS names to FILENAME.EXT
+*/
+void
+fat_dir_entry_get_name (const FatDirEntry *dir_entry, char *result) {
+       size_t i;
+       const char *src;
+       const char *ext;
+
+       src = dir_entry->name;
+
+       for (i=0; i < sizeof dir_entry->name; i++) {
+               if (src[i] == ' ' || src[i] == 0) break;
+               *result++ = src[i];
+       }
+
+       ext = (const char *) dir_entry->extension;
+       if (ext[0] != ' ' && ext[0] != 0) {
+               *result++ = '.';
+               for (i=0; i < sizeof dir_entry->extension; i++) {
+                       if (ext[i] == ' ' || ext[i] == 0) break;
+                       *result++ = ext[i];
+               }
+       }
+
+       *result = 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/traverse.h b/libparted/fs/r/fat/traverse.h
new file mode 100644
index 0000000..1ad3c37
--- /dev/null
+++ b/libparted/fs/r/fat/traverse.h
@@ -0,0 +1,74 @@
+/*
+    libparted
+    Copyright (C) 1998-2000, 2007-2011 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/>.
+*/
+
+#ifndef TRAVERSE_H_INCLUDED
+#define TRAVERSE_H_INCLUDED
+
+#include "fatio.h"
+
+typedef struct _FatTraverseInfo                FatTraverseInfo;
+
+struct _FatTraverseInfo {
+       PedFileSystem*          fs;
+       char*                   dir_name;
+
+       int                     is_legacy_root_dir;
+       int                     dirty;
+       int                     eof;
+
+       FatDirEntry*            dir_entries;
+       int                     current_entry;
+       FatCluster              this_buffer, next_buffer;
+       int                     buffer_size;
+};
+
+extern int fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info);
+
+/* starts traversal at an arbitary cluster.  if start_cluster==0, then uses
+   root directory */
+extern FatTraverseInfo* fat_traverse_begin (PedFileSystem* fs,
+                                           FatCluster start_cluster,
+                                           const char* dir_name);
+
+extern int fat_traverse_complete (FatTraverseInfo* trav_info);
+
+extern FatTraverseInfo* fat_traverse_directory (FatTraverseInfo* trav_info,
+                                               FatDirEntry* parent);
+
+extern void fat_traverse_mark_dirty (FatTraverseInfo* trav_info);
+
+extern FatDirEntry* fat_traverse_next_dir_entry (FatTraverseInfo* trav_info);
+
+extern FatCluster fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry,
+                                                  PedFileSystem* fs);
+
+extern void fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry,
+                                       PedFileSystem* fs, FatCluster cluster);
+
+extern uint32_t fat_dir_entry_get_length (FatDirEntry* dir_entry);
+
+extern int fat_dir_entry_is_null_term (const FatDirEntry* dir_entry);
+extern int fat_dir_entry_is_file (FatDirEntry* dir_entry);
+extern int fat_dir_entry_is_system_file (FatDirEntry* dir_entry);
+extern int fat_dir_entry_is_directory (FatDirEntry* dir_entry);
+extern void fat_dir_entry_get_name (const FatDirEntry* dir_entry, char* 
result);
+extern int fat_dir_entry_is_active (FatDirEntry* dir_entry);
+extern int fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry,
+                                           PedFileSystem* fs);
+
+#endif /* TRAVERSE_H_INCLUDED */
diff --git a/libparted/fs/r/filesys.c b/libparted/fs/r/filesys.c
new file mode 100644
index 0000000..1bddc1a
--- /dev/null
+++ b/libparted/fs/r/filesys.c
@@ -0,0 +1,164 @@
+/* libparted - a library for manipulating disk partitions
+    Copyright (C) 1999-2001, 2007-2011 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/>.
+*/
+
+/** \file filesys.c */
+
+/**
+ * \addtogroup PedFileSystem
+ *
+ * \note File systems exist on a PedGeometry - NOT a PedPartition.
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/**
+ * This function opens the file system stored on \p geom, if it
+ * can find one.
+ * It is often called in the following manner:
+ * \code
+ *     fs = ped_file_system_open (&part.geom)
+ * \endcode
+ *
+ * \throws PED_EXCEPTION_ERROR if file system could not be detected
+ * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume
+ * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on
+ *     \p geom is not implemented
+ *
+ * \return a PedFileSystem on success, \c NULL on failure.
+ */
+PedFileSystem *
+ped_file_system_open (PedGeometry* geom)
+{
+       PedFileSystem*          fs;
+       PedGeometry*            probed_geom;
+
+       PED_ASSERT (geom != NULL);
+
+       if (!ped_device_open (geom->dev))
+               goto error;
+
+       PedFileSystemType *type = ped_file_system_probe (geom);
+       if (!type) {
+               ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+                                    _("Could not detect file system."));
+               goto error_close_dev;
+       }
+
+       probed_geom = ped_file_system_probe_specific (type, geom);
+       if (!probed_geom)
+               goto error_close_dev;
+       if (!ped_geometry_test_inside (geom, probed_geom)) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("The file system is bigger than its volume!"))
+                               != PED_EXCEPTION_IGNORE)
+                       goto error_destroy_probed_geom;
+       }
+
+       if (!type->ops->open) {
+               ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+                                    PED_EXCEPTION_CANCEL,
+                                    _("Support for opening %s file systems "
+                                      "is not implemented yet."),
+                                    type->name);
+               goto error_destroy_probed_geom;
+       }
+
+       fs = type->ops->open (probed_geom);
+       if (!fs)
+               goto error_destroy_probed_geom;
+       ped_geometry_destroy (probed_geom);
+       return fs;
+
+error_destroy_probed_geom:
+       ped_geometry_destroy (probed_geom);
+error_close_dev:
+       ped_device_close (geom->dev);
+error:
+       return NULL;
+}
+
+/**
+ * Close file system \p fs.
+ *
+ * \return \c 1 on success, \c 0 on failure
+ */
+int
+ped_file_system_close (PedFileSystem* fs)
+{
+       PedDevice*      dev = fs->geom->dev;
+
+       PED_ASSERT (fs != NULL);
+
+       if (!fs->type->ops->close (fs))
+               goto error_close_dev;
+       ped_device_close (dev);
+       return 1;
+
+error_close_dev:
+       ped_device_close (dev);
+       return 0;
+}
+
+/**
+ * Resize \p fs to new geometry \p geom.
+ *
+ * \p geom should satisfy the ped_file_system_get_resize_constraint().
+ * (This isn't asserted, so it's not a bug not to... just it's likely
+ * to fail ;)  If \p timer is non-NULL, it is used as the progress meter.
+ *
+ * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs
+ *     is not implemented yet
+ *
+ * \return \c 0 on failure
+ */
+int
+ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+       PED_ASSERT (fs != NULL);
+       PED_ASSERT (geom != NULL);
+
+       if (!fs->type->ops->resize) {
+               ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+                                    PED_EXCEPTION_CANCEL,
+                                    _("Support for resizing %s file systems "
+                                      "is not implemented yet."),
+                                    fs->type->name);
+               return 0;
+       }
+       if (!fs->checked && fs->type->ops->check) {
+               if (!ped_file_system_check (fs, timer))
+                       return 0;
+       }
+       if (!ped_file_system_clobber_exclude (geom, fs->geom))
+               return 0;
+
+       return fs->type->ops->resize (fs, geom, timer);
+}
diff --git a/libparted/fs/r/hfs/DOC b/libparted/fs/r/hfs/DOC
new file mode 100644
index 0000000..9ee8b4d
--- /dev/null
+++ b/libparted/fs/r/hfs/DOC
@@ -0,0 +1,92 @@
+WARNING : Both HFS and HFS+ implementations of Linux 2.4 are buggy, at
+least when used before or after this implementation. Some workarounds
+are used in this implementation, but there can still be incompatibilities.
+Try Linux 2.6 if you want to play with HFS(+) resizing (though some bugs
+might also be there in 2.6, there is of course no warranty)
+
+---
+
+    Technical doc about Apple HFS and HFS+ file systems is available at :
+    * For HFS, section "Data Organization on Volumes",
+                       "Chapter 2 - File Manager"
+           of the book "Inside Macintosh: Files"
+     http://developer.apple.com/documentation/mac/Files/Files-99.html
+    * For HFS+, "Technical Note TN1150", "HFS Plus Volume Format"
+     http://developer.apple.com/technotes/tn/tn1150.html
+
+    Some useful HFS precisions concerning alignement, bit ordering, and
+    order of fields for extent key comparaisons are only in the HFS+ TN
+
+    These Apple Creator Codes are reserved for us :
+    Shnk traP GP16 GnuP PH+x Xpnd Resz GP17 GP18 GP19 GP20
+
+---
+
+* Cache design *
+
+Versions before HFS Patch 15 were very slow when data relocation was needed,
+because every extent to relocate involved scanning the whole file system,
+looking for a reference to its physical position on the volume (this was a
+dummy algorithm, I know :)
+
+HFS Patch 16 introduced a cache that allows to efficiently retrieve the place
+of the reference in the file system given the physical position of an extent.
+The cache is designed for : - efficiency
+                           - scaling
+                           - simplicity
+                           - avoiding memory allocation while resizing
+
+This cache involves quite big worst case memory consumption, but without it
+the time needed to complete the operation in the worst case would be huge
+anyway (maybe several years...) so this isn't really an issue. The cache size
+is nearly proportional to the number of files you have, or if you have very few
+files, to the size of your volume, so worst cases situations occure when you
+fill a drive with millions of < 4 ko files :p For this very special usage you
+will just need a very special amount of RAM (on typical systems about
+(FS size) / 256 )... On a more "normal" volume it's about
+(# of files) * 20 bytes. With very few files it's about (FS Size) / 1024 / 256.
+
+At the beginning of the resize process, the cache is filed by scanning the FS.
+The position of each extent is cut into 2 parts : high order is used as
+an index into a table of pointer to a linked list which contains :
+- the next ptr                         (sizeof struct *)
+- the extent start                     (4 bytes)
+- the extent size                      (4 bytes)
+- number of BTree block or 0 if in prim (4 bytes)
+- offset of the extent start reference
+  from the block beginning              (2 bytes)
+- sectors by BTree block, or
+  1 for VH/MDB                         (1 byte)
+- FS special file / primary structure
+  where the extent reference is stored  (1 byte)
+  (3 bits for the extent index, 5 for
+   the actual ref)
+
+  0 : dont exists                              --- reserved
+  1 : mdb / vh : catalog                       ---
+  2 : mdb / vh : extent                                ---
+  3 :       vh : attributes                    X+-
+  4 :       vh : allocation                    X+-
+  5 :       vh : startup                       X+-
+  6 :            catalog                       ---
+  7 :            attributes                    X+-
+  8 :            extent (nothing to update)    ---
+  9 :            extent (update catalog)       ---
+ 10 :            extent (update extent !?!)    --- should not exist
+ 11 :            extent (update attributes)    X+-
+ 12 :            extent (update allocation)    X+-
+ 13 :            extent (update startup)       X+- reserved
+ 14 :       vh : journal info block            X+J
+ 15 :       jib: journal                       X+J
+ 16 - 31 :                                     ---
+
+mdb : Master Directory Block
+vh  : Volume Header
+X+  : HFSX or HFS+ only
+J   : Journaled only
+
+Large amount of memory is allocated at once (first enough memory to fit
+every files if there isn't any fragmentation +6.25%, then this value / 4,
+if this wasn't enough). On a typical FS, the first allocation should be enough.
+
+---
diff --git a/libparted/fs/r/hfs/HISTORY b/libparted/fs/r/hfs/HISTORY
new file mode 100644
index 0000000..5e138a6
--- /dev/null
+++ b/libparted/fs/r/hfs/HISTORY
@@ -0,0 +1,115 @@
+## modifications                                               dd-mm-yyyy
+---------------------- PATCH FOR PARTED 1.6.5 ----------------------------
+ 1 initial revision                                            07-04-2003
+ 2 one pass resizing, removal of debug info                    08-04-2003
+ 3 safe abort if resize failed, code cleanups, timer,          10-04-2003
+   source file split, won't resize if not unmounted,
+   only relocate data if needed, minimize disk operations
+ 4 memory leaks removal, code cleanups, resize hfs+ code,      17-04-2003
+   more checks, minor hfs resize bugfix, probe code
+   returns real geometry
+ 5 hfs+ resize bugfixes :                                      19-04-2003
+   * fragmented fs could be corrupted
+   * VH wasn't written on error during alloc map writing
+   * attributes file could be corrupted
+ 6 Use PedSector to be able to use 2To+ HD                     23-04-2003
+   Minor probe bugfix, Cleanups, HFS+ Timer tuning,
+ 7 80 columns indentation                                      23-04-2003
+ 8 Bugfix free blocks calculation in wrapper
+   (makes Mac OS boot !)                                       28-04-2003
+---------------------- PATCH FOR PARTED 1.6.6 ----------------------------
+ 9 Don't destroy the file being worked on in case of
+   interruption of Parted                                      28-10-2003
+---------------------- PATCH FOR PARTED 1.6.10 ---------------------------
+10 Regression tests, URL correction, In effect_move_extent :
+   corrected memory leak & corrected a bug in precondition checks
+   Added error messages, Check ped_alloc results
+   Use macro for test / set / clear in the allocation bitmap
+   Light probe correction, Check return value of get_empty_end
+   Moved dynamic memory allocation out of effect_move_extent
+   Check HFS+ version, Set implementation creator code
+   Check journal absence, Corrected a bug in HFS+ block number
+   calculation                                                 24-04-2004
+--------------------- PATCH FOR PARTED 1.6.11 ----------------------------
+11-Some pointer dereference moved after non nul assertion
+  -Error messages for HFS(+) file IO
+  -Memory leak correction in hfs(plus)_read_bad_blocks
+  -Mark out of volume blocks as used
+    (improve compatibility with broken HFS+ Linux
+    implementation)
+   WARNING : this fix is not 100% tn1150 compatible :
+   "The allocation file may be larger than the minimum
+   number of bits required for the given volume size.
+   Any unused bits in the bitmap must be set to _zero_."
+   Anyway neither is the Linux implementation, nor was my
+   previous implementations
+   Maybe I should ask Apple to change the specifications
+  -HISTORY, DOC and TODO files                                 29-04-2004
+12 Corrected a bug in hfsplus_volume_resize : size of alloc
+   bitmap could be miscalculated                               29-04-2004
+--------------------- PATCH FOR PARTED 1.6.12 ----------------------------
+13-Finally partial rewrite of *_search_move_*
+   Easier to maintain and prepare for extent search and
+   relocation algorithm changes for better ones.
+  -"An extent has not been relocated!" message now only when
+   relocation requested
+  -Slightly better and simpler relocation algorithm
+  -Update of Makefile.in and Makefile.am in fs_hfs
+  -Sign correction for some 8bits HFS integers
+  -Added the option --enable-hfs-extract-fs in 'configure'
+  -Added every ped_geometry_sync where needed
+  -Bugfix : "A root node does not need to exist
+               (if the tree is empty)."
+           - now handled correctly in btree_search
+  -Bugfix : failure wasn't detected in some cases
+           during 2 pass relocation (*_search_move_*)
+  -Bugfix : The extent key comparaison was done in a wrong order
+           and a pad field was used during the comparaison
+  -Bugfix : in hfs_file_find_sector and hfsplus_file_find_sector
+            the absolute position of a file sector could be
+           miscalculated in case of fragmentation, resulting
+           in potential data corruption, or various errors
+  -Bugfix : The end of the HFS bitmap compatibility block was
+           miscalculated ( (1<<16)/8 instead of (1<<16) )
+           in hfs_resize
+                                                               07-09-2004
+--------------------- PATCH FOR PARTED 1.6.14 ----------------------------
+14 Port of Patch 13 for Parted 1.6.14 (update timestamps)
+                                                               08-09-2004
+--------------------- PATCH FOR PARTED 1.6.15 ----------------------------
+15-hfsplus_open : added a warning message if the "attributes"
+   special file exists
+  -hfsplus_open : added a test to check if the "allocation"
+   special file has been correctly opened
+  -optimisation of hfs+ block access : don't recalculate
+   the address of each sector, and avoid checking the cache if
+   obviously not useful
+   ( hfsplus_file_read && hfsplus_file_write
+     && hfsplus_file_find_extent && hfs_file_find_sector)
+  -cut the "hfs.c" file in several parts
+  -Bugfix: in hfsplus_do_move_primary, hfs_effect_move_extent
+   was called instead of hfsplus_effect_move_extent !!!
+   This could not produce data corruption, because of a welcome
+   ASSERT in *_effect_move_extent that would detect the bug :)
+  -Bugfix: in hfs_effect_move_extent, do
+       PED_ASSERT(*ptr_to_fblock <= *ptr_fblock, return -1);
+   instead of
+       PED_ASSERT(*ptr_to_fblock < *ptr_fblock, return -1);
+   and added that assertion to hfsplus_effect_move_extent
+  -Bugfix: bugs introduced in rewrite of hfsplus_file_read
+   && hfsplus_file_write : last sector was incorrectly detected
+   as out of file.
+  -Cache the extent references (speed improvement ?)
+                                                               23-09-2004
+16-Bugfix: in hfsplus_do_move (reloc_plus.c), case CR_BTREE_EXT_ATTR
+   incorrectly updated the cached part of priv_data->catalog_file
+   instead of priv_data->attributes_file
+  -Bugfix: in hfs_read_bad_blocks && hfsplus_read_bad_blocks,
+   now generate an error if file_ID or type mismatch after the
+   first pass
+   Also check return value of ped_malloc
+  -Bugfix: in hfsplus_btree_search, check return value of ped_malloc
+                                                               29-09-2004
+---------------- INTEGRATION IN PARTED 1.6.22 (cvs) ----------------------
+Futur changes will be described in ../../ChangeLog
+                                                               02-02-2005
diff --git a/libparted/fs/r/hfs/TODO b/libparted/fs/r/hfs/TODO
new file mode 100644
index 0000000..6e408e3
--- /dev/null
+++ b/libparted/fs/r/hfs/TODO
@@ -0,0 +1,27 @@
+--- TODO ---
+
+   * Continue to write regressions tests and try on 2.6 kernel  -- (high)
+   * Fix timer progression calculation, according to the new
+     caching code                                              -- (high)
+   * write doc, website, ...                                   -- (high)
+   * Check block allocation in linux 2.4 and remove
+     compatibility code if possible                            -- (high)
+
+   * In hfs(plus)_btree_search , use a static variable to detect
+     illegal recursion and abort in that case. (find the right
+     number of recursion before reporting bug) -- easy         -- (medium)
+   * Finish the HFS Extractor                                  -- (medium)
+     (add mdb & vh extraction, and maybe journal)
+
+   * Write code to allow enlarging and moving HFS[+x]          -- (low)
+   * Use a bitmap to internaly store the bad blocks            -- (low)
+   * Less bitmap saves ?                                       -- (low)
+   * Continue to change the relocation algorithm
+     for a better one :)                                       -- (low)
+
+--- NOT todo ---
+
+   * debug HFS(+) Linux implementation (block allocation for HFS+,
+     hard and sym links for HFS+, filename length for HFS, ...)        -- 
(dont)
+       /// Linux 2.6 contains HFS(+) implementations with less bugs
+       /// Linux 2.4 should not be used anymore to access HFS(+)
diff --git a/libparted/fs/r/hfs/advfs.c b/libparted/fs/r/hfs/advfs.c
new file mode 100644
index 0000000..a8adfb3
--- /dev/null
+++ b/libparted/fs/r/hfs/advfs.c
@@ -0,0 +1,328 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "file.h"
+
+#include "advfs.h"
+
+/* - if a < b, 0 if a == b, + if a > b */
+/* Comparaison is done in the following order : */
+/* CNID, then fork type, then start block */
+/* Note that HFS implementation in linux has a bug */
+/* in this function */
+static int
+hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b)
+{
+       HfsExtentKey* key1 = (HfsExtentKey*) a;
+       HfsExtentKey* key2 = (HfsExtentKey*) b;
+
+       /* do NOT use a substraction, because */
+       /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */
+       /* would return -2, despite the fact */
+       /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */
+       if (key1->file_ID != key2->file_ID)
+               return PED_BE32_TO_CPU(key1->file_ID) <
+                      PED_BE32_TO_CPU(key2->file_ID) ?
+                               -1 : +1;
+
+       if (key1->type != key2->type)
+               return (int)(key1->type - key2->type);
+
+       if (key1->start == key2->start)
+               return 0;
+       /* the whole thing wont work with 16 bits ints */
+       /* anyway */
+       return (int)( PED_BE16_TO_CPU(key1->start) -
+                     PED_BE16_TO_CPU(key2->start) );
+}
+
+/* do a B-Tree lookup */
+/* read the first record immediatly inferior or egal to the given key */
+/* return 0 on error */
+/* record_out _must_ be large enough to receive record_size bytes */
+/* WARNING : the compare function called only handle Extents BTree */
+/*          so modify this function if you want to do lookup in */
+/*          other BTrees has well */
+int
+hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
+                 void *record_out, unsigned int record_size,
+                 HfsCPrivateLeafRec* record_ref)
+{
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+       HfsHeaderRecord*        header;
+       HfsNodeDescriptor*      desc = (HfsNodeDescriptor*) node;
+       HfsPrivateGenericKey*   record_key = NULL;
+       unsigned int            node_number, record_number;
+       int                     i;
+
+       /* Read the header node */
+       if (!hfs_file_read_sector(b_tree_file, node, 0))
+               return 0;
+       header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                               
(node+(PED_SECTOR_SIZE_DEFAULT-2))))));
+
+       /* Get the node number of the root */
+       node_number = PED_BE32_TO_CPU(header->root_node);
+       if (!node_number)
+               return 0;
+
+       /* Read the root node */
+       if (!hfs_file_read_sector(b_tree_file, node, node_number))
+               return 0;
+
+       /* Follow the white rabbit */
+       while (1) {
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = record_number; i; i--) {
+                       record_key = (HfsPrivateGenericKey*)
+                               (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                           (node+(PED_SECTOR_SIZE_DEFAULT - 
2*i)))));
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)record_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)record_key - node
+                               >= PED_SECTOR_SIZE_DEFAULT
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               return 0;
+                       }
+                       if (hfs_extent_key_cmp(record_key, key) <= 0)
+                               break;
+               }
+               if (!i) return 0;
+               if (desc->type == HFS_IDX_NODE) {
+                       unsigned int    skip;
+
+                       skip = (1 + record_key->key_length + 1) & ~1;
+                       node_number = PED_BE32_TO_CPU (*((uint32_t *)
+                                       (((uint8_t *) record_key) + skip)));
+                       if (!hfs_file_read_sector(b_tree_file, node,
+                                                 node_number))
+                               return 0;
+               } else
+                       break;
+       }
+
+       /* copy the result if needed */
+       if (record_size)
+               memcpy (record_out, record_key, record_size);
+
+       /* send record reference if needed */
+       if (record_ref) {
+               record_ref->node_size = 1;      /* in sectors */
+               record_ref->node_number = node_number;
+               record_ref->record_pos = (uint8_t*)record_key - node;
+               record_ref->record_number = i;
+       }
+
+       /* success */
+       return 1;
+}
+
+/* free the bad blocks linked list */
+void
+hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first)
+{
+       HfsPrivateLinkExtent*   next;
+
+       while (first) {
+               next = first->next;
+               free (first);
+               first = next;
+       }
+}
+
+/* This function reads bad blocks extents in the extents file
+   and store it in f.s. specific data of fs */
+int
+hfs_read_bad_blocks (const PedFileSystem *fs)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+
+       if (priv_data->bad_blocks_loaded)
+               return 1;
+
+       {
+       uint8_t                 record[sizeof (HfsExtentKey)
+                                              + sizeof (HfsExtDataRec)];
+       HfsExtentKey            search;
+       HfsExtentKey*           ret_key = (HfsExtentKey*) record;
+       HfsExtDescriptor*       ret_cache = (HfsExtDescriptor*)
+                                            (record + sizeof (HfsExtentKey));
+       unsigned int            block, last_start, first_pass = 1;
+
+       search.key_length = sizeof (HfsExtentKey) - 1;
+       search.type = HFS_DATA_FORK;
+       search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
+
+       last_start = -1; block = 0;
+       while (1) {
+               int i;
+
+               search.start = PED_CPU_TO_BE16 (block);
+               if (!hfs_btree_search (priv_data->extent_file,
+                                      (HfsPrivateGenericKey*) &search,
+                                      record, sizeof (record), NULL)
+                   || ret_key->file_ID != search.file_ID
+                   || ret_key->type != search.type) {
+                       if (first_pass)
+                               break;
+                       else
+                               goto errbb;
+               }
+               if (PED_BE16_TO_CPU (ret_key->start) == last_start)
+                   break;
+
+               last_start = PED_BE16_TO_CPU (ret_key->start);
+               for (i = 0; i < HFS_EXT_NB; i++) {
+                       if (ret_cache[i].block_count) {
+                               HfsPrivateLinkExtent*   new_xt =
+                                  (HfsPrivateLinkExtent*) ped_malloc (
+                                       sizeof (HfsPrivateLinkExtent));
+                               if (!new_xt)
+                                       goto errbb;
+                               new_xt->next = priv_data->bad_blocks_xtent_list;
+                               memcpy(&(new_xt->extent), ret_cache+i,
+                                       sizeof (HfsExtDescriptor));
+                               priv_data->bad_blocks_xtent_list = new_xt;
+                               priv_data->bad_blocks_xtent_nb++;
+                               block += PED_BE16_TO_CPU (
+                                               ret_cache[i].block_count);
+                       }
+               }
+               first_pass = 0;
+       }
+
+       priv_data->bad_blocks_loaded = 1;
+       return 1;}
+
+errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
+       priv_data->bad_blocks_xtent_list=NULL;
+       priv_data->bad_blocks_xtent_nb=0;
+       return 0;
+}
+
+/* This function check if fblock is a bad block */
+int
+hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsPrivateLinkExtent*   walk;
+
+       for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
+               /* Won't compile without the strange cast ! gcc bug ? */
+               /* or maybe C subtilties... */
+               if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) &&
+                   (fblock <  (unsigned int) (PED_BE16_TO_CPU (
+                                                   walk->extent.start_block)
+                                              + PED_BE16_TO_CPU (
+                                                   walk->extent.block_count))))
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* This function returns the first sector of the last free block of an
+   HFS volume we can get after a hfs_pack_free_space_from_block call */
+/* On error this function returns 0 */
+PedSector
+hfs_get_empty_end (const PedFileSystem *fs)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsMasterDirectoryBlock* mdb = priv_data->mdb;
+       unsigned int            block, last_bad, end_free_blocks;
+
+       /* find the next block to the last bad block of the volume */
+       if (!hfs_read_bad_blocks (fs))
+               return 0;
+
+       HfsPrivateLinkExtent*   l;
+       last_bad = 0;
+       for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) {
+               if ((unsigned int) PED_BE16_TO_CPU (l->extent.start_block)
+                   + PED_BE16_TO_CPU (l->extent.block_count) > last_bad)
+                       last_bad = PED_BE16_TO_CPU (l->extent.start_block)
+                                  + PED_BE16_TO_CPU (l->extent.block_count);
+       }
+
+       /* Count the free blocks from last_bad to the end of the volume */
+       end_free_blocks = 0;
+       for (block = last_bad;
+            block < PED_BE16_TO_CPU (mdb->total_blocks);
+            block++) {
+               if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+                       end_free_blocks++;
+       }
+
+       /* Calculate the block that will by the first free at the
+          end of the volume */
+       block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks;
+
+       return (PedSector) PED_BE16_TO_CPU (mdb->start_block)
+               + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size)
+                                      / PED_SECTOR_SIZE_DEFAULT);
+}
+
+/* return the block which should be used to pack data to have at
+   least free fblock blocks at the end of the volume */
+unsigned int
+hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       unsigned int            block;
+
+       for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1;
+            block && fblock;
+            block--) {
+               if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+                       fblock--;
+       }
+
+       while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+               block--;
+       if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+               block++;
+
+       return block;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/advfs.h b/libparted/fs/r/hfs/advfs.h
new file mode 100644
index 0000000..aff503e
--- /dev/null
+++ b/libparted/fs/r/hfs/advfs.h
@@ -0,0 +1,48 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _ADVFS_H
+#define _ADVFS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
+                 void *record_out, unsigned int record_size,
+                 HfsCPrivateLeafRec* record_ref);
+
+void
+hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first);
+
+int
+hfs_read_bad_blocks (const PedFileSystem *fs);
+
+int
+hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock);
+
+PedSector
+hfs_get_empty_end (const PedFileSystem *fs);
+
+unsigned int
+hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock);
+
+#endif /* _ADVFS_H */
diff --git a/libparted/fs/r/hfs/advfs_plus.c b/libparted/fs/r/hfs/advfs_plus.c
new file mode 100644
index 0000000..08d2f61
--- /dev/null
+++ b/libparted/fs/r/hfs/advfs_plus.c
@@ -0,0 +1,382 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "advfs.h"
+#include "file_plus.h"
+
+#include "advfs_plus.h"
+
+/* - if a < b, 0 if a == b, + if a > b */
+/* Comparaison is done in the following order : */
+/* CNID, then fork type, then start block */
+static int
+hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b)
+{
+       HfsPExtentKey* key1 = (HfsPExtentKey*) a;
+       HfsPExtentKey* key2 = (HfsPExtentKey*) b;
+
+       if (key1->file_ID != key2->file_ID)
+               return PED_BE32_TO_CPU(key1->file_ID) <
+                      PED_BE32_TO_CPU(key2->file_ID) ?
+                               -1 : +1;
+
+       if (key1->type != key2->type)
+               return (int)(key1->type - key2->type);
+
+       if (key1->start == key2->start)
+               return 0;
+       return PED_BE32_TO_CPU(key1->start) <
+              PED_BE32_TO_CPU(key2->start) ?
+                       -1 : +1;
+}
+
+/* do a B-Tree lookup */
+/* read the first record immediatly inferior or egal to the given key */
+/* return 0 on error */
+/* record_out _must_ be large enough to receive the whole record (key + data) 
*/
+/* WARNING : the search function called only handle Extents BTree */
+/*          so modify this function if you want to do lookup in */
+/*          other BTrees has well */
+int
+hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key,
+                     void *record_out, unsigned int record_size,
+                     HfsCPrivateLeafRec* record_ref)
+{
+       uint8_t                 node_1[PED_SECTOR_SIZE_DEFAULT];
+       uint8_t*                node;
+       HfsPHeaderRecord*       header;
+       HfsPPrivateGenericKey*  record_key = NULL;
+       unsigned int            node_number, record_number, size, bsize;
+       int                     i;
+
+       /* Read the header node */
+       if (!hfsplus_file_read_sector(b_tree_file, node_1, 0))
+               return 0;
+       header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC);
+
+       /* Get the node number of the root */
+       node_number = PED_BE32_TO_CPU (header->root_node);
+       if (!node_number)
+               return 0;
+
+       /* Get the size of a node in sectors and allocate buffer */
+       size = (bsize = PED_BE16_TO_CPU (header->node_size)) / 
PED_SECTOR_SIZE_DEFAULT;
+       node = (uint8_t*) ped_malloc (bsize);
+       if (!node)
+               return 0;
+       HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
+
+       /* Read the root node */
+       if (!hfsplus_file_read (b_tree_file, node,
+                               (PedSector) node_number * size, size))
+               return 0;
+
+       /* Follow the white rabbit */
+       while (1) {
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = record_number; i; i--) {
+                       record_key = (HfsPPrivateGenericKey*)
+                           (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                       (node+(bsize - 2*i)))));
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)record_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)record_key - node
+                               >= (signed)bsize
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               free (node);
+                               return 0;
+                       }
+                       if (hfsplus_extent_key_cmp(record_key, key) <= 0)
+                               break;
+               }
+               if (!i) { free (node); return 0; }
+               if (desc->type == HFS_IDX_NODE) {
+                       unsigned int    skip;
+
+                       skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length)
+                                + 1 ) & ~1;
+                       node_number = PED_BE32_TO_CPU (*((uint32_t *)
+                                       (((uint8_t *) record_key) + skip)));
+                       if (!hfsplus_file_read(b_tree_file, node,
+                                              (PedSector) node_number * size,
+                                              size)) {
+                               free (node);
+                               return 0;
+                       }
+               } else
+                       break;
+       }
+
+       /* copy the result if needed */
+       if (record_size)
+               memcpy (record_out, record_key, record_size);
+
+       /* send record reference if needed */
+       if (record_ref) {
+               record_ref->node_size = size;   /* in sectors */
+               record_ref->node_number = node_number;
+               record_ref->record_pos = (uint8_t*)record_key - node;
+               record_ref->record_number = i;
+       }
+
+       /* success */
+       free (node);
+       return 1;
+}
+
+/* free the bad blocks linked list */
+void
+hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first)
+{
+       HfsPPrivateLinkExtent*  next;
+
+       while (first) {
+               next = first->next;
+               free (first);
+               first = next;
+       }
+}
+
+/* This function reads bad blocks extents in the extents file
+   and store it in f.s. specific data of fs */
+int
+hfsplus_read_bad_blocks (const PedFileSystem *fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                                   fs->type_specific;
+
+       if (priv_data->bad_blocks_loaded)
+               return 1;
+
+       {
+       uint8_t                 record[sizeof (HfsPExtentKey)
+                                      + sizeof (HfsPExtDataRec)];
+       HfsPExtentKey           search;
+       HfsPExtentKey*          ret_key = (HfsPExtentKey*) record;
+       HfsPExtDescriptor*      ret_cache = (HfsPExtDescriptor*)
+                                   (record + sizeof (HfsPExtentKey));
+       int                     block, first_pass = 1;
+       unsigned int            last_start;
+
+       search.key_length = sizeof (HfsExtentKey) - 2;
+       search.type = HFS_DATA_FORK;
+       search.pad = 0;
+       search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
+
+       last_start = -1; block = 0;
+       while (1) {
+               int i;
+
+               search.start = PED_CPU_TO_BE32 (block);
+               if (!hfsplus_btree_search (priv_data->extents_file,
+                                          (HfsPPrivateGenericKey*) &search,
+                                          record, sizeof (record), NULL)
+                   || ret_key->file_ID != search.file_ID
+                   || ret_key->type != search.type) {
+                       if (first_pass)
+                               break;
+                       else
+                               goto errbbp;
+               }
+               if (PED_BE32_TO_CPU (ret_key->start) == last_start)
+                       break;
+
+               last_start = PED_BE32_TO_CPU (ret_key->start);
+               for (i = 0; i < HFSP_EXT_NB; i++) {
+                       if (ret_cache[i].block_count) {
+                               HfsPPrivateLinkExtent*  new_xt =
+                                 (HfsPPrivateLinkExtent*) ped_malloc (
+                                   sizeof (HfsPPrivateLinkExtent));
+                               if (!new_xt)
+                                       goto errbbp;
+                               new_xt->next = priv_data->bad_blocks_xtent_list;
+                               memcpy (&(new_xt->extent), ret_cache+i,
+                                       sizeof (HfsPExtDescriptor));
+                               priv_data->bad_blocks_xtent_list = new_xt;
+                               priv_data->bad_blocks_xtent_nb++;
+                               block += PED_BE32_TO_CPU (
+                                               ret_cache[i].block_count);
+                       }
+               }
+               first_pass = 0;
+       }
+
+       priv_data->bad_blocks_loaded = 1;
+       return 1;}
+
+errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
+       priv_data->bad_blocks_xtent_list=NULL;
+       priv_data->bad_blocks_xtent_nb=0;
+       return 0;
+}
+
+/* This function check if fblock is a bad block */
+int
+hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPPrivateLinkExtent*  walk;
+
+       for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
+               /* Won't compile without the strange cast ! gcc bug ? */
+               /* or maybe C subtilties... */
+               if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) &&
+                   (fblock <  (unsigned int)(PED_BE32_TO_CPU (
+                                               walk->extent.start_block)
+                              + PED_BE32_TO_CPU (walk->extent.block_count))))
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* This function returns the first sector of the last free block of
+   an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */
+PedSector
+hfsplus_get_empty_end (const PedFileSystem *fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                                   fs->type_specific;
+       HfsPVolumeHeader*       vh = priv_data->vh;
+       unsigned int            block, last_bad, end_free_blocks;
+
+       /* find the next block to the last bad block of the volume */
+       if (!hfsplus_read_bad_blocks (fs)) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Bad blocks could not be read."));
+               return 0;
+       }
+
+       HfsPPrivateLinkExtent*  l;
+       last_bad = 0;
+       for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) {
+               if ((unsigned int) PED_BE32_TO_CPU (l->extent.start_block)
+                   + PED_BE32_TO_CPU (l->extent.block_count) > last_bad)
+                       last_bad = PED_BE32_TO_CPU (l->extent.start_block)
+                                  + PED_BE32_TO_CPU (l->extent.block_count);
+       }
+
+       /* Count the free blocks from last_bad to the end of the volume */
+       end_free_blocks = 0;
+       for (block = last_bad;
+            block < PED_BE32_TO_CPU (vh->total_blocks);
+            block++) {
+               if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+                       end_free_blocks++;
+       }
+
+       /* Calculate the block that will by the first free at
+          the end of the volume */
+       block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks;
+
+       return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size)
+                                    / PED_SECTOR_SIZE_DEFAULT );
+}
+
+/* On error, returns 0 */
+PedSector
+hfsplus_get_min_size (const PedFileSystem *fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       PedSector               min_size;
+
+       /* don't need to add anything because every sector
+          can be part of allocation blocks in HFS+, and
+          the last block _must_ be reserved */
+       min_size = hfsplus_get_empty_end(fs);
+       if (!min_size) return 0;
+
+       if (priv_data->wrapper) {
+               HfsPrivateFSData*       hfs_priv_data = (HfsPrivateFSData*)
+                                           priv_data->wrapper->type_specific;
+               unsigned int            hfs_sect_block;
+               PedSector               hgee;
+               hfs_sect_block =
+                   PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+                   / PED_SECTOR_SIZE_DEFAULT;
+               /*
+                * if hfs+ is embedded in an hfs wrapper then the new size is :
+                * the new size of the hfs+ volume rounded up to the size
+                *     of hfs blocks
+                * + the minimum size of the hfs wrapper without any hfs+
+                *     modification
+                * - the current size of the hfs+ volume in the hfs wrapper
+                */
+               hgee = hfs_get_empty_end(priv_data->wrapper);
+               if (!hgee) return 0;
+               min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block)
+                          * hfs_sect_block
+                        + hgee + 2
+                        - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb
+                                                       ->old_new.embedded
+                                                       .location.block_count )
+                          * hfs_sect_block;
+       }
+
+       return min_size;
+}
+
+/* return the block which should be used to pack data to have
+   at least free fblock blocks at the end of the volume */
+unsigned int
+hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       unsigned int            block;
+
+       for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1;
+            block && fblock;
+            block--) {
+               if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+                       fblock--;
+       }
+
+       while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+               block--;
+       if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+               block++;
+
+       return block;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/advfs_plus.h b/libparted/fs/r/hfs/advfs_plus.h
new file mode 100644
index 0000000..fdf61c6
--- /dev/null
+++ b/libparted/fs/r/hfs/advfs_plus.h
@@ -0,0 +1,51 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _ADVFS_PLUS_H
+#define _ADVFS_PLUS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key,
+                     void *record_out, unsigned int record_size,
+                     HfsCPrivateLeafRec* record_ref);
+
+void
+hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first);
+
+int
+hfsplus_read_bad_blocks (const PedFileSystem *fs);
+
+int
+hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock);
+
+PedSector
+hfsplus_get_empty_end (const PedFileSystem *fs);
+
+PedSector
+hfsplus_get_min_size (const PedFileSystem *fs);
+
+unsigned int
+hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock);
+
+#endif /* _ADVFS_PLUS_H */
diff --git a/libparted/fs/r/hfs/cache.c b/libparted/fs/r/hfs/cache.c
new file mode 100644
index 0000000..97dcca7
--- /dev/null
+++ b/libparted/fs/r/hfs/cache.c
@@ -0,0 +1,238 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+
+#include "cache.h"
+
+static HfsCPrivateCacheTable*
+hfsc_new_cachetable(unsigned int size)
+{
+       HfsCPrivateCacheTable* ret;
+
+       ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret));
+       if (!ret) return NULL;
+
+       ret->next_cache = NULL;
+       ret->table_size = size;
+       ret->table_first_free = 0;
+
+       ret->table = ped_malloc(sizeof(*ret->table)*size);
+       if (!ret->table) { free(ret); return NULL; }
+       memset(ret->table, 0, sizeof(*ret->table)*size);
+
+       return ret;
+}
+
+HfsCPrivateCache*
+hfsc_new_cache(unsigned int block_number, unsigned int file_number)
+{
+       unsigned int            cachetable_size, i;
+       HfsCPrivateCache*       ret;
+
+       ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret));
+       if (!ret) return NULL;
+       ret->block_number = block_number;
+       /* following code avoid integer overflow */
+       ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ?
+                               ( block_number >> CR_SHIFT ) + 1 :
+                               ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT
+                            ;
+
+       ret->linked_ref = (HfsCPrivateExtent**)
+                          ped_malloc( sizeof(*ret->linked_ref)
+                                       * ret->linked_ref_size );
+       if (!ret->linked_ref) { free(ret); return NULL; }
+
+       cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST;
+       if (cachetable_size < file_number) cachetable_size = (unsigned) -1;
+       ret->first_cachetable_size = cachetable_size;
+       ret->table_list = hfsc_new_cachetable(cachetable_size);
+       if (!ret->table_list) {
+               free(ret->linked_ref);
+               free(ret);
+               return NULL;
+       }
+       ret->last_table = ret->table_list;
+
+       for (i = 0; i < ret->linked_ref_size; ++i)
+               ret->linked_ref[i] = NULL;
+
+       ret->needed_alloc_size = 0;
+
+       return ret;
+}
+
+static void
+hfsc_delete_cachetable(HfsCPrivateCacheTable* list)
+{
+       HfsCPrivateCacheTable* next;
+
+       while (list) {
+               free (list->table);
+               next = list->next_cache;
+               free (list);
+               list = next;
+       }
+}
+
+void
+hfsc_delete_cache(HfsCPrivateCache* cache)
+{
+       hfsc_delete_cachetable(cache->table_list);
+       free(cache->linked_ref);
+       free(cache);
+}
+
+HfsCPrivateExtent*
+hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
+                     uint32_t block, uint16_t offset, uint8_t sbb,
+                     uint8_t where, uint8_t ref_index)
+{
+       HfsCPrivateExtent*      ext;
+       unsigned int            idx = start >> CR_SHIFT;
+
+       PED_ASSERT(idx < cache->linked_ref_size);
+
+       for (ext = cache->linked_ref[idx];
+            ext && start != ext->ext_start;
+            ext = ext->next);
+
+       if (ext) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Trying to register an extent starting at block "
+                         "0x%X, but another one already exists at this "
+                         "position.  You should check the file system!"),
+                       start);
+               return NULL;
+       }
+
+       if ( cache->last_table->table_first_free
+            == cache->last_table->table_size ) {
+               cache->last_table->next_cache =
+                       hfsc_new_cachetable( ( cache->first_cachetable_size
+                                              / CR_NEW_ALLOC_DIV )
+                                            + CR_ADD_CST );
+               if (!cache->last_table->next_cache)
+                       return NULL;
+               cache->last_table = cache->last_table->next_cache;
+       }
+
+       ext = cache->last_table->table+(cache->last_table->table_first_free++);
+
+       ext->ext_start = start;
+       ext->ext_length = length;
+       ext->ref_block = block;
+       ext->ref_offset = offset;
+       ext->sect_by_block = sbb;
+       ext->where = where;
+       ext->ref_index = ref_index;
+
+       ext->next = cache->linked_ref[idx];
+       cache->linked_ref[idx] = ext;
+
+       cache->needed_alloc_size = cache->needed_alloc_size >
+                                  (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ?
+                                  cache->needed_alloc_size :
+                                  (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb;
+
+       return ext;
+}
+
+HfsCPrivateExtent*
+hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start)
+{
+       HfsCPrivateExtent*      ret;
+       unsigned int    idx = start >> CR_SHIFT;
+
+       PED_ASSERT(idx < cache->linked_ref_size);
+
+       for (ret = cache->linked_ref[idx];
+            ret && start != ret->ext_start;
+            ret = ret->next);
+
+       return ret;
+}
+
+/* Can't fail if extent begining at old_start exists */
+/* Returns 0 if no such extent, or on error */
+HfsCPrivateExtent*
+hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
+                       uint32_t new_start)
+{
+       HfsCPrivateExtent**     ppext;
+       HfsCPrivateExtent*      pext;
+
+       unsigned int            idx1 = old_start >> CR_SHIFT;
+       unsigned int            idx2 = new_start >> CR_SHIFT;
+
+       PED_ASSERT(idx1 < cache->linked_ref_size);
+       PED_ASSERT(idx2 < cache->linked_ref_size);
+
+       for (pext = cache->linked_ref[idx2];
+            pext && new_start != pext->ext_start;
+            pext = pext->next);
+
+       if (pext) {
+               ped_exception_throw (
+                       PED_EXCEPTION_BUG,
+                       PED_EXCEPTION_CANCEL,
+                       _("Trying to move an extent from block Ox%X to block "
+                         "Ox%X, but another one already exists at this "
+                         "position.  This should not happen!"),
+                       old_start, new_start);
+               return NULL;
+       }
+
+       for (ppext = &(cache->linked_ref[idx1]);
+            (*ppext) && old_start != (*ppext)->ext_start;
+            ppext = &((*ppext)->next));
+
+       if (!(*ppext)) return NULL;
+
+       /* removing the extent from the cache */
+       pext = *ppext;
+       (*ppext) = pext->next;
+
+       /* change ext_start and insert the extent again */
+       pext->ext_start = new_start;
+       pext->next = cache->linked_ref[idx2];
+       cache->linked_ref[idx2] = pext;
+
+       return pext;
+}
+
+#endif /* DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/cache.h b/libparted/fs/r/hfs/cache.h
new file mode 100644
index 0000000..e0c73e0
--- /dev/null
+++ b/libparted/fs/r/hfs/cache.h
@@ -0,0 +1,117 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _CACHE_H
+#define _CACHE_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+/* CR => CACHE REF */
+#define CR_NULL                         0 /* reserved */
+#define CR_PRIM_CAT             1
+#define CR_PRIM_EXT             2
+#define CR_PRIM_ATTR            3
+#define CR_PRIM_ALLOC           4
+#define CR_PRIM_START           5
+#define CR_BTREE_CAT            6
+#define CR_BTREE_ATTR           7
+#define CR_BTREE_EXT_0          8
+#define CR_BTREE_EXT_CAT        9
+#define CR_BTREE_EXT_EXT       10 /* should not happen ! */
+#define CR_BTREE_EXT_ATTR      11
+#define CR_BTREE_EXT_ALLOC     12
+#define CR_BTREE_EXT_START     13 /* unneeded in current code */
+#define CR_BTREE_CAT_JIB       14 /* journal info block */
+#define CR_BTREE_CAT_JL                15 /* journal */
+/* 16 -> 31 || high order bit */   /* reserved */
+
+/* tuning */
+#define CR_SHIFT                8 /* number of bits to shift start_block by */
+                                  /* to get the index of the linked list */
+#define CR_OVER_DIV            16 /* alloc a table for (1+1/CR_OVER_DIV) *
+                                     file_number + CR_ADD_CST */
+#define CR_ADD_CST             16
+#define CR_NEW_ALLOC_DIV        4 /* divide the size of the first alloc table
+                                     by this value to allocate next tables */
+
+/* See DOC for an explaination of this structure */
+/* Access read only from outside cache.c */
+struct _HfsCPrivateExtent {
+       struct _HfsCPrivateExtent*      next;
+       uint32_t                        ext_start;
+       uint32_t                        ext_length;
+       uint32_t                        ref_block;
+       uint16_t                        ref_offset;
+       uint8_t                         sect_by_block;
+       unsigned                        where : 5;
+       unsigned                        ref_index : 3; /* 0 -> 7 */
+};
+typedef struct _HfsCPrivateExtent HfsCPrivateExtent;
+
+/* Internaly used by cache.c for custom memory managment only */
+struct _HfsCPrivateCacheTable {
+       struct _HfsCPrivateCacheTable*  next_cache;
+       HfsCPrivateExtent*              table;
+       unsigned int                    table_size;
+       unsigned int                    table_first_free;
+       /* first_elemt ? */
+};
+typedef struct _HfsCPrivateCacheTable HfsCPrivateCacheTable;
+
+/* Internaly used by cache.c for custom memory managment
+   and cache handling only */
+struct _HfsCPrivateCache {
+       HfsCPrivateCacheTable*          table_list;
+       HfsCPrivateCacheTable*          last_table;
+       HfsCPrivateExtent**             linked_ref;
+       unsigned int                    linked_ref_size;
+       unsigned int                    block_number;
+       unsigned int                    first_cachetable_size;
+       unsigned int                    needed_alloc_size;
+};
+typedef struct _HfsCPrivateCache HfsCPrivateCache;
+
+HfsCPrivateCache*
+hfsc_new_cache(unsigned int block_number, unsigned int file_number);
+
+void
+hfsc_delete_cache(HfsCPrivateCache* cache);
+
+HfsCPrivateExtent*
+hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
+                     uint32_t block, uint16_t offset, uint8_t sbb,
+                     uint8_t where, uint8_t index);
+
+HfsCPrivateExtent*
+hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start);
+
+HfsCPrivateExtent*
+hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
+                       uint32_t new_start);
+
+static __inline__ unsigned int
+hfsc_cache_needed_buffer(HfsCPrivateCache* cache)
+{
+       return cache->needed_alloc_size;
+}
+
+#endif /* _CACHE_H */
diff --git a/libparted/fs/r/hfs/file.c b/libparted/fs/r/hfs/file.c
new file mode 100644
index 0000000..9fc501b
--- /dev/null
+++ b/libparted/fs/r/hfs/file.c
@@ -0,0 +1,228 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "advfs.h"
+
+#include "file.h"
+
+/* Open the data fork of a file with its first three extents and its CNID */
+HfsPrivateFile*
+hfs_file_open (PedFileSystem *fs, uint32_t CNID,
+              HfsExtDataRec ext_desc, PedSector sect_nb)
+{
+       HfsPrivateFile* file;
+
+       file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile));
+       if (!file) return NULL;
+
+       file->fs = fs;
+       file->sect_nb = sect_nb;
+       file->CNID = CNID;
+       memcpy(file->first, ext_desc, sizeof (HfsExtDataRec));
+       file->start_cache = 0;
+
+       return file;
+}
+
+/* Close an HFS file */
+void
+hfs_file_close (HfsPrivateFile* file)
+{
+       free (file);
+}
+
+/* warning : only works on data forks */
+static int
+hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block,
+                          HfsExtDataRec cache, uint16_t* ptr_start_cache)
+{
+       uint8_t                 record[sizeof (HfsExtentKey)
+                                      + sizeof (HfsExtDataRec)];
+       HfsExtentKey            search;
+       HfsExtentKey*           ret_key = (HfsExtentKey*) record;
+       HfsExtDescriptor*       ret_cache = (HfsExtDescriptor*)
+                                             (record + sizeof (HfsExtentKey));
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                             file->fs->type_specific;
+
+       search.key_length = sizeof (HfsExtentKey) - 1;
+       search.type = HFS_DATA_FORK;
+       search.file_ID = file->CNID;
+       search.start = PED_CPU_TO_BE16 (block);
+
+       if (!hfs_btree_search (priv_data->extent_file,
+                              (HfsPrivateGenericKey*) &search,
+                              record, sizeof (record), NULL))
+               return 0;
+
+       if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
+               return 0;
+
+       memcpy (cache, ret_cache, sizeof(HfsExtDataRec));
+       *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start);
+
+       return 1;
+}
+
+/* find and return the nth sector of a file */
+/* return 0 on error */
+static PedSector
+hfs_file_find_sector (HfsPrivateFile* file, PedSector sector)
+{
+       HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+                                     file->fs->type_specific;
+       unsigned int    sect_by_block = PED_BE32_TO_CPU (
+                                           priv_data->mdb->block_size)
+                                       / PED_SECTOR_SIZE_DEFAULT;
+       unsigned int    i, s, vol_block;
+       unsigned int    block  = sector / sect_by_block;
+       unsigned int    offset = sector % sect_by_block;
+
+       /* in the three first extent */
+       for (s = 0, i = 0; i < HFS_EXT_NB; i++) {
+                       if ((block >= s) && ( block < s + PED_BE16_TO_CPU (
+                                               file->first[i].block_count))) {
+                       vol_block = (block - s) + PED_BE16_TO_CPU (
+                                                   file->first[i].start_block);
+                       goto sector_found;
+               }
+               s += PED_BE16_TO_CPU (file->first[i].block_count);
+       }
+
+       /* in the three cached extent */
+       if (file->start_cache && block >= file->start_cache)
+       for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
+               if ((block >= s) && (block < s + PED_BE16_TO_CPU (
+                                               file->cache[i].block_count))) {
+                       vol_block = (block - s) + PED_BE16_TO_CPU (
+                                                   file->cache[i].start_block);
+                       goto sector_found;
+               }
+               s += PED_BE16_TO_CPU (file->cache[i].block_count);
+       }
+
+       /* update cache */
+       if (!hfs_get_extent_containing (file, block, file->cache,
+                                       &(file->start_cache))) {
+               ped_exception_throw (
+                       PED_EXCEPTION_WARNING,
+                       PED_EXCEPTION_CANCEL,
+                       _("Could not update the extent cache for HFS file with "
+                         "CNID %X."),
+                       PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       /* in the three cached extent */
+       PED_ASSERT(file->start_cache && block >= file->start_cache);
+       for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
+               if ((block >= s) && (block < s + PED_BE16_TO_CPU (
+                                               file->cache[i].block_count))) {
+                       vol_block = (block - s) + PED_BE16_TO_CPU (
+                                                   file->cache[i].start_block);
+                       goto sector_found;
+               }
+               s += PED_BE16_TO_CPU (file->cache[i].block_count);
+       }
+
+       return 0;
+
+    sector_found:
+       return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block)
+               + (PedSector) vol_block * sect_by_block
+               + offset;
+}
+
+/* Read the nth sector of a file */
+/* return 0 on error */
+int
+hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector)
+{
+       PedSector       abs_sector;
+
+       if (sector >= file->sect_nb) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Trying to read HFS file with CNID %X behind EOF."),
+                       PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       abs_sector = hfs_file_find_sector (file, sector);
+       if (!abs_sector) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Could not find sector %lli of HFS file with "
+                         "CNID %X."),
+                       sector, PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       return ped_geometry_read (file->fs->geom, buf, abs_sector, 1);
+}
+
+/* Write the nth sector of a file */
+/* return 0 on error */
+int
+hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector)
+{
+       PedSector       abs_sector;
+
+       if (sector >= file->sect_nb) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Trying to write HFS file with CNID %X behind EOF."),
+                         PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       abs_sector = hfs_file_find_sector (file, sector);
+       if (!abs_sector) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Could not find sector %lli of HFS file with "
+                         "CNID %X."),
+                       sector, PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       return ped_geometry_write (file->fs->geom, buf, abs_sector, 1);
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/file.h b/libparted/fs/r/hfs/file.h
new file mode 100644
index 0000000..a16f835
--- /dev/null
+++ b/libparted/fs/r/hfs/file.h
@@ -0,0 +1,41 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _FILE_H
+#define _FILE_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+HfsPrivateFile*
+hfs_file_open (PedFileSystem *fs, uint32_t CNID,
+              HfsExtDataRec ext_desc, PedSector sect_nb);
+
+void
+hfs_file_close (HfsPrivateFile* file);
+
+int
+hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector);
+
+int
+hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector);
+
+#endif /* _FILE_H */
diff --git a/libparted/fs/r/hfs/file_plus.c b/libparted/fs/r/hfs/file_plus.c
new file mode 100644
index 0000000..acacc93
--- /dev/null
+++ b/libparted/fs/r/hfs/file_plus.c
@@ -0,0 +1,273 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "advfs_plus.h"
+
+#include "file_plus.h"
+
+/* Open the data fork of a file with its first eight extents and its CNID */
+/* CNID and ext_desc must be in disc order, sect_nb in CPU order */
+/* return null on failure */
+HfsPPrivateFile*
+hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
+                  HfsPExtDataRec ext_desc, PedSector sect_nb)
+{
+       HfsPPrivateFile* file;
+
+       file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile));
+       if (!file) return NULL;
+
+       file->fs = fs;
+       file->sect_nb = sect_nb;
+       file->CNID = CNID;
+       memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec));
+       file->start_cache = 0;
+
+       return file;
+}
+
+/* Close an HFS+ file */
+void
+hfsplus_file_close (HfsPPrivateFile* file)
+{
+       free (file);
+}
+
+/* warning : only works on data forks */
+static int
+hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block,
+                              HfsPExtDataRec cache, uint32_t* ptr_start_cache)
+{
+       uint8_t                 record[sizeof (HfsPExtentKey)
+                                      + sizeof (HfsPExtDataRec)];
+       HfsPExtentKey           search;
+       HfsPExtentKey*          ret_key = (HfsPExtentKey*) record;
+       HfsPExtDescriptor*      ret_cache = (HfsPExtDescriptor*)
+                                             (record + sizeof (HfsPExtentKey));
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               file->fs->type_specific;
+
+       search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2);
+       search.type = HFS_DATA_FORK;
+       search.pad = 0;
+       search.file_ID = file->CNID;
+       search.start = PED_CPU_TO_BE32 (block);
+
+       if (!hfsplus_btree_search (priv_data->extents_file,
+                                  (HfsPPrivateGenericKey*) &search,
+                                  record, sizeof (record), NULL))
+               return 0;
+
+       if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
+               return 0;
+
+       memcpy (cache, ret_cache, sizeof(HfsPExtDataRec));
+       *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start);
+
+       return 1;
+}
+
+/* find a sub extent contained in the desired area */
+/* and with the same starting point */
+/* return 0 in sector_count on error, or the physical area */
+/* on the volume corresponding to the logical area in the file */
+static HfsPPrivateExtent
+hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector,
+                         unsigned int nb)
+{
+       HfsPPrivateExtent ret = {0,0};
+       HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+                                       file->fs->type_specific;
+       unsigned int    sect_by_block = PED_BE32_TO_CPU (
+                                           priv_data->vh->block_size)
+                                       / PED_SECTOR_SIZE_DEFAULT;
+       unsigned int    i, s, vol_block, size;
+       PedSector       sect_size;
+       unsigned int    block  = sector / sect_by_block;
+       unsigned int    offset = sector % sect_by_block;
+
+       /* in the 8 first extent */
+       for (s = 0, i = 0; i < HFSP_EXT_NB; i++) {
+               if ((block >= s) && (block < s + PED_BE32_TO_CPU (
+                                               file->first[i].block_count))) {
+                       vol_block = (block - s)
+                                   + PED_BE32_TO_CPU (file->first[i]
+                                                      .start_block);
+                       size = PED_BE32_TO_CPU (file->first[i].block_count)
+                               + s - block;
+                       goto plus_sector_found;
+               }
+               s += PED_BE32_TO_CPU (file->first[i].block_count);
+       }
+
+       /* in the 8 cached extent */
+       if (file->start_cache && block >= file->start_cache)
+       for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
+               if ((block >= s) && (block < s + PED_BE32_TO_CPU (
+                                               file->cache[i].block_count))) {
+                       vol_block = (block - s)
+                                   + PED_BE32_TO_CPU (file->cache[i]
+                                                      .start_block);
+                       size = PED_BE32_TO_CPU (file->cache[i].block_count)
+                               + s - block;
+                       goto plus_sector_found;
+               }
+               s += PED_BE32_TO_CPU (file->cache[i].block_count);
+       }
+
+       /* update cache */
+       if (!hfsplus_get_extent_containing (file, block, file->cache,
+                                           &(file->start_cache))) {
+               ped_exception_throw (
+                       PED_EXCEPTION_WARNING,
+                       PED_EXCEPTION_CANCEL,
+                       _("Could not update the extent cache for HFS+ file "
+                         "with CNID %X."),
+                       PED_BE32_TO_CPU(file->CNID));
+               return ret; /* ret == {0,0} */
+       }
+
+       /* ret == {0,0} */
+       PED_ASSERT(file->start_cache && block >= file->start_cache);
+
+       for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
+               if ((block >= s) && (block < s + PED_BE32_TO_CPU (
+                                               file->cache[i].block_count))) {
+                       vol_block = (block - s)
+                                   + PED_BE32_TO_CPU (file->cache[i]
+                                                      .start_block);
+                       size = PED_BE32_TO_CPU (file->cache[i].block_count)
+                               + s - block;
+                       goto plus_sector_found;
+               }
+               s += PED_BE32_TO_CPU (file->cache[i].block_count);
+       }
+
+       return ret;
+
+plus_sector_found:
+       sect_size = (PedSector) size * sect_by_block - offset;
+       ret.start_sector = vol_block * sect_by_block + offset;
+       ret.sector_count = (sect_size < nb) ? sect_size : nb;
+       return ret;
+}
+
+int
+hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector,
+                 unsigned int nb)
+{
+       HfsPPrivateExtent phy_area;
+       HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+                                       file->fs->type_specific;
+        char *b = buf;
+
+       if (sector+nb < sector /* detect overflow */
+           || sector+nb > file->sect_nb) /* out of file */ {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Trying to read HFS+ file with CNID %X behind EOF."),
+                       PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       while (nb) {
+               phy_area = hfsplus_file_find_extent(file, sector, nb);
+               if (phy_area.sector_count == 0) {
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("Could not find sector %lli of HFS+ file "
+                                 "with CNID %X."),
+                               sector, PED_BE32_TO_CPU(file->CNID));
+                       return 0;
+               }
+                if (!ped_geometry_read(priv_data->plus_geom, b,
+                                      phy_area.start_sector,
+                                      phy_area.sector_count))
+                       return 0;
+
+               nb -= phy_area.sector_count; /* < nb anyway ... */
+               sector += phy_area.sector_count;
+                b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
+       }
+
+       return 1;
+}
+
+int
+hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector,
+                 unsigned int nb)
+{
+       HfsPPrivateExtent phy_area;
+       HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+                                       file->fs->type_specific;
+        char *b = buf;
+
+       if (sector+nb < sector /* detect overflow */
+           || sector+nb > file->sect_nb) /* out of file */ {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Trying to write HFS+ file with CNID %X behind EOF."),
+                       PED_BE32_TO_CPU(file->CNID));
+               return 0;
+       }
+
+       while (nb) {
+               phy_area = hfsplus_file_find_extent(file, sector, nb);
+               if (phy_area.sector_count == 0) {
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("Could not find sector %lli of HFS+ file "
+                                 "with CNID %X."),
+                               sector, PED_BE32_TO_CPU(file->CNID));
+                       return 0;
+               }
+                if (!ped_geometry_write(priv_data->plus_geom, b,
+                                      phy_area.start_sector,
+                                      phy_area.sector_count))
+                       return 0;
+
+               nb -= phy_area.sector_count; /* < nb anyway ... */
+               sector += phy_area.sector_count;
+                b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
+       }
+
+       return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/file_plus.h b/libparted/fs/r/hfs/file_plus.h
new file mode 100644
index 0000000..e2dcbd2
--- /dev/null
+++ b/libparted/fs/r/hfs/file_plus.h
@@ -0,0 +1,60 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _FILE_PLUS_H
+#define _FILE_PLUS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+HfsPPrivateFile*
+hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
+                  HfsPExtDataRec ext_desc, PedSector sect_nb);
+
+void
+hfsplus_file_close (HfsPPrivateFile* file);
+
+int
+hfsplus_file_read(HfsPPrivateFile* file, void *buf,
+                 PedSector sector, unsigned int nb);
+
+int
+hfsplus_file_write(HfsPPrivateFile* file, void *buf,
+                 PedSector sector, unsigned int nb);
+
+/* Read the nth sector of a file */
+/* return 0 on error */
+static __inline__ int
+hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector)
+{
+       return hfsplus_file_read(file, buf, sector, 1);
+}
+
+/* Write the nth sector of a file */
+/* return 0 on error */
+static __inline__ int
+hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector)
+{
+       return hfsplus_file_write(file, buf, sector, 1);
+}
+
+
+#endif /* _FILE_PLUS_H */
diff --git a/libparted/fs/r/hfs/hfs.c b/libparted/fs/r/hfs/hfs.c
new file mode 100644
index 0000000..9ff6d54
--- /dev/null
+++ b/libparted/fs/r/hfs/hfs.c
@@ -0,0 +1,1356 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2000, 2003-2005, 2007, 2009-2011 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/>.
+*/
+
+/*
+   Author : Guillaume Knispel <address@hidden>
+   Report bug to <address@hidden>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "probe.h"
+
+uint8_t* hfs_block = NULL;
+uint8_t* hfsp_block = NULL;
+unsigned hfs_block_count;
+unsigned hfsp_block_count;
+
+#define HFS_BLOCK_SIZES       ((int[2]){512, 0})
+#define HFSP_BLOCK_SIZES       ((int[2]){512, 0})
+#define HFSX_BLOCK_SIZES       ((int[2]){512, 0})
+
+#ifndef DISCOVER_ONLY
+#include "file.h"
+#include "reloc.h"
+#include "advfs.h"
+
+static PedFileSystemType hfs_type;
+static PedFileSystemType hfsplus_type;
+
+
+/* ----- HFS ----- */
+
+/* This is a very unundoable operation */
+/* Maybe I shouldn't touch the alternate MDB ? */
+/* Anyway clobber is call before other fs creation */
+/* So this is a non-issue */
+static int
+hfs_clobber (PedGeometry* geom)
+{
+       uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+
+       memset (buf, 0, PED_SECTOR_SIZE_DEFAULT);
+
+       /* destroy boot blocks, mdb, alternate mdb ... */
+       return  (!!ped_geometry_write (geom, buf, 0, 1)) &
+               (!!ped_geometry_write (geom, buf, 1, 1)) &
+               (!!ped_geometry_write (geom, buf, 2, 1)) &
+               (!!ped_geometry_write (geom, buf, geom->length - 2, 1)) &
+               (!!ped_geometry_write (geom, buf, geom->length - 1, 1)) &
+               (!!ped_geometry_sync  (geom));
+}
+
+static PedFileSystem*
+hfs_open (PedGeometry* geom)
+{
+       uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
+       PedFileSystem*          fs;
+       HfsMasterDirectoryBlock* mdb;
+       HfsPrivateFSData*       priv_data;
+
+       if (!hfsc_can_use_geom (geom))
+               return NULL;
+
+       /* Read MDB */
+       if (!ped_geometry_read (geom, buf, 2, 1))
+               return NULL;
+
+       /* Allocate memory */
+       fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+       if (!fs) goto ho;
+       mdb = (HfsMasterDirectoryBlock*)
+               ped_malloc (sizeof (HfsMasterDirectoryBlock));
+       if (!mdb) goto ho_fs;
+       priv_data = (HfsPrivateFSData*)
+               ped_malloc (sizeof (HfsPrivateFSData));
+       if (!priv_data) goto ho_mdb;
+
+       memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock));
+
+       /* init structures */
+       priv_data->mdb = mdb;
+       priv_data->bad_blocks_loaded = 0;
+       priv_data->bad_blocks_xtent_nb = 0;
+       priv_data->bad_blocks_xtent_list = NULL;
+       priv_data->extent_file =
+           hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
+                          mdb->extents_file_rec,
+                          PED_CPU_TO_BE32 (mdb->extents_file_size)
+                          / PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->extent_file) goto ho_pd;
+       priv_data->catalog_file =
+           hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
+                          mdb->catalog_file_rec,
+                          PED_CPU_TO_BE32 (mdb->catalog_file_size)
+                          / PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->catalog_file) goto ho_ce;
+       /* Read allocation blocks */
+       if (!ped_geometry_read(geom, priv_data->alloc_map,
+                              PED_BE16_TO_CPU (mdb->volume_bitmap_block),
+                              ( PED_BE16_TO_CPU (mdb->total_blocks)
+                                + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+                              / (PED_SECTOR_SIZE_DEFAULT * 8) ) )
+               goto ho_cf;
+
+       fs->type = &hfs_type;
+       fs->geom = ped_geometry_duplicate (geom);
+       if (!fs->geom) goto ho_cf;
+       fs->type_specific = (void*) priv_data;
+       fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes)
+                       >> HFS_UNMOUNTED ) & 1;
+
+       return fs;
+
+/*--- clean error handling ---*/
+ho_cf: hfs_file_close(priv_data->catalog_file);
+ho_ce: hfs_file_close(priv_data->extent_file);
+ho_pd: free(priv_data);
+ho_mdb: free(mdb);
+ho_fs: free(fs);
+ho:    return NULL;
+}
+
+static int
+hfs_close (PedFileSystem *fs)
+{
+       HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific;
+
+       hfs_file_close (priv_data->extent_file);
+       hfs_file_close (priv_data->catalog_file);
+       if (priv_data->bad_blocks_loaded)
+               hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list);
+       free (priv_data->mdb);
+       free (priv_data);
+       ped_geometry_destroy (fs->geom);
+       free (fs);
+
+       return 1;
+}
+
+static PedConstraint*
+hfs_get_resize_constraint (const PedFileSystem *fs)
+{
+       PedDevice*      dev = fs->geom->dev;
+       PedAlignment    start_align;
+       PedGeometry     start_sector;
+       PedGeometry     full_dev;
+       PedSector       min_size;
+
+       if (!ped_alignment_init (&start_align, fs->geom->start, 0))
+               return NULL;
+       if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
+               return NULL;
+       if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+               return NULL;
+       /* 2 = last two sectors (alternate MDB and unused sector) */
+       min_size = hfs_get_empty_end(fs) + 2;
+       if (min_size == 2) return NULL;
+
+       return ped_constraint_new (&start_align, ped_alignment_any,
+                                  &start_sector, &full_dev, min_size,
+                                  fs->geom->length);
+}
+
+static int
+hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+       uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
+       unsigned int            nblock, nfree;
+       unsigned int            block, to_free;
+       HfsPrivateFSData*       priv_data;
+       HfsMasterDirectoryBlock* mdb;
+       int                     resize = 1;
+       unsigned int            hfs_sect_block;
+       PedSector               hgee;
+
+       /* check preconditions */
+       PED_ASSERT (fs != NULL);
+       PED_ASSERT (fs->geom != NULL);
+       PED_ASSERT (geom != NULL);
+#ifdef DEBUG
+        PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0);
+#else
+        if ((hgee = hfs_get_empty_end(fs)) == 0)
+                return 0;
+#endif
+
+       PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0);
+
+       if (ped_geometry_test_equal(fs->geom, geom))
+               return 1;
+
+       priv_data = (HfsPrivateFSData*) fs->type_specific;
+       mdb = priv_data->mdb;
+       hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size)
+                        / PED_SECTOR_SIZE_DEFAULT;
+
+       if (fs->geom->start != geom->start
+           || geom->length > fs->geom->length
+           || geom->length < hgee + 2) {
+               ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_CANCEL,
+                       _("Sorry, HFS cannot be resized that way yet."));
+               return 0;
+       }
+
+       /* Flush caches */
+       if (!ped_geometry_sync(fs->geom))
+               return 0;
+
+       /* Clear the unmounted bit */
+       mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED ));
+       if (!ped_geometry_read (fs->geom, buf, 2, 1))
+               return 0;
+       memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock));
+       if (   !ped_geometry_write (fs->geom, buf, 2, 1)
+           || !ped_geometry_sync  (fs->geom))
+               return 0;
+
+       ped_timer_reset (timer);
+       ped_timer_set_state_name(timer, _("shrinking"));
+       ped_timer_update(timer, 0.0);
+       /* relocate data */
+       to_free = ( fs->geom->length - geom->length
+                   + hfs_sect_block - 1 )
+                 / hfs_sect_block ;
+       block = hfs_find_start_pack (fs, to_free);
+       if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) {
+               resize = 0;
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Data relocation has failed."));
+               goto write_MDB;
+       }
+
+       /* Calculate new block number and other MDB field */
+       nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) )
+                / hfs_sect_block;
+       nfree = PED_BE16_TO_CPU (mdb->free_blocks)
+               - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock );
+
+       /* Check that all block after future end are really free */
+       for (block = nblock;
+            block < PED_BE16_TO_CPU (mdb->total_blocks);
+            block++) {
+               if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
+                       resize = 0;
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("Data relocation left some data in the end "
+                                 "of the volume."));
+                       goto write_MDB;
+               }
+       }
+
+       /* Mark out of volume blocks as used
+       (broken implementations compatibility) */
+       for ( block = nblock; block < (1 << 16); ++block)
+               SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
+
+       /* save the allocation map
+       I do not write until start of allocation blocks
+       but only until pre-resize end of bitmap blocks
+       because the specifications do _not_ assert that everything
+       until allocation blocks is boot, mdb and alloc */
+       ped_geometry_write(fs->geom, priv_data->alloc_map,
+               PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
+               ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
+                 + PED_SECTOR_SIZE_DEFAULT * 8 - 1)
+               / (PED_SECTOR_SIZE_DEFAULT * 8));
+
+       /* Update geometry */
+       if (resize) {
+               /* update in fs structure */
+               if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock)
+                       mdb->next_allocation = PED_CPU_TO_BE16 (0);
+               mdb->total_blocks = PED_CPU_TO_BE16 (nblock);
+               mdb->free_blocks = PED_CPU_TO_BE16 (nfree);
+               /* update parted structure */
+               fs->geom->length = geom->length;
+               fs->geom->end = fs->geom->start + geom->length - 1;
+       }
+
+       /* Set the unmounted bit */
+       mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED );
+
+       /* Effective write */
+    write_MDB:
+       ped_timer_set_state_name(timer,_("writing HFS Master Directory Block"));
+
+       if (!hfs_update_mdb(fs)) {
+               ped_geometry_sync(geom);
+               return 0;
+       }
+
+       if (!ped_geometry_sync(geom))
+               return 0;
+
+       ped_timer_update(timer, 1.0);
+
+       return (resize);
+}
+
+/* ----- HFS+ ----- */
+
+#include "file_plus.h"
+#include "advfs_plus.h"
+#include "reloc_plus.h"
+#include "journal.h"
+
+static int
+hfsplus_clobber (PedGeometry* geom)
+{
+       unsigned int i = 1;
+       uint8_t                         buf[PED_SECTOR_SIZE_DEFAULT];
+       HfsMasterDirectoryBlock         *mdb;
+
+       mdb = (HfsMasterDirectoryBlock *) buf;
+
+       if (!ped_geometry_read (geom, buf, 2, 1))
+               return 0;
+
+       if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) {
+               /* embedded hfs+ */
+               PedGeometry     *embedded;
+
+               i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT;
+               embedded = ped_geometry_new (
+                   geom->dev,
+                   (PedSector) geom->start
+                    + PED_BE16_TO_CPU (mdb->start_block)
+                    + (PedSector) PED_BE16_TO_CPU (
+                       mdb->old_new.embedded.location.start_block ) * i,
+                   (PedSector) PED_BE16_TO_CPU (
+                       mdb->old_new.embedded.location.block_count ) * i );
+               if (!embedded) i = 0;
+               else {
+                       i = hfs_clobber (embedded);
+                       ped_geometry_destroy (embedded);
+               }
+       }
+
+       /* non-embedded or envelop destroy as hfs */
+       return ( hfs_clobber (geom) && i );
+}
+
+static int
+hfsplus_close (PedFileSystem *fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+
+       if (priv_data->bad_blocks_loaded)
+               hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
+       free(priv_data->alloc_map);
+       free(priv_data->dirty_alloc_map);
+       hfsplus_file_close (priv_data->allocation_file);
+       hfsplus_file_close (priv_data->attributes_file);
+       hfsplus_file_close (priv_data->catalog_file);
+       hfsplus_file_close (priv_data->extents_file);
+       if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
+       if (priv_data->wrapper) hfs_close(priv_data->wrapper);
+       ped_geometry_destroy (fs->geom);
+       free(priv_data->vh);
+       free(priv_data);
+       free(fs);
+
+       return 1;
+}
+
+static PedFileSystem*
+hfsplus_open (PedGeometry* geom)
+{
+       uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
+       PedFileSystem*          fs;
+       HfsPVolumeHeader*       vh;
+       HfsPPrivateFSData*      priv_data;
+       PedGeometry*            wrapper_geom;
+       unsigned int            map_sectors;
+
+       if (!hfsc_can_use_geom (geom))
+               return NULL;
+
+       fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+       if (!fs) goto hpo;
+       vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader));
+       if (!vh) goto hpo_fs;
+       priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData));
+       if (!priv_data) goto hpo_vh;
+
+       fs->geom = ped_geometry_duplicate (geom);
+       if (!fs->geom) goto hpo_pd;
+       fs->type_specific = (void*) priv_data;
+
+       if ((wrapper_geom = hfs_and_wrapper_probe (geom))) {
+               HfsPrivateFSData*       hfs_priv_data;
+               PedSector               abs_sect, length;
+               unsigned int            bs;
+
+               ped_geometry_destroy (wrapper_geom);
+               priv_data->wrapper = hfs_open(geom);
+               if (!priv_data->wrapper) goto hpo_gm;
+               hfs_priv_data = (HfsPrivateFSData*)
+                       priv_data->wrapper->type_specific;
+               bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+                    / PED_SECTOR_SIZE_DEFAULT;
+               abs_sect = (PedSector) geom->start
+                          + (PedSector) PED_BE16_TO_CPU (
+                                           hfs_priv_data->mdb->start_block)
+                          + (PedSector) PED_BE16_TO_CPU (
+                                           hfs_priv_data->mdb->old_new
+                                           .embedded.location.start_block )
+                                        * bs;
+               length = (PedSector) PED_BE16_TO_CPU (
+                                           hfs_priv_data->mdb->old_new
+                                           .embedded.location.block_count)
+                                    * bs;
+               priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect,
+                                                        length);
+               if (!priv_data->plus_geom) goto hpo_wr;
+               priv_data->free_geom = 1;
+       } else {
+               priv_data->wrapper = NULL;
+               priv_data->plus_geom = fs->geom;
+               priv_data->free_geom = 0;
+       }
+
+       if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg;
+       memcpy (vh, buf, sizeof (HfsPVolumeHeader));
+       priv_data->vh = vh;
+
+       if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE)
+           && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) {
+               ped_exception_throw (
+                       PED_EXCEPTION_BUG,
+                       PED_EXCEPTION_CANCEL,
+                       _("No valid HFS[+X] signature has been found while "
+                         "opening."));
+               goto hpo_pg;
+       }
+
+       if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE)
+           && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("Version %d of HFS+ isn't supported."),
+                       PED_BE16_TO_CPU(vh->version))
+                               != PED_EXCEPTION_IGNORE)
+                       goto hpo_pg;
+       }
+
+       if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE)
+           && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) {
+               if (ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_IGNORE_CANCEL,
+                       _("Version %d of HFSX isn't supported."),
+                       PED_BE16_TO_CPU(vh->version))
+                               != PED_EXCEPTION_IGNORE)
+                       goto hpo_pg;
+       }
+
+       priv_data->jib_start_block = 0;
+       priv_data->jl_start_block = 0;
+       if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) {
+               if (!hfsj_replay_journal(fs))
+                       goto hpo_pg;
+       }
+
+       priv_data->bad_blocks_loaded = 0;
+       priv_data->bad_blocks_xtent_nb = 0;
+       priv_data->bad_blocks_xtent_list = NULL;
+       priv_data->extents_file =
+               hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
+                                  vh->extents_file.extents,
+                                  PED_BE64_TO_CPU (
+                                       vh->extents_file.logical_size )
+                                  / PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->extents_file) goto hpo_pg;
+       priv_data->catalog_file =
+               hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
+                                  vh->catalog_file.extents,
+                                  PED_BE64_TO_CPU (
+                                       vh->catalog_file.logical_size )
+                                  / PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->catalog_file) goto hpo_ce;
+       priv_data->attributes_file =
+               hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID),
+                                  vh->attributes_file.extents,
+                                  PED_BE64_TO_CPU (
+                                       vh->attributes_file.logical_size)
+                                  / PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->attributes_file) goto hpo_cc;
+
+       map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks)
+                       + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+                     / (PED_SECTOR_SIZE_DEFAULT * 8);
+       priv_data->dirty_alloc_map = (uint8_t*)
+               ped_malloc ((map_sectors + 7) / 8);
+       if (!priv_data->dirty_alloc_map) goto hpo_cl;
+       memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8);
+       priv_data->alloc_map = (uint8_t*)
+               ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->alloc_map) goto hpo_dm;
+
+       priv_data->allocation_file =
+               hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID),
+                                  vh->allocation_file.extents,
+                                  PED_BE64_TO_CPU (
+                                       vh->allocation_file.logical_size)
+                                  / PED_SECTOR_SIZE_DEFAULT);
+       if (!priv_data->allocation_file) goto hpo_am;
+       if (!hfsplus_file_read (priv_data->allocation_file,
+                               priv_data->alloc_map, 0, map_sectors)) {
+               hfsplus_close(fs);
+               return NULL;
+       }
+
+       fs->type = &hfsplus_type;
+       fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1)
+             && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1);
+
+       return fs;
+
+/*--- clean error handling ---*/
+hpo_am: free(priv_data->alloc_map);
+hpo_dm: free(priv_data->dirty_alloc_map);
+hpo_cl: hfsplus_file_close (priv_data->attributes_file);
+hpo_cc:        hfsplus_file_close (priv_data->catalog_file);
+hpo_ce:        hfsplus_file_close (priv_data->extents_file);
+hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
+hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper);
+hpo_gm: ped_geometry_destroy (fs->geom);
+hpo_pd: free(priv_data);
+hpo_vh: free(vh);
+hpo_fs: free(fs);
+hpo:   return NULL;
+}
+
+static PedConstraint*
+hfsplus_get_resize_constraint (const PedFileSystem *fs)
+{
+       PedDevice*      dev = fs->geom->dev;
+       PedAlignment    start_align;
+       PedGeometry     start_sector;
+       PedGeometry     full_dev;
+       PedSector       min_size;
+
+       if (!ped_alignment_init (&start_align, fs->geom->start, 0))
+               return NULL;
+       if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
+               return NULL;
+       if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+               return NULL;
+
+       min_size = hfsplus_get_min_size (fs);
+       if (!min_size) return NULL;
+
+       return ped_constraint_new (&start_align, ped_alignment_any,
+                                  &start_sector, &full_dev, min_size,
+                                  fs->geom->length);
+}
+
+static int
+hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+       uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
+       unsigned int            nblock, nfree, mblock;
+       unsigned int            block, to_free, old_blocks;
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPVolumeHeader*       vh = priv_data->vh;
+       int                     resize = 1;
+       unsigned int            hfsp_sect_block =
+                                   ( PED_BE32_TO_CPU (vh->block_size)
+                                     / PED_SECTOR_SIZE_DEFAULT );
+       unsigned int            map_sectors;
+
+       old_blocks = PED_BE32_TO_CPU (vh->total_blocks);
+
+       /* Flush caches */
+       if (!ped_geometry_sync(priv_data->plus_geom))
+               return 0;
+
+       /* Clear the unmounted bit */
+       /* and set the implementation code (Apple Creator Code) */
+       vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED ));
+       vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk);
+       if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1))
+               return 0;
+       memcpy (buf, vh, sizeof (HfsPVolumeHeader));
+       if (   !ped_geometry_write (priv_data->plus_geom, buf, 2, 1)
+           || !ped_geometry_sync (priv_data->plus_geom))
+               return 0;
+
+       ped_timer_reset (timer);
+       ped_timer_set_state_name(timer, _("shrinking"));
+       ped_timer_update(timer, 0.0);
+       /* relocate data */
+       to_free = ( priv_data->plus_geom->length
+                 - geom->length + hfsp_sect_block
+                 - 1 ) / hfsp_sect_block;
+       block = hfsplus_find_start_pack (fs, to_free);
+       if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) {
+               resize = 0;
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Data relocation has failed."));
+               goto write_VH;
+       }
+
+       /* Calculate new block number and other VH field */
+       /* nblock must be rounded _down_ */
+       nblock = geom->length / hfsp_sect_block;
+       nfree = PED_BE32_TO_CPU (vh->free_blocks)
+               - (old_blocks - nblock);
+       /* free block readjustement is only needed when incorrect nblock
+          was used by my previous implementation, so detect the case */
+       if (priv_data->plus_geom->length < old_blocks
+                                          * ( PED_BE32_TO_CPU (vh->block_size)
+                                              / PED_SECTOR_SIZE_DEFAULT) ) {
+               if (priv_data->plus_geom->length % hfsp_sect_block == 1)
+                       nfree++;
+       }
+
+       /* Check that all block after future end are really free */
+       mblock = ( priv_data->plus_geom->length - 2 )
+                / hfsp_sect_block;
+       if (mblock > old_blocks - 1)
+               mblock = old_blocks - 1;
+       for ( block = nblock;
+             block < mblock;
+             block++ ) {
+               if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
+                       resize = 0;
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("Data relocation left some data at the end "
+                                 "of the volume."));
+                       goto write_VH;
+               }
+       }
+
+       /* Mark out of volume blocks as used */
+       map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+                       / (PED_SECTOR_SIZE_DEFAULT * 8) )
+                     * (PED_SECTOR_SIZE_DEFAULT * 8);
+       for ( block = nblock; block < map_sectors; ++block)
+               SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
+
+       /* Update geometry */
+       if (resize) {
+               /* update in fs structure */
+               if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock)
+                       vh->next_allocation = PED_CPU_TO_BE32 (0);
+               vh->total_blocks = PED_CPU_TO_BE32 (nblock);
+               vh->free_blocks = PED_CPU_TO_BE32 (nfree);
+               /* update parted structure */
+               priv_data->plus_geom->length = geom->length;
+               priv_data->plus_geom->end = priv_data->plus_geom->start
+                                           + geom->length - 1;
+       }
+
+       /* Effective write */
+    write_VH:
+       /* lasts two sectors are allocated by the alternate VH
+          and a reserved sector, and last block is always reserved */
+       block = (priv_data->plus_geom->length - 1) / hfsp_sect_block;
+       if (block < PED_BE32_TO_CPU (vh->total_blocks))
+               SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
+       block = (priv_data->plus_geom->length - 2) / hfsp_sect_block;
+       if (block < PED_BE32_TO_CPU (vh->total_blocks))
+               SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
+       SET_BLOC_OCCUPATION(priv_data->alloc_map,
+                           PED_BE32_TO_CPU (vh->total_blocks) - 1);
+
+       /* Write the _old_ area to set out of volume blocks as used */
+       map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+                     / (PED_SECTOR_SIZE_DEFAULT * 8);
+       if (!hfsplus_file_write (priv_data->allocation_file,
+                                priv_data->alloc_map, 0, map_sectors)) {
+               resize = 0;
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Error while writing the allocation file."));
+       } else {
+       /* Write remaining part of allocation bitmap */
+       /* This is necessary to handle pre patch-11 and third party */
+       /* implementations */
+               memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT);
+               for (block = map_sectors;
+                    block < priv_data->allocation_file->sect_nb;
+                    ++block) {
+                       if (!hfsplus_file_write_sector (
+                                       priv_data->allocation_file,
+                                       buf, block)) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_WARNING,
+                                       PED_EXCEPTION_IGNORE,
+                                       _("Error while writing the "
+                                         "compatibility part of the "
+                                         "allocation file."));
+                               break;
+                       }
+               }
+       }
+       ped_geometry_sync (priv_data->plus_geom);
+
+       if (resize) {
+               /* Set the unmounted bit and clear the inconsistent bit */
+               vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED );
+               vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT );
+       }
+
+       ped_timer_set_state_name(timer, _("writing HFS+ Volume Header"));
+       if (!hfsplus_update_vh(fs)) {
+               ped_geometry_sync(priv_data->plus_geom);
+               return 0;
+       }
+
+       if (!ped_geometry_sync(priv_data->plus_geom))
+               return 0;
+
+       ped_timer_update(timer, 1.0);
+
+       return (resize);
+}
+
+/* Update the HFS wrapper mdb and bad blocks file to reflect
+   the new geometry of the embedded HFS+ volume */
+static int
+hfsplus_wrapper_update (PedFileSystem* fs)
+{
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+       HfsCPrivateLeafRec      ref;
+       HfsExtentKey            key;
+       HfsNodeDescriptor*      node_desc = (HfsNodeDescriptor*) node;
+       HfsExtentKey*           ret_key;
+       HfsExtDescriptor*       ret_data;
+       unsigned int            i;
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPrivateFSData*       hfs_priv_data = (HfsPrivateFSData*)
+                                           priv_data->wrapper->type_specific;
+       unsigned int            hfs_sect_block =
+                       PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+                       / PED_SECTOR_SIZE_DEFAULT ;
+       PedSector               hfsplus_sect = (PedSector)
+                       PED_BE32_TO_CPU (priv_data->vh->total_blocks)
+                       * ( PED_BE32_TO_CPU (priv_data->vh->block_size)
+                           / PED_SECTOR_SIZE_DEFAULT );
+       unsigned int            hfs_blocks_embedded =
+                                   (hfsplus_sect + hfs_sect_block - 1)
+                                   / hfs_sect_block;
+       unsigned int            hfs_blocks_embedded_old;
+
+       /* update HFS wrapper MDB */
+       hfs_blocks_embedded_old = PED_BE16_TO_CPU (
+                                       hfs_priv_data->mdb->old_new
+                                       .embedded.location.block_count );
+       hfs_priv_data->mdb->old_new.embedded.location.block_count =
+               PED_CPU_TO_BE16 (hfs_blocks_embedded);
+       /* maybe macOS will boot with this */
+       /* update : yes it does \o/ :) */
+       hfs_priv_data->mdb->free_blocks =
+           PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks)
+                           + hfs_blocks_embedded_old
+                           - hfs_blocks_embedded );
+
+       if (!hfs_update_mdb(priv_data->wrapper))
+               return 0;
+
+       /* force reload bad block list */
+       if (hfs_priv_data->bad_blocks_loaded) {
+               hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list);
+               hfs_priv_data->bad_blocks_xtent_list = NULL;
+               hfs_priv_data->bad_blocks_xtent_nb = 0;
+               hfs_priv_data->bad_blocks_loaded = 0;
+       }
+
+       /* clean HFS wrapper allocation map */
+       for (i = PED_BE16_TO_CPU (
+                       hfs_priv_data->mdb->old_new.embedded
+                       .location.start_block )
+                + hfs_blocks_embedded;
+            i < PED_BE16_TO_CPU (
+                       hfs_priv_data->mdb->old_new.embedded
+                       .location.start_block )
+                + hfs_blocks_embedded_old;
+            i++ ) {
+               CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i);
+       }
+       /* and save it */
+       if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map,
+                                PED_BE16_TO_CPU (
+                                     hfs_priv_data->mdb->volume_bitmap_block ),
+                                ( PED_BE16_TO_CPU (
+                                       hfs_priv_data->mdb->total_blocks )
+                                  + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+                                / (PED_SECTOR_SIZE_DEFAULT * 8)))
+               return 0;
+       if (!ped_geometry_sync (fs->geom))
+               return 0;
+
+       /* search and update the bad blocks file */
+       key.key_length = sizeof(key) - 1;
+       key.type = HFS_DATA_FORK;
+       key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
+       key.start = 0;
+       if (!hfs_btree_search (hfs_priv_data->extent_file,
+                              (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("An error occurred while looking for the mandatory "
+                         "bad blocks file."));
+               return 0;
+       }
+       if (!hfs_file_read_sector (hfs_priv_data->extent_file, node,
+                                  ref.node_number))
+               return 0;
+       ret_key = (HfsExtentKey*) (node + ref.record_pos);
+       ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
+                                        + sizeof (HfsExtentKey) );
+
+       while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) {
+               for (i = 0; i < HFS_EXT_NB; i++) {
+                       if ( ret_data[i].start_block
+                            == hfs_priv_data->mdb->old_new
+                               .embedded.location.start_block) {
+                               ret_data[i].block_count =
+                                   hfs_priv_data->mdb->old_new
+                                   .embedded.location.block_count;
+                               /* found ! : update */
+                               if (!hfs_file_write_sector (
+                                         hfs_priv_data->extent_file,
+                                         node, ref.node_number)
+                                   || !ped_geometry_sync(fs->geom))
+                                       return 0;
+                               return 1;
+                       }
+               }
+
+               if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) {
+                       ref.record_number++;
+               } else {
+                       ref.node_number = PED_BE32_TO_CPU (node_desc->next);
+                       if (!ref.node_number
+                           || !hfs_file_read_sector(hfs_priv_data->extent_file,
+                                                    node, ref.node_number))
+                               goto bb_not_found;
+                       ref.record_number = 1;
+               }
+
+               ref.record_pos =
+                       PED_BE16_TO_CPU (*((uint16_t *)
+                               (node + (PED_SECTOR_SIZE_DEFAULT
+                                        - 2*ref.record_number))));
+               ret_key = (HfsExtentKey*) (node + ref.record_pos);
+               ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
+                                                + sizeof (HfsExtentKey) );
+       }
+
+bb_not_found:
+       /* not found : not a valid hfs+ wrapper : failure */
+       ped_exception_throw (
+               PED_EXCEPTION_ERROR,
+               PED_EXCEPTION_CANCEL,
+               _("It seems there is an error in the HFS wrapper: the bad "
+                 "blocks file doesn't contain the embedded HFS+ volume."));
+       return 0;
+}
+
+static int
+hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data;
+       PedTimer*               timer_plus;
+       PedGeometry*            embedded_geom;
+       PedSector               hgms;
+
+       /* check preconditions */
+       PED_ASSERT (fs != NULL);
+       PED_ASSERT (fs->geom != NULL);
+       PED_ASSERT (geom != NULL);
+       PED_ASSERT (fs->geom->dev == geom->dev);
+#ifdef DEBUG
+        PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0);
+#else
+        if ((hgms = hfsplus_get_min_size (fs)) == 0)
+                return 0;
+#endif
+
+       if (ped_geometry_test_equal(fs->geom, geom))
+               return 1;
+
+       priv_data = (HfsPPrivateFSData*) fs->type_specific;
+
+       if (fs->geom->start != geom->start
+           || geom->length > fs->geom->length
+           || geom->length < hgms) {
+               ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_CANCEL,
+                       _("Sorry, HFS+ cannot be resized that way yet."));
+               return 0;
+       }
+
+       if (priv_data->wrapper) {
+               PedSector               red, hgee;
+               HfsPrivateFSData*       hfs_priv_data = (HfsPrivateFSData*)
+                                           priv_data->wrapper->type_specific;
+               unsigned int            hfs_sect_block =
+                           PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+                           / PED_SECTOR_SIZE_DEFAULT;
+
+               /* There is a wrapper so we must calculate the new geometry
+                  of the embedded HFS+ volume */
+               red = ( (fs->geom->length - geom->length + hfs_sect_block - 1)
+                       / hfs_sect_block ) * hfs_sect_block;
+               /* Can't we shrink the hfs+ volume by the desired size ? */
+               hgee = hfsplus_get_empty_end (fs);
+               if (!hgee) return 0;
+               if (red > priv_data->plus_geom->length - hgee) {
+                       /* No, shrink hfs+ by the greatest possible value */
+                       hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block)
+                              * hfs_sect_block;
+                       red = priv_data->plus_geom->length - hgee;
+               }
+               embedded_geom = ped_geometry_new (geom->dev,
+                                                 priv_data->plus_geom->start,
+                                                 priv_data->plus_geom->length
+                                                 - red);
+
+               /* There is a wrapper so the resize process is a two stages
+                  process (embedded resizing then wrapper resizing) :
+                  we create a sub timer */
+               ped_timer_reset (timer);
+               ped_timer_set_state_name (timer,
+                                         _("shrinking embedded HFS+ volume"));
+               ped_timer_update(timer, 0.0);
+               timer_plus = ped_timer_new_nested (timer, 0.98);
+       } else {
+               /* No wrapper : the desired geometry is the desired
+                  HFS+ volume geometry */
+               embedded_geom = geom;
+               timer_plus = timer;
+       }
+
+       /* Resize the HFS+ volume */
+       if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) {
+               if (timer_plus != timer) ped_timer_destroy_nested (timer_plus);
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Resizing the HFS+ volume has failed."));
+               return 0;
+       }
+
+       if (priv_data->wrapper) {
+               ped_geometry_destroy (embedded_geom);
+               ped_timer_destroy_nested (timer_plus);
+               ped_timer_set_state_name(timer, _("shrinking HFS wrapper"));
+               timer_plus = ped_timer_new_nested (timer, 0.02);
+               /* There's a wrapper : second stage = resizing it */
+               if (!hfsplus_wrapper_update (fs)
+                   || !hfs_resize (priv_data->wrapper, geom, timer_plus)) {
+                       ped_timer_destroy_nested (timer_plus);
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("Updating the HFS wrapper has failed."));
+                       return 0;
+               }
+               ped_timer_destroy_nested (timer_plus);
+       }
+       ped_timer_update(timer, 1.0);
+
+       return 1;
+}
+
+#ifdef HFS_EXTRACT_FS
+/* The following is for debugging purpose only, NOT for packaging */
+
+#include <stdio.h>
+
+uint8_t* extract_buffer = NULL;
+
+static int
+hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file)
+{
+       FILE*           fout;
+       PedSector       sect;
+
+       fout = fopen(filename, "w");
+       if (!fout) return 0;
+
+       for (sect = 0; sect < hfs_file->sect_nb; ++sect) {
+               if (!hfs_file_read_sector(hfs_file, extract_buffer, sect))
+                       goto err_close;
+               if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
+                       goto err_close;
+       }
+
+       return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+       fclose(fout);
+       return 0;
+}
+
+static int
+hfs_extract_bitmap(const char* filename, PedFileSystem* fs)
+{
+       HfsPrivateFSData*               priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsMasterDirectoryBlock*        mdb = priv_data->mdb;
+       unsigned int    count;
+       FILE*           fout;
+       PedSector       sect;
+
+       fout = fopen(filename, "w");
+       if (!fout) return 0;
+
+       for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block);
+            sect < PED_BE16_TO_CPU(mdb->start_block);
+            sect += count) {
+               uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block);
+               count = (st_block-sect) < BLOCK_MAX_BUFF ?
+                       (st_block-sect) : BLOCK_MAX_BUFF;
+               if (!ped_geometry_read(fs->geom, extract_buffer, sect, count))
+                       goto err_close;
+               if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT,
+                            1, fout))
+                       goto err_close;
+       }
+
+       return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+       fclose(fout);
+       return 0;
+}
+
+static int
+hfs_extract_mdb (const char* filename, PedFileSystem* fs)
+{
+       FILE*           fout;
+
+       fout = fopen(filename, "w");
+       if (!fout) return 0;
+
+       if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1))
+               goto err_close;
+       if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
+               goto err_close;
+
+       return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+       fclose(fout);
+       return 0;
+}
+
+static int
+hfs_extract (PedFileSystem* fs, PedTimer* timer)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+
+       ped_exception_throw (
+               PED_EXCEPTION_INFORMATION,
+               PED_EXCEPTION_OK,
+               _("This is not a real %s check.  This is going to extract "
+                 "special low level files for debugging purposes."),
+               "HFS");
+
+       extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
+       if (!extract_buffer) return 0;
+
+       hfs_extract_mdb(HFS_MDB_FILENAME, fs);
+       hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file);
+       hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file);
+       hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs);
+
+       free(extract_buffer); extract_buffer = NULL;
+       return 0; /* nothing has been fixed by us ! */
+}
+
+static int
+hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file)
+{
+       FILE*           fout;
+       unsigned int    cp_sect;
+       PedSector       rem_sect;
+
+       fout = fopen(filename, "w");
+       if (!fout) return 0;
+
+       for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) {
+               cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF;
+               if (!hfsplus_file_read(hfsp_file, extract_buffer,
+                                      hfsp_file->sect_nb - rem_sect, cp_sect))
+                       goto err_close;
+               if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT,
+                            1, fout))
+                       goto err_close;
+       }
+
+       return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+       fclose(fout);
+       return 0;
+}
+
+static int
+hfsplus_extract_vh (const char* filename, PedFileSystem* fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       FILE*           fout;
+       PedGeometry*    geom = priv_data->plus_geom;
+
+
+       fout = fopen(filename, "w");
+       if (!fout) return 0;
+
+       if (!ped_geometry_read(geom, extract_buffer, 2, 1))
+               goto err_close;
+       if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
+               goto err_close;
+
+       return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+       fclose(fout);
+       return 0;
+}
+
+/* TODO : use the timer to report what is happening */
+/* TODO : use exceptions to report errors */
+static int
+hfsplus_extract (PedFileSystem* fs, PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPVolumeHeader*       vh = priv_data->vh;
+       HfsPPrivateFile*        startup_file;
+
+       if (priv_data->wrapper) {
+               /* TODO : create nested timer */
+               hfs_extract (priv_data->wrapper, timer);
+       }
+
+       ped_exception_throw (
+               PED_EXCEPTION_INFORMATION,
+               PED_EXCEPTION_OK,
+               _("This is not a real %s check.  This is going to extract "
+                 "special low level files for debugging purposes."),
+               "HFS+");
+
+       extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
+       if (!extract_buffer) return 0;
+
+       hfsplus_extract_vh(HFSP_VH_FILENAME, fs);
+       hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file);
+       hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file);
+       hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file);
+       hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file);
+
+       startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID),
+                                       vh->startup_file.extents,
+                                       PED_BE64_TO_CPU (
+                                          vh->startup_file.logical_size)
+                                       / PED_SECTOR_SIZE_DEFAULT);
+       if (startup_file) {
+               hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file);
+               hfsplus_file_close(startup_file); startup_file = NULL;
+       }
+
+       free(extract_buffer); extract_buffer = NULL;
+       return 0; /* nothing has been fixed by us ! */
+}
+#endif /* HFS_EXTRACT_FS */
+
+#endif /* !DISCOVER_ONLY */
+
+#if 0
+static PedFileSystemOps hfs_ops = {
+       probe:          hfs_probe,
+#ifndef DISCOVER_ONLY
+       clobber:        hfs_clobber,
+       open:           hfs_open,
+       create:         NULL,
+       close:          hfs_close,
+#ifndef HFS_EXTRACT_FS
+       check:          NULL,
+#else
+       check:          hfs_extract,
+#endif
+       copy:           NULL,
+       resize:         hfs_resize,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  hfs_get_resize_constraint,
+       get_copy_constraint:    NULL,
+#else /* DISCOVER_ONLY */
+       clobber:        NULL,
+       open:           NULL,
+       create:         NULL,
+       close:          NULL,
+       check:          NULL,
+       copy:           NULL,
+       resize:         NULL,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  NULL,
+       get_copy_constraint:    NULL,
+#endif /* DISCOVER_ONLY */
+};
+
+static PedFileSystemOps hfsplus_ops = {
+       probe:          hfsplus_probe,
+#ifndef DISCOVER_ONLY
+       clobber:        hfsplus_clobber,
+       open:           hfsplus_open,
+       create:         NULL,
+       close:          hfsplus_close,
+#ifndef HFS_EXTRACT_FS
+       check:          NULL,
+#else
+       check:          hfsplus_extract,
+#endif
+       copy:           NULL,
+       resize:         hfsplus_resize,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  hfsplus_get_resize_constraint,
+       get_copy_constraint:    NULL,
+#else /* DISCOVER_ONLY */
+       clobber:        NULL,
+       open:           NULL,
+       create:         NULL,
+       close:          NULL,
+       check:          NULL,
+       copy:           NULL,
+       resize:         NULL,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  NULL,
+       get_copy_constraint:    NULL,
+#endif /* DISCOVER_ONLY */
+};
+
+static PedFileSystemOps hfsx_ops = {
+       probe:          hfsx_probe,
+#ifndef DISCOVER_ONLY
+       clobber:        hfs_clobber, /* NOT hfsplus_clobber !
+                                       HFSX can't be embedded */
+       open:           hfsplus_open,
+       create:         NULL,
+       close:          hfsplus_close,
+#ifndef HFS_EXTRACT_FS
+       check:          NULL,
+#else
+       check:          hfsplus_extract,
+#endif
+       copy:           NULL,
+       resize:         hfsplus_resize,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  hfsplus_get_resize_constraint,
+       get_copy_constraint:    NULL,
+#else /* DISCOVER_ONLY */
+       clobber:        NULL,
+       open:           NULL,
+       create:         NULL,
+       close:          NULL,
+       check:          NULL,
+       copy:           NULL,
+       resize:         NULL,
+       get_create_constraint:  NULL,
+       get_resize_constraint:  NULL,
+       get_copy_constraint:    NULL,
+#endif /* DISCOVER_ONLY */
+};
+
+
+static PedFileSystemType hfs_type = {
+       next:   NULL,
+       ops:    &hfs_ops,
+       name:   "hfs",
+       block_sizes: HFS_BLOCK_SIZES
+};
+
+static PedFileSystemType hfsplus_type = {
+       next:   NULL,
+       ops:    &hfsplus_ops,
+       name:   "hfs+",
+       block_sizes: HFSP_BLOCK_SIZES
+};
+
+static PedFileSystemType hfsx_type = {
+       next:   NULL,
+       ops:    &hfsx_ops,
+       name:   "hfsx",
+       block_sizes: HFSX_BLOCK_SIZES
+};
+
+void
+ped_file_system_hfs_init ()
+{
+       ped_file_system_type_register (&hfs_type);
+       ped_file_system_type_register (&hfsplus_type);
+       ped_file_system_type_register (&hfsx_type);
+}
+
+void
+ped_file_system_hfs_done ()
+{
+       ped_file_system_type_unregister (&hfs_type);
+       ped_file_system_type_unregister (&hfsplus_type);
+       ped_file_system_type_unregister (&hfsx_type);
+}
+#endif
diff --git a/libparted/fs/r/hfs/hfs.h b/libparted/fs/r/hfs/hfs.h
new file mode 100644
index 0000000..fe5de2c
--- /dev/null
+++ b/libparted/fs/r/hfs/hfs.h
@@ -0,0 +1,647 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2003-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _HFS_H
+#define _HFS_H
+
+/* WARNING : bn is used 2 times in theses macro */
+/* so _never_ use side effect operators when using them */
+#define TST_BLOC_OCCUPATION(tab,bn) \
+       (((tab)[(bn)/8])  &  (1<<(7-((bn)&7))))
+#define SET_BLOC_OCCUPATION(tab,bn) \
+       (((tab)[(bn)/8]) |=  (1<<(7-((bn)&7))))
+#define CLR_BLOC_OCCUPATION(tab,bn) \
+       (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7))))
+
+/* Maximum number of blocks for the copy buffers */
+#define BLOCK_MAX_BUFF 256
+/* Maximum size of the copy buffers, in bytes */
+#define BYTES_MAX_BUFF 8388608
+
+/* Apple Creator Codes follow */
+#define HFSP_IMPL_Shnk 0x53686e6b      /* in use */
+#define HFSP_IMPL_Xpnd 0x58706e64      /* reserved */
+#define HFSP_IMPL_Resz 0x5265737a      /* reserved */
+#define HFSP_IMPL_PHpx 0x50482b78      /* reserved */
+#define HFSP_IMPL_traP  0x74726150     /* reserved */
+#define HFSP_IMPL_GnuP  0x476e7550     /* reserved */
+
+#define HFS_SIGNATURE  0x4244          /* 'BD' */
+#define HFSP_SIGNATURE 0x482B          /* 'H+' */
+#define HFSX_SIGNATURE  0x4858         /* 'HX' */
+
+#define HFSP_VERSION    4
+#define HFSX_VERSION    5
+
+#define HFS_HARD_LOCK   7
+#define HFS_UNMOUNTED   8
+#define HFS_BAD_SPARED  9
+#define HFS_SOFT_LOCK  15
+#define HFSP_NO_CACHE  10
+#define HFSP_INCONSISTENT 11
+#define HFSP_REUSE_CNID        12
+#define HFSP_JOURNALED 13
+
+#define HFS_IDX_NODE   0x00
+#define HFS_HDR_NODE   0x01
+#define HFS_MAP_NODE   0x02
+#define HFS_LEAF_NODE  0xFF
+
+#define HFS_FIRST_REC  0x0E
+#define HFS_NSD_HD_REC 0x78
+#define HFS_MAP_REC    0xF8
+
+#define HFS_DATA_FORK  0x00
+#define HFS_RES_FORK   0xFF
+
+#define HFS_CAT_DIR    0x01
+#define HFS_CAT_FILE   0x02
+#define HFS_CAT_DIR_TH 0x03
+#define HFS_CAT_FILE_TH        0x04
+
+#define HFSP_ATTR_INLINE       0x10
+#define HFSP_ATTR_FORK         0x20
+#define HFSP_ATTR_EXTENTS      0x30
+
+#define HFS_ROOT_PAR_ID                0x01
+#define HFS_ROOT_DIR_ID                0x02
+#define HFS_XTENT_ID           0x03
+#define HFS_CATALOG_ID         0x04
+#define HFS_BAD_BLOCK_ID       0x05
+#define HFSP_ALLOC_ID          0x06
+#define HFSP_STARTUP_ID                0x07
+#define HFSP_ATTRIB_ID         0x08
+#define HFSP_BOGUS_ID          0x0F
+#define HFSP_FIRST_AV_ID       0x10
+
+#define HFSJ_JOURN_IN_FS       0x00
+#define HFSJ_JOURN_OTHER_DEV   0x01
+#define HFSJ_JOURN_NEED_INIT   0x02
+
+#define HFSJ_HEADER_MAGIC      0x4a4e4c78
+#define HFSJ_ENDIAN_MAGIC      0x12345678
+
+#define HFSX_CASE_FOLDING      0xCF    /* case insensitive HFSX */
+#define HFSX_BINARY_COMPARE    0xBC    /* case sensitive   HFSX */
+
+#define HFS_EXT_NB     3
+#define HFSP_EXT_NB    8
+
+/* Define the filenames used by the FS extractor */
+#ifdef HFS_EXTRACT_FS
+
+#define HFS_MDB_FILENAME       "mdb.hfs"
+#define HFS_CATALOG_FILENAME   "catalog.hfs"
+#define HFS_EXTENTS_FILENAME   "extents.hfs"
+#define HFS_BITMAP_FILENAME    "bitmap.hfs"
+
+#define HFSP_VH_FILENAME       "vh.hfsplus"
+#define HFSP_CATALOG_FILENAME  "catalog.hfsplus"
+#define HFSP_EXTENTS_FILENAME  "extents.hfsplus"
+#define HFSP_BITMAP_FILENAME   "bitmap.hfsplus"
+#define HFSP_ATTRIB_FILENAME   "attributes.hfsplus"
+#define HFSP_STARTUP_FILENAME  "startup.hfsplus"
+
+#endif /* HFS_EXTRACT_FS */
+
+
+
+/* ----------------------------------- */
+/* --      HFS DATA STRUCTURES      -- */
+/* ----------------------------------- */
+
+/* Extent descriptor */
+struct __attribute__ ((packed)) _HfsExtDescriptor {
+        uint16_t       start_block;
+        uint16_t       block_count;
+};
+typedef struct _HfsExtDescriptor HfsExtDescriptor;
+typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB];
+
+/* Volume header */
+struct __attribute__ ((packed)) _HfsMasterDirectoryBlock {
+        uint16_t         signature;
+        uint32_t         create_date;
+        uint32_t         modify_date;
+        uint16_t         volume_attributes;
+        uint16_t         files_in_root;
+        uint16_t         volume_bitmap_block;       /* in sectors */
+        uint16_t         next_allocation;
+        uint16_t         total_blocks;
+        uint32_t         block_size;                /* in bytes */
+        uint32_t         def_clump_size;            /* in bytes */
+        uint16_t         start_block;               /* in sectors */
+        uint32_t         next_free_node;
+        uint16_t         free_blocks;
+        uint8_t          name_length;
+        char             name[27];
+        uint32_t         backup_date;
+        uint16_t         backup_number;
+        uint32_t         write_count;
+        uint32_t         extents_clump;
+        uint32_t         catalog_clump;
+        uint16_t         dirs_in_root;
+        uint32_t         file_count;
+        uint32_t         dir_count;
+        uint32_t         finder_info[8];
+        union __attribute__ ((packed)) {
+                struct __attribute__ ((packed)) {
+                        uint16_t    volume_cache_size;    /* in blocks */
+                        uint16_t    bitmap_cache_size;    /* in blocks */
+                        uint16_t    common_cache_size;    /* in blocks */
+                } legacy;
+                struct __attribute__ ((packed)) {
+                        uint16_t            signature;
+                        HfsExtDescriptor    location;
+                } embedded;
+        } old_new;
+        uint32_t         extents_file_size;  /* in bytes, block size multiple 
*/
+        HfsExtDataRec    extents_file_rec;
+        uint32_t         catalog_file_size;  /* in bytes, block size multiple 
*/
+        HfsExtDataRec    catalog_file_rec;
+};
+typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock;
+
+/* B*-Tree Node Descriptor */
+struct __attribute__ ((packed)) _HfsNodeDescriptor {
+        uint32_t        next;
+        uint32_t        previous;
+        int8_t          type;
+        uint8_t         height;
+        uint16_t        rec_nb;
+        uint16_t        reserved;
+};
+typedef struct _HfsNodeDescriptor HfsNodeDescriptor;
+
+/* Header record of a whole B*-Tree */
+struct __attribute__ ((packed)) _HfsHeaderRecord {
+        uint16_t        depth;
+        uint32_t        root_node;
+        uint32_t        leaf_records;
+        uint32_t        first_leaf_node;
+        uint32_t        last_leaf_node;
+        uint16_t        node_size;
+        uint16_t        max_key_len;
+        uint32_t        total_nodes;
+        uint32_t        free_nodes;
+        int8_t          reserved[76];
+};
+typedef struct _HfsHeaderRecord HfsHeaderRecord;
+
+/* Catalog key for B*-Tree lookup in the catalog file */
+struct __attribute__ ((packed)) _HfsCatalogKey {
+        uint8_t         key_length; /* length of the key without key_length */
+        uint8_t         reserved;
+        uint32_t        parent_ID;
+        uint8_t         name_length;
+        char            name[31];   /* in fact physicaly 1 upto 31 */
+};
+typedef struct _HfsCatalogKey HfsCatalogKey;
+
+/* Extents overflow key for B*-Tree lookup */
+struct __attribute__ ((packed)) _HfsExtentKey {
+        uint8_t         key_length; /* length of the key without key_length */
+        uint8_t         type;       /* data or ressource fork */
+        uint32_t        file_ID;
+        uint16_t        start;
+};
+typedef struct _HfsExtentKey HfsExtentKey;
+
+/* Catalog subdata case directory */
+struct __attribute__ ((packed)) _HfsDir {
+        uint16_t        flags;
+        uint16_t        valence;        /* number of files in this directory */
+        uint32_t        dir_ID;
+        uint32_t        create_date;
+        uint32_t        modify_date;
+        uint32_t        backup_date;
+        int8_t          DInfo[16];      /* used by Finder, handle as reserved 
*/
+        int8_t          DXInfo[16];     /* used by Finder, handle as reserved 
*/
+        uint32_t        reserved[4];
+};
+typedef struct _HfsDir HfsDir;
+
+/* Catalog subdata case file */
+struct __attribute__ ((packed)) _HfsFile {
+        int8_t          flags;
+        int8_t          type;           /* should be 0 */
+        int8_t          FInfo[16];      /* used by Finder, handle as reserved 
*/
+        uint32_t        file_ID;
+        uint16_t        data_start_block;
+        uint32_t        data_sz_byte;
+        uint32_t        data_sz_block;
+        uint16_t        res_start_block;
+        uint32_t        res_sz_byte;
+        uint32_t        res_sz_block;
+        uint32_t        create_date;
+        uint32_t        modify_date;
+        uint32_t        backup_date;
+        int8_t          FXInfo[16];     /* used by Finder, handle as reserved 
*/
+        uint16_t        clump_size;
+        HfsExtDataRec   extents_data;
+        HfsExtDataRec   extents_res;
+        uint32_t        reserved;
+};
+typedef struct _HfsFile HfsFile;
+
+/* Catalog subdata case directory thread */
+struct __attribute__ ((packed)) _HfsDirTh {
+        uint32_t        reserved[2];
+        uint32_t        parent_ID;
+        int8_t          name_length;
+        char            name[31];
+};
+typedef struct _HfsDirTh HfsDirTh;
+
+/* Catalog subdata case file thread */
+typedef struct _HfsDirTh HfsFileTh;        /* same as directory thread */
+
+/* Catalog data */
+struct __attribute__ ((packed)) _HfsCatalog {
+        int8_t          type;
+        int8_t          reserved;
+        union {
+                HfsDir       dir;
+                HfsFile      file;
+                HfsDirTh     dir_th;
+                HfsFileTh    file_th;
+        } sel;
+};
+typedef struct _HfsCatalog HfsCatalog;
+
+
+
+/* ------------------------------------ */
+/* --      HFS+ DATA STRUCTURES      -- */
+/* ------------------------------------ */
+
+/* documented since 2004 in tn1150 */
+struct __attribute__ ((packed)) _HfsPPerms {
+        uint32_t        owner_ID;
+        uint32_t        group_ID;
+        uint32_t        permissions;
+        uint32_t        special_devices;
+};
+typedef struct _HfsPPerms HfsPPerms;
+
+/* HFS+ extent descriptor*/
+struct __attribute__ ((packed)) _HfsPExtDescriptor {
+        uint32_t        start_block;
+        uint32_t        block_count;
+};
+typedef struct _HfsPExtDescriptor HfsPExtDescriptor;
+typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB];
+
+/* HFS+ fork data structure */
+struct __attribute__ ((packed)) _HfsPForkData {
+        uint64_t        logical_size;
+        uint32_t        clump_size;
+        uint32_t        total_blocks;
+        HfsPExtDataRec  extents;
+};
+typedef struct _HfsPForkData HfsPForkData;
+
+/* HFS+ catalog node ID */
+typedef uint32_t HfsPNodeID;
+
+/* HFS+ file names */
+typedef uint16_t unichar;
+struct __attribute__ ((packed)) _HfsPUniStr255 {
+        uint16_t        length;
+        unichar         unicode[255];        /* 1 upto 255 */
+};
+typedef struct _HfsPUniStr255 HfsPUniStr255;
+
+/* HFS+ volume header */
+struct __attribute__ ((packed)) _HfsPVolumeHeader {
+        uint16_t        signature;
+        uint16_t        version;
+        uint32_t        attributes;
+        uint32_t        last_mounted_version;
+        uint32_t        journal_info_block;
+
+        uint32_t        create_date;
+        uint32_t        modify_date;
+        uint32_t        backup_date;
+        uint32_t        checked_date;
+
+        uint32_t        file_count;
+        uint32_t        dir_count;
+
+        uint32_t        block_size;
+        uint32_t        total_blocks;
+        uint32_t        free_blocks;
+
+        uint32_t        next_allocation;
+        uint32_t        res_clump_size;
+        uint32_t        data_clump_size;
+        HfsPNodeID      next_catalog_ID;
+
+        uint32_t        write_count;
+        uint64_t        encodings_bitmap;
+
+        uint8_t         finder_info[32];
+
+        HfsPForkData    allocation_file;
+        HfsPForkData    extents_file;
+        HfsPForkData    catalog_file;
+        HfsPForkData    attributes_file;
+        HfsPForkData    startup_file;
+};
+typedef struct _HfsPVolumeHeader HfsPVolumeHeader;
+
+/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */
+struct __attribute__ ((packed)) _HfsPNodeDescriptor {
+        uint32_t        next;
+        uint32_t        previous;
+        int8_t          type;
+        uint8_t         height;
+        uint16_t        rec_nb;
+        uint16_t        reserved;
+};
+typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor;
+
+/* Header record of a whole HFS+ B-Tree. */
+struct __attribute__ ((packed)) _HfsPHeaderRecord {
+        uint16_t        depth;
+        uint32_t        root_node;
+        uint32_t        leaf_records;
+        uint32_t        first_leaf_node;
+        uint32_t        last_leaf_node;
+        uint16_t        node_size;
+        uint16_t        max_key_len;
+        uint32_t        total_nodes;
+        uint32_t        free_nodes;        /* same as hfs btree until here */
+        uint16_t        reserved1;
+
+        uint32_t        clump_size;
+        uint8_t         btree_type;        /* must be 0 for HFS+ B-Tree */
+        uint8_t         key_compare_type; /* hfsx => 0xCF = case folding */
+                                          /*         0xBC = binary compare */
+                                          /* otherwise, reserved */
+        uint32_t        attributes;
+        uint32_t        reserved3[16];
+};
+typedef struct _HfsPHeaderRecord HfsPHeaderRecord;
+
+/* Catalog key for B-Tree lookup in the HFS+ catalog file */
+struct __attribute__ ((packed)) _HfsPCatalogKey {
+        uint16_t        key_length;
+        HfsPNodeID      parent_ID;
+        HfsPUniStr255   node_name;
+};
+typedef struct _HfsPCatalogKey HfsPCatalogKey;
+
+/* HFS+ catalog subdata case dir */
+struct __attribute__ ((packed)) _HfsPDir {
+        uint16_t        flags;
+        uint32_t        valence;
+        HfsPNodeID      dir_ID;
+        uint32_t        create_date;
+        uint32_t        modify_date;
+        uint32_t        attrib_mod_date;
+        uint32_t        access_date;
+        uint32_t        backup_date;
+        HfsPPerms       permissions;
+        int8_t          DInfo[16];        /* used by Finder, handle as 
reserved */
+        int8_t          DXInfo[16];       /* used by Finder, handle as 
reserved */
+        uint32_t        text_encoding;
+        uint32_t        reserved;
+};
+typedef struct _HfsPDir HfsPDir;
+
+/* HFS+ catalog subdata case file */
+struct __attribute__ ((packed)) _HfsPFile {
+        uint16_t        flags;
+        uint32_t        reserved1;
+        HfsPNodeID      file_ID;
+        uint32_t        create_date;
+        uint32_t        modify_date;
+        uint32_t        attrib_mod_date;
+        uint32_t        access_date;
+        uint32_t        backup_date;
+        HfsPPerms       permissions;
+        int8_t          FInfo[16];        /* used by Finder, handle as 
reserved */
+        int8_t          FXInfo[16];       /* used by Finder, handle as 
reserved */
+        uint32_t        text_encoding;
+        uint32_t        reserved2;
+
+        HfsPForkData    data_fork;
+        HfsPForkData    res_fork;
+};
+typedef struct _HfsPFile HfsPFile;
+
+/* HFS+ catalog subdata case thread */
+struct __attribute__ ((packed)) _HfsPThread {
+        int16_t         reserved;
+        HfsPNodeID      parent_ID;
+        HfsPUniStr255   node_name;
+};
+typedef struct _HfsPThread HfsPDirTh;
+typedef struct _HfsPThread HfsPFileTh;
+
+/* HFS+ Catalog leaf data */
+struct __attribute__ ((packed)) _HfsPCatalog {
+        int16_t         type;
+        union {
+                HfsPDir         dir;
+                HfsPFile        file;
+                HfsPDirTh       dir_th;
+                HfsPFileTh      file_th;
+        } sel;
+};
+typedef struct _HfsPCatalog HfsPCatalog;
+
+/* HFS+ extents file key */
+struct __attribute__ ((packed)) _HfsPExtentKey {
+        uint16_t        key_length;
+        uint8_t         type;
+        uint8_t         pad;
+        HfsPNodeID      file_ID;
+        uint32_t        start;
+};
+typedef struct _HfsPExtentKey HfsPExtentKey;
+
+/* extent file data is HfsPExtDataRec */
+
+/* Fork data attribute file */
+struct __attribute__ ((packed)) _HfsPForkDataAttr {
+        uint32_t        record_type;
+        uint32_t        reserved;
+        union __attribute__ ((packed)) {
+                HfsPForkData        fork;
+                HfsPExtDataRec      extents;
+        } fork_res;
+};
+typedef struct _HfsPForkDataAttr HfsPForkDataAttr;
+
+
+/* ----------- Journal data structures ----------- */
+
+/* Info block : stored in a block # defined in the VH */
+struct __attribute__ ((packed)) _HfsJJournalInfoBlock {
+        uint32_t        flags;
+        uint32_t        device_signature[8];
+        uint64_t        offset;
+        uint64_t        size;
+        uint32_t        reserved[32];
+};
+typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock;
+
+struct __attribute__ ((packed)) _HfsJJournalHeader {
+        uint32_t        magic;
+        uint32_t        endian;
+        uint64_t        start;
+        uint64_t        end;
+        uint64_t        size;
+        uint32_t        blhdr_size;
+        uint32_t        checksum;
+        uint32_t        jhdr_size;
+};
+typedef struct _HfsJJournalHeader HfsJJournalHeader;
+
+struct __attribute__ ((packed)) _HfsJBlockInfo {
+        uint64_t        bnum;          /* sector number */
+        uint32_t        bsize;         /* size in bytes */
+        uint32_t        next;
+};
+typedef struct _HfsJBlockInfo HfsJBlockInfo;
+
+struct __attribute__ ((packed)) _HfsJBlockListHeader {
+        uint16_t        max_blocks;    /* reserved */
+        uint16_t        num_blocks;
+        uint32_t        bytes_used;
+        uint32_t        checksum;
+        uint32_t        pad;
+        HfsJBlockInfo   binfo[1];
+};
+typedef struct _HfsJBlockListHeader HfsJBlockListHeader;
+
+
+
+/* ---------------------------------------- */
+/* --      INTERNAL DATA STRUCTURES      -- */
+/* ---------------------------------------- */
+
+/* Data of an opened HFS file */
+struct _HfsPrivateFile {
+        PedSector       sect_nb;
+        PedFileSystem*  fs;
+        uint32_t        CNID;          /* disk order (BE) */
+        HfsExtDataRec   first;         /* disk order (BE) */
+        HfsExtDataRec   cache;         /* disk order (BE) */
+        uint16_t        start_cache;   /* CPU order */
+};
+typedef struct _HfsPrivateFile HfsPrivateFile;
+
+/* To store bad block list */
+struct _HfsPrivateLinkExtent {
+        HfsExtDescriptor                 extent;
+        struct _HfsPrivateLinkExtent*    next;
+};
+typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent;
+
+/* HFS Filesystem specific data */
+struct _HfsPrivateFSData {
+        uint8_t                     alloc_map[(1<<16) / 8];
+        HfsMasterDirectoryBlock*    mdb;
+        HfsPrivateFile*             extent_file;
+        HfsPrivateFile*             catalog_file;
+        HfsPrivateLinkExtent*       bad_blocks_xtent_list;
+        unsigned int                bad_blocks_xtent_nb;
+        char                        bad_blocks_loaded;
+};
+typedef struct _HfsPrivateFSData HfsPrivateFSData;
+
+/* Generic btree key */
+struct __attribute__ ((packed)) _HfsPrivateGenericKey {
+        uint8_t         key_length;
+        uint8_t         key_content[1];                /* we use 1 as a 
minimum size */
+};
+typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey;
+
+/* ----- HFS+ ----- */
+
+/* Data of an opened HFS file */
+struct _HfsPPrivateFile {
+        PedSector       sect_nb;
+        PedFileSystem*  fs;
+        HfsPNodeID      CNID;          /* disk order (BE) */
+        HfsPExtDataRec  first;         /* disk order (BE) */
+        HfsPExtDataRec  cache;         /* disk order (BE) */
+        uint32_t        start_cache;   /* CPU order */
+};
+typedef struct _HfsPPrivateFile HfsPPrivateFile;
+
+struct _HfsPPrivateExtent {
+        PedSector       start_sector;
+        PedSector       sector_count;
+};
+typedef struct _HfsPPrivateExtent HfsPPrivateExtent;
+
+/* To store bad block list */
+struct _HfsPPrivateLinkExtent {
+        HfsPExtDescriptor                 extent;
+        struct _HfsPPrivateLinkExtent*    next;
+};
+typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent;
+
+/* HFS+ file system specific data */
+struct _HfsPPrivateFSData {
+        PedFileSystem*          wrapper;      /* NULL if hfs+ is not embedded 
*/
+        PedGeometry*            plus_geom;    /* Geometry of HFS+ _volume_ */
+        uint8_t*                alloc_map;
+        uint8_t*                dirty_alloc_map;
+        HfsPVolumeHeader*       vh;
+        HfsPPrivateFile*        extents_file;
+        HfsPPrivateFile*        catalog_file;
+        HfsPPrivateFile*        attributes_file;
+        HfsPPrivateFile*        allocation_file;
+        HfsPPrivateLinkExtent*  bad_blocks_xtent_list;
+        uint32_t                jib_start_block;
+        uint32_t                jl_start_block;
+        unsigned int            bad_blocks_xtent_nb;
+        char                    bad_blocks_loaded;
+        char                    free_geom;    /* 1 = plus_geom must be freed */
+};
+typedef struct _HfsPPrivateFSData HfsPPrivateFSData;
+
+/* Generic + btree key */
+struct __attribute__ ((packed)) _HfsPPrivateGenericKey {
+        uint16_t        key_length;
+        uint8_t         key_content[1];       /* we use 1 as a minimum size */
+};
+typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey;
+
+/* ---- common ---- */
+
+/* node and lead record reference for a BTree search */
+struct _HfsCPrivateLeafRec {
+        unsigned int    node_size;     /* in sectors */
+        unsigned int    node_number;
+        unsigned int    record_pos;
+        unsigned int    record_number;
+};
+typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec;
+
+extern uint8_t*    hfs_block;
+extern uint8_t*    hfsp_block;
+extern unsigned    hfs_block_count;
+extern unsigned    hfsp_block_count;
+
+#endif /* _HFS_H */
diff --git a/libparted/fs/r/hfs/journal.c b/libparted/fs/r/hfs/journal.c
new file mode 100644
index 0000000..6e5c267
--- /dev/null
+++ b/libparted/fs/r/hfs/journal.c
@@ -0,0 +1,389 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "reloc_plus.h"
+
+#include "journal.h"
+
+static int hfsj_vh_replayed = 0;
+static int is_le = 0;
+
+static uint32_t
+hfsj_calc_checksum(uint8_t *ptr, int len)
+{
+       int      i;
+       uint32_t cksum=0;
+
+       for (i=0; i < len; i++, ptr++) {
+               cksum = (cksum << 8) ^ (cksum + *ptr);
+       }
+
+       return (~cksum);
+}
+
+int
+hfsj_update_jib(PedFileSystem* fs, uint32_t block)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                                   fs->type_specific;
+
+       priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block);
+
+       if (!hfsplus_update_vh (fs))
+               return 0;
+
+       priv_data->jib_start_block = block;
+       return 1;
+}
+
+int
+hfsj_update_jl(PedFileSystem* fs, uint32_t block)
+{
+       uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
+       PedSector               sector;
+       uint64_t                offset;
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                                   fs->type_specific;
+       HfsJJournalInfoBlock*   jib;
+       int                     binsect;
+
+       binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / 
PED_SECTOR_SIZE_DEFAULT;
+       sector = (PedSector) priv_data->jib_start_block * binsect;
+       if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
+               return 0;
+       jib = (HfsJJournalInfoBlock*) buf;
+
+       offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect;
+       jib->offset = HFS_CPU_TO_64(offset, is_le);
+
+       if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
+           || !ped_geometry_sync(priv_data->plus_geom))
+               return 0;
+
+       priv_data->jl_start_block = block;
+       return 1;
+}
+
+/* Return the sector in the journal that is after the area read */
+/* or 0 on error */
+static PedSector
+hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh,
+                 PedSector journ_sect, PedSector journ_length,
+                 PedSector read_sect, unsigned int nb_sect,
+                 void* buf)
+{
+       int r;
+
+       while (nb_sect--) {
+               r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1);
+               if (!r) return 0;
+
+               buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT;
+               read_sect++;
+               if (read_sect == journ_length)
+                       read_sect = 1; /* skip journal header
+                                         which is asserted to be
+                                         1 sector long */
+       }
+
+       return read_sect;
+}
+
+static int
+hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh,
+                       PedSector jsector, PedSector jlength)
+{
+       PedSector               start, sector;
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsJBlockListHeader*    blhdr;
+       uint8_t*                block;
+       unsigned int            blhdr_nbsect;
+       int                     i, r;
+       uint32_t                cksum, size;
+
+       blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / 
PED_SECTOR_SIZE_DEFAULT;
+       blhdr = (HfsJBlockListHeader*)
+                 ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT);
+       if (!blhdr) return 0;
+
+       start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT;
+       do {
+               start = hfsj_journal_read(priv_data->plus_geom, jh, jsector,
+                                         jlength, start, blhdr_nbsect, blhdr);
+               if (!start) goto err_replay;
+
+               cksum = HFS_32_TO_CPU(blhdr->checksum, is_le);
+               blhdr->checksum = 0;
+               if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("Bad block list header checksum."));
+                       goto err_replay;
+               }
+               blhdr->checksum = HFS_CPU_TO_32(cksum, is_le);
+
+               for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) {
+                       size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le);
+                       sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le);
+                       if (!size) continue;
+                       if (size % PED_SECTOR_SIZE_DEFAULT) {
+                               ped_exception_throw(
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("Invalid size of a transaction "
+                                         "block while replaying the journal "
+                                         "(%i bytes)."),
+                                       size);
+                               goto err_replay;
+                       }
+                       block = (uint8_t*) ped_malloc(size);
+                       if (!block) goto err_replay;
+                       start = hfsj_journal_read(priv_data->plus_geom, jh,
+                                                 jsector, jlength, start,
+                                                 size / 
PED_SECTOR_SIZE_DEFAULT,
+                                                 block);
+                       if (!start) {
+                               free (block);
+                               goto err_replay;
+                       }
+                       /* the sector stored in the journal seems to be
+                          relative to the begin of the block device which
+                          contains the hfs+ journaled volume */
+                       if (sector != ~0LL)
+                               r = ped_geometry_write (fs->geom, block, sector,
+                                                       size / 
PED_SECTOR_SIZE_DEFAULT);
+                       else
+                               r = 1;
+                       free (block);
+                       /* check if wrapper mdb or vh with no wrapper has
+                          changed */
+                       if (   (sector != ~0LL)
+                           && (2 >= sector)
+                           && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) )
+                               hfsj_vh_replayed = 1;
+                       /* check if vh of embedded hfs+ has changed */
+                       if (   (sector != ~0LL)
+                           && (priv_data->plus_geom != fs->geom)
+                           && (sector
+                               + fs->geom->start
+                               - priv_data->plus_geom->start <= 2)
+                           && (sector
+                               + size / PED_SECTOR_SIZE_DEFAULT
+                               + fs->geom->start
+                               - priv_data->plus_geom->start > 2) )
+                               hfsj_vh_replayed = 1;
+                       if (!r) goto err_replay;
+               }
+       } while (blhdr->binfo[0].next);
+
+       jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le);
+
+       free (blhdr);
+       return (ped_geometry_sync (fs->geom));
+
+err_replay:
+       free (blhdr);
+       return 0;
+}
+
+/* 0 => Failure, don't continue to open ! */
+/* 1 => Success, the journal has been completly replayed, or don't need to */
+int
+hfsj_replay_journal(PedFileSystem* fs)
+{
+       uint8_t                 buf[PED_SECTOR_SIZE_DEFAULT];
+       PedSector               sector, length;
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                                   fs->type_specific;
+       HfsJJournalInfoBlock*   jib;
+       HfsJJournalHeader*      jh;
+       int                     binsect;
+       uint32_t                cksum;
+
+       binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / 
PED_SECTOR_SIZE_DEFAULT;
+       priv_data->jib_start_block =
+               PED_BE32_TO_CPU(priv_data->vh->journal_info_block);
+       sector  = (PedSector) priv_data->jib_start_block * binsect;
+       if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
+               return 0;
+       jib = (HfsJJournalInfoBlock*) buf;
+
+       if (    (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
+           && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
+               priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le)
+                                           / ( PED_SECTOR_SIZE_DEFAULT * 
binsect );
+       }
+
+       if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT))
+               return 1;
+
+       if (  !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
+           || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
+               ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_CANCEL,
+                       _("Journal stored outside of the volume are "
+                         "not supported.  Try to desactivate the "
+                         "journal and run Parted again."));
+               return 0;
+       }
+
+       if (   (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT)
+           || (PED_BE64_TO_CPU(jib->size)   % PED_SECTOR_SIZE_DEFAULT) ) {
+               ped_exception_throw (
+                       PED_EXCEPTION_NO_FEATURE,
+                       PED_EXCEPTION_CANCEL,
+                       _("Journal offset or size is not multiple of "
+                         "the sector size."));
+               return 0;
+       }
+
+       sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT;
+       length = PED_BE64_TO_CPU(jib->size)   / PED_SECTOR_SIZE_DEFAULT;
+
+       jib = NULL;
+       if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
+               return 0;
+       jh = (HfsJJournalHeader*) buf;
+
+       if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC))
+           is_le = 1;
+
+       if (   (jh->magic  != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le))
+           || (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Incorrect magic values in the journal header."));
+               return 0;
+       }
+
+       if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT)
+         || (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT
+                 != (uint64_t)length) ) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Journal size mismatch between journal info block "
+                         "and journal header."));
+               return 0;
+       }
+
+       if (   (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT)
+           || (HFS_64_TO_CPU(jh->end, is_le)   % PED_SECTOR_SIZE_DEFAULT)
+           || (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT)
+           || (HFS_32_TO_CPU(jh->jhdr_size, is_le)  % PED_SECTOR_SIZE_DEFAULT) 
) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Some header fields are not multiple of the sector "
+                         "size."));
+               return 0;
+       }
+
+       if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("The sector size stored in the journal is not 512 "
+                         "bytes.  Parted only supports 512 bytes length "
+                         "sectors."));
+               return 0;
+       }
+
+       cksum = HFS_32_TO_CPU(jh->checksum, is_le);
+       jh->checksum = 0;
+       if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Bad journal checksum."));
+               return 0;
+       }
+       jh->checksum = HFS_CPU_TO_32(cksum, is_le);
+
+       /* The 2 following test are in the XNU Darwin source code */
+       /* so I assume they're needed */
+       if (jh->start == jh->size)
+               jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
+       if (jh->end   == jh->size)
+               jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
+
+       if (jh->start == jh->end)
+               return 1;
+
+       if (ped_exception_throw (
+               PED_EXCEPTION_WARNING,
+               PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
+               _("The journal is not empty.  Parted must replay the "
+                 "transactions before opening the file system.  This will "
+                 "modify the file system."))
+                       != PED_EXCEPTION_FIX)
+               return 0;
+
+       while (jh->start != jh->end) {
+               /* Replay one complete transaction */
+               if (!hfsj_replay_transaction(fs, jh, sector, length))
+                       return 0;
+
+               /* Recalculate cksum of the journal header */
+               jh->checksum = 0; /* need to be 0 while calculating the cksum */
+               cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh));
+               jh->checksum = HFS_CPU_TO_32(cksum, is_le);
+
+               /* Update the Journal Header */
+               if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
+                   || !ped_geometry_sync(priv_data->plus_geom))
+                       return 0;
+       }
+
+       if (hfsj_vh_replayed) {
+               /* probe could have reported incorrect info ! */
+               /* is there a way to ask parted to quit ? */
+               ped_exception_throw(
+                       PED_EXCEPTION_WARNING,
+                       PED_EXCEPTION_OK,
+                       _("The volume header or the master directory block has "
+                         "changed while replaying the journal.  You should "
+                         "restart Parted."));
+               return 0;
+       }
+
+       return 1;
+}
+
+#endif /* DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/journal.h b/libparted/fs/r/hfs/journal.h
new file mode 100644
index 0000000..4a40e02
--- /dev/null
+++ b/libparted/fs/r/hfs/journal.h
@@ -0,0 +1,44 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _JOURNAL_H
+#define _JOURNAL_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsj_replay_journal(PedFileSystem* fs);
+
+int
+hfsj_update_jib(PedFileSystem* fs, uint32_t block);
+
+int
+hfsj_update_jl(PedFileSystem* fs, uint32_t block);
+
+#define HFS_16_TO_CPU(x, is_little_endian) ((is_little_endian) ? 
(uint16_t)PED_LE16_TO_CPU(x) : (uint16_t)PED_BE16_TO_CPU(x))
+#define HFS_32_TO_CPU(x, is_little_endian) ((is_little_endian) ? 
(uint32_t)PED_LE32_TO_CPU(x) : (uint32_t)PED_BE32_TO_CPU(x))
+#define HFS_64_TO_CPU(x, is_little_endian) ((is_little_endian) ? 
(uint64_t)PED_LE64_TO_CPU(x) : (uint64_t)PED_BE64_TO_CPU(x))
+#define HFS_CPU_TO_16(x, is_little_endian) ((is_little_endian) ? 
(uint16_t)PED_CPU_TO_LE16(x) : (uint16_t)PED_CPU_TO_BE16(x))
+#define HFS_CPU_TO_32(x, is_little_endian) ((is_little_endian) ? 
(uint32_t)PED_CPU_TO_LE32(x) : (uint32_t)PED_CPU_TO_BE32(x))
+#define HFS_CPU_TO_64(x, is_little_endian) ((is_little_endian) ? 
(uint64_t)PED_CPU_TO_LE64(x) : (uint64_t)PED_CPU_TO_BE64(x))
+
+#endif /* _JOURNAL_H */
diff --git a/libparted/fs/r/hfs/probe.c b/libparted/fs/r/hfs/probe.c
new file mode 100644
index 0000000..8c656cf
--- /dev/null
+++ b/libparted/fs/r/hfs/probe.c
@@ -0,0 +1,230 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+
+#include "probe.h"
+
+int
+hfsc_can_use_geom (PedGeometry* geom)
+{
+       PedDevice* dev;
+
+       dev = geom->dev;
+       PED_ASSERT (geom != NULL);
+       PED_ASSERT (dev != NULL);
+
+       if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Parted can't use HFS file systems on disks "
+                         "with a sector size not equal to %d bytes."),
+                       (int)PED_SECTOR_SIZE_DEFAULT );
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Probe an HFS volume, detecting it even if
+it is in fact a wrapper to an HFS+ volume */
+/* Used by hfsplus_probe and hfs_probe */
+PedGeometry*
+hfs_and_wrapper_probe (PedGeometry* geom)
+{
+       uint8_t         buf[PED_SECTOR_SIZE_DEFAULT];
+       HfsMasterDirectoryBlock *mdb;
+       PedGeometry*    geom_ret;
+       PedSector       search, max;
+
+       PED_ASSERT (geom != NULL);
+       PED_ASSERT (hfsc_can_use_geom (geom));
+
+       mdb = (HfsMasterDirectoryBlock *) buf;
+
+       /* is 5 an intelligent value ? */
+       if ((geom->length < 5)
+           || (!ped_geometry_read (geom, buf, 2, 1))
+           || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) )
+               return NULL;
+
+       search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block)
+                 + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks)
+                    * (PED_BE32_TO_CPU (mdb->block_size) / 
PED_SECTOR_SIZE_DEFAULT )));
+       max = search + (PED_BE32_TO_CPU (mdb->block_size) / 
PED_SECTOR_SIZE_DEFAULT);
+       if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2)))
+               return NULL;
+
+       for (; search < max; search++) {
+               if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2)
+                   || !ped_geometry_read (geom_ret, buf, search, 1))
+                       break;
+               if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE))
+                       return geom_ret;
+       }
+
+       ped_geometry_destroy (geom_ret);
+       return NULL;
+}
+
+PedGeometry*
+hfsplus_probe (PedGeometry* geom)
+{
+       PedGeometry*    geom_ret;
+       uint8_t         buf[PED_SECTOR_SIZE_DEFAULT];
+
+       PED_ASSERT (geom != NULL);
+
+       if (!hfsc_can_use_geom (geom))
+               return NULL;
+
+       if ((geom_ret = hfs_and_wrapper_probe(geom))) {
+               /* HFS+ is embedded in an HFS volume ? */
+               HfsMasterDirectoryBlock *mdb;
+               mdb = (HfsMasterDirectoryBlock *) buf;
+
+               if (!ped_geometry_read (geom, buf, 2, 1)
+                   || (mdb->old_new.embedded.signature
+                       != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) {
+                       ped_geometry_destroy (geom_ret);
+                       return NULL;
+               } else
+                       return geom_ret;
+       } else {
+               /* This is a standalone HFS+ volume ? */
+               PedSector       search, max;
+               HfsPVolumeHeader *vh;
+               vh = (HfsPVolumeHeader *) buf;
+
+               if ((geom->length < 5)
+                   || !ped_geometry_read (geom, buf, 2, 1)
+                   || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)))
+                       return NULL;
+
+               /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */
+               /* But previous versions of my implementation used to */
+               /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */
+               /* (blocks-1)*sz-1 has to be scanned last, because */
+               /* it can belong to a regular file */
+               max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1)
+                     * ( PED_BE32_TO_CPU (vh->block_size) / 
PED_SECTOR_SIZE_DEFAULT )
+                     - 2;
+               search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size)
+                                    / PED_SECTOR_SIZE_DEFAULT ) + 2;
+               if (!(geom_ret = ped_geometry_new (geom->dev, geom->start,
+                                                  search + 2)))
+                       return NULL;
+
+               for (; search < max; search++) {
+                       if (!ped_geometry_set (geom_ret, geom_ret->start,
+                                              search + 2)
+                           || !ped_geometry_read (geom_ret, buf, search, 1))
+                               break;
+                       if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE))
+                               return geom_ret;
+               }
+               search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1)
+                     * ( PED_BE32_TO_CPU (vh->block_size) / 
PED_SECTOR_SIZE_DEFAULT )
+                     - 1;
+               if (!ped_geometry_set (geom_ret, geom_ret->start,
+                                              search + 2)
+                   || !ped_geometry_read (geom_ret, buf, search, 1)
+                   || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) {
+                       ped_geometry_destroy (geom_ret);
+                       return NULL;
+               } else
+                       return geom_ret;
+       }
+}
+
+PedGeometry*
+hfs_probe (PedGeometry* geom)
+{
+       PedGeometry*    geom_base;
+       PedGeometry*    geom_plus = NULL;
+
+       PED_ASSERT (geom != NULL);
+
+       if (!hfsc_can_use_geom (geom))
+               return NULL;
+
+       if ((geom_base = hfs_and_wrapper_probe(geom))
+           && (!(geom_plus = hfsplus_probe(geom_base))))
+               return geom_base;
+       else {
+               if (geom_base) ped_geometry_destroy (geom_base);
+               if (geom_plus) ped_geometry_destroy (geom_plus);
+               return NULL;
+       }
+}
+
+PedGeometry*
+hfsx_probe (PedGeometry* geom)
+{
+       PedGeometry*    geom_ret;
+       uint8_t         buf[PED_SECTOR_SIZE_DEFAULT];
+       PedSector       search, max;
+       HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf;
+
+       PED_ASSERT (geom != NULL);
+
+       if (!hfsc_can_use_geom (geom))
+               return NULL;
+
+       if ((geom->length < 5)
+           || !ped_geometry_read (geom, buf, 2, 1)
+           || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE)))
+               return NULL;
+
+       /* unlike the hfs+ code, which should be kept compatible
+       with my old previous implementations, we only care here
+       about legal alternate VH positions, like TN1150 describes them */
+       max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1)
+                     * ( PED_BE32_TO_CPU (vh->block_size) / 
PED_SECTOR_SIZE_DEFAULT )
+                     - 2;
+       search = max - ( PED_BE32_TO_CPU (vh->block_size) / 
PED_SECTOR_SIZE_DEFAULT );
+       if (!(geom_ret = ped_geometry_new (geom->dev, geom->start,
+                                          search + 2)))
+               return NULL;
+       for (; search < max; search++) {
+               if (!ped_geometry_set (geom_ret, geom_ret->start,
+                                      search + 2)
+                   || !ped_geometry_read (geom_ret, buf, search, 1))
+                       break;
+               if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE))
+                       return geom_ret;
+       }
+
+       ped_geometry_destroy (geom_ret);
+       return NULL;
+}
diff --git a/libparted/fs/r/hfs/probe.h b/libparted/fs/r/hfs/probe.h
new file mode 100644
index 0000000..48d14b2
--- /dev/null
+++ b/libparted/fs/r/hfs/probe.h
@@ -0,0 +1,43 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _PROBE_H
+#define _PROBE_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsc_can_use_geom (PedGeometry* geom);
+
+PedGeometry*
+hfs_and_wrapper_probe (PedGeometry* geom);
+
+PedGeometry*
+hfsplus_probe (PedGeometry* geom);
+
+PedGeometry*
+hfs_probe (PedGeometry* geom);
+
+PedGeometry*
+hfsx_probe (PedGeometry* geom);
+
+#endif /* _PROBE_H */
diff --git a/libparted/fs/r/hfs/reloc.c b/libparted/fs/r/hfs/reloc.c
new file mode 100644
index 0000000..81d1057
--- /dev/null
+++ b/libparted/fs/r/hfs/reloc.c
@@ -0,0 +1,669 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "file.h"
+#include "advfs.h"
+#include "cache.h"
+
+#include "reloc.h"
+
+/* This function moves data of size blocks starting
+   at block *ptr_fblock to block *ptr_to_fblock */
+/* return new start or -1 on failure */
+static int
+hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
+                       unsigned int *ptr_to_fblock, unsigned int size)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       unsigned int            i, ok = 0;
+       unsigned int            next_to_fblock;
+       unsigned int            start, stop;
+
+       PED_ASSERT (hfs_block != NULL);
+       PED_ASSERT (*ptr_to_fblock <= *ptr_fblock);
+       /* quiet gcc */
+       start = stop = 0;
+
+/*
+       Try to fit the extent AT or _BEFORE_ the wanted place,
+       or then in the gap between dest and source.
+       If failed try to fit the extent after source, for 2 pass relocation
+       The extent is always copied in a non overlapping way
+*/
+
+       /* Backward search */
+       /* 1 pass relocation AT or BEFORE *ptr_to_fblock */
+       if (*ptr_to_fblock != *ptr_fblock) {
+               start = stop = *ptr_fblock < *ptr_to_fblock+size ?
+                              *ptr_fblock : *ptr_to_fblock+size;
+               while (start && stop-start != size) {
+                       --start;
+                       if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
+                               stop = start;
+               }
+               ok = (stop-start == size);
+       }
+
+       /* Forward search */
+       /* 1 pass relocation in the gap merged with 2 pass reloc after source */
+       if (!ok && *ptr_to_fblock != *ptr_fblock) {
+               start = stop = *ptr_to_fblock+1;
+               while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks)
+                      && stop-start != size) {
+                       if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
+                               start = stop + 1;
+                       ++stop;
+               }
+               ok = (stop-start == size);
+       }
+
+       /* new non overlapping room has been found ? */
+       if (ok) {
+               /* enough room */
+               unsigned int j;
+               unsigned int start_block =
+                       PED_BE16_TO_CPU (priv_data->mdb->start_block );
+               unsigned int block_sz =
+                       (PED_BE32_TO_CPU (priv_data->mdb->block_size)
+                        / PED_SECTOR_SIZE_DEFAULT);
+
+               if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
+                       /* Fit in the gap */
+                       next_to_fblock = stop;
+               else
+                       /* Before or after the gap */
+                       next_to_fblock = *ptr_to_fblock;
+
+               /* move blocks */
+               for (i = 0; i < size; /*i+=j*/) {
+                       PedSector       abs_sector;
+                       unsigned int    ai;
+
+                       j = size - i; j = (j < hfs_block_count) ?
+                                          j : hfs_block_count ;
+
+                       abs_sector = start_block
+                                    + (PedSector) (*ptr_fblock + i) * block_sz;
+                       if (!ped_geometry_read (fs->geom, hfs_block, abs_sector,
+                                               block_sz * j))
+                               return -1;
+
+                       abs_sector = start_block
+                                    + (PedSector) (start + i) * block_sz;
+                       if (!ped_geometry_write (fs->geom,hfs_block,abs_sector,
+                                                block_sz * j))
+                               return -1;
+
+                       for (ai = i+j; i < ai; i++) {
+                               /* free source block */
+                               CLR_BLOC_OCCUPATION(priv_data->alloc_map,
+                                                   *ptr_fblock + i);
+
+                               /* set dest block */
+                               SET_BLOC_OCCUPATION(priv_data->alloc_map,
+                                                   start + i);
+                       }
+               }
+               if (!ped_geometry_sync_fast (fs->geom))
+                       return -1;
+
+               *ptr_fblock += size;
+               *ptr_to_fblock = next_to_fblock;
+       } else {
+               if (*ptr_fblock != *ptr_to_fblock)
+                       /* not enough room, but try to continue */
+                       ped_exception_throw (PED_EXCEPTION_WARNING,
+                                       PED_EXCEPTION_IGNORE,
+                                       _("An extent has not been relocated."));
+               start = *ptr_fblock;
+               *ptr_fblock = *ptr_to_fblock = start + size;
+       }
+
+       return start;
+}
+
+/* Update MDB */
+/* Return 0 if an error occurred */
+/* Return 1 if everything ok */
+int
+hfs_update_mdb (PedFileSystem *fs)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+
+       if (!ped_geometry_read (fs->geom, node, 2, 1))
+               return 0;
+       memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock));
+       if (   !ped_geometry_write (fs->geom, node, 2, 1)
+           || !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1)
+           || !ped_geometry_sync_fast (fs->geom))
+               return 0;
+       return 1;
+}
+
+/* Generic relocator */
+/* replace previous hfs_do_move_* */
+static int
+hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src,
+            unsigned int *ptr_dest, HfsCPrivateCache* cache,
+            HfsCPrivateExtent* ref)
+{
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsPrivateFile*         file;
+       HfsExtDescriptor*       extent;
+       HfsCPrivateExtent*      move;
+       int                     new_start;
+
+       new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest,
+                                           ref->ext_length);
+       if (new_start == -1) return -1;
+
+       if (ref->ext_start != (unsigned) new_start) {
+               /* Load, modify & save */
+               switch (ref->where) {
+               /******** MDB *********/
+                   case CR_PRIM_CAT :
+                       priv_data->catalog_file
+                       ->first[ref->ref_index].start_block =
+                               PED_CPU_TO_BE16(new_start);
+                       goto CR_PRIM;
+                   case CR_PRIM_EXT :
+                       priv_data->extent_file
+                       ->first[ref->ref_index].start_block =
+                               PED_CPU_TO_BE16(new_start);
+                   CR_PRIM :
+                       extent = ( HfsExtDescriptor* )
+                                ( (uint8_t*)priv_data->mdb + ref->ref_offset );
+                       extent[ref->ref_index].start_block =
+                               PED_CPU_TO_BE16(new_start);
+                       if (!hfs_update_mdb(fs)) return -1;
+                       break;
+
+               /********* BTREE *******/
+                   case CR_BTREE_EXT_CAT :
+                       if (priv_data->catalog_file
+                           ->cache[ref->ref_index].start_block
+                           == PED_CPU_TO_BE16(ref->ext_start))
+                               priv_data->catalog_file
+                               ->cache[ref->ref_index].start_block =
+                               PED_CPU_TO_BE16(new_start);
+                   case CR_BTREE_EXT_0 :
+                       file = priv_data->extent_file;
+                       goto CR_BTREE;
+                   case CR_BTREE_CAT :
+                       file = priv_data->catalog_file;
+                   CR_BTREE:
+                       PED_ASSERT(ref->sect_by_block == 1
+                                  && ref->ref_offset < 
PED_SECTOR_SIZE_DEFAULT);
+                       if (!hfs_file_read_sector(file, node, ref->ref_block))
+                               return -1;
+                       extent = ( HfsExtDescriptor* ) (node + ref->ref_offset);
+                       extent[ref->ref_index].start_block =
+                               PED_CPU_TO_BE16(new_start);
+                       if (!hfs_file_write_sector(file, node, ref->ref_block)
+                           || !ped_geometry_sync_fast (fs->geom))
+                               return -1;
+                       break;
+
+               /********** BUG ********/
+                   default :
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("A reference to an extent comes from a place "
+                                 "it should not.  You should check the file "
+                                 "system!"));
+                       return -1;
+                       break;
+               }
+
+               /* Update the cache */
+               move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
+               if (!move) return -1; /* "cleanly" fail */
+               PED_ASSERT(move == ref); /* generate a bug */
+       }
+
+       return new_start;
+}
+
+/* 0 error, 1 ok */
+static int
+hfs_save_allocation(PedFileSystem* fs)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       unsigned int            map_sectors;
+
+       map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
+                       + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+                     / (PED_SECTOR_SIZE_DEFAULT * 8);
+       return ( ped_geometry_write (fs->geom, priv_data->alloc_map,
+                       PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
+                       map_sectors) );
+}
+
+/* This function moves an extent starting at block fblock to block to_fblock
+   if there's enough room */
+/* Return 1 if everything was fine */
+/* Return -1 if an error occurred */
+/* Return 0 if no extent was found */
+/* Generic search thanks to the file system cache */
+static int
+hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
+                            unsigned int *ptr_to_fblock,
+                            HfsCPrivateCache* cache)
+{
+       HfsCPrivateExtent*      ref;
+       unsigned int            old_start, new_start;
+
+       /* Reference search powered by the cache... */
+       /* This is the optimisation secret :) */
+       ref = hfsc_cache_search_extent(cache, *ptr_fblock);
+       if (!ref) return 0; /* not found */
+
+       old_start = *ptr_fblock;
+       new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
+       if (new_start == (unsigned int) -1) return -1;
+       if (new_start > old_start) { /* detect 2 pass reloc */
+               new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref);
+               if (new_start == (unsigned int) -1 || new_start > old_start)
+                       return -1;
+       }
+
+       /* allocation bitmap save is not atomic with data relocation */
+       /* so we only do it a few times, and without syncing */
+       /* The unmounted bit protect us anyway */
+       hfs_save_allocation(fs);
+       return 1;
+}
+
+static int
+hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs,
+                  PedTimer* timer)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsExtDescriptor*       extent;
+       unsigned int            j;
+
+       extent = priv_data->mdb->extents_file_rec;
+       for (j = 0; j < HFS_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE16_TO_CPU(extent[j].start_block),
+                       PED_BE16_TO_CPU(extent[j].block_count),
+                       0, /* unused for mdb */
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
+                       1, /* load/save only 1 sector */
+                       CR_PRIM_EXT,
+                       j )
+                  )
+                       return 0;
+       }
+
+       extent = priv_data->mdb->catalog_file_rec;
+       for (j = 0; j < HFS_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE16_TO_CPU(extent[j].start_block),
+                       PED_BE16_TO_CPU(extent[j].block_count),
+                       0,
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
+                       1,
+                       CR_PRIM_CAT,
+                       j )
+                  )
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int
+hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
+                  PedTimer* timer)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+       HfsHeaderRecord*        header;
+       HfsNodeDescriptor*      desc = (HfsNodeDescriptor*) node;
+       HfsCatalogKey*          catalog_key;
+       HfsCatalog*             catalog_data;
+       HfsExtDescriptor*       extent;
+       unsigned int            leaf_node, record_number;
+       unsigned int            i, j;
+
+       if (!priv_data->catalog_file->sect_nb) {
+               ped_exception_throw (
+                       PED_EXCEPTION_INFORMATION,
+                       PED_EXCEPTION_OK,
+                       _("This HFS volume has no catalog file.  "
+                         "This is very unusual!"));
+               return 1;
+       }
+
+       if (!hfs_file_read_sector (priv_data->catalog_file, node, 0))
+               return 0;
+       header = (HfsHeaderRecord*)(node +  PED_BE16_TO_CPU(*((uint16_t*)
+                                               
(node+(PED_SECTOR_SIZE_DEFAULT-2)))));
+
+       for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+            leaf_node;
+            leaf_node = PED_BE32_TO_CPU (desc->next)) {
+               if (!hfs_file_read_sector (priv_data->catalog_file,
+                                          node, leaf_node))
+                       return 0;
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = 1; i <= record_number; ++i) {
+                      /* undocumented alignement */
+                       unsigned int skip;
+                       catalog_key = (HfsCatalogKey*) (node + PED_BE16_TO_CPU(
+                               *((uint16_t*)(node+(PED_SECTOR_SIZE_DEFAULT - 
2*i)))));
+                       skip = (1 + catalog_key->key_length + 1) & ~1;
+                       catalog_data = (HfsCatalog*)( ((uint8_t*)catalog_key)
+                                                       + skip );
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)catalog_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)catalog_data - node
+                               >= PED_SECTOR_SIZE_DEFAULT
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               return 0;
+                       }
+
+                       if (catalog_data->type != HFS_CAT_FILE) continue;
+
+                       extent = catalog_data->sel.file.extents_data;
+                       for (j = 0; j < HFS_EXT_NB; ++j) {
+                               if (!extent[j].block_count) break;
+                               if (!hfsc_cache_add_extent(
+                                       cache,
+                                       PED_BE16_TO_CPU(extent[j].start_block),
+                                       PED_BE16_TO_CPU(extent[j].block_count),
+                                       leaf_node,
+                                       (uint8_t*)extent - node,
+                                       1, /* hfs => btree block = 512 b */
+                                       CR_BTREE_CAT,
+                                       j )
+                                  )
+                                       return 0;
+                       }
+
+                       extent = catalog_data->sel.file.extents_res;
+                       for (j = 0; j < HFS_EXT_NB; ++j) {
+                               if (!extent[j].block_count) break;
+                               if (!hfsc_cache_add_extent(
+                                       cache,
+                                       PED_BE16_TO_CPU(extent[j].start_block),
+                                       PED_BE16_TO_CPU(extent[j].block_count),
+                                       leaf_node,
+                                       (uint8_t*)extent - node,
+                                       1, /* hfs => btree block = 512 b */
+                                       CR_BTREE_CAT,
+                                       j )
+                                  )
+                                       return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+static int
+hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
+                  PedTimer* timer)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+       HfsHeaderRecord*        header;
+       HfsNodeDescriptor*      desc = (HfsNodeDescriptor*) node;
+       HfsExtentKey*           extent_key;
+       HfsExtDescriptor*       extent;
+       unsigned int            leaf_node, record_number;
+       unsigned int            i, j;
+
+       if (!priv_data->extent_file->sect_nb) {
+               ped_exception_throw (
+                       PED_EXCEPTION_INFORMATION,
+                       PED_EXCEPTION_OK,
+                       _("This HFS volume has no extents overflow "
+                         "file.  This is quite unusual!"));
+               return 1;
+       }
+
+       if (!hfs_file_read_sector (priv_data->extent_file, node, 0))
+               return 0;
+       header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                               
(node+(PED_SECTOR_SIZE_DEFAULT-2))))));
+
+       for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+            leaf_node;
+            leaf_node = PED_BE32_TO_CPU (desc->next)) {
+               if (!hfs_file_read_sector (priv_data->extent_file, node,
+                                          leaf_node))
+                       return 0;
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = 1; i <= record_number; i++) {
+                       uint8_t where;
+                       extent_key = (HfsExtentKey*)
+                                       (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                             (node+(PED_SECTOR_SIZE_DEFAULT - 
2*i)))));
+                       /* size is cst */
+                       extent = (HfsExtDescriptor*)(((uint8_t*)extent_key)
+                                                      + sizeof (HfsExtentKey));
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)extent_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)extent - node
+                               >= PED_SECTOR_SIZE_DEFAULT
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               return 0;
+                       }
+
+                       switch (extent_key->file_ID) {
+                           case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
+                               if (ped_exception_throw (
+                                       PED_EXCEPTION_WARNING,
+                                       PED_EXCEPTION_IGNORE_CANCEL,
+                                       _("The extents overflow file should not"
+                                         " contain its own extents!  You "
+                                         "should check the file system."))
+                                               != PED_EXCEPTION_IGNORE)
+                                       return 0;
+                               where = CR_BTREE_EXT_EXT;
+                               break;
+                           case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
+                               where = CR_BTREE_EXT_CAT;
+                               break;
+                           default :
+                               where = CR_BTREE_EXT_0;
+                               break;
+                       }
+
+                       for (j = 0; j < HFS_EXT_NB; ++j) {
+                               if (!extent[j].block_count) break;
+                               if (!hfsc_cache_add_extent(
+                                       cache,
+                                       PED_BE16_TO_CPU(extent[j].start_block),
+                                       PED_BE16_TO_CPU(extent[j].block_count),
+                                       leaf_node,
+                                       (uint8_t*)extent - node,
+                                       1, /* hfs => btree block = 512 b */
+                                       where,
+                                       j )
+                                  )
+                                       return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+/* This function cache every extents start and length stored in any
+   fs structure into the adt defined in cache.[ch]
+   Returns NULL on failure */
+static HfsCPrivateCache*
+hfs_cache_extents(PedFileSystem *fs, PedTimer* timer)
+{
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsCPrivateCache*       ret;
+       unsigned int            file_number, block_number;
+
+       file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count);
+       block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks);
+       ret = hfsc_new_cache(block_number, file_number);
+       if (!ret) return NULL;
+
+       if (!hfs_cache_from_mdb(ret, fs, timer) ||
+           !hfs_cache_from_catalog(ret, fs, timer) ||
+           !hfs_cache_from_extent(ret, fs, timer)) {
+               ped_exception_throw(
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Could not cache the file system in memory."));
+               hfsc_delete_cache(ret);
+               return NULL;
+       }
+
+       return ret;
+}
+
+/* This function moves file's data to compact used and free space,
+   starting at fblock block */
+/* return 0 on error */
+int
+hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+                               PedTimer* timer, unsigned int to_free)
+{
+       PedSector               bytes_buff;
+       HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
+                                               fs->type_specific;
+       HfsMasterDirectoryBlock* mdb = priv_data->mdb;
+       HfsCPrivateCache*       cache;
+       unsigned int            to_fblock = fblock;
+       unsigned int            start = fblock;
+       unsigned int            divisor = PED_BE16_TO_CPU (mdb->total_blocks)
+                                         + 1 - start - to_free;
+       int                     ret;
+
+       PED_ASSERT (!hfs_block);
+
+       cache = hfs_cache_extents (fs, timer);
+       if (!cache)
+               return 0;
+
+       /* Calculate the size of the copy buffer :
+        * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
+        * takes the maximum number of HFS blocks so that the buffer
+        * will remain smaller than or equal to BYTES_MAX_BUFF, with
+        * a minimum of 1 HFS block */
+       bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size)
+                    * (PedSector) BLOCK_MAX_BUFF;
+       if (bytes_buff > BYTES_MAX_BUFF) {
+               hfs_block_count = BYTES_MAX_BUFF
+                                / PED_BE32_TO_CPU (priv_data->mdb->block_size);
+               if (!hfs_block_count)
+                       hfs_block_count = 1;
+               bytes_buff = (PedSector) hfs_block_count
+                            * PED_BE32_TO_CPU (priv_data->mdb->block_size);
+       } else
+               hfs_block_count = BLOCK_MAX_BUFF;
+
+       /* If the cache code requests more space, give it to him */
+       if (bytes_buff < hfsc_cache_needed_buffer (cache))
+               bytes_buff = hfsc_cache_needed_buffer (cache);
+
+       hfs_block = (uint8_t*) ped_malloc (bytes_buff);
+       if (!hfs_block)
+               goto error_cache;
+
+       if (!hfs_read_bad_blocks (fs)) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Bad blocks list could not be loaded."));
+               goto error_alloc;
+       }
+
+       while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) {
+               if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock)
+                   && (!hfs_is_bad_block (fs, fblock))) {
+                       if (!(ret = hfs_move_extent_starting_at (fs, &fblock,
+                                               &to_fblock, cache)))
+                               to_fblock = ++fblock;
+                       else if (ret == -1) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("An error occurred during extent "
+                                         "relocation."));
+                               goto error_alloc;
+                       }
+               } else {
+                       fblock++;
+               }
+
+               ped_timer_update(timer, (float)(to_fblock - start)/divisor);
+       }
+
+       free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
+       hfsc_delete_cache (cache);
+       return 1;
+
+error_alloc:
+       free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
+error_cache:
+       hfsc_delete_cache (cache);
+       return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/reloc.h b/libparted/fs/r/hfs/reloc.h
new file mode 100644
index 0000000..536c071
--- /dev/null
+++ b/libparted/fs/r/hfs/reloc.h
@@ -0,0 +1,35 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _RELOC_H
+#define _RELOC_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfs_update_mdb (PedFileSystem *fs);
+
+int
+hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+                               PedTimer* timer, unsigned int to_free);
+
+#endif /* _RELOC_H */
diff --git a/libparted/fs/r/hfs/reloc_plus.c b/libparted/fs/r/hfs/reloc_plus.c
new file mode 100644
index 0000000..750927f
--- /dev/null
+++ b/libparted/fs/r/hfs/reloc_plus.c
@@ -0,0 +1,942 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004-2005, 2007, 2009-2011 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/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+#  include <libintl.h>
+#  define _(String) dgettext (PACKAGE, String)
+#else
+#  define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "file_plus.h"
+#include "advfs_plus.h"
+#include "cache.h"
+#include "journal.h"
+
+#include "reloc_plus.h"
+
+/* This function moves data of size blocks starting at block *ptr_fblock
+   to block *ptr_to_fblock */
+/* return new start or -1 on failure */
+/* -1 is ok because there can only be 2^32-1 blocks, so the max possible
+   last one is 2^32-2 (and anyway it contains Alternate VH), so
+   -1 (== 2^32-1[2^32]) never represent a valid block */
+static int
+hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
+                           unsigned int *ptr_to_fblock, unsigned int size)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       unsigned int            i, ok = 0;
+       unsigned int            next_to_fblock;
+       unsigned int            start, stop;
+
+       PED_ASSERT (hfsp_block != NULL);
+       PED_ASSERT (*ptr_to_fblock <= *ptr_fblock);
+       /* quiet GCC */
+       start = stop = 0;
+
+/*
+       Try to fit the extent AT or _BEFORE_ the wanted place,
+       or then in the gap between dest and source.
+       If failed try to fit the extent after source, for 2 pass relocation
+       The extent is always copied in a non overlapping way
+*/
+
+       /* Backward search */
+       /* 1 pass relocation AT or BEFORE *ptr_to_fblock */
+       if (*ptr_to_fblock != *ptr_fblock) {
+               start = stop = *ptr_fblock < *ptr_to_fblock+size ?
+                              *ptr_fblock : *ptr_to_fblock+size;
+               while (start && stop-start != size) {
+                       --start;
+                       if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
+                               stop = start;
+               }
+               ok = (stop-start == size);
+       }
+
+       /* Forward search */
+       /* 1 pass relocation in the gap merged with 2 pass reloc after source */
+       if (!ok && *ptr_to_fblock != *ptr_fblock) {
+               start = stop = *ptr_to_fblock+1;
+               while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks)
+                      && stop-start != size) {
+                       if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
+                               start = stop + 1;
+                       ++stop;
+               }
+               ok = (stop-start == size);
+       }
+
+       /* new non overlapping room has been found ? */
+       if (ok) {
+               /* enough room */
+               PedSector       abs_sector;
+               unsigned int    ai, j, block;
+               unsigned int    block_sz = (PED_BE32_TO_CPU (
+                                       priv_data->vh->block_size)
+                                       / PED_SECTOR_SIZE_DEFAULT);
+
+               if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
+                       /* Fit in the gap */
+                       next_to_fblock = stop;
+               else
+                       /* Before or after the gap */
+                       next_to_fblock = *ptr_to_fblock;
+
+               /* move blocks */
+               for (i = 0; i < size; /*i++*/) {
+                       j = size - i; j = (j < hfsp_block_count) ?
+                                          j : hfsp_block_count ;
+
+                       abs_sector = (PedSector) (*ptr_fblock + i) * block_sz;
+                       if (!ped_geometry_read (priv_data->plus_geom,
+                                               hfsp_block, abs_sector,
+                                               block_sz * j))
+                               return -1;
+
+                       abs_sector = (PedSector) (start + i) * block_sz;
+                       if (!ped_geometry_write (priv_data->plus_geom,
+                                                hfsp_block, abs_sector,
+                                                block_sz * j))
+                               return -1;
+
+                       for (ai = i+j; i < ai; i++) {
+                               /* free source block */
+                               block = *ptr_fblock + i;
+                               CLR_BLOC_OCCUPATION(priv_data->alloc_map,block);
+                               SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map,
+                                                   
block/(PED_SECTOR_SIZE_DEFAULT*8));
+
+                               /* set dest block */
+                               block = start + i;
+                               SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
+                               SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map,
+                                                   
block/(PED_SECTOR_SIZE_DEFAULT*8));
+                       }
+               }
+               if (!ped_geometry_sync_fast (priv_data->plus_geom))
+                       return -1;
+
+               *ptr_fblock += size;
+               *ptr_to_fblock = next_to_fblock;
+       } else {
+               if (*ptr_fblock != *ptr_to_fblock)
+                       /* not enough room */
+                       ped_exception_throw (PED_EXCEPTION_WARNING,
+                             PED_EXCEPTION_IGNORE,
+                             _("An extent has not been relocated."));
+               start = *ptr_fblock;
+               *ptr_fblock = *ptr_to_fblock = start + size;
+       }
+
+       return start;
+}
+
+/* Returns 0 on error */
+/*         1 on succes */
+int
+hfsplus_update_vh (PedFileSystem *fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
+
+       if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1))
+               return 0;
+       memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader));
+       if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1)
+           || !ped_geometry_write (priv_data->plus_geom, node,
+                                priv_data->plus_geom->length - 2, 1)
+           || !ped_geometry_sync_fast (priv_data->plus_geom))
+               return 0;
+       return 1;
+}
+
+static int
+hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src,
+                unsigned int *ptr_dest, HfsCPrivateCache* cache,
+                HfsCPrivateExtent* ref)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPPrivateFile*        file;
+       HfsPExtDescriptor*      extent;
+       HfsCPrivateExtent*      move;
+       int                     new_start;
+
+       new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest,
+                                               ref->ext_length);
+
+       if (new_start == -1) return -1;
+
+       if (ref->ext_start != (unsigned) new_start) {
+               switch (ref->where) {
+               /************ VH ************/
+                   case CR_PRIM_CAT :
+                       priv_data->catalog_file
+                       ->first[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_PRIM;
+                   case CR_PRIM_EXT :
+                       priv_data->extents_file
+                       ->first[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_PRIM;
+                   case CR_PRIM_ATTR :
+                       priv_data->attributes_file
+                       ->first[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_PRIM;
+                   case CR_PRIM_ALLOC :
+                       priv_data->allocation_file
+                       ->first[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_PRIM;
+                   case CR_PRIM_START :
+                       /* No startup file opened */
+                   CR_PRIM :
+                       extent = ( HfsPExtDescriptor* )
+                                ( (uint8_t*)priv_data->vh + ref->ref_offset );
+                       extent[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       if (!hfsplus_update_vh(fs))
+                               return -1;
+                       break;
+
+               /************** BTREE *************/
+                   case CR_BTREE_CAT_JIB :
+                       if (!hfsj_update_jib(fs, new_start))
+                               return -1;
+                       goto BTREE_CAT;
+
+                   case CR_BTREE_CAT_JL :
+                       if (!hfsj_update_jl(fs, new_start))
+                               return -1;
+                       goto BTREE_CAT;
+
+                   BTREE_CAT:
+                   case CR_BTREE_CAT :
+                       file = priv_data->catalog_file;
+                       goto CR_BTREE;
+
+                   case CR_BTREE_ATTR :
+                       file = priv_data->attributes_file;
+                       goto CR_BTREE;
+
+                   case CR_BTREE_EXT_ATTR :
+                       if (priv_data->attributes_file
+                           ->cache[ref->ref_index].start_block
+                           == PED_CPU_TO_BE32(ref->ext_start))
+                               priv_data->attributes_file
+                               ->cache[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_BTREE_EXT;
+                   case CR_BTREE_EXT_CAT :
+                       if (priv_data->catalog_file
+                           ->cache[ref->ref_index].start_block
+                           == PED_CPU_TO_BE32(ref->ext_start))
+                               priv_data->catalog_file
+                               ->cache[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_BTREE_EXT;
+                   case CR_BTREE_EXT_ALLOC :
+                       if (priv_data->allocation_file
+                           ->cache[ref->ref_index].start_block
+                           == PED_CPU_TO_BE32(ref->ext_start))
+                               priv_data->allocation_file
+                               ->cache[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       goto CR_BTREE_EXT;
+                   case CR_BTREE_EXT_START :
+                       /* No startup file opened */
+                   CR_BTREE_EXT :
+                   case CR_BTREE_EXT_0 :
+                       file = priv_data->extents_file;
+
+                   CR_BTREE :
+                       PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block
+                                  > ref->ref_offset);
+                       if (!hfsplus_file_read(file, hfsp_block,
+                               (PedSector)ref->ref_block * ref->sect_by_block,
+                               ref->sect_by_block))
+                               return -1;
+                       extent = ( HfsPExtDescriptor* )
+                               ( hfsp_block + ref->ref_offset );
+                       extent[ref->ref_index].start_block =
+                               PED_CPU_TO_BE32(new_start);
+                       if (!hfsplus_file_write(file, hfsp_block,
+                               (PedSector)ref->ref_block * ref->sect_by_block,
+                               ref->sect_by_block)
+                           || !ped_geometry_sync_fast (priv_data->plus_geom))
+                               return -1;
+                       break;
+
+                   /********** BUG *********/
+                   default :
+                       ped_exception_throw (
+                               PED_EXCEPTION_ERROR,
+                               PED_EXCEPTION_CANCEL,
+                               _("A reference to an extent comes from a place "
+                                 "it should not.  You should check the file "
+                                 "system!"));
+                       return -1;
+                       break;
+               }
+
+               move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
+               if (!move) return -1;
+               PED_ASSERT(move == ref);
+       }
+
+       return new_start;
+}
+
+/* save any dirty sector of the allocation bitmap file */
+static int
+hfsplus_save_allocation(PedFileSystem *fs)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       unsigned int            map_sectors, i, j;
+       int                     ret = 1;
+
+       map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks)
+                       + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / 
(PED_SECTOR_SIZE_DEFAULT * 8);
+
+       for (i = 0; i < map_sectors;) {
+               for (j = i;
+                    (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j));
+                    ++j)
+                       CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j);
+               if (j-i) {
+                       ret = hfsplus_file_write(priv_data->allocation_file,
+                                   priv_data->alloc_map + i * 
PED_SECTOR_SIZE_DEFAULT,
+                                   i, j-i) && ret;
+                       i = j;
+               } else
+                       ++i;
+       }
+
+       return ret;
+}
+
+/* This function moves an extent starting at block fblock
+   to block to_fblock if there's enough room */
+/* Return 1 if everything was fine */
+/* Return -1 if an error occurred */
+/* Return 0 if no extent was found */
+static int
+hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
+                                unsigned int *ptr_to_fblock,
+                                HfsCPrivateCache* cache)
+{
+       HfsCPrivateExtent*      ref;
+       unsigned int            old_start, new_start;
+
+       ref = hfsc_cache_search_extent(cache, *ptr_fblock);
+       if (!ref) return 0;
+
+       old_start = *ptr_fblock;
+       new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
+       if (new_start == (unsigned)-1) return -1;
+       if (new_start > old_start) {
+               new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock,
+                                           cache, ref);
+               if (new_start == (unsigned)-1 || new_start > old_start)
+                       return -1;
+       }
+
+       hfsplus_save_allocation(fs);
+       return 1;
+}
+
+static int
+hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs,
+                     PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPExtDescriptor*      extent;
+       unsigned int            j;
+
+       extent = priv_data->vh->allocation_file.extents;
+       for (j = 0; j < HFSP_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE32_TO_CPU(extent[j].start_block),
+                       PED_BE32_TO_CPU(extent[j].block_count),
+                       0, /* unused for vh */
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+                       1, /* load / save 1 sector */
+                       CR_PRIM_ALLOC,
+                       j )
+                  )
+                       return 0;
+       }
+
+       extent = priv_data->vh->extents_file.extents;
+       for (j = 0; j < HFSP_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE32_TO_CPU(extent[j].start_block),
+                       PED_BE32_TO_CPU(extent[j].block_count),
+                       0, /* unused for vh */
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+                       1, /* load / save 1 sector */
+                       CR_PRIM_EXT,
+                       j )
+                  )
+                       return 0;
+       }
+
+       extent = priv_data->vh->catalog_file.extents;
+       for (j = 0; j < HFSP_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE32_TO_CPU(extent[j].start_block),
+                       PED_BE32_TO_CPU(extent[j].block_count),
+                       0, /* unused for vh */
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+                       1, /* load / save 1 sector */
+                       CR_PRIM_CAT,
+                       j )
+                  )
+                       return 0;
+       }
+
+       extent = priv_data->vh->attributes_file.extents;
+       for (j = 0; j < HFSP_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE32_TO_CPU(extent[j].start_block),
+                       PED_BE32_TO_CPU(extent[j].block_count),
+                       0, /* unused for vh */
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+                       1, /* load / save 1 sector */
+                       CR_PRIM_ATTR,
+                       j )
+                  )
+                       return 0;
+       }
+
+       extent = priv_data->vh->startup_file.extents;
+       for (j = 0; j < HFSP_EXT_NB; ++j) {
+               if (!extent[j].block_count) break;
+               if (!hfsc_cache_add_extent(
+                       cache,
+                       PED_BE32_TO_CPU(extent[j].start_block),
+                       PED_BE32_TO_CPU(extent[j].block_count),
+                       0, /* unused for vh */
+                       ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+                       1, /* load / save 1 sector */
+                       CR_PRIM_START,
+                       j )
+                  )
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int
+hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
+                          PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node_1[PED_SECTOR_SIZE_DEFAULT];
+       uint8_t*                node;
+       HfsPHeaderRecord*       header;
+       HfsPCatalogKey*         catalog_key;
+       HfsPCatalog*            catalog_data;
+       HfsPExtDescriptor*      extent;
+       unsigned int            leaf_node, record_number;
+       unsigned int            i, j, size, bsize;
+       uint32_t                jib = priv_data->jib_start_block,
+                               jl  = priv_data->jl_start_block;
+
+       if (!priv_data->catalog_file->sect_nb) {
+               ped_exception_throw (
+                       PED_EXCEPTION_INFORMATION,
+                       PED_EXCEPTION_OK,
+                       _("This HFS+ volume has no catalog file.  "
+                         "This is very unusual!"));
+               return 1;
+       }
+
+       /* Search the extent starting at *ptr_block in the catalog file */
+       if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0))
+               return 0;
+       header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC);
+       leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+       bsize = PED_BE16_TO_CPU (header->node_size);
+       size = bsize / PED_SECTOR_SIZE_DEFAULT;
+       PED_ASSERT(size < 256);
+
+       node = (uint8_t*) ped_malloc(bsize);
+       if (!node) return 0;
+       HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
+
+       for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
+               if (!hfsplus_file_read (priv_data->catalog_file, node,
+                                       (PedSector) leaf_node * size, size)) {
+                       free (node);
+                       return 0;
+               }
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = 1; i <= record_number; i++) {
+                       unsigned int    skip;
+                       uint8_t         where;
+
+                       catalog_key = (HfsPCatalogKey*)
+                           ( node + PED_BE16_TO_CPU (*((uint16_t *)
+                                       (node+(bsize - 2*i)))) );
+                       skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length)
+                                + 1) & ~1;
+                       catalog_data = (HfsPCatalog*)
+                                           (((uint8_t*)catalog_key) + skip);
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)catalog_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)catalog_data - node
+                               >= (signed) bsize
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               free (node);
+                               return 0;
+                       }
+
+                       if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE)
+                               continue;
+
+                       extent = catalog_data->sel.file.data_fork.extents;
+                       for (j = 0; j < HFSP_EXT_NB; ++j) {
+                               if (!extent[j].block_count) break;
+                               where = CR_BTREE_CAT;
+                               if ( PED_BE32_TO_CPU(extent[j].start_block)
+                                    == jib ) {
+                                       jib = 0;
+                                       where = CR_BTREE_CAT_JIB;
+                               } else
+                                 if ( PED_BE32_TO_CPU(extent[j].start_block)
+                                      == jl ) {
+                                       jl = 0;
+                                       where = CR_BTREE_CAT_JL;
+                               }
+                               if (!hfsc_cache_add_extent(
+                                       cache,
+                                       PED_BE32_TO_CPU(extent[j].start_block),
+                                       PED_BE32_TO_CPU(extent[j].block_count),
+                                       leaf_node,
+                                       (uint8_t*)extent - node,
+                                       size,
+                                       where,
+                                       j )
+                                  ) {
+                                       free (node);
+                                       return 0;
+                               }
+                       }
+
+                       extent = catalog_data->sel.file.res_fork.extents;
+                       for (j = 0; j < HFSP_EXT_NB; ++j) {
+                               if (!extent[j].block_count) break;
+                               if (!hfsc_cache_add_extent(
+                                       cache,
+                                       PED_BE32_TO_CPU(extent[j].start_block),
+                                       PED_BE32_TO_CPU(extent[j].block_count),
+                                       leaf_node,
+                                       (uint8_t*)extent - node,
+                                       size,
+                                       CR_BTREE_CAT,
+                                       j )
+                                  ) {
+                                       free (node);
+                                       return 0;
+                               }
+                       }
+               }
+       }
+
+       free (node);
+       return 1;
+}
+
+static int
+hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
+                         PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node_1[PED_SECTOR_SIZE_DEFAULT];
+       uint8_t*                node;
+       HfsPHeaderRecord*       header;
+       HfsPExtentKey*          extent_key;
+       HfsPExtDescriptor*      extent;
+       unsigned int            leaf_node, record_number;
+       unsigned int            i, j, size, bsize;
+
+       if (!priv_data->extents_file->sect_nb) {
+               ped_exception_throw (
+                       PED_EXCEPTION_INFORMATION,
+                       PED_EXCEPTION_OK,
+                       _("This HFS+ volume has no extents overflow "
+                         "file.  This is quite unusual!"));
+               return 1;
+       }
+
+       if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0))
+               return 0;
+       header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC));
+       leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+       bsize = PED_BE16_TO_CPU (header->node_size);
+       size = bsize / PED_SECTOR_SIZE_DEFAULT;
+       PED_ASSERT(size < 256);
+
+       node = (uint8_t*) ped_malloc (bsize);
+       if (!node) return -1;
+       HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
+
+       for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
+               if (!hfsplus_file_read (priv_data->extents_file, node,
+                                       (PedSector) leaf_node * size, size)) {
+                       free (node);
+                       return 0;
+               }
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = 1; i <= record_number; i++) {
+                       uint8_t where;
+                       extent_key = (HfsPExtentKey*)
+                           (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                           (node+(bsize - 2*i)))));
+                       extent = (HfsPExtDescriptor*)
+                           (((uint8_t*)extent_key) + sizeof (HfsPExtentKey));
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)extent_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)extent - node
+                               >= (signed)bsize
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               free (node);
+                               return -1;
+                       }
+
+                       switch (extent_key->file_ID) {
+                           case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
+                               if (ped_exception_throw (
+                                       PED_EXCEPTION_WARNING,
+                                       PED_EXCEPTION_IGNORE_CANCEL,
+                                       _("The extents overflow file should not"
+                                       " contain its own extents!  You should "
+                                       "check the file system."))
+                                               != PED_EXCEPTION_IGNORE)
+                                       return 0;
+                               where = CR_BTREE_EXT_EXT;
+                               break;
+                           case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
+                               where = CR_BTREE_EXT_CAT;
+                               break;
+                           case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) :
+                               where = CR_BTREE_EXT_ALLOC;
+                               break;
+                           case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) :
+                               where = CR_BTREE_EXT_START;
+                               break;
+                           case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) :
+                               where = CR_BTREE_EXT_ATTR;
+                               break;
+                           default :
+                               where = CR_BTREE_EXT_0;
+                               break;
+                       }
+
+                       for (j = 0; j < HFSP_EXT_NB; ++j) {
+                               if (!extent[j].block_count) break;
+                               if (!hfsc_cache_add_extent(
+                                       cache,
+                                       PED_BE32_TO_CPU(extent[j].start_block),
+                                       PED_BE32_TO_CPU(extent[j].block_count),
+                                       leaf_node,
+                                       (uint8_t*)extent - node,
+                                       size,
+                                       where,
+                                       j )
+                                  ) {
+                                       free (node);
+                                       return 0;
+                               }
+                       }
+               }
+       }
+
+       free (node);
+       return 1;
+}
+
+static int
+hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs,
+                             PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       uint8_t                 node_1[PED_SECTOR_SIZE_DEFAULT];
+       uint8_t*                node;
+       HfsPHeaderRecord*       header;
+       HfsPPrivateGenericKey*  generic_key;
+       HfsPForkDataAttr*       fork_ext_data;
+       HfsPExtDescriptor*      extent;
+       unsigned int            leaf_node, record_number;
+       unsigned int            i, j, size, bsize;
+
+       /* attributes file is facultative */
+       if (!priv_data->attributes_file->sect_nb)
+               return 1;
+
+       /* Search the extent starting at *ptr_block in the catalog file */
+       if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0))
+               return 0;
+       header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC));
+       leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+       bsize = PED_BE16_TO_CPU (header->node_size);
+       size = bsize / PED_SECTOR_SIZE_DEFAULT;
+       PED_ASSERT(size < 256);
+
+       node = (uint8_t*) ped_malloc(bsize);
+       if (!node) return 0;
+       HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
+
+       for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
+               if (!hfsplus_file_read (priv_data->attributes_file, node,
+                                       (PedSector) leaf_node * size, size)) {
+                       free (node);
+                       return 0;
+               }
+               record_number = PED_BE16_TO_CPU (desc->rec_nb);
+               for (i = 1; i <= record_number; i++) {
+                       unsigned int    skip;
+                       generic_key = (HfsPPrivateGenericKey*)
+                               (node + PED_BE16_TO_CPU(*((uint16_t *)
+                                           (node+(bsize - 2*i)))));
+                       skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length)
+                                + 1 ) & ~1;
+                       fork_ext_data = (HfsPForkDataAttr*)
+                                           (((uint8_t*)generic_key) + skip);
+                       /* check for obvious error in FS */
+                       if (((uint8_t*)generic_key - node < HFS_FIRST_REC)
+                           || ((uint8_t*)fork_ext_data - node
+                               >= (signed) bsize
+                                  - 2 * (signed)(record_number+1))) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("The file system contains errors."));
+                               free (node);
+                               return 0;
+                       }
+
+                       if (fork_ext_data->record_type
+                           == PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) {
+                               extent = fork_ext_data->fork_res.fork.extents;
+                               for (j = 0; j < HFSP_EXT_NB; ++j) {
+                                       if (!extent[j].block_count) break;
+                                       if (!hfsc_cache_add_extent(
+                                               cache,
+                                               PED_BE32_TO_CPU (
+                                                       extent[j].start_block ),
+                                               PED_BE32_TO_CPU (
+                                                       extent[j].block_count ),
+                                               leaf_node,
+                                               (uint8_t*)extent-node,
+                                               size,
+                                               CR_BTREE_ATTR,
+                                               j )
+                                          ) {
+                                               free(node);
+                                               return 0;
+                                       }
+                               }
+                       } else if (fork_ext_data->record_type
+                           == PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) {
+                               extent = fork_ext_data->fork_res.extents;
+                               for (j = 0; j < HFSP_EXT_NB; ++j) {
+                                       if (!extent[j].block_count) break;
+                                       if (!hfsc_cache_add_extent(
+                                               cache,
+                                               PED_BE32_TO_CPU (
+                                                       extent[j].start_block ),
+                                               PED_BE32_TO_CPU (
+                                                       extent[j].block_count ),
+                                               leaf_node,
+                                               (uint8_t*)extent-node,
+                                               size,
+                                               CR_BTREE_ATTR,
+                                               j )
+                                          ) {
+                                               free(node);
+                                               return 0;
+                                       }
+                               }
+                       } else continue;
+               }
+       }
+
+       free (node);
+       return 1;
+}
+
+static HfsCPrivateCache*
+hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer)
+{
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsCPrivateCache*       ret;
+       unsigned int            file_number, block_number;
+
+       file_number = PED_BE32_TO_CPU(priv_data->vh->file_count);
+       block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks);
+       ret = hfsc_new_cache(block_number, file_number);
+       if (!ret) return NULL;
+
+       if (!hfsplus_cache_from_vh(ret, fs, timer) ||
+           !hfsplus_cache_from_catalog(ret, fs, timer) ||
+           !hfsplus_cache_from_extent(ret, fs, timer) ||
+           !hfsplus_cache_from_attributes(ret, fs, timer)) {
+               ped_exception_throw(
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Could not cache the file system in memory."));
+               hfsc_delete_cache(ret);
+               return NULL;
+       }
+
+       return ret;
+}
+
+/* This function moves file's data to compact used and free space,
+   starting at fblock block */
+/* return 0 on error */
+int
+hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+                                   PedTimer* timer, unsigned int to_free)
+{
+       PedSector               bytes_buff;
+       HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
+                                               fs->type_specific;
+       HfsPVolumeHeader*       vh = priv_data->vh;
+       HfsCPrivateCache*       cache;
+       unsigned int            to_fblock = fblock;
+       unsigned int            start = fblock;
+       unsigned int            divisor = PED_BE32_TO_CPU (vh->total_blocks)
+                                         + 1 - start - to_free;
+       int                     ret;
+
+       PED_ASSERT (!hfsp_block);
+
+       cache = hfsplus_cache_extents (fs, timer);
+       if (!cache)
+               return 0;
+
+       /* Calculate the size of the copy buffer :
+        * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
+        * takes the maximum number of HFS blocks so that the buffer
+        * will remain smaller than or equal to BYTES_MAX_BUFF, with
+        * a minimum of 1 HFS block */
+       bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size)
+                    * (PedSector) BLOCK_MAX_BUFF;
+       if (bytes_buff > BYTES_MAX_BUFF) {
+               hfsp_block_count = BYTES_MAX_BUFF
+                                / PED_BE32_TO_CPU (priv_data->vh->block_size);
+               if (!hfsp_block_count)
+                       hfsp_block_count = 1;
+               bytes_buff = (PedSector) hfsp_block_count
+                            * PED_BE32_TO_CPU (priv_data->vh->block_size);
+       } else
+               hfsp_block_count = BLOCK_MAX_BUFF;
+
+       /* If the cache code requests more space, give it to him */
+       if (bytes_buff < hfsc_cache_needed_buffer (cache))
+               bytes_buff = hfsc_cache_needed_buffer (cache);
+
+       hfsp_block = (uint8_t*) ped_malloc (bytes_buff);
+       if (!hfsp_block)
+               goto error_cache;
+
+       if (!hfsplus_read_bad_blocks (fs)) {
+               ped_exception_throw (
+                       PED_EXCEPTION_ERROR,
+                       PED_EXCEPTION_CANCEL,
+                       _("Bad blocks list could not be loaded."));
+               goto error_alloc;
+       }
+
+       while ( fblock < ( priv_data->plus_geom->length - 2 )
+                        / ( PED_BE32_TO_CPU (vh->block_size)
+                            / PED_SECTOR_SIZE_DEFAULT ) ) {
+               if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock)
+                   && (!hfsplus_is_bad_block (fs, fblock))) {
+                       if (!(ret = hfsplus_move_extent_starting_at (fs,
+                                               &fblock, &to_fblock, cache)))
+                               to_fblock = ++fblock;
+                       else if (ret == -1) {
+                               ped_exception_throw (
+                                       PED_EXCEPTION_ERROR,
+                                       PED_EXCEPTION_CANCEL,
+                                       _("An error occurred during extent "
+                                         "relocation."));
+                               goto error_alloc;
+                       }
+               } else {
+                       fblock++;
+               }
+
+               ped_timer_update(timer, (float)(to_fblock - start) / divisor);
+       }
+
+       free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0;
+       hfsc_delete_cache (cache);
+       return 1;
+
+error_alloc:
+       free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0;
+error_cache:
+       hfsc_delete_cache (cache);
+       return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/hfs/reloc_plus.h b/libparted/fs/r/hfs/reloc_plus.h
new file mode 100644
index 0000000..2b2e12c
--- /dev/null
+++ b/libparted/fs/r/hfs/reloc_plus.h
@@ -0,0 +1,36 @@
+/*
+    libparted - a library for manipulating disk partitions
+    Copyright (C) 2004, 2007, 2009-2011 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/>.
+*/
+
+#ifndef _RELOC_PLUS_H
+#define _RELOC_PLUS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsplus_update_vh (PedFileSystem *fs);
+
+int
+hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+                                   PedTimer* timer, unsigned int to_free);
+
+
+#endif /* _RELOC_PLUS_H */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4979ceb..6eefa26 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -53,6 +53,25 @@ libparted/fs/fat/bootsector.c
 libparted/fs/linux_swap/linux_swap.c
 libparted/fs/hfs/probe.c

+libparted/fs/r/fat/bootsector.c
+libparted/fs/r/fat/calc.c
+libparted/fs/r/fat/context.c
+libparted/fs/r/fat/count.c
+libparted/fs/r/fat/fat.c
+libparted/fs/r/fat/resize.c
+libparted/fs/r/fat/table.c
+libparted/fs/r/filesys.c
+libparted/fs/r/hfs/advfs.c
+libparted/fs/r/hfs/advfs_plus.c
+libparted/fs/r/hfs/cache.c
+libparted/fs/r/hfs/file.c
+libparted/fs/r/hfs/file_plus.c
+libparted/fs/r/hfs/hfs.c
+libparted/fs/r/hfs/journal.c
+libparted/fs/r/hfs/probe.c
+libparted/fs/r/hfs/reloc.c
+libparted/fs/r/hfs/reloc_plus.c
+
 # parted
 parted/parted.c
 parted/ui.c
-- 
1.7.9.112.gb85f2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]