summaryrefslogtreecommitdiffstats
path: root/libparted/fs/r/fat
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libparted/fs/r/fat/bootsector.c441
-rw-r--r--libparted/fs/r/fat/bootsector.h130
-rw-r--r--libparted/fs/r/fat/calc.c433
-rw-r--r--libparted/fs/r/fat/calc.h77
-rw-r--r--libparted/fs/r/fat/clstdup.c423
-rw-r--r--libparted/fs/r/fat/clstdup.h28
-rw-r--r--libparted/fs/r/fat/context.c261
-rw-r--r--libparted/fs/r/fat/context.h70
-rw-r--r--libparted/fs/r/fat/count.c319
-rw-r--r--libparted/fs/r/fat/count.h46
-rw-r--r--libparted/fs/r/fat/fat.c652
-rw-r--r--libparted/fs/r/fat/fat.h159
-rw-r--r--libparted/fs/r/fat/fatio.c150
-rw-r--r--libparted/fs/r/fat/fatio.h49
-rw-r--r--libparted/fs/r/fat/resize.c876
-rw-r--r--libparted/fs/r/fat/table.c481
-rw-r--r--libparted/fs/r/fat/table.h74
-rw-r--r--libparted/fs/r/fat/traverse.c368
-rw-r--r--libparted/fs/r/fat/traverse.h75
19 files changed, 5112 insertions, 0 deletions
diff --git a/libparted/fs/r/fat/bootsector.c b/libparted/fs/r/fat/bootsector.c
new file mode 100644
index 0000000..85ccc0f
--- /dev/null
+++ b/libparted/fs/r/fat/bootsector.c
@@ -0,0 +1,441 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2002, 2004, 2007, 2009-2014, 2019-2023 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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** bsp, const PedGeometry *geom)
+{
+ PED_ASSERT (bsp != NULL);
+ PED_ASSERT (geom != NULL);
+
+ if (!ped_geometry_read_alloc (geom, (void **)bsp, 0, 1))
+ return 0;
+ FatBootSector *bs = *bsp;
+ 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->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 _GL_ATTRIBUTE_PURE
+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);
+
+ 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** bsp, const PedFileSystem* fs)
+{
+ PED_ASSERT (bsp != NULL);
+ *bsp = ped_malloc (fs->geom->dev->sector_size);
+ FatBootSector *bs = *bsp;
+ PED_ASSERT (bs != NULL);
+
+ memset (bs, 0, 512);
+ memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3);
+ PED_ASSERT (sizeof(FAT_BOOT_CODE) < sizeof(bs->u.fat32.boot_code));
+ strcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE);
+ return 1;
+}
+
+int
+fat_boot_sector_generate (FatBootSector** bsp, const PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (bsp != NULL);
+ FatBootSector *bs = *bsp;
+ 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** isp, const PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int status;
+
+ PED_ASSERT (isp != NULL);
+
+ if (!ped_geometry_read_alloc (fs->geom, (void **)isp, fs_info->info_sector_offset, 1))
+ return 0;
+ FatInfoSector *is = *isp;
+ 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** isp, const PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (isp != NULL);
+ *isp = ped_malloc (fs->geom->dev->sector_size);
+ FatInfoSector *is = *isp;
+
+ 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..699d6cf
--- /dev/null
+++ b/libparted/fs/r/fat/bootsector.h
@@ -0,0 +1,130 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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
+
+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, const PedFileSystem* fs);
+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..4ba1030
--- /dev/null
+++ b/libparted/fs/r/fat/calc.c
@@ -0,0 +1,433 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2002, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+/* returns the minimum size of clusters for a given file system type */
+PedSector _GL_ATTRIBUTE_CONST
+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 _GL_ATTRIBUTE_CONST
+_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 _GL_ATTRIBUTE_CONST
+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 _GL_ATTRIBUTE_CONST
+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 _GL_ATTRIBUTE_CONST
+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 _GL_ATTRIBUTE_CONST
+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 _GL_ATTRIBUTE_CONST
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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..d4884c1
--- /dev/null
+++ b/libparted/fs/r/fat/calc.h
@@ -0,0 +1,77 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..6a3054f
--- /dev/null
+++ b/libparted/fs/r/fat/clstdup.c
@@ -0,0 +1,423 @@
+/*
+ libparted
+ Copyright (C) 1998-2001, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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..23e51b4
--- /dev/null
+++ b/libparted/fs/r/fat/clstdup.h
@@ -0,0 +1,28 @@
+/*
+ libparted
+ Copyright (C) 1999, 2007, 2009-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..c782323
--- /dev/null
+++ b/libparted/fs/r/fat/context.c
@@ -0,0 +1,261 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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..9a76a47
--- /dev/null
+++ b/libparted/fs/r/fat/context.h
@@ -0,0 +1,70 @@
+/*
+ libparted
+ Copyright (C) 1999-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..e23404b
--- /dev/null
+++ b/libparted/fs/r/fat/count.c
@@ -0,0 +1,319 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+/*
+ 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 [4096];
+ 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 (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 _GL_ATTRIBUTE_PURE
+fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ return fs_info->cluster_info [cluster].flag;
+}
+
+PedSector _GL_ATTRIBUTE_PURE
+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..bb7d6af
--- /dev/null
+++ b/libparted/fs/r/fat/count.h
@@ -0,0 +1,46 @@
+/*
+ libparted
+ Copyright (C) 1999-2000, 2007-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..6583b5b
--- /dev/null
+++ b/libparted/fs/r/fat/fat.c
@@ -0,0 +1,652 @@
+/*
+ libparted
+ Copyright (C) 1998-2001, 2007-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <string.h>
+
+#include "fat.h"
+#include "calc.h"
+#include "../../../labels/misc.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;
+ FatSpecific* fs_info = (FatSpecific*) fs->type_specific;
+ fs_info->boot_sector = NULL;
+ fs_info->info_sector = NULL;
+ 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)
+{
+ FatSpecific* fs_info = (FatSpecific*) fs->type_specific;
+ free (fs_info->boot_sector);
+ 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;
+}
+
+#ifndef DISCOVER_ONLY
+int
+fat_clobber (PedGeometry* geom)
+{
+ FatBootSector *boot_sector;
+ int ok;
+
+ 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;
+
+ ok = ped_geometry_write (geom, boot_sector, 0, 1);
+ free (boot_sector);
+ return ok;
+}
+
+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);
+}
+
+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))
+ goto error_free_buffers;
+ }
+
+ fs_info->serial_number = generate_random_uint32 ();
+
+ if (!fat_boot_sector_set_boot_code (&fs_info->boot_sector, fs))
+ 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);
+}
+
+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;
+
+ min_size = 65794;
+ max_size = 2097153;
+
+ 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;
+
+ min_size = 525224;
+
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ min_size, dev->length);
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/libparted/fs/r/fat/fat.h b/libparted/fs/r/fat/fat.h
new file mode 100644
index 0000000..54f0669
--- /dev/null
+++ b/libparted/fs/r/fat/fat.h
@@ -0,0 +1,159 @@
+/*
+ libparted
+ Copyright (C) 1998-2001, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..3a947ff
--- /dev/null
+++ b/libparted/fs/r/fat/fatio.c
@@ -0,0 +1,150 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..53ebed7
--- /dev/null
+++ b/libparted/fs/r/fat/fatio.h
@@ -0,0 +1,49 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..78dede4
--- /dev/null
+++ b/libparted/fs/r/fat/resize.c
@@ -0,0 +1,876 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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);
+ if (!sub_old_dir_trav) return 0;
+ sub_new_dir_trav
+ = fat_traverse_directory (new_trav, new_dir_entry);
+ if (!sub_new_dir_trav) {
+ fat_traverse_complete (sub_old_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, "\\");
+ if (!new_trav_info) return 0;
+ 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,
+ "\\");
+ if (!new_trav_info) return 0;
+ old_trav_info = fat_traverse_begin (ctx->old_fs,
+ old_fs_info->root_cluster, "\\");
+ }
+ if (!old_trav_info) {
+ fat_traverse_complete (new_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 (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. */
+ new_fs_info->boot_sector = ped_malloc (new_geom->dev->sector_size);
+ memcpy (new_fs_info->boot_sector, fs_info->boot_sector,
+ new_geom->dev->sector_size);
+ new_fs_info->info_sector = NULL;
+ if (fs_info->fat_type == FAT_TYPE_FAT32)
+ {
+ PED_ASSERT (fs_info->info_sector != NULL);
+ new_fs_info->info_sector =
+ ped_malloc (new_geom->dev->sector_size);
+ memcpy (new_fs_info->info_sector, fs_info->info_sector,
+ new_geom->dev->sector_size);
+ }
+
+ 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..ec0907f
--- /dev/null
+++ b/libparted/fs/r/fat/table.c
@@ -0,0 +1,481 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_CONST
+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..a70241b
--- /dev/null
+++ b/libparted/fs/r/fat/table.h
@@ -0,0 +1,74 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007, 2009-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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..42eeff9
--- /dev/null
+++ b/libparted/fs/r/fat/traverse.c
@@ -0,0 +1,368 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2005, 2007-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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 _GL_ATTRIBUTE_PURE
+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..02318ba
--- /dev/null
+++ b/libparted/fs/r/fat/traverse.h
@@ -0,0 +1,75 @@
+/*
+ libparted
+ Copyright (C) 1998-2000, 2007-2014, 2019-2023 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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 */