diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:16:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:16:34 +0000 |
commit | a398d2c2b5fd6ab0545d8bb019f9a970b2309404 (patch) | |
tree | 272fc7ab226258d7ceddee12c8c682c8e711c2b0 /libparted/fs/r/fat | |
parent | Initial commit. (diff) | |
download | parted-a398d2c2b5fd6ab0545d8bb019f9a970b2309404.tar.xz parted-a398d2c2b5fd6ab0545d8bb019f9a970b2309404.zip |
Adding upstream version 3.6.upstream/3.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libparted/fs/r/fat')
-rw-r--r-- | libparted/fs/r/fat/bootsector.c | 441 | ||||
-rw-r--r-- | libparted/fs/r/fat/bootsector.h | 130 | ||||
-rw-r--r-- | libparted/fs/r/fat/calc.c | 433 | ||||
-rw-r--r-- | libparted/fs/r/fat/calc.h | 77 | ||||
-rw-r--r-- | libparted/fs/r/fat/clstdup.c | 423 | ||||
-rw-r--r-- | libparted/fs/r/fat/clstdup.h | 28 | ||||
-rw-r--r-- | libparted/fs/r/fat/context.c | 261 | ||||
-rw-r--r-- | libparted/fs/r/fat/context.h | 70 | ||||
-rw-r--r-- | libparted/fs/r/fat/count.c | 319 | ||||
-rw-r--r-- | libparted/fs/r/fat/count.h | 46 | ||||
-rw-r--r-- | libparted/fs/r/fat/fat.c | 652 | ||||
-rw-r--r-- | libparted/fs/r/fat/fat.h | 159 | ||||
-rw-r--r-- | libparted/fs/r/fat/fatio.c | 150 | ||||
-rw-r--r-- | libparted/fs/r/fat/fatio.h | 49 | ||||
-rw-r--r-- | libparted/fs/r/fat/resize.c | 876 | ||||
-rw-r--r-- | libparted/fs/r/fat/table.c | 481 | ||||
-rw-r--r-- | libparted/fs/r/fat/table.h | 74 | ||||
-rw-r--r-- | libparted/fs/r/fat/traverse.c | 368 | ||||
-rw-r--r-- | libparted/fs/r/fat/traverse.h | 75 |
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 */ |