pmt: initial 3.0.2 update

This commit is contained in:
2024-12-14 11:17:56 +03:00
parent bbf76e4925
commit a6c9feb4d6
1292 changed files with 500838 additions and 2817 deletions

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -0,0 +1,320 @@
/* libparted - a library for manipulating disk partitions
Copyright (C) 1999-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/>.
*/
/** \file filesys.c */
/**
* \addtogroup PedFileSystem
*
* \note File systems exist on a PedGeometry - NOT a PedPartition.
*
* @{
*/
#include <config.h>
#include <parted/parted.h>
#include <parted/debug.h>
#include "pt-tools.h"
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#define STREQ(a, b) (strcmp (a, b) == 0)
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
typedef PedFileSystem * (*open_fn_t) (PedGeometry *);
extern PedFileSystem *hfsplus_open (PedGeometry *);
extern PedFileSystem *hfs_open (PedGeometry *);
extern PedFileSystem *fat_open (PedGeometry *);
typedef int (*close_fn_t) (PedFileSystem *);
extern int hfsplus_close (PedFileSystem *);
extern int hfs_close (PedFileSystem *);
extern int fat_close (PedFileSystem *);
typedef int (*resize_fn_t) (PedFileSystem *fs, PedGeometry *geom,
PedTimer *timer);
extern int hfsplus_resize (PedFileSystem *fs, PedGeometry *geom,
PedTimer *timer);
extern int hfs_resize (PedFileSystem *fs, PedGeometry *geom,
PedTimer *timer);
extern int fat_resize (PedFileSystem *fs, PedGeometry *geom,
PedTimer *timer);
typedef PedConstraint * (*resize_constraint_fn_t) (PedFileSystem const *fs);
extern PedConstraint *hfsplus_get_resize_constraint (PedFileSystem const *fs);
extern PedConstraint *hfs_get_resize_constraint (PedFileSystem const *fs);
extern PedConstraint *fat_get_resize_constraint (PedFileSystem const *fs);
static bool
is_hfs_plus (char const *fs_type_name)
{
return STREQ (fs_type_name, "hfsx") || STREQ (fs_type_name, "hfs+");
}
static open_fn_t
open_fn (char const *fs_type_name)
{
if (is_hfs_plus (fs_type_name))
return hfsplus_open;
if (STREQ (fs_type_name, "hfs"))
return hfs_open;
if (strncmp (fs_type_name, "fat", 3) == 0)
return fat_open;
return NULL;
}
static close_fn_t
close_fn (char const *fs_type_name)
{
if (is_hfs_plus (fs_type_name))
return hfsplus_close;
if (STREQ (fs_type_name, "hfs"))
return hfs_close;
if (strncmp (fs_type_name, "fat", 3) == 0)
return fat_close;
return NULL;
}
static resize_fn_t
resize_fn (char const *fs_type_name)
{
if (is_hfs_plus (fs_type_name))
return hfsplus_resize;
if (STREQ (fs_type_name, "hfs"))
return hfs_resize;
if (strncmp (fs_type_name, "fat", 3) == 0)
return fat_resize;
return NULL;
}
static resize_constraint_fn_t
resize_constraint_fn (char const *fs_type_name)
{
if (is_hfs_plus (fs_type_name))
return hfsplus_get_resize_constraint;
if (STREQ (fs_type_name, "hfs"))
return hfs_get_resize_constraint;
if (strncmp (fs_type_name, "fat", 3) == 0)
return fat_get_resize_constraint;
return NULL;
}
/**
* This function opens the file system stored on \p geom, if it
* can find one.
* It is often called in the following manner:
* \code
* fs = ped_file_system_open (&part.geom)
* \endcode
*
* \throws PED_EXCEPTION_ERROR if file system could not be detected
* \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume
* \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on
* \p geom is not implemented
*
* \return a PedFileSystem on success, \c NULL on failure.
*/
PedFileSystem *
ped_file_system_open (PedGeometry* geom)
{
PED_ASSERT (geom != NULL);
if (!ped_device_open (geom->dev))
goto error;
PedFileSystemType *type = ped_file_system_probe (geom);
if (!type) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Could not detect file system."));
goto error_close_dev;
}
open_fn_t open_f = open_fn (type->name);
if (open_f == NULL) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("resizing %s file systems is not supported"),
type->name);
goto error_close_dev;
}
PedGeometry *probed_geom = ped_file_system_probe_specific (type, geom);
if (!probed_geom)
goto error_close_dev;
if (!ped_geometry_test_inside (geom, probed_geom)) {
if (ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_IGNORE_CANCEL,
_("The file system is bigger than its volume!"))
!= PED_EXCEPTION_IGNORE)
goto error_destroy_probed_geom;
}
PedFileSystem *fs = (*open_f) (probed_geom);
if (!fs)
goto error_destroy_probed_geom;
ped_geometry_destroy (probed_geom);
fs->type = type;
return fs;
error_destroy_probed_geom:
ped_geometry_destroy (probed_geom);
error_close_dev:
ped_device_close (geom->dev);
error:
return NULL;
}
/**
* Close file system \p fs.
*
* \return \c 1 on success, \c 0 on failure
*/
int
ped_file_system_close (PedFileSystem* fs)
{
PED_ASSERT (fs != NULL);
PedDevice *dev = fs->geom->dev;
close_fn_t fn = close_fn (fs->type->name);
if (!fn || !(fn (fs)))
goto error_close_dev;
ped_device_close (dev);
return 1;
error_close_dev:
ped_device_close (dev);
return 0;
}
/**
* This function erases all file system signatures that indicate that a
* file system occupies a given region described by \p geom.
* After this operation ped_file_system_probe() won't detect any file system.
*
* \return \c 1 on success, \c 0 on failure
*/
static int
ped_file_system_clobber (PedGeometry* geom)
{
PED_ASSERT (geom != NULL);
if (!ped_device_open (geom->dev))
return 0;
/* Clear the first three and the last two sectors, albeit fewer
when GEOM is too small. */
PedSector len = MIN (geom->length, geom->dev->length);
int ok = (len <= 5
? ptt_geom_clear_sectors (geom, 0, len)
: (ptt_geom_clear_sectors (geom, 0, 3)
&& ptt_geom_clear_sectors (geom, geom->dev->length - 2, 2)));
ped_device_close (geom->dev);
return !!ok;
}
/* This function erases all signatures that indicate the presence of
* a file system in a particular region, without erasing any data
* contained inside the "exclude" region.
*/
static int
ped_file_system_clobber_exclude (PedGeometry* geom,
const PedGeometry* exclude)
{
PedGeometry* clobber_geom;
int status;
if (ped_geometry_test_sector_inside (exclude, geom->start))
return 1;
clobber_geom = ped_geometry_duplicate (geom);
if (ped_geometry_test_overlap (clobber_geom, exclude))
ped_geometry_set_end (clobber_geom, exclude->start - 1);
status = ped_file_system_clobber (clobber_geom);
ped_geometry_destroy (clobber_geom);
return status;
}
/**
* Resize \p fs to new geometry \p geom.
*
* \p geom should satisfy the ped_file_system_get_resize_constraint().
* (This isn't asserted, so it's not a bug not to... just it's likely
* to fail ;) If \p timer is non-NULL, it is used as the progress meter.
*
* \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs
* is not implemented yet
*
* \return \c 0 on failure
*/
int
ped_file_system_resize (PedFileSystem *fs, PedGeometry *geom, PedTimer *timer)
{
PED_ASSERT (fs != NULL);
PED_ASSERT (geom != NULL);
resize_fn_t resize_f = resize_fn (fs->type->name);
if (resize_f == NULL) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("resizing %s file systems is not supported"),
fs->type->name);
return 0;
}
if (!ped_file_system_clobber_exclude (geom, fs->geom))
return 0;
return resize_f (fs, geom, timer);
}
/**
* Return a constraint that represents all of the possible ways the
* file system \p fs can be resized with ped_file_system_resize().
* This takes into account the amount of used space on
* the filesystem \p fs and the capabilities of the resize algorithm.
* Hints:
* -# if constraint->start_align->grain_size == 0, or
* constraint->start_geom->length == 1, then the start cannot be moved
* -# constraint->min_size is the minimum size you can resize the partition
* to. You might want to tell the user this ;-).
*
* \return a PedConstraint on success, \c NULL on failure
*/
PedConstraint *
ped_file_system_get_resize_constraint (const PedFileSystem *fs)
{
PED_ASSERT (fs != NULL);
resize_constraint_fn_t resize_constraint_f =
resize_constraint_fn (fs->type->name);
if (resize_constraint_f == NULL)
return NULL;
return resize_constraint_f (fs);
}

View File

@@ -0,0 +1,332 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "file.h"
#include "advfs.h"
/* - if a < b, 0 if a == b, + if a > b */
/* Comparaison is done in the following order : */
/* CNID, then fork type, then start block */
/* Note that HFS implementation in linux has a bug */
/* in this function */
static int
hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b)
{
HfsExtentKey* key1 = (HfsExtentKey*) a;
HfsExtentKey* key2 = (HfsExtentKey*) b;
/* do NOT use a substraction, because */
/* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */
/* would return -2, despite the fact */
/* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */
if (key1->file_ID != key2->file_ID)
return PED_BE32_TO_CPU(key1->file_ID) <
PED_BE32_TO_CPU(key2->file_ID) ?
-1 : +1;
if (key1->type != key2->type)
return (int)(key1->type - key2->type);
if (key1->start == key2->start)
return 0;
/* the whole thing wont work with 16 bits ints */
/* anyway */
return (int)( PED_BE16_TO_CPU(key1->start) -
PED_BE16_TO_CPU(key2->start) );
}
/* do a B-Tree lookup */
/* read the first record immediatly inferior or egal to the given key */
/* return 0 on error */
/* record_out _must_ be large enough to receive record_size bytes */
/* WARNING : the compare function called only handle Extents BTree */
/* so modify this function if you want to do lookup in */
/* other BTrees has well */
int
hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
void *record_out, unsigned int record_size,
HfsCPrivateLeafRec* record_ref)
{
uint8_t node[PED_SECTOR_SIZE_DEFAULT];
HfsHeaderRecord* header;
HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
HfsPrivateGenericKey* record_key = NULL;
unsigned int node_number, record_number;
int i;
uint16_t record_pos;
/* Read the header node */
if (!hfs_file_read_sector(b_tree_file, node, 0))
return 0;
uint16_t offset;
memcpy(&offset, node+(PED_SECTOR_SIZE_DEFAULT-2), sizeof(uint16_t));
header = (HfsHeaderRecord*) (node + PED_BE16_TO_CPU(offset));
/* Get the node number of the root */
node_number = PED_BE32_TO_CPU(header->root_node);
if (!node_number)
return 0;
/* Read the root node */
if (!hfs_file_read_sector(b_tree_file, node, node_number))
return 0;
/* Follow the white rabbit */
while (1) {
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = record_number; i; i--) {
uint16_t value;
memcpy(&value, node+(PED_SECTOR_SIZE_DEFAULT - (2*i)), sizeof(uint16_t));
record_pos = PED_BE16_TO_CPU(value);
record_key = (HfsPrivateGenericKey*) (node + record_pos);
/* check for obvious error in FS */
if ((record_pos< HFS_FIRST_REC)
|| (record_pos>= PED_SECTOR_SIZE_DEFAULT
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
return 0;
}
if (hfs_extent_key_cmp(record_key, key) <= 0)
break;
}
if (!i) return 0;
if (desc->type == HFS_IDX_NODE) {
unsigned int skip;
skip = (1 + record_key->key_length + 1) & ~1;
uint32_t value;
memcpy(&value, node+record_pos+skip, sizeof(uint32_t));
node_number = PED_BE32_TO_CPU(value);
if (!hfs_file_read_sector(b_tree_file, node,
node_number))
return 0;
} else
break;
}
/* copy the result if needed */
if (record_size)
memcpy (record_out, record_key, record_size);
/* send record reference if needed */
if (record_ref) {
record_ref->node_size = 1; /* in sectors */
record_ref->node_number = node_number;
record_ref->record_pos = record_pos;
record_ref->record_number = i;
}
/* success */
return 1;
}
/* free the bad blocks linked list */
void
hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first)
{
HfsPrivateLinkExtent* next;
while (first) {
next = first->next;
free (first);
first = next;
}
}
/* This function reads bad blocks extents in the extents file
and store it in f.s. specific data of fs */
int
hfs_read_bad_blocks (const PedFileSystem *fs)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
if (priv_data->bad_blocks_loaded)
return 1;
{
uint8_t record[sizeof (HfsExtentKey)
+ sizeof (HfsExtDataRec)];
HfsExtentKey search;
HfsExtentKey* ret_key = (HfsExtentKey*) record;
HfsExtDescriptor* ret_cache = (HfsExtDescriptor*)
(record + sizeof (HfsExtentKey));
unsigned int block, last_start, first_pass = 1;
search.key_length = sizeof (HfsExtentKey) - 1;
search.type = HFS_DATA_FORK;
search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
last_start = -1; block = 0;
while (1) {
int i;
search.start = PED_CPU_TO_BE16 (block);
if (!hfs_btree_search (priv_data->extent_file,
(HfsPrivateGenericKey*) &search,
record, sizeof (record), NULL)
|| ret_key->file_ID != search.file_ID
|| ret_key->type != search.type) {
if (first_pass)
break;
else
goto errbb;
}
if (PED_BE16_TO_CPU (ret_key->start) == last_start)
break;
last_start = PED_BE16_TO_CPU (ret_key->start);
for (i = 0; i < HFS_EXT_NB; i++) {
if (ret_cache[i].block_count) {
HfsPrivateLinkExtent* new_xt =
(HfsPrivateLinkExtent*) ped_malloc (
sizeof (HfsPrivateLinkExtent));
if (!new_xt)
goto errbb;
new_xt->next = priv_data->bad_blocks_xtent_list;
memcpy(&(new_xt->extent), ret_cache+i,
sizeof (HfsExtDescriptor));
priv_data->bad_blocks_xtent_list = new_xt;
priv_data->bad_blocks_xtent_nb++;
block += PED_BE16_TO_CPU (
ret_cache[i].block_count);
}
}
first_pass = 0;
}
priv_data->bad_blocks_loaded = 1;
return 1;}
errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
priv_data->bad_blocks_xtent_list=NULL;
priv_data->bad_blocks_xtent_nb=0;
return 0;
}
/* This function check if fblock is a bad block */
int _GL_ATTRIBUTE_PURE
hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
HfsPrivateLinkExtent* walk;
for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
/* Won't compile without the strange cast ! gcc bug ? */
/* or maybe C subtilties... */
if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) &&
(fblock < (unsigned int) (PED_BE16_TO_CPU (
walk->extent.start_block)
+ PED_BE16_TO_CPU (
walk->extent.block_count))))
return 1;
}
return 0;
}
/* This function returns the first sector of the last free block of an
HFS volume we can get after a hfs_pack_free_space_from_block call */
/* On error this function returns 0 */
PedSector
hfs_get_empty_end (const PedFileSystem *fs)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
HfsMasterDirectoryBlock* mdb = priv_data->mdb;
unsigned int block, last_bad, end_free_blocks;
/* find the next block to the last bad block of the volume */
if (!hfs_read_bad_blocks (fs))
return 0;
HfsPrivateLinkExtent* l;
last_bad = 0;
for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) {
if ((unsigned int) PED_BE16_TO_CPU (l->extent.start_block)
+ PED_BE16_TO_CPU (l->extent.block_count) > last_bad)
last_bad = PED_BE16_TO_CPU (l->extent.start_block)
+ PED_BE16_TO_CPU (l->extent.block_count);
}
/* Count the free blocks from last_bad to the end of the volume */
end_free_blocks = 0;
for (block = last_bad;
block < PED_BE16_TO_CPU (mdb->total_blocks);
block++) {
if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
end_free_blocks++;
}
/* Calculate the block that will by the first free at the
end of the volume */
block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks;
return (PedSector) PED_BE16_TO_CPU (mdb->start_block)
+ (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size)
/ PED_SECTOR_SIZE_DEFAULT);
}
/* return the block which should be used to pack data to have at
least free fblock blocks at the end of the volume */
unsigned int _GL_ATTRIBUTE_PURE
hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
unsigned int block;
for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1;
block && fblock;
block--) {
if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
fblock--;
}
while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
block--;
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
block++;
return block;
}
#endif /* !DISCOVER_ONLY */

View File

@@ -0,0 +1,49 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _ADVFS_H
#define _ADVFS_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
int
hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
void *record_out, unsigned int record_size,
HfsCPrivateLeafRec* record_ref);
void
hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first);
int
hfs_read_bad_blocks (const PedFileSystem *fs);
int
hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock);
PedSector
hfs_get_empty_end (const PedFileSystem *fs);
unsigned int
hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock);
#endif /* _ADVFS_H */

View File

@@ -0,0 +1,385 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "advfs.h"
#include "file_plus.h"
#include "advfs_plus.h"
/* - if a < b, 0 if a == b, + if a > b */
/* Comparaison is done in the following order : */
/* CNID, then fork type, then start block */
static int
hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b)
{
HfsPExtentKey* key1 = (HfsPExtentKey*) a;
HfsPExtentKey* key2 = (HfsPExtentKey*) b;
if (key1->file_ID != key2->file_ID)
return PED_BE32_TO_CPU(key1->file_ID) <
PED_BE32_TO_CPU(key2->file_ID) ?
-1 : +1;
if (key1->type != key2->type)
return (int)(key1->type - key2->type);
if (key1->start == key2->start)
return 0;
return PED_BE32_TO_CPU(key1->start) <
PED_BE32_TO_CPU(key2->start) ?
-1 : +1;
}
/* do a B-Tree lookup */
/* read the first record immediatly inferior or egal to the given key */
/* return 0 on error */
/* record_out _must_ be large enough to receive the whole record (key + data) */
/* WARNING : the search function called only handle Extents BTree */
/* so modify this function if you want to do lookup in */
/* other BTrees has well */
int
hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key,
void *record_out, unsigned int record_size,
HfsCPrivateLeafRec* record_ref)
{
uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
uint8_t* node;
HfsPHeaderRecord* header;
HfsPPrivateGenericKey* record_key = NULL;
unsigned int node_number, record_number, size, bsize;
int i;
uint16_t record_pos;
/* Read the header node */
if (!hfsplus_file_read_sector(b_tree_file, node_1, 0))
return 0;
header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC);
/* Get the node number of the root */
node_number = PED_BE32_TO_CPU (header->root_node);
if (!node_number)
return 0;
/* Get the size of a node in sectors and allocate buffer */
size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE_DEFAULT;
node = ped_malloc (bsize);
if (!node)
return 0;
HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
/* Read the root node */
if (!hfsplus_file_read (b_tree_file, node,
(PedSector) node_number * size, size))
return 0;
/* Follow the white rabbit */
while (1) {
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = record_number; i; i--) {
uint16_t value;
memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t));
record_pos = PED_BE16_TO_CPU(value);
record_key = (HfsPPrivateGenericKey*) (node + record_pos);
/* check for obvious error in FS */
if ((record_pos < HFS_FIRST_REC)
|| (record_pos >= (signed)bsize
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
free (node);
return 0;
}
if (hfsplus_extent_key_cmp(record_key, key) <= 0)
break;
}
if (!i) { free (node); return 0; }
if (desc->type == HFS_IDX_NODE) {
unsigned int skip;
skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length)
+ 1 ) & ~1;
uint32_t value;
memcpy(&value, node+record_pos+skip, sizeof(uint32_t));
node_number = PED_BE32_TO_CPU(value);
if (!hfsplus_file_read(b_tree_file, node,
(PedSector) node_number * size,
size)) {
free (node);
return 0;
}
} else
break;
}
/* copy the result if needed */
if (record_size)
memcpy (record_out, record_key, record_size);
/* send record reference if needed */
if (record_ref) {
record_ref->node_size = size; /* in sectors */
record_ref->node_number = node_number;
record_ref->record_pos = record_pos;
record_ref->record_number = i;
}
/* success */
free (node);
return 1;
}
/* free the bad blocks linked list */
void
hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first)
{
HfsPPrivateLinkExtent* next;
while (first) {
next = first->next;
free (first);
first = next;
}
}
/* This function reads bad blocks extents in the extents file
and store it in f.s. specific data of fs */
int
hfsplus_read_bad_blocks (const PedFileSystem *fs)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
if (priv_data->bad_blocks_loaded)
return 1;
{
uint8_t record[sizeof (HfsPExtentKey)
+ sizeof (HfsPExtDataRec)];
HfsPExtentKey search;
HfsPExtentKey* ret_key = (HfsPExtentKey*) record;
HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*)
(record + sizeof (HfsPExtentKey));
int block, first_pass = 1;
unsigned int last_start;
search.key_length = sizeof (HfsExtentKey) - 2;
search.type = HFS_DATA_FORK;
search.pad = 0;
search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
last_start = -1; block = 0;
while (1) {
int i;
search.start = PED_CPU_TO_BE32 (block);
if (!hfsplus_btree_search (priv_data->extents_file,
(HfsPPrivateGenericKey*) &search,
record, sizeof (record), NULL)
|| ret_key->file_ID != search.file_ID
|| ret_key->type != search.type) {
if (first_pass)
break;
else
goto errbbp;
}
if (PED_BE32_TO_CPU (ret_key->start) == last_start)
break;
last_start = PED_BE32_TO_CPU (ret_key->start);
for (i = 0; i < HFSP_EXT_NB; i++) {
if (ret_cache[i].block_count) {
HfsPPrivateLinkExtent* new_xt =
(HfsPPrivateLinkExtent*) ped_malloc (
sizeof (HfsPPrivateLinkExtent));
if (!new_xt)
goto errbbp;
new_xt->next = priv_data->bad_blocks_xtent_list;
memcpy (&(new_xt->extent), ret_cache+i,
sizeof (HfsPExtDescriptor));
priv_data->bad_blocks_xtent_list = new_xt;
priv_data->bad_blocks_xtent_nb++;
block += PED_BE32_TO_CPU (
ret_cache[i].block_count);
}
}
first_pass = 0;
}
priv_data->bad_blocks_loaded = 1;
return 1;}
errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
priv_data->bad_blocks_xtent_list=NULL;
priv_data->bad_blocks_xtent_nb=0;
return 0;
}
/* This function check if fblock is a bad block */
int _GL_ATTRIBUTE_PURE
hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsPPrivateLinkExtent* walk;
for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
/* Won't compile without the strange cast ! gcc bug ? */
/* or maybe C subtilties... */
if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) &&
(fblock < (unsigned int)(PED_BE32_TO_CPU (
walk->extent.start_block)
+ PED_BE32_TO_CPU (walk->extent.block_count))))
return 1;
}
return 0;
}
/* This function returns the first sector of the last free block of
an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */
PedSector
hfsplus_get_empty_end (const PedFileSystem *fs)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsPVolumeHeader* vh = priv_data->vh;
unsigned int block, last_bad, end_free_blocks;
/* find the next block to the last bad block of the volume */
if (!hfsplus_read_bad_blocks (fs)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Bad blocks could not be read."));
return 0;
}
HfsPPrivateLinkExtent* l;
last_bad = 0;
for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) {
if ((unsigned int) PED_BE32_TO_CPU (l->extent.start_block)
+ PED_BE32_TO_CPU (l->extent.block_count) > last_bad)
last_bad = PED_BE32_TO_CPU (l->extent.start_block)
+ PED_BE32_TO_CPU (l->extent.block_count);
}
/* Count the free blocks from last_bad to the end of the volume */
end_free_blocks = 0;
for (block = last_bad;
block < PED_BE32_TO_CPU (vh->total_blocks);
block++) {
if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
end_free_blocks++;
}
/* Calculate the block that will by the first free at
the end of the volume */
block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks;
return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size)
/ PED_SECTOR_SIZE_DEFAULT );
}
/* On error, returns 0 */
PedSector
hfsplus_get_min_size (const PedFileSystem *fs)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
PedSector min_size;
/* don't need to add anything because every sector
can be part of allocation blocks in HFS+, and
the last block _must_ be reserved */
min_size = hfsplus_get_empty_end(fs);
if (!min_size) return 0;
if (priv_data->wrapper) {
HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*)
priv_data->wrapper->type_specific;
unsigned int hfs_sect_block;
PedSector hgee;
hfs_sect_block =
PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
/ PED_SECTOR_SIZE_DEFAULT;
/*
* if hfs+ is embedded in an hfs wrapper then the new size is :
* the new size of the hfs+ volume rounded up to the size
* of hfs blocks
* + the minimum size of the hfs wrapper without any hfs+
* modification
* - the current size of the hfs+ volume in the hfs wrapper
*/
hgee = hfs_get_empty_end(priv_data->wrapper);
if (!hgee) return 0;
min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block)
* hfs_sect_block
+ hgee + 2
- (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb
->old_new.embedded
.location.block_count )
* hfs_sect_block;
}
return min_size;
}
/* return the block which should be used to pack data to have
at least free fblock blocks at the end of the volume */
unsigned int _GL_ATTRIBUTE_PURE
hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
unsigned int block;
for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1;
block && fblock;
block--) {
if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
fblock--;
}
while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
block--;
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
block++;
return block;
}
#endif /* !DISCOVER_ONLY */

View File

@@ -0,0 +1,52 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _ADVFS_PLUS_H
#define _ADVFS_PLUS_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
int
hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key,
void *record_out, unsigned int record_size,
HfsCPrivateLeafRec* record_ref);
void
hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first);
int
hfsplus_read_bad_blocks (const PedFileSystem *fs);
int
hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock);
PedSector
hfsplus_get_empty_end (const PedFileSystem *fs);
PedSector
hfsplus_get_min_size (const PedFileSystem *fs);
unsigned int
hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock);
#endif /* _ADVFS_PLUS_H */

View File

@@ -0,0 +1,239 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "cache.h"
static HfsCPrivateCacheTable*
hfsc_new_cachetable(unsigned int size)
{
HfsCPrivateCacheTable* ret;
ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret));
if (!ret) return NULL;
ret->next_cache = NULL;
ret->table_size = size;
ret->table_first_free = 0;
ret->table = ped_malloc(sizeof(*ret->table)*size);
if (!ret->table) { free(ret); return NULL; }
memset(ret->table, 0, sizeof(*ret->table)*size);
return ret;
}
HfsCPrivateCache*
hfsc_new_cache(unsigned int block_number, unsigned int file_number)
{
unsigned int cachetable_size, i;
HfsCPrivateCache* ret;
ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret));
if (!ret) return NULL;
ret->block_number = block_number;
/* following code avoid integer overflow */
ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ?
( block_number >> CR_SHIFT ) + 1 :
( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT
;
ret->linked_ref = (HfsCPrivateExtent**)
ped_malloc( sizeof(*ret->linked_ref)
* ret->linked_ref_size );
if (!ret->linked_ref) { free(ret); return NULL; }
cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST;
if (cachetable_size < file_number) cachetable_size = (unsigned) -1;
ret->first_cachetable_size = cachetable_size;
ret->table_list = hfsc_new_cachetable(cachetable_size);
if (!ret->table_list) {
free(ret->linked_ref);
free(ret);
return NULL;
}
ret->last_table = ret->table_list;
for (i = 0; i < ret->linked_ref_size; ++i)
ret->linked_ref[i] = NULL;
ret->needed_alloc_size = 0;
return ret;
}
static void
hfsc_delete_cachetable(HfsCPrivateCacheTable* list)
{
HfsCPrivateCacheTable* next;
while (list) {
free (list->table);
next = list->next_cache;
free (list);
list = next;
}
}
void
hfsc_delete_cache(HfsCPrivateCache* cache)
{
hfsc_delete_cachetable(cache->table_list);
free(cache->linked_ref);
free(cache);
}
HfsCPrivateExtent*
hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
uint32_t block, uint16_t offset, uint8_t sbb,
uint8_t where, uint8_t ref_index)
{
HfsCPrivateExtent* ext;
unsigned int idx = start >> CR_SHIFT;
PED_ASSERT(idx < cache->linked_ref_size);
for (ext = cache->linked_ref[idx];
ext && start != ext->ext_start;
ext = ext->next);
if (ext) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Trying to register an extent starting at block "
"0x%X, but another one already exists at this "
"position. You should check the file system!"),
start);
return NULL;
}
if ( cache->last_table->table_first_free
== cache->last_table->table_size ) {
cache->last_table->next_cache =
hfsc_new_cachetable( ( cache->first_cachetable_size
/ CR_NEW_ALLOC_DIV )
+ CR_ADD_CST );
if (!cache->last_table->next_cache)
return NULL;
cache->last_table = cache->last_table->next_cache;
}
ext = cache->last_table->table+(cache->last_table->table_first_free++);
ext->ext_start = start;
ext->ext_length = length;
ext->ref_block = block;
ext->ref_offset = offset;
ext->sect_by_block = sbb;
ext->where = where;
ext->ref_index = ref_index;
ext->next = cache->linked_ref[idx];
cache->linked_ref[idx] = ext;
cache->needed_alloc_size = cache->needed_alloc_size >
(unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ?
cache->needed_alloc_size :
(unsigned) PED_SECTOR_SIZE_DEFAULT * sbb;
return ext;
}
HfsCPrivateExtent* _GL_ATTRIBUTE_PURE
hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start)
{
HfsCPrivateExtent* ret;
unsigned int idx = start >> CR_SHIFT;
PED_ASSERT(idx < cache->linked_ref_size);
for (ret = cache->linked_ref[idx];
ret && start != ret->ext_start;
ret = ret->next);
return ret;
}
/* Can't fail if extent begining at old_start exists */
/* Returns 0 if no such extent, or on error */
HfsCPrivateExtent*
hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
uint32_t new_start)
{
HfsCPrivateExtent** ppext;
HfsCPrivateExtent* pext;
unsigned int idx1 = old_start >> CR_SHIFT;
unsigned int idx2 = new_start >> CR_SHIFT;
PED_ASSERT(idx1 < cache->linked_ref_size);
PED_ASSERT(idx2 < cache->linked_ref_size);
for (pext = cache->linked_ref[idx2];
pext && new_start != pext->ext_start;
pext = pext->next);
if (pext) {
ped_exception_throw (
PED_EXCEPTION_BUG,
PED_EXCEPTION_CANCEL,
_("Trying to move an extent from block 0x%X to block "
"0x%X, but another one already exists at this "
"position. This should not happen!"),
old_start, new_start);
return NULL;
}
for (ppext = &(cache->linked_ref[idx1]);
(*ppext) && old_start != (*ppext)->ext_start;
ppext = &((*ppext)->next));
if (!(*ppext)) return NULL;
/* removing the extent from the cache */
pext = *ppext;
(*ppext) = pext->next;
/* change ext_start and insert the extent again */
pext->ext_start = new_start;
pext->next = cache->linked_ref[idx2];
cache->linked_ref[idx2] = pext;
return pext;
}
#endif /* DISCOVER_ONLY */

View File

@@ -0,0 +1,118 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 _CACHE_H
#define _CACHE_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
/* CR => CACHE REF */
#define CR_NULL 0 /* reserved */
#define CR_PRIM_CAT 1
#define CR_PRIM_EXT 2
#define CR_PRIM_ATTR 3
#define CR_PRIM_ALLOC 4
#define CR_PRIM_START 5
#define CR_BTREE_CAT 6
#define CR_BTREE_ATTR 7
#define CR_BTREE_EXT_0 8
#define CR_BTREE_EXT_CAT 9
#define CR_BTREE_EXT_EXT 10 /* should not happen ! */
#define CR_BTREE_EXT_ATTR 11
#define CR_BTREE_EXT_ALLOC 12
#define CR_BTREE_EXT_START 13 /* unneeded in current code */
#define CR_BTREE_CAT_JIB 14 /* journal info block */
#define CR_BTREE_CAT_JL 15 /* journal */
/* 16 -> 31 || high order bit */ /* reserved */
/* tuning */
#define CR_SHIFT 8 /* number of bits to shift start_block by */
/* to get the index of the linked list */
#define CR_OVER_DIV 16 /* alloc a table for (1+1/CR_OVER_DIV) *
file_number + CR_ADD_CST */
#define CR_ADD_CST 16
#define CR_NEW_ALLOC_DIV 4 /* divide the size of the first alloc table
by this value to allocate next tables */
/* See DOC for an explaination of this structure */
/* Access read only from outside cache.c */
struct _HfsCPrivateExtent {
struct _HfsCPrivateExtent* next;
uint32_t ext_start;
uint32_t ext_length;
uint32_t ref_block;
uint16_t ref_offset;
uint8_t sect_by_block;
unsigned where : 5;
unsigned ref_index : 3; /* 0 -> 7 */
};
typedef struct _HfsCPrivateExtent HfsCPrivateExtent;
/* Internaly used by cache.c for custom memory managment only */
struct _HfsCPrivateCacheTable {
struct _HfsCPrivateCacheTable* next_cache;
HfsCPrivateExtent* table;
unsigned int table_size;
unsigned int table_first_free;
/* first_elemt ? */
};
typedef struct _HfsCPrivateCacheTable HfsCPrivateCacheTable;
/* Internaly used by cache.c for custom memory managment
and cache handling only */
struct _HfsCPrivateCache {
HfsCPrivateCacheTable* table_list;
HfsCPrivateCacheTable* last_table;
HfsCPrivateExtent** linked_ref;
unsigned int linked_ref_size;
unsigned int block_number;
unsigned int first_cachetable_size;
unsigned int needed_alloc_size;
};
typedef struct _HfsCPrivateCache HfsCPrivateCache;
HfsCPrivateCache*
hfsc_new_cache(unsigned int block_number, unsigned int file_number);
void
hfsc_delete_cache(HfsCPrivateCache* cache);
HfsCPrivateExtent*
hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
uint32_t block, uint16_t offset, uint8_t sbb,
uint8_t where, uint8_t index);
HfsCPrivateExtent*
hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start);
HfsCPrivateExtent*
hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
uint32_t new_start);
static __inline__ unsigned int
hfsc_cache_needed_buffer(HfsCPrivateCache* cache)
{
return cache->needed_alloc_size;
}
#endif /* _CACHE_H */

View File

@@ -0,0 +1,229 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "advfs.h"
#include "file.h"
/* Open the data fork of a file with its first three extents and its CNID */
HfsPrivateFile*
hfs_file_open (PedFileSystem *fs, uint32_t CNID,
HfsExtDataRec ext_desc, PedSector sect_nb)
{
HfsPrivateFile* file;
file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile));
if (!file) return NULL;
file->fs = fs;
file->sect_nb = sect_nb;
file->CNID = CNID;
memcpy(file->first, ext_desc, sizeof (HfsExtDataRec));
file->start_cache = 0;
return file;
}
/* Close an HFS file */
void
hfs_file_close (HfsPrivateFile* file)
{
free (file);
}
/* warning : only works on data forks */
static int
hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block,
HfsExtDataRec cache, uint16_t* ptr_start_cache)
{
uint8_t record[sizeof (HfsExtentKey)
+ sizeof (HfsExtDataRec)];
HfsExtentKey search;
HfsExtentKey* ret_key = (HfsExtentKey*) record;
HfsExtDescriptor* ret_cache = (HfsExtDescriptor*)
(record + sizeof (HfsExtentKey));
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
file->fs->type_specific;
search.key_length = sizeof (HfsExtentKey) - 1;
search.type = HFS_DATA_FORK;
search.file_ID = file->CNID;
search.start = PED_CPU_TO_BE16 (block);
if (!hfs_btree_search (priv_data->extent_file,
(HfsPrivateGenericKey*) &search,
record, sizeof (record), NULL))
return 0;
if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
return 0;
memcpy (cache, ret_cache, sizeof(HfsExtDataRec));
*ptr_start_cache = PED_BE16_TO_CPU (ret_key->start);
return 1;
}
/* find and return the nth sector of a file */
/* return 0 on error */
static PedSector
hfs_file_find_sector (HfsPrivateFile* file, PedSector sector)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
file->fs->type_specific;
unsigned int sect_by_block = PED_BE32_TO_CPU (
priv_data->mdb->block_size)
/ PED_SECTOR_SIZE_DEFAULT;
unsigned int i, s, vol_block;
unsigned int block = sector / sect_by_block;
unsigned int offset = sector % sect_by_block;
/* in the three first extent */
for (s = 0, i = 0; i < HFS_EXT_NB; i++) {
if ((block >= s) && ( block < s + PED_BE16_TO_CPU (
file->first[i].block_count))) {
vol_block = (block - s) + PED_BE16_TO_CPU (
file->first[i].start_block);
goto sector_found;
}
s += PED_BE16_TO_CPU (file->first[i].block_count);
}
/* in the three cached extent */
if (file->start_cache && block >= file->start_cache)
for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
if ((block >= s) && (block < s + PED_BE16_TO_CPU (
file->cache[i].block_count))) {
vol_block = (block - s) + PED_BE16_TO_CPU (
file->cache[i].start_block);
goto sector_found;
}
s += PED_BE16_TO_CPU (file->cache[i].block_count);
}
/* update cache */
if (!hfs_get_extent_containing (file, block, file->cache,
&(file->start_cache))) {
ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_CANCEL,
_("Could not update the extent cache for HFS file with "
"CNID %X."),
PED_BE32_TO_CPU(file->CNID));
return 0;
}
/* in the three cached extent */
PED_ASSERT(file->start_cache && block >= file->start_cache);
for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
if ((block >= s) && (block < s + PED_BE16_TO_CPU (
file->cache[i].block_count))) {
vol_block = (block - s) + PED_BE16_TO_CPU (
file->cache[i].start_block);
goto sector_found;
}
s += PED_BE16_TO_CPU (file->cache[i].block_count);
}
return 0;
sector_found:
return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block)
+ (PedSector) vol_block * sect_by_block
+ offset;
}
/* Read the nth sector of a file */
/* return 0 on error */
int
hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector)
{
PedSector abs_sector;
if (sector >= file->sect_nb) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Trying to read HFS file with CNID %X behind EOF."),
PED_BE32_TO_CPU(file->CNID));
return 0;
}
abs_sector = hfs_file_find_sector (file, sector);
if (!abs_sector) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Could not find sector %lli of HFS file with "
"CNID %X."),
sector, PED_BE32_TO_CPU(file->CNID));
return 0;
}
return ped_geometry_read (file->fs->geom, buf, abs_sector, 1);
}
/* Write the nth sector of a file */
/* return 0 on error */
int
hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector)
{
PedSector abs_sector;
if (sector >= file->sect_nb) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Trying to write HFS file with CNID %X behind EOF."),
PED_BE32_TO_CPU(file->CNID));
return 0;
}
abs_sector = hfs_file_find_sector (file, sector);
if (!abs_sector) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Could not find sector %lli of HFS file with "
"CNID %X."),
sector, PED_BE32_TO_CPU(file->CNID));
return 0;
}
return ped_geometry_write (file->fs->geom, buf, abs_sector, 1);
}
#endif /* !DISCOVER_ONLY */

View File

@@ -0,0 +1,42 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _FILE_H
#define _FILE_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
HfsPrivateFile*
hfs_file_open (PedFileSystem *fs, uint32_t CNID,
HfsExtDataRec ext_desc, PedSector sect_nb);
void
hfs_file_close (HfsPrivateFile* file);
int
hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector);
int
hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector);
#endif /* _FILE_H */

View File

@@ -0,0 +1,274 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "advfs_plus.h"
#include "file_plus.h"
/* Open the data fork of a file with its first eight extents and its CNID */
/* CNID and ext_desc must be in disc order, sect_nb in CPU order */
/* return null on failure */
HfsPPrivateFile*
hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
HfsPExtDataRec ext_desc, PedSector sect_nb)
{
HfsPPrivateFile* file;
file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile));
if (!file) return NULL;
file->fs = fs;
file->sect_nb = sect_nb;
file->CNID = CNID;
memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec));
file->start_cache = 0;
return file;
}
/* Close an HFS+ file */
void
hfsplus_file_close (HfsPPrivateFile* file)
{
free (file);
}
/* warning : only works on data forks */
static int
hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block,
HfsPExtDataRec cache, uint32_t* ptr_start_cache)
{
uint8_t record[sizeof (HfsPExtentKey)
+ sizeof (HfsPExtDataRec)];
HfsPExtentKey search;
HfsPExtentKey* ret_key = (HfsPExtentKey*) record;
HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*)
(record + sizeof (HfsPExtentKey));
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
file->fs->type_specific;
search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2);
search.type = HFS_DATA_FORK;
search.pad = 0;
search.file_ID = file->CNID;
search.start = PED_CPU_TO_BE32 (block);
if (!hfsplus_btree_search (priv_data->extents_file,
(HfsPPrivateGenericKey*) &search,
record, sizeof (record), NULL))
return 0;
if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
return 0;
memcpy (cache, ret_cache, sizeof(HfsPExtDataRec));
*ptr_start_cache = PED_BE32_TO_CPU (ret_key->start);
return 1;
}
/* find a sub extent contained in the desired area */
/* and with the same starting point */
/* return 0 in sector_count on error, or the physical area */
/* on the volume corresponding to the logical area in the file */
static HfsPPrivateExtent
hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector,
unsigned int nb)
{
HfsPPrivateExtent ret = {0,0};
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
file->fs->type_specific;
unsigned int sect_by_block = PED_BE32_TO_CPU (
priv_data->vh->block_size)
/ PED_SECTOR_SIZE_DEFAULT;
unsigned int i, s, vol_block, size;
PedSector sect_size;
unsigned int block = sector / sect_by_block;
unsigned int offset = sector % sect_by_block;
/* in the 8 first extent */
for (s = 0, i = 0; i < HFSP_EXT_NB; i++) {
if ((block >= s) && (block < s + PED_BE32_TO_CPU (
file->first[i].block_count))) {
vol_block = (block - s)
+ PED_BE32_TO_CPU (file->first[i]
.start_block);
size = PED_BE32_TO_CPU (file->first[i].block_count)
+ s - block;
goto plus_sector_found;
}
s += PED_BE32_TO_CPU (file->first[i].block_count);
}
/* in the 8 cached extent */
if (file->start_cache && block >= file->start_cache)
for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
if ((block >= s) && (block < s + PED_BE32_TO_CPU (
file->cache[i].block_count))) {
vol_block = (block - s)
+ PED_BE32_TO_CPU (file->cache[i]
.start_block);
size = PED_BE32_TO_CPU (file->cache[i].block_count)
+ s - block;
goto plus_sector_found;
}
s += PED_BE32_TO_CPU (file->cache[i].block_count);
}
/* update cache */
if (!hfsplus_get_extent_containing (file, block, file->cache,
&(file->start_cache))) {
ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_CANCEL,
_("Could not update the extent cache for HFS+ file "
"with CNID %X."),
PED_BE32_TO_CPU(file->CNID));
return ret; /* ret == {0,0} */
}
/* ret == {0,0} */
PED_ASSERT(file->start_cache && block >= file->start_cache);
for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
if ((block >= s) && (block < s + PED_BE32_TO_CPU (
file->cache[i].block_count))) {
vol_block = (block - s)
+ PED_BE32_TO_CPU (file->cache[i]
.start_block);
size = PED_BE32_TO_CPU (file->cache[i].block_count)
+ s - block;
goto plus_sector_found;
}
s += PED_BE32_TO_CPU (file->cache[i].block_count);
}
return ret;
plus_sector_found:
sect_size = (PedSector) size * sect_by_block - offset;
ret.start_sector = vol_block * sect_by_block + offset;
ret.sector_count = (sect_size < nb) ? sect_size : nb;
return ret;
}
int
hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector,
unsigned int nb)
{
HfsPPrivateExtent phy_area;
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
file->fs->type_specific;
char *b = buf;
if (sector+nb < sector /* detect overflow */
|| sector+nb > file->sect_nb) /* out of file */ {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Trying to read HFS+ file with CNID %X behind EOF."),
PED_BE32_TO_CPU(file->CNID));
return 0;
}
while (nb) {
phy_area = hfsplus_file_find_extent(file, sector, nb);
if (phy_area.sector_count == 0) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Could not find sector %lli of HFS+ file "
"with CNID %X."),
sector, PED_BE32_TO_CPU(file->CNID));
return 0;
}
if (!ped_geometry_read(priv_data->plus_geom, b,
phy_area.start_sector,
phy_area.sector_count))
return 0;
nb -= phy_area.sector_count; /* < nb anyway ... */
sector += phy_area.sector_count;
b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
}
return 1;
}
int
hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector,
unsigned int nb)
{
HfsPPrivateExtent phy_area;
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
file->fs->type_specific;
char *b = buf;
if (sector+nb < sector /* detect overflow */
|| sector+nb > file->sect_nb) /* out of file */ {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Trying to write HFS+ file with CNID %X behind EOF."),
PED_BE32_TO_CPU(file->CNID));
return 0;
}
while (nb) {
phy_area = hfsplus_file_find_extent(file, sector, nb);
if (phy_area.sector_count == 0) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Could not find sector %lli of HFS+ file "
"with CNID %X."),
sector, PED_BE32_TO_CPU(file->CNID));
return 0;
}
if (!ped_geometry_write(priv_data->plus_geom, b,
phy_area.start_sector,
phy_area.sector_count))
return 0;
nb -= phy_area.sector_count; /* < nb anyway ... */
sector += phy_area.sector_count;
b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
}
return 1;
}
#endif /* !DISCOVER_ONLY */

View File

@@ -0,0 +1,61 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _FILE_PLUS_H
#define _FILE_PLUS_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
HfsPPrivateFile*
hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
HfsPExtDataRec ext_desc, PedSector sect_nb);
void
hfsplus_file_close (HfsPPrivateFile* file);
int
hfsplus_file_read(HfsPPrivateFile* file, void *buf,
PedSector sector, unsigned int nb);
int
hfsplus_file_write(HfsPPrivateFile* file, void *buf,
PedSector sector, unsigned int nb);
/* Read the nth sector of a file */
/* return 0 on error */
static __inline__ int
hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector)
{
return hfsplus_file_read(file, buf, sector, 1);
}
/* Write the nth sector of a file */
/* return 0 on error */
static __inline__ int
hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector)
{
return hfsplus_file_write(file, buf, sector, 1);
}
#endif /* _FILE_PLUS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,648 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2003-2005, 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 _HFS_H
#define _HFS_H
/* WARNING : bn is used 2 times in theses macro */
/* so _never_ use side effect operators when using them */
#define TST_BLOC_OCCUPATION(tab,bn) \
(((tab)[(bn)/8]) & (1<<(7-((bn)&7))))
#define SET_BLOC_OCCUPATION(tab,bn) \
(((tab)[(bn)/8]) |= (1<<(7-((bn)&7))))
#define CLR_BLOC_OCCUPATION(tab,bn) \
(((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7))))
/* Maximum number of blocks for the copy buffers */
#define BLOCK_MAX_BUFF 256
/* Maximum size of the copy buffers, in bytes */
#define BYTES_MAX_BUFF 8388608
/* Apple Creator Codes follow */
#define HFSP_IMPL_Shnk 0x53686e6b /* in use */
#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */
#define HFSP_IMPL_Resz 0x5265737a /* reserved */
#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */
#define HFSP_IMPL_traP 0x74726150 /* reserved */
#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */
#define HFS_SIGNATURE 0x4244 /* 'BD' */
#define HFSP_SIGNATURE 0x482B /* 'H+' */
#define HFSX_SIGNATURE 0x4858 /* 'HX' */
#define HFSP_VERSION 4
#define HFSX_VERSION 5
#define HFS_HARD_LOCK 7
#define HFS_UNMOUNTED 8
#define HFS_BAD_SPARED 9
#define HFS_SOFT_LOCK 15
#define HFSP_NO_CACHE 10
#define HFSP_INCONSISTENT 11
#define HFSP_REUSE_CNID 12
#define HFSP_JOURNALED 13
#define HFS_IDX_NODE 0x00
#define HFS_HDR_NODE 0x01
#define HFS_MAP_NODE 0x02
#define HFS_LEAF_NODE 0xFF
#define HFS_FIRST_REC 0x0E
#define HFS_NSD_HD_REC 0x78
#define HFS_MAP_REC 0xF8
#define HFS_DATA_FORK 0x00
#define HFS_RES_FORK 0xFF
#define HFS_CAT_DIR 0x01
#define HFS_CAT_FILE 0x02
#define HFS_CAT_DIR_TH 0x03
#define HFS_CAT_FILE_TH 0x04
#define HFSP_ATTR_INLINE 0x10
#define HFSP_ATTR_FORK 0x20
#define HFSP_ATTR_EXTENTS 0x30
#define HFS_ROOT_PAR_ID 0x01
#define HFS_ROOT_DIR_ID 0x02
#define HFS_XTENT_ID 0x03
#define HFS_CATALOG_ID 0x04
#define HFS_BAD_BLOCK_ID 0x05
#define HFSP_ALLOC_ID 0x06
#define HFSP_STARTUP_ID 0x07
#define HFSP_ATTRIB_ID 0x08
#define HFSP_BOGUS_ID 0x0F
#define HFSP_FIRST_AV_ID 0x10
#define HFSJ_JOURN_IN_FS 0x00
#define HFSJ_JOURN_OTHER_DEV 0x01
#define HFSJ_JOURN_NEED_INIT 0x02
#define HFSJ_HEADER_MAGIC 0x4a4e4c78
#define HFSJ_ENDIAN_MAGIC 0x12345678
#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */
#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */
#define HFS_EXT_NB 3
#define HFSP_EXT_NB 8
/* Define the filenames used by the FS extractor */
#ifdef HFS_EXTRACT_FS
#define HFS_MDB_FILENAME "mdb.hfs"
#define HFS_CATALOG_FILENAME "catalog.hfs"
#define HFS_EXTENTS_FILENAME "extents.hfs"
#define HFS_BITMAP_FILENAME "bitmap.hfs"
#define HFSP_VH_FILENAME "vh.hfsplus"
#define HFSP_CATALOG_FILENAME "catalog.hfsplus"
#define HFSP_EXTENTS_FILENAME "extents.hfsplus"
#define HFSP_BITMAP_FILENAME "bitmap.hfsplus"
#define HFSP_ATTRIB_FILENAME "attributes.hfsplus"
#define HFSP_STARTUP_FILENAME "startup.hfsplus"
#endif /* HFS_EXTRACT_FS */
/* ----------------------------------- */
/* -- HFS DATA STRUCTURES -- */
/* ----------------------------------- */
/* Extent descriptor */
struct __attribute__ ((packed)) _HfsExtDescriptor {
uint16_t start_block;
uint16_t block_count;
};
typedef struct _HfsExtDescriptor HfsExtDescriptor;
typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB];
/* Volume header */
struct __attribute__ ((packed)) _HfsMasterDirectoryBlock {
uint16_t signature;
uint32_t create_date;
uint32_t modify_date;
uint16_t volume_attributes;
uint16_t files_in_root;
uint16_t volume_bitmap_block; /* in sectors */
uint16_t next_allocation;
uint16_t total_blocks;
uint32_t block_size; /* in bytes */
uint32_t def_clump_size; /* in bytes */
uint16_t start_block; /* in sectors */
uint32_t next_free_node;
uint16_t free_blocks;
uint8_t name_length;
char name[27];
uint32_t backup_date;
uint16_t backup_number;
uint32_t write_count;
uint32_t extents_clump;
uint32_t catalog_clump;
uint16_t dirs_in_root;
uint32_t file_count;
uint32_t dir_count;
uint32_t finder_info[8];
union __attribute__ ((packed)) {
struct __attribute__ ((packed)) {
uint16_t volume_cache_size; /* in blocks */
uint16_t bitmap_cache_size; /* in blocks */
uint16_t common_cache_size; /* in blocks */
} legacy;
struct __attribute__ ((packed)) {
uint16_t signature;
HfsExtDescriptor location;
} embedded;
} old_new;
uint32_t extents_file_size; /* in bytes, block size multiple */
HfsExtDataRec extents_file_rec;
uint32_t catalog_file_size; /* in bytes, block size multiple */
HfsExtDataRec catalog_file_rec;
};
typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock;
/* B*-Tree Node Descriptor */
struct __attribute__ ((packed)) _HfsNodeDescriptor {
uint32_t next;
uint32_t previous;
int8_t type;
uint8_t height;
uint16_t rec_nb;
uint16_t reserved;
};
typedef struct _HfsNodeDescriptor HfsNodeDescriptor;
/* Header record of a whole B*-Tree */
struct __attribute__ ((packed)) _HfsHeaderRecord {
uint16_t depth;
uint32_t root_node;
uint32_t leaf_records;
uint32_t first_leaf_node;
uint32_t last_leaf_node;
uint16_t node_size;
uint16_t max_key_len;
uint32_t total_nodes;
uint32_t free_nodes;
int8_t reserved[76];
};
typedef struct _HfsHeaderRecord HfsHeaderRecord;
/* Catalog key for B*-Tree lookup in the catalog file */
struct __attribute__ ((packed)) _HfsCatalogKey {
uint8_t key_length; /* length of the key without key_length */
uint8_t reserved;
uint32_t parent_ID;
uint8_t name_length;
char name[31]; /* in fact physicaly 1 upto 31 */
};
typedef struct _HfsCatalogKey HfsCatalogKey;
/* Extents overflow key for B*-Tree lookup */
struct __attribute__ ((packed)) _HfsExtentKey {
uint8_t key_length; /* length of the key without key_length */
uint8_t type; /* data or ressource fork */
uint32_t file_ID;
uint16_t start;
};
typedef struct _HfsExtentKey HfsExtentKey;
/* Catalog subdata case directory */
struct __attribute__ ((packed)) _HfsDir {
uint16_t flags;
uint16_t valence; /* number of files in this directory */
uint32_t dir_ID;
uint32_t create_date;
uint32_t modify_date;
uint32_t backup_date;
int8_t DInfo[16]; /* used by Finder, handle as reserved */
int8_t DXInfo[16]; /* used by Finder, handle as reserved */
uint32_t reserved[4];
};
typedef struct _HfsDir HfsDir;
/* Catalog subdata case file */
struct __attribute__ ((packed)) _HfsFile {
int8_t flags;
int8_t type; /* should be 0 */
int8_t FInfo[16]; /* used by Finder, handle as reserved */
uint32_t file_ID;
uint16_t data_start_block;
uint32_t data_sz_byte;
uint32_t data_sz_block;
uint16_t res_start_block;
uint32_t res_sz_byte;
uint32_t res_sz_block;
uint32_t create_date;
uint32_t modify_date;
uint32_t backup_date;
int8_t FXInfo[16]; /* used by Finder, handle as reserved */
uint16_t clump_size;
HfsExtDataRec extents_data;
HfsExtDataRec extents_res;
uint32_t reserved;
};
typedef struct _HfsFile HfsFile;
/* Catalog subdata case directory thread */
struct __attribute__ ((packed)) _HfsDirTh {
uint32_t reserved[2];
uint32_t parent_ID;
int8_t name_length;
char name[31];
};
typedef struct _HfsDirTh HfsDirTh;
/* Catalog subdata case file thread */
typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */
/* Catalog data */
struct __attribute__ ((packed)) _HfsCatalog {
int8_t type;
int8_t reserved;
union {
HfsDir dir;
HfsFile file;
HfsDirTh dir_th;
HfsFileTh file_th;
} sel;
};
typedef struct _HfsCatalog HfsCatalog;
/* ------------------------------------ */
/* -- HFS+ DATA STRUCTURES -- */
/* ------------------------------------ */
/* documented since 2004 in tn1150 */
struct __attribute__ ((packed)) _HfsPPerms {
uint32_t owner_ID;
uint32_t group_ID;
uint32_t permissions;
uint32_t special_devices;
};
typedef struct _HfsPPerms HfsPPerms;
/* HFS+ extent descriptor*/
struct __attribute__ ((packed)) _HfsPExtDescriptor {
uint32_t start_block;
uint32_t block_count;
};
typedef struct _HfsPExtDescriptor HfsPExtDescriptor;
typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB];
/* HFS+ fork data structure */
struct __attribute__ ((packed)) _HfsPForkData {
uint64_t logical_size;
uint32_t clump_size;
uint32_t total_blocks;
HfsPExtDataRec extents;
};
typedef struct _HfsPForkData HfsPForkData;
/* HFS+ catalog node ID */
typedef uint32_t HfsPNodeID;
/* HFS+ file names */
typedef uint16_t unichar;
struct __attribute__ ((packed)) _HfsPUniStr255 {
uint16_t length;
unichar unicode[255]; /* 1 upto 255 */
};
typedef struct _HfsPUniStr255 HfsPUniStr255;
/* HFS+ volume header */
struct __attribute__ ((packed)) _HfsPVolumeHeader {
uint16_t signature;
uint16_t version;
uint32_t attributes;
uint32_t last_mounted_version;
uint32_t journal_info_block;
uint32_t create_date;
uint32_t modify_date;
uint32_t backup_date;
uint32_t checked_date;
uint32_t file_count;
uint32_t dir_count;
uint32_t block_size;
uint32_t total_blocks;
uint32_t free_blocks;
uint32_t next_allocation;
uint32_t res_clump_size;
uint32_t data_clump_size;
HfsPNodeID next_catalog_ID;
uint32_t write_count;
uint64_t encodings_bitmap;
uint8_t finder_info[32];
HfsPForkData allocation_file;
HfsPForkData extents_file;
HfsPForkData catalog_file;
HfsPForkData attributes_file;
HfsPForkData startup_file;
};
typedef struct _HfsPVolumeHeader HfsPVolumeHeader;
/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */
struct __attribute__ ((packed)) _HfsPNodeDescriptor {
uint32_t next;
uint32_t previous;
int8_t type;
uint8_t height;
uint16_t rec_nb;
uint16_t reserved;
};
typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor;
/* Header record of a whole HFS+ B-Tree. */
struct __attribute__ ((packed)) _HfsPHeaderRecord {
uint16_t depth;
uint32_t root_node;
uint32_t leaf_records;
uint32_t first_leaf_node;
uint32_t last_leaf_node;
uint16_t node_size;
uint16_t max_key_len;
uint32_t total_nodes;
uint32_t free_nodes; /* same as hfs btree until here */
uint16_t reserved1;
uint32_t clump_size;
uint8_t btree_type; /* must be 0 for HFS+ B-Tree */
uint8_t key_compare_type; /* hfsx => 0xCF = case folding */
/* 0xBC = binary compare */
/* otherwise, reserved */
uint32_t attributes;
uint32_t reserved3[16];
};
typedef struct _HfsPHeaderRecord HfsPHeaderRecord;
/* Catalog key for B-Tree lookup in the HFS+ catalog file */
struct __attribute__ ((packed)) _HfsPCatalogKey {
uint16_t key_length;
HfsPNodeID parent_ID;
HfsPUniStr255 node_name;
};
typedef struct _HfsPCatalogKey HfsPCatalogKey;
/* HFS+ catalog subdata case dir */
struct __attribute__ ((packed)) _HfsPDir {
uint16_t flags;
uint32_t valence;
HfsPNodeID dir_ID;
uint32_t create_date;
uint32_t modify_date;
uint32_t attrib_mod_date;
uint32_t access_date;
uint32_t backup_date;
HfsPPerms permissions;
int8_t DInfo[16]; /* used by Finder, handle as reserved */
int8_t DXInfo[16]; /* used by Finder, handle as reserved */
uint32_t text_encoding;
uint32_t reserved;
};
typedef struct _HfsPDir HfsPDir;
/* HFS+ catalog subdata case file */
struct __attribute__ ((packed)) _HfsPFile {
uint16_t flags;
uint32_t reserved1;
HfsPNodeID file_ID;
uint32_t create_date;
uint32_t modify_date;
uint32_t attrib_mod_date;
uint32_t access_date;
uint32_t backup_date;
HfsPPerms permissions;
int8_t FInfo[16]; /* used by Finder, handle as reserved */
int8_t FXInfo[16]; /* used by Finder, handle as reserved */
uint32_t text_encoding;
uint32_t reserved2;
HfsPForkData data_fork;
HfsPForkData res_fork;
};
typedef struct _HfsPFile HfsPFile;
/* HFS+ catalog subdata case thread */
struct __attribute__ ((packed)) _HfsPThread {
int16_t reserved;
HfsPNodeID parent_ID;
HfsPUniStr255 node_name;
};
typedef struct _HfsPThread HfsPDirTh;
typedef struct _HfsPThread HfsPFileTh;
/* HFS+ Catalog leaf data */
struct __attribute__ ((packed)) _HfsPCatalog {
int16_t type;
union {
HfsPDir dir;
HfsPFile file;
HfsPDirTh dir_th;
HfsPFileTh file_th;
} sel;
};
typedef struct _HfsPCatalog HfsPCatalog;
/* HFS+ extents file key */
struct __attribute__ ((packed)) _HfsPExtentKey {
uint16_t key_length;
uint8_t type;
uint8_t pad;
HfsPNodeID file_ID;
uint32_t start;
};
typedef struct _HfsPExtentKey HfsPExtentKey;
/* extent file data is HfsPExtDataRec */
/* Fork data attribute file */
struct __attribute__ ((packed)) _HfsPForkDataAttr {
uint32_t record_type;
uint32_t reserved;
union __attribute__ ((packed)) {
HfsPForkData fork;
HfsPExtDataRec extents;
} fork_res;
};
typedef struct _HfsPForkDataAttr HfsPForkDataAttr;
/* ----------- Journal data structures ----------- */
/* Info block : stored in a block # defined in the VH */
struct __attribute__ ((packed)) _HfsJJournalInfoBlock {
uint32_t flags;
uint32_t device_signature[8];
uint64_t offset;
uint64_t size;
uint32_t reserved[32];
};
typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock;
struct __attribute__ ((packed)) _HfsJJournalHeader {
uint32_t magic;
uint32_t endian;
uint64_t start;
uint64_t end;
uint64_t size;
uint32_t blhdr_size;
uint32_t checksum;
uint32_t jhdr_size;
};
typedef struct _HfsJJournalHeader HfsJJournalHeader;
struct __attribute__ ((packed)) _HfsJBlockInfo {
uint64_t bnum; /* sector number */
uint32_t bsize; /* size in bytes */
uint32_t next;
};
typedef struct _HfsJBlockInfo HfsJBlockInfo;
struct __attribute__ ((packed)) _HfsJBlockListHeader {
uint16_t max_blocks; /* reserved */
uint16_t num_blocks;
uint32_t bytes_used;
uint32_t checksum;
uint32_t pad;
HfsJBlockInfo binfo[];
};
typedef struct _HfsJBlockListHeader HfsJBlockListHeader;
/* ---------------------------------------- */
/* -- INTERNAL DATA STRUCTURES -- */
/* ---------------------------------------- */
/* Data of an opened HFS file */
struct _HfsPrivateFile {
PedSector sect_nb;
PedFileSystem* fs;
uint32_t CNID; /* disk order (BE) */
HfsExtDataRec first; /* disk order (BE) */
HfsExtDataRec cache; /* disk order (BE) */
uint16_t start_cache; /* CPU order */
};
typedef struct _HfsPrivateFile HfsPrivateFile;
/* To store bad block list */
struct _HfsPrivateLinkExtent {
HfsExtDescriptor extent;
struct _HfsPrivateLinkExtent* next;
};
typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent;
/* HFS Filesystem specific data */
struct _HfsPrivateFSData {
uint8_t alloc_map[(1<<16) / 8];
HfsMasterDirectoryBlock* mdb;
HfsPrivateFile* extent_file;
HfsPrivateFile* catalog_file;
HfsPrivateLinkExtent* bad_blocks_xtent_list;
unsigned int bad_blocks_xtent_nb;
char bad_blocks_loaded;
};
typedef struct _HfsPrivateFSData HfsPrivateFSData;
/* Generic btree key */
struct __attribute__ ((packed)) _HfsPrivateGenericKey {
uint8_t key_length;
uint8_t key_content[1]; /* we use 1 as a minimum size */
};
typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey;
/* ----- HFS+ ----- */
/* Data of an opened HFS file */
struct _HfsPPrivateFile {
PedSector sect_nb;
PedFileSystem* fs;
HfsPNodeID CNID; /* disk order (BE) */
HfsPExtDataRec first; /* disk order (BE) */
HfsPExtDataRec cache; /* disk order (BE) */
uint32_t start_cache; /* CPU order */
};
typedef struct _HfsPPrivateFile HfsPPrivateFile;
struct _HfsPPrivateExtent {
PedSector start_sector;
PedSector sector_count;
};
typedef struct _HfsPPrivateExtent HfsPPrivateExtent;
/* To store bad block list */
struct _HfsPPrivateLinkExtent {
HfsPExtDescriptor extent;
struct _HfsPPrivateLinkExtent* next;
};
typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent;
/* HFS+ file system specific data */
struct _HfsPPrivateFSData {
PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */
PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */
uint8_t* alloc_map;
uint8_t* dirty_alloc_map;
HfsPVolumeHeader* vh;
HfsPPrivateFile* extents_file;
HfsPPrivateFile* catalog_file;
HfsPPrivateFile* attributes_file;
HfsPPrivateFile* allocation_file;
HfsPPrivateLinkExtent* bad_blocks_xtent_list;
uint32_t jib_start_block;
uint32_t jl_start_block;
unsigned int bad_blocks_xtent_nb;
char bad_blocks_loaded;
char free_geom; /* 1 = plus_geom must be freed */
};
typedef struct _HfsPPrivateFSData HfsPPrivateFSData;
/* Generic + btree key */
struct __attribute__ ((packed)) _HfsPPrivateGenericKey {
uint16_t key_length;
uint8_t key_content[1]; /* we use 1 as a minimum size */
};
typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey;
/* ---- common ---- */
/* node and lead record reference for a BTree search */
struct _HfsCPrivateLeafRec {
unsigned int node_size; /* in sectors */
unsigned int node_number;
unsigned int record_pos;
unsigned int record_number;
};
typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec;
extern uint8_t* hfs_block;
extern uint8_t* hfsp_block;
extern unsigned hfs_block_count;
extern unsigned hfsp_block_count;
#endif /* _HFS_H */

View File

@@ -0,0 +1,392 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "reloc_plus.h"
#include "journal.h"
static int hfsj_vh_replayed = 0;
static int is_le = 0;
static uint32_t
hfsj_calc_checksum(uint8_t *ptr, int len)
{
int i;
uint32_t cksum=0;
for (i=0; i < len; i++, ptr++) {
cksum = (cksum << 8) ^ (cksum + *ptr);
}
return (~cksum);
}
int
hfsj_update_jib(PedFileSystem* fs, uint32_t block)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block);
if (!hfsplus_update_vh (fs))
return 0;
priv_data->jib_start_block = block;
return 1;
}
int
hfsj_update_jl(PedFileSystem* fs, uint32_t block)
{
uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
PedSector sector;
uint64_t offset;
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsJJournalInfoBlock* jib;
int binsect;
binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / PED_SECTOR_SIZE_DEFAULT;
sector = (PedSector) priv_data->jib_start_block * binsect;
if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
return 0;
jib = (HfsJJournalInfoBlock*) buf;
offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect;
jib->offset = HFS_CPU_TO_64(offset, is_le);
if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
|| !ped_geometry_sync(priv_data->plus_geom))
return 0;
priv_data->jl_start_block = block;
return 1;
}
/* Return the sector in the journal that is after the area read */
/* or 0 on error */
static PedSector
hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh,
PedSector journ_sect, PedSector journ_length,
PedSector read_sect, unsigned int nb_sect,
void* buf)
{
int r;
while (nb_sect--) {
r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1);
if (!r) return 0;
buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT;
read_sect++;
if (read_sect == journ_length)
read_sect = 1; /* skip journal header
which is asserted to be
1 sector long */
}
return read_sect;
}
static int
hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh,
PedSector jsector, PedSector jlength)
{
PedSector start, sector;
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsJBlockListHeader* blhdr;
uint8_t* block;
unsigned int blhdr_nbsect;
int i, r;
uint32_t cksum, size;
blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / PED_SECTOR_SIZE_DEFAULT;
blhdr = (HfsJBlockListHeader*)
ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT);
if (!blhdr) return 0;
start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT;
do {
start = hfsj_journal_read(priv_data->plus_geom, jh, jsector,
jlength, start, blhdr_nbsect, blhdr);
if (!start) goto err_replay;
cksum = HFS_32_TO_CPU(blhdr->checksum, is_le);
blhdr->checksum = 0;
if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Bad block list header checksum."));
goto err_replay;
}
blhdr->checksum = HFS_CPU_TO_32(cksum, is_le);
for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) {
size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le);
sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le);
if (!size) continue;
if (size % PED_SECTOR_SIZE_DEFAULT) {
ped_exception_throw(
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Invalid size of a transaction "
"block while replaying the journal "
"(%i bytes)."),
size);
goto err_replay;
}
block = (uint8_t*) ped_malloc(size);
if (!block) goto err_replay;
start = hfsj_journal_read(priv_data->plus_geom, jh,
jsector, jlength, start,
size / PED_SECTOR_SIZE_DEFAULT,
block);
if (!start) {
free (block);
goto err_replay;
}
/* the sector stored in the journal seems to be
relative to the begin of the block device which
contains the hfs+ journaled volume */
if (sector != ~0LL)
r = ped_geometry_write (fs->geom, block, sector,
size / PED_SECTOR_SIZE_DEFAULT);
else
r = 1;
free (block);
/* check if wrapper mdb or vh with no wrapper has
changed */
if ( (sector != ~0LL)
&& (2 >= sector)
&& (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) )
hfsj_vh_replayed = 1;
/* check if vh of embedded hfs+ has changed */
if ( (sector != ~0LL)
&& (priv_data->plus_geom != fs->geom)
&& (sector
+ fs->geom->start
- priv_data->plus_geom->start <= 2)
&& (sector
+ size / PED_SECTOR_SIZE_DEFAULT
+ fs->geom->start
- priv_data->plus_geom->start > 2) )
hfsj_vh_replayed = 1;
if (!r) goto err_replay;
}
} while (blhdr->binfo[0].next);
jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le);
free (blhdr);
return (ped_geometry_sync (fs->geom));
err_replay:
free (blhdr);
return 0;
}
/* 0 => Failure, don't continue to open ! */
/* 1 => Success, the journal has been completly replayed, or don't need to */
int
hfsj_replay_journal(PedFileSystem* fs)
{
uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
PedSector sector, length;
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsJJournalInfoBlock* jib;
HfsJJournalHeader* jh;
int binsect;
uint32_t cksum;
binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT;
priv_data->jib_start_block =
PED_BE32_TO_CPU(priv_data->vh->journal_info_block);
sector = (PedSector) priv_data->jib_start_block * binsect;
if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
return 0;
jib = (HfsJJournalInfoBlock*) buf;
if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
&& !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le)
/ ( PED_SECTOR_SIZE_DEFAULT * binsect );
}
if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT))
return 1;
if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
|| (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
ped_exception_throw (
PED_EXCEPTION_NO_FEATURE,
PED_EXCEPTION_CANCEL,
_("Journal stored outside of the volume are "
"not supported. Try to deactivate the "
"journal and run Parted again."));
return 0;
}
if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT)
|| (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) {
ped_exception_throw (
PED_EXCEPTION_NO_FEATURE,
PED_EXCEPTION_CANCEL,
_("Journal offset or size is not multiple of "
"the sector size."));
return 0;
}
sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT;
length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT;
jib = NULL;
if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
return 0;
jh = (HfsJJournalHeader*) buf;
if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC))
is_le = 1;
if ( (jh->magic != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le))
|| (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Incorrect magic values in the journal header."));
return 0;
}
if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT)
|| (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT
!= (uint64_t)length) ) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Journal size mismatch between journal info block "
"and journal header."));
return 0;
}
if ( (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT)
|| (HFS_64_TO_CPU(jh->end, is_le) % PED_SECTOR_SIZE_DEFAULT)
|| (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT)
|| (HFS_32_TO_CPU(jh->jhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) ) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Some header fields are not multiple of the sector "
"size."));
return 0;
}
if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The sector size stored in the journal is not 512 "
"bytes. Parted only supports 512 bytes length "
"sectors."));
return 0;
}
cksum = HFS_32_TO_CPU(jh->checksum, is_le);
jh->checksum = 0;
if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Bad journal checksum."));
return 0;
}
jh->checksum = HFS_CPU_TO_32(cksum, is_le);
/* https://github.com/apple-opensource/hfs/blob/master/core/hfs_journal.c#L1167
* indicates that this is:
* wrap the start ptr if it points to the very end of the journal
*/
if (jh->start == jh->size)
jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
if (jh->end == jh->size)
jh->end = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
if (jh->start == jh->end)
return 1;
if (ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
_("The journal is not empty. Parted must replay the "
"transactions before opening the file system. This will "
"modify the file system."))
!= PED_EXCEPTION_FIX)
return 0;
while (jh->start != jh->end) {
/* Replay one complete transaction */
if (!hfsj_replay_transaction(fs, jh, sector, length))
return 0;
/* Recalculate cksum of the journal header */
jh->checksum = 0; /* need to be 0 while calculating the cksum */
cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh));
jh->checksum = HFS_CPU_TO_32(cksum, is_le);
/* Update the Journal Header */
if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
|| !ped_geometry_sync(priv_data->plus_geom))
return 0;
}
if (hfsj_vh_replayed) {
/* probe could have reported incorrect info ! */
/* is there a way to ask parted to quit ? */
ped_exception_throw(
PED_EXCEPTION_WARNING,
PED_EXCEPTION_OK,
_("The volume header or the master directory block has "
"changed while replaying the journal. You should "
"restart Parted."));
return 0;
}
return 1;
}
#endif /* DISCOVER_ONLY */

View File

@@ -0,0 +1,45 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _JOURNAL_H
#define _JOURNAL_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
int
hfsj_replay_journal(PedFileSystem* fs);
int
hfsj_update_jib(PedFileSystem* fs, uint32_t block);
int
hfsj_update_jl(PedFileSystem* fs, uint32_t block);
#define HFS_16_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_LE16_TO_CPU(x) : (uint16_t)PED_BE16_TO_CPU(x))
#define HFS_32_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_LE32_TO_CPU(x) : (uint32_t)PED_BE32_TO_CPU(x))
#define HFS_64_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_LE64_TO_CPU(x) : (uint64_t)PED_BE64_TO_CPU(x))
#define HFS_CPU_TO_16(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_CPU_TO_LE16(x) : (uint16_t)PED_CPU_TO_BE16(x))
#define HFS_CPU_TO_32(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_CPU_TO_LE32(x) : (uint32_t)PED_CPU_TO_BE32(x))
#define HFS_CPU_TO_64(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_CPU_TO_LE64(x) : (uint64_t)PED_CPU_TO_BE64(x))
#endif /* _JOURNAL_H */

View File

@@ -0,0 +1,99 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "probe.h"
int
hfsc_can_use_geom (PedGeometry* geom)
{
PedDevice* dev;
dev = geom->dev;
PED_ASSERT (geom != NULL);
PED_ASSERT (dev != NULL);
if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Parted can't use HFS file systems on disks "
"with a sector size not equal to %d bytes."),
(int)PED_SECTOR_SIZE_DEFAULT );
return 0;
}
return 1;
}
/* Probe an HFS volume, detecting it even if
it is in fact a wrapper to an HFS+ volume */
/* Used by hfsplus_probe and hfs_probe */
PedGeometry*
hfs_and_wrapper_probe (PedGeometry* geom)
{
uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
HfsMasterDirectoryBlock *mdb;
PedGeometry* geom_ret;
PedSector search, max;
PED_ASSERT (geom != NULL);
PED_ASSERT (hfsc_can_use_geom (geom));
mdb = (HfsMasterDirectoryBlock *) buf;
/* is 5 an intelligent value ? */
if ((geom->length < 5)
|| (!ped_geometry_read (geom, buf, 2, 1))
|| (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) )
return NULL;
search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block)
+ ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks)
* (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT )));
max = search + (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT);
if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2)))
return NULL;
for (; search < max; search++) {
if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2)
|| !ped_geometry_read (geom_ret, buf, search, 1))
break;
if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE))
return geom_ret;
}
ped_geometry_destroy (geom_ret);
return NULL;
}

View File

@@ -0,0 +1,35 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 _PROBE_H
#define _PROBE_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
int
hfsc_can_use_geom (PedGeometry* geom);
PedGeometry*
hfs_and_wrapper_probe (PedGeometry* geom);
#endif /* _PROBE_H */

View File

@@ -0,0 +1,676 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "file.h"
#include "advfs.h"
#include "cache.h"
#include "reloc.h"
/* This function moves data of size blocks starting
at block *ptr_fblock to block *ptr_to_fblock */
/* return new start or -1 on failure */
static int
hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
unsigned int *ptr_to_fblock, unsigned int size)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
unsigned int i, ok = 0;
unsigned int next_to_fblock;
unsigned int start, stop;
PED_ASSERT (hfs_block != NULL);
PED_ASSERT (*ptr_to_fblock <= *ptr_fblock);
/* quiet gcc */
start = stop = 0;
/*
Try to fit the extent AT or _BEFORE_ the wanted place,
or then in the gap between dest and source.
If failed try to fit the extent after source, for 2 pass relocation
The extent is always copied in a non overlapping way
*/
/* Backward search */
/* 1 pass relocation AT or BEFORE *ptr_to_fblock */
if (*ptr_to_fblock != *ptr_fblock) {
start = stop = *ptr_fblock < *ptr_to_fblock+size ?
*ptr_fblock : *ptr_to_fblock+size;
while (start && stop-start != size) {
--start;
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
stop = start;
}
ok = (stop-start == size);
}
/* Forward search */
/* 1 pass relocation in the gap merged with 2 pass reloc after source */
if (!ok && *ptr_to_fblock != *ptr_fblock) {
start = stop = *ptr_to_fblock+1;
while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks)
&& stop-start != size) {
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
start = stop + 1;
++stop;
}
ok = (stop-start == size);
}
/* new non overlapping room has been found ? */
if (ok) {
/* enough room */
unsigned int j;
unsigned int start_block =
PED_BE16_TO_CPU (priv_data->mdb->start_block );
unsigned int block_sz =
(PED_BE32_TO_CPU (priv_data->mdb->block_size)
/ PED_SECTOR_SIZE_DEFAULT);
if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
/* Fit in the gap */
next_to_fblock = stop;
else
/* Before or after the gap */
next_to_fblock = *ptr_to_fblock;
/* move blocks */
for (i = 0; i < size; /*i+=j*/) {
PedSector abs_sector;
unsigned int ai;
j = size - i; j = (j < hfs_block_count) ?
j : hfs_block_count ;
abs_sector = start_block
+ (PedSector) (*ptr_fblock + i) * block_sz;
if (!ped_geometry_read (fs->geom, hfs_block, abs_sector,
block_sz * j))
return -1;
abs_sector = start_block
+ (PedSector) (start + i) * block_sz;
if (!ped_geometry_write (fs->geom,hfs_block,abs_sector,
block_sz * j))
return -1;
for (ai = i+j; i < ai; i++) {
/* free source block */
CLR_BLOC_OCCUPATION(priv_data->alloc_map,
*ptr_fblock + i);
/* set dest block */
SET_BLOC_OCCUPATION(priv_data->alloc_map,
start + i);
}
}
if (!ped_geometry_sync_fast (fs->geom))
return -1;
*ptr_fblock += size;
*ptr_to_fblock = next_to_fblock;
} else {
if (*ptr_fblock != *ptr_to_fblock)
/* not enough room, but try to continue */
ped_exception_throw (PED_EXCEPTION_WARNING,
PED_EXCEPTION_IGNORE,
_("An extent has not been relocated."));
start = *ptr_fblock;
*ptr_fblock = *ptr_to_fblock = start + size;
}
return start;
}
/* Update MDB */
/* Return 0 if an error occurred */
/* Return 1 if everything ok */
int
hfs_update_mdb (PedFileSystem *fs)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
uint8_t node[PED_SECTOR_SIZE_DEFAULT];
if (!ped_geometry_read (fs->geom, node, 2, 1))
return 0;
memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock));
if ( !ped_geometry_write (fs->geom, node, 2, 1)
|| !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1)
|| !ped_geometry_sync_fast (fs->geom))
return 0;
return 1;
}
/* Generic relocator */
/* replace previous hfs_do_move_* */
static int
hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src,
unsigned int *ptr_dest, HfsCPrivateCache* cache,
HfsCPrivateExtent* ref)
{
uint8_t node[PED_SECTOR_SIZE_DEFAULT];
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
HfsPrivateFile* file;
HfsExtDescriptor* extent;
HfsCPrivateExtent* move;
int new_start;
new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest,
ref->ext_length);
if (new_start == -1) return -1;
if (ref->ext_start != (unsigned) new_start) {
/* Load, modify & save */
switch (ref->where) {
/******** MDB *********/
case CR_PRIM_CAT :
priv_data->catalog_file
->first[ref->ref_index].start_block =
PED_CPU_TO_BE16(new_start);
goto CR_PRIM;
case CR_PRIM_EXT :
priv_data->extent_file
->first[ref->ref_index].start_block =
PED_CPU_TO_BE16(new_start);
CR_PRIM :
extent = ( HfsExtDescriptor* )
( (uint8_t*)priv_data->mdb + ref->ref_offset );
extent[ref->ref_index].start_block =
PED_CPU_TO_BE16(new_start);
if (!hfs_update_mdb(fs)) return -1;
break;
/********* BTREE *******/
case CR_BTREE_EXT_CAT :
if (priv_data->catalog_file
->cache[ref->ref_index].start_block
== PED_CPU_TO_BE16(ref->ext_start))
priv_data->catalog_file
->cache[ref->ref_index].start_block =
PED_CPU_TO_BE16(new_start);
/* FALLTHROUGH */
case CR_BTREE_EXT_0 :
file = priv_data->extent_file;
goto CR_BTREE;
case CR_BTREE_CAT :
file = priv_data->catalog_file;
CR_BTREE:
PED_ASSERT(ref->sect_by_block == 1
&& ref->ref_offset < PED_SECTOR_SIZE_DEFAULT);
if (!hfs_file_read_sector(file, node, ref->ref_block))
return -1;
extent = ( HfsExtDescriptor* ) (node + ref->ref_offset);
extent[ref->ref_index].start_block =
PED_CPU_TO_BE16(new_start);
if (!hfs_file_write_sector(file, node, ref->ref_block)
|| !ped_geometry_sync_fast (fs->geom))
return -1;
break;
/********** BUG ********/
default :
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("A reference to an extent comes from a place "
"it should not. You should check the file "
"system!"));
return -1;
break;
}
/* Update the cache */
move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
if (!move) return -1; /* "cleanly" fail */
PED_ASSERT(move == ref); /* generate a bug */
}
return new_start;
}
/* 0 error, 1 ok */
static int
hfs_save_allocation(PedFileSystem* fs)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
unsigned int map_sectors;
map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
+ PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
/ (PED_SECTOR_SIZE_DEFAULT * 8);
return ( ped_geometry_write (fs->geom, priv_data->alloc_map,
PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
map_sectors) );
}
/* This function moves an extent starting at block fblock to block to_fblock
if there's enough room */
/* Return 1 if everything was fine */
/* Return -1 if an error occurred */
/* Return 0 if no extent was found */
/* Generic search thanks to the file system cache */
static int
hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
unsigned int *ptr_to_fblock,
HfsCPrivateCache* cache)
{
HfsCPrivateExtent* ref;
unsigned int old_start, new_start;
/* Reference search powered by the cache... */
/* This is the optimisation secret :) */
ref = hfsc_cache_search_extent(cache, *ptr_fblock);
if (!ref) return 0; /* not found */
old_start = *ptr_fblock;
new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
if (new_start == (unsigned int) -1) return -1;
if (new_start > old_start) { /* detect 2 pass reloc */
new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref);
if (new_start == (unsigned int) -1 || new_start > old_start)
return -1;
}
/* allocation bitmap save is not atomic with data relocation */
/* so we only do it a few times, and without syncing */
/* The unmounted bit protect us anyway */
hfs_save_allocation(fs);
return 1;
}
static int
hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
HfsExtDescriptor* extent;
unsigned int j;
extent = priv_data->mdb->extents_file_rec;
for (j = 0; j < HFS_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE16_TO_CPU(extent[j].start_block),
PED_BE16_TO_CPU(extent[j].block_count),
0, /* unused for mdb */
((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
1, /* load/save only 1 sector */
CR_PRIM_EXT,
j )
)
return 0;
}
extent = priv_data->mdb->catalog_file_rec;
for (j = 0; j < HFS_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE16_TO_CPU(extent[j].start_block),
PED_BE16_TO_CPU(extent[j].block_count),
0,
((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
1,
CR_PRIM_CAT,
j )
)
return 0;
}
return 1;
}
static int
hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
uint8_t node[PED_SECTOR_SIZE_DEFAULT];
HfsHeaderRecord* header;
HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
HfsCatalogKey* catalog_key;
HfsCatalog* catalog_data;
HfsExtDescriptor* extent;
unsigned int leaf_node, record_number;
unsigned int i, j;
uint16_t catalog_pos;
if (!priv_data->catalog_file->sect_nb) {
ped_exception_throw (
PED_EXCEPTION_INFORMATION,
PED_EXCEPTION_OK,
_("This HFS volume has no catalog file. "
"This is very unusual!"));
return 1;
}
if (!hfs_file_read_sector (priv_data->catalog_file, node, 0))
return 0;
uint16_t offset;
memcpy(&offset, node+(PED_SECTOR_SIZE_DEFAULT-2), sizeof(uint16_t));
header = (HfsHeaderRecord*) (node + PED_BE16_TO_CPU(offset));
for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
leaf_node;
leaf_node = PED_BE32_TO_CPU (desc->next)) {
if (!hfs_file_read_sector (priv_data->catalog_file,
node, leaf_node))
return 0;
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = 1; i <= record_number; ++i) {
/* undocumented alignement */
uint16_t value;
memcpy(&value, node+(PED_SECTOR_SIZE_DEFAULT - (2*i)), sizeof(uint16_t));
catalog_pos = PED_BE16_TO_CPU(value);
catalog_key = (HfsCatalogKey*) (node + catalog_pos);
unsigned int skip;
skip = (1 + catalog_key->key_length + 1) & ~1;
catalog_data = (HfsCatalog*)(node+catalog_pos+skip);
/* check for obvious error in FS */
if ((catalog_pos < HFS_FIRST_REC)
|| ((uint8_t*)catalog_data - node
>= PED_SECTOR_SIZE_DEFAULT
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
return 0;
}
if (catalog_data->type != HFS_CAT_FILE) continue;
extent = catalog_data->sel.file.extents_data;
for (j = 0; j < HFS_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE16_TO_CPU(extent[j].start_block),
PED_BE16_TO_CPU(extent[j].block_count),
leaf_node,
(uint8_t*)extent - node,
1, /* hfs => btree block = 512 b */
CR_BTREE_CAT,
j )
)
return 0;
}
extent = catalog_data->sel.file.extents_res;
for (j = 0; j < HFS_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE16_TO_CPU(extent[j].start_block),
PED_BE16_TO_CPU(extent[j].block_count),
leaf_node,
(uint8_t*)extent - node,
1, /* hfs => btree block = 512 b */
CR_BTREE_CAT,
j )
)
return 0;
}
}
}
return 1;
}
static int
hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
uint8_t node[PED_SECTOR_SIZE_DEFAULT];
HfsHeaderRecord* header;
HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
HfsExtentKey* extent_key;
HfsExtDescriptor* extent;
unsigned int leaf_node, record_number;
unsigned int i, j;
uint16_t extent_pos;
if (!priv_data->extent_file->sect_nb) {
ped_exception_throw (
PED_EXCEPTION_INFORMATION,
PED_EXCEPTION_OK,
_("This HFS volume has no extents overflow "
"file. This is quite unusual!"));
return 1;
}
if (!hfs_file_read_sector (priv_data->extent_file, node, 0))
return 0;
uint16_t offset;
memcpy(&offset, node+(PED_SECTOR_SIZE_DEFAULT-2), sizeof(uint16_t));
header = (HfsHeaderRecord*) (node + PED_BE16_TO_CPU(offset));
for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
leaf_node;
leaf_node = PED_BE32_TO_CPU (desc->next)) {
if (!hfs_file_read_sector (priv_data->extent_file, node,
leaf_node))
return 0;
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = 1; i <= record_number; i++) {
uint8_t where;
uint16_t value;
memcpy(&value, node+(PED_SECTOR_SIZE_DEFAULT - (2*i)), sizeof(uint16_t));
extent_pos = PED_BE16_TO_CPU(value);
extent_key = (HfsExtentKey*)(node + extent_pos);
/* size is cst */
extent = (HfsExtDescriptor*)(node+extent_pos+sizeof(HfsExtentKey));
/* check for obvious error in FS */
if ((extent_pos < HFS_FIRST_REC)
|| ((uint8_t*)extent - node
>= PED_SECTOR_SIZE_DEFAULT
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
return 0;
}
switch (extent_key->file_ID) {
case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
if (ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_IGNORE_CANCEL,
_("The extents overflow file should not"
" contain its own extents! You "
"should check the file system."))
!= PED_EXCEPTION_IGNORE)
return 0;
where = CR_BTREE_EXT_EXT;
break;
case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
where = CR_BTREE_EXT_CAT;
break;
default :
where = CR_BTREE_EXT_0;
break;
}
for (j = 0; j < HFS_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE16_TO_CPU(extent[j].start_block),
PED_BE16_TO_CPU(extent[j].block_count),
leaf_node,
(uint8_t*)extent - node,
1, /* hfs => btree block = 512 b */
where,
j )
)
return 0;
}
}
}
return 1;
}
/* This function cache every extents start and length stored in any
fs structure into the adt defined in cache.[ch]
Returns NULL on failure */
static HfsCPrivateCache*
hfs_cache_extents(PedFileSystem *fs, PedTimer* timer)
{
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
HfsCPrivateCache* ret;
unsigned int file_number, block_number;
file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count);
block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks);
ret = hfsc_new_cache(block_number, file_number);
if (!ret) return NULL;
if (!hfs_cache_from_mdb(ret, fs, timer) ||
!hfs_cache_from_catalog(ret, fs, timer) ||
!hfs_cache_from_extent(ret, fs, timer)) {
ped_exception_throw(
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Could not cache the file system in memory."));
hfsc_delete_cache(ret);
return NULL;
}
return ret;
}
/* This function moves file's data to compact used and free space,
starting at fblock block */
/* return 0 on error */
int
hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
PedTimer* timer, unsigned int to_free)
{
PedSector bytes_buff;
HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
fs->type_specific;
HfsMasterDirectoryBlock* mdb = priv_data->mdb;
HfsCPrivateCache* cache;
unsigned int to_fblock = fblock;
unsigned int start = fblock;
unsigned int divisor = PED_BE16_TO_CPU (mdb->total_blocks)
+ 1 - start - to_free;
int ret;
PED_ASSERT (!hfs_block);
cache = hfs_cache_extents (fs, timer);
if (!cache)
return 0;
/* Calculate the size of the copy buffer :
* Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
* takes the maximum number of HFS blocks so that the buffer
* will remain smaller than or equal to BYTES_MAX_BUFF, with
* a minimum of 1 HFS block */
bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size)
* (PedSector) BLOCK_MAX_BUFF;
if (bytes_buff > BYTES_MAX_BUFF) {
hfs_block_count = BYTES_MAX_BUFF
/ PED_BE32_TO_CPU (priv_data->mdb->block_size);
if (!hfs_block_count)
hfs_block_count = 1;
bytes_buff = (PedSector) hfs_block_count
* PED_BE32_TO_CPU (priv_data->mdb->block_size);
} else
hfs_block_count = BLOCK_MAX_BUFF;
/* If the cache code requests more space, give it to him */
if (bytes_buff < hfsc_cache_needed_buffer (cache))
bytes_buff = hfsc_cache_needed_buffer (cache);
hfs_block = (uint8_t*) ped_malloc (bytes_buff);
if (!hfs_block)
goto error_cache;
if (!hfs_read_bad_blocks (fs)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Bad blocks list could not be loaded."));
goto error_alloc;
}
while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) {
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock)
&& (!hfs_is_bad_block (fs, fblock))) {
if (!(ret = hfs_move_extent_starting_at (fs, &fblock,
&to_fblock, cache)))
to_fblock = ++fblock;
else if (ret == -1) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("An error occurred during extent "
"relocation."));
goto error_alloc;
}
} else {
fblock++;
}
ped_timer_update(timer, (float)(to_fblock - start)/divisor);
}
free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
hfsc_delete_cache (cache);
return 1;
error_alloc:
free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
error_cache:
hfsc_delete_cache (cache);
return 0;
}
#endif /* !DISCOVER_ONLY */

View File

@@ -0,0 +1,36 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _RELOC_H
#define _RELOC_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
int
hfs_update_mdb (PedFileSystem *fs);
int
hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
PedTimer* timer, unsigned int to_free);
#endif /* _RELOC_H */

View File

@@ -0,0 +1,948 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2004-2005, 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 DISCOVER_ONLY
#include <config.h>
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include <stdint.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include "hfs.h"
#include "file_plus.h"
#include "advfs_plus.h"
#include "cache.h"
#include "journal.h"
#include "reloc_plus.h"
/* This function moves data of size blocks starting at block *ptr_fblock
to block *ptr_to_fblock */
/* return new start or -1 on failure */
/* -1 is ok because there can only be 2^32-1 blocks, so the max possible
last one is 2^32-2 (and anyway it contains Alternate VH), so
-1 (== 2^32-1[2^32]) never represent a valid block */
static int
hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
unsigned int *ptr_to_fblock, unsigned int size)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
unsigned int i, ok = 0;
unsigned int next_to_fblock;
unsigned int start, stop;
PED_ASSERT (hfsp_block != NULL);
PED_ASSERT (*ptr_to_fblock <= *ptr_fblock);
/* quiet GCC */
start = stop = 0;
/*
Try to fit the extent AT or _BEFORE_ the wanted place,
or then in the gap between dest and source.
If failed try to fit the extent after source, for 2 pass relocation
The extent is always copied in a non overlapping way
*/
/* Backward search */
/* 1 pass relocation AT or BEFORE *ptr_to_fblock */
if (*ptr_to_fblock != *ptr_fblock) {
start = stop = *ptr_fblock < *ptr_to_fblock+size ?
*ptr_fblock : *ptr_to_fblock+size;
while (start && stop-start != size) {
--start;
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
stop = start;
}
ok = (stop-start == size);
}
/* Forward search */
/* 1 pass relocation in the gap merged with 2 pass reloc after source */
if (!ok && *ptr_to_fblock != *ptr_fblock) {
start = stop = *ptr_to_fblock+1;
while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks)
&& stop-start != size) {
if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
start = stop + 1;
++stop;
}
ok = (stop-start == size);
}
/* new non overlapping room has been found ? */
if (ok) {
/* enough room */
PedSector abs_sector;
unsigned int ai, j, block;
unsigned int block_sz = (PED_BE32_TO_CPU (
priv_data->vh->block_size)
/ PED_SECTOR_SIZE_DEFAULT);
if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
/* Fit in the gap */
next_to_fblock = stop;
else
/* Before or after the gap */
next_to_fblock = *ptr_to_fblock;
/* move blocks */
for (i = 0; i < size; /*i++*/) {
j = size - i; j = (j < hfsp_block_count) ?
j : hfsp_block_count ;
abs_sector = (PedSector) (*ptr_fblock + i) * block_sz;
if (!ped_geometry_read (priv_data->plus_geom,
hfsp_block, abs_sector,
block_sz * j))
return -1;
abs_sector = (PedSector) (start + i) * block_sz;
if (!ped_geometry_write (priv_data->plus_geom,
hfsp_block, abs_sector,
block_sz * j))
return -1;
for (ai = i+j; i < ai; i++) {
/* free source block */
block = *ptr_fblock + i;
CLR_BLOC_OCCUPATION(priv_data->alloc_map,block);
SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map,
block/(PED_SECTOR_SIZE_DEFAULT*8));
/* set dest block */
block = start + i;
SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map,
block/(PED_SECTOR_SIZE_DEFAULT*8));
}
}
if (!ped_geometry_sync_fast (priv_data->plus_geom))
return -1;
*ptr_fblock += size;
*ptr_to_fblock = next_to_fblock;
} else {
if (*ptr_fblock != *ptr_to_fblock)
/* not enough room */
ped_exception_throw (PED_EXCEPTION_WARNING,
PED_EXCEPTION_IGNORE,
_("An extent has not been relocated."));
start = *ptr_fblock;
*ptr_fblock = *ptr_to_fblock = start + size;
}
return start;
}
/* Returns 0 on error */
/* 1 on succes */
int
hfsplus_update_vh (PedFileSystem *fs)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
uint8_t node[PED_SECTOR_SIZE_DEFAULT];
if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1))
return 0;
memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader));
if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1)
|| !ped_geometry_write (priv_data->plus_geom, node,
priv_data->plus_geom->length - 2, 1)
|| !ped_geometry_sync_fast (priv_data->plus_geom))
return 0;
return 1;
}
static int
hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src,
unsigned int *ptr_dest, HfsCPrivateCache* cache,
HfsCPrivateExtent* ref)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsPPrivateFile* file;
HfsPExtDescriptor* extent;
HfsCPrivateExtent* move;
int new_start;
new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest,
ref->ext_length);
if (new_start == -1) return -1;
if (ref->ext_start != (unsigned) new_start) {
switch (ref->where) {
/************ VH ************/
case CR_PRIM_CAT :
priv_data->catalog_file
->first[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_PRIM;
case CR_PRIM_EXT :
priv_data->extents_file
->first[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_PRIM;
case CR_PRIM_ATTR :
priv_data->attributes_file
->first[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_PRIM;
case CR_PRIM_ALLOC :
priv_data->allocation_file
->first[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_PRIM;
case CR_PRIM_START :
/* No startup file opened */
CR_PRIM :
extent = ( HfsPExtDescriptor* )
( (uint8_t*)priv_data->vh + ref->ref_offset );
extent[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
if (!hfsplus_update_vh(fs))
return -1;
break;
/************** BTREE *************/
case CR_BTREE_CAT_JIB :
if (!hfsj_update_jib(fs, new_start))
return -1;
goto BTREE_CAT;
case CR_BTREE_CAT_JL :
if (!hfsj_update_jl(fs, new_start))
return -1;
goto BTREE_CAT;
BTREE_CAT:
case CR_BTREE_CAT :
file = priv_data->catalog_file;
goto CR_BTREE;
case CR_BTREE_ATTR :
file = priv_data->attributes_file;
goto CR_BTREE;
case CR_BTREE_EXT_ATTR :
if (priv_data->attributes_file
->cache[ref->ref_index].start_block
== PED_CPU_TO_BE32(ref->ext_start))
priv_data->attributes_file
->cache[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_BTREE_EXT;
case CR_BTREE_EXT_CAT :
if (priv_data->catalog_file
->cache[ref->ref_index].start_block
== PED_CPU_TO_BE32(ref->ext_start))
priv_data->catalog_file
->cache[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_BTREE_EXT;
case CR_BTREE_EXT_ALLOC :
if (priv_data->allocation_file
->cache[ref->ref_index].start_block
== PED_CPU_TO_BE32(ref->ext_start))
priv_data->allocation_file
->cache[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
goto CR_BTREE_EXT;
case CR_BTREE_EXT_START :
/* No startup file opened */
CR_BTREE_EXT :
case CR_BTREE_EXT_0 :
file = priv_data->extents_file;
CR_BTREE :
PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block
> ref->ref_offset);
if (!hfsplus_file_read(file, hfsp_block,
(PedSector)ref->ref_block * ref->sect_by_block,
ref->sect_by_block))
return -1;
extent = ( HfsPExtDescriptor* )
( hfsp_block + ref->ref_offset );
extent[ref->ref_index].start_block =
PED_CPU_TO_BE32(new_start);
if (!hfsplus_file_write(file, hfsp_block,
(PedSector)ref->ref_block * ref->sect_by_block,
ref->sect_by_block)
|| !ped_geometry_sync_fast (priv_data->plus_geom))
return -1;
break;
/********** BUG *********/
default :
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("A reference to an extent comes from a place "
"it should not. You should check the file "
"system!"));
return -1;
break;
}
move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
if (!move) return -1;
PED_ASSERT(move == ref);
}
return new_start;
}
/* save any dirty sector of the allocation bitmap file */
static int
hfsplus_save_allocation(PedFileSystem *fs)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
unsigned int map_sectors, i, j;
int ret = 1;
map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks)
+ PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8);
for (i = 0; i < map_sectors;) {
for (j = i;
(TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j));
++j)
CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j);
if (j-i) {
ret = hfsplus_file_write(priv_data->allocation_file,
priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT,
i, j-i) && ret;
i = j;
} else
++i;
}
return ret;
}
/* This function moves an extent starting at block fblock
to block to_fblock if there's enough room */
/* Return 1 if everything was fine */
/* Return -1 if an error occurred */
/* Return 0 if no extent was found */
static int
hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
unsigned int *ptr_to_fblock,
HfsCPrivateCache* cache)
{
HfsCPrivateExtent* ref;
unsigned int old_start, new_start;
ref = hfsc_cache_search_extent(cache, *ptr_fblock);
if (!ref) return 0;
old_start = *ptr_fblock;
new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
if (new_start == (unsigned)-1) return -1;
if (new_start > old_start) {
new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock,
cache, ref);
if (new_start == (unsigned)-1 || new_start > old_start)
return -1;
}
hfsplus_save_allocation(fs);
return 1;
}
static int
hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsPExtDescriptor* extent;
unsigned int j;
extent = priv_data->vh->allocation_file.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
0, /* unused for vh */
((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
1, /* load / save 1 sector */
CR_PRIM_ALLOC,
j )
)
return 0;
}
extent = priv_data->vh->extents_file.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
0, /* unused for vh */
((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
1, /* load / save 1 sector */
CR_PRIM_EXT,
j )
)
return 0;
}
extent = priv_data->vh->catalog_file.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
0, /* unused for vh */
((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
1, /* load / save 1 sector */
CR_PRIM_CAT,
j )
)
return 0;
}
extent = priv_data->vh->attributes_file.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
0, /* unused for vh */
((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
1, /* load / save 1 sector */
CR_PRIM_ATTR,
j )
)
return 0;
}
extent = priv_data->vh->startup_file.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
0, /* unused for vh */
((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
1, /* load / save 1 sector */
CR_PRIM_START,
j )
)
return 0;
}
return 1;
}
static int
hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
uint8_t* node;
HfsPHeaderRecord* header;
HfsPCatalogKey* catalog_key;
HfsPCatalog* catalog_data;
HfsPExtDescriptor* extent;
unsigned int leaf_node, record_number;
unsigned int i, j, size, bsize;
uint32_t jib = priv_data->jib_start_block,
jl = priv_data->jl_start_block;
uint16_t catalog_pos;
if (!priv_data->catalog_file->sect_nb) {
ped_exception_throw (
PED_EXCEPTION_INFORMATION,
PED_EXCEPTION_OK,
_("This HFS+ volume has no catalog file. "
"This is very unusual!"));
return 1;
}
/* Search the extent starting at *ptr_block in the catalog file */
if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0))
return 0;
header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC);
leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
bsize = PED_BE16_TO_CPU (header->node_size);
size = bsize / PED_SECTOR_SIZE_DEFAULT;
PED_ASSERT(size < 256);
node = (uint8_t*) ped_malloc(bsize);
if (!node) return 0;
HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
if (!hfsplus_file_read (priv_data->catalog_file, node,
(PedSector) leaf_node * size, size)) {
free (node);
return 0;
}
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = 1; i <= record_number; i++) {
unsigned int skip;
uint8_t where;
uint16_t value;
memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t));
catalog_pos = PED_BE16_TO_CPU(value);
catalog_key = (HfsPCatalogKey*)(node + catalog_pos);
skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length)
+ 1) & ~1;
catalog_data = (HfsPCatalog*)
(((uint8_t*)catalog_key) + skip);
/* check for obvious error in FS */
if ((catalog_pos < HFS_FIRST_REC)
|| ((uint8_t*)catalog_data - node
>= (signed) bsize
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
free (node);
return 0;
}
if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE)
continue;
extent = catalog_data->sel.file.data_fork.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
where = CR_BTREE_CAT;
if ( PED_BE32_TO_CPU(extent[j].start_block)
== jib ) {
jib = 0;
where = CR_BTREE_CAT_JIB;
} else
if ( PED_BE32_TO_CPU(extent[j].start_block)
== jl ) {
jl = 0;
where = CR_BTREE_CAT_JL;
}
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
leaf_node,
(uint8_t*)extent - node,
size,
where,
j )
) {
free (node);
return 0;
}
}
extent = catalog_data->sel.file.res_fork.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
leaf_node,
(uint8_t*)extent - node,
size,
CR_BTREE_CAT,
j )
) {
free (node);
return 0;
}
}
}
}
free (node);
return 1;
}
static int
hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
uint8_t* node;
HfsPHeaderRecord* header;
HfsPExtentKey* extent_key;
HfsPExtDescriptor* extent;
unsigned int leaf_node, record_number;
unsigned int i, j, size, bsize;
uint16_t extent_pos;
if (!priv_data->extents_file->sect_nb) {
ped_exception_throw (
PED_EXCEPTION_INFORMATION,
PED_EXCEPTION_OK,
_("This HFS+ volume has no extents overflow "
"file. This is quite unusual!"));
return 1;
}
if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0))
return 0;
header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC));
leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
bsize = PED_BE16_TO_CPU (header->node_size);
size = bsize / PED_SECTOR_SIZE_DEFAULT;
PED_ASSERT(size < 256);
node = (uint8_t*) ped_malloc (bsize);
if (!node) return -1;
HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
if (!hfsplus_file_read (priv_data->extents_file, node,
(PedSector) leaf_node * size, size)) {
free (node);
return 0;
}
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = 1; i <= record_number; i++) {
uint8_t where;
uint16_t value;
memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t));
extent_pos = PED_BE16_TO_CPU(value);
extent_key = (HfsPExtentKey*)(node + extent_pos);
extent = (HfsPExtDescriptor*)
(((uint8_t*)extent_key) + sizeof (HfsPExtentKey));
/* check for obvious error in FS */
if ((extent_pos < HFS_FIRST_REC)
|| ((uint8_t*)extent - node
>= (signed)bsize
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
free (node);
return -1;
}
switch (extent_key->file_ID) {
case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
if (ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_IGNORE_CANCEL,
_("The extents overflow file should not"
" contain its own extents! You should "
"check the file system."))
!= PED_EXCEPTION_IGNORE)
return 0;
where = CR_BTREE_EXT_EXT;
break;
case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
where = CR_BTREE_EXT_CAT;
break;
case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) :
where = CR_BTREE_EXT_ALLOC;
break;
case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) :
where = CR_BTREE_EXT_START;
break;
case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) :
where = CR_BTREE_EXT_ATTR;
break;
default :
where = CR_BTREE_EXT_0;
break;
}
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU(extent[j].start_block),
PED_BE32_TO_CPU(extent[j].block_count),
leaf_node,
(uint8_t*)extent - node,
size,
where,
j )
) {
free (node);
return 0;
}
}
}
}
free (node);
return 1;
}
static int
hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs,
PedTimer* timer)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
uint8_t* node;
HfsPHeaderRecord* header;
HfsPPrivateGenericKey* generic_key;
HfsPForkDataAttr* fork_ext_data;
HfsPExtDescriptor* extent;
unsigned int leaf_node, record_number;
unsigned int i, j, size, bsize;
uint16_t generic_pos;
/* attributes file is facultative */
if (!priv_data->attributes_file->sect_nb)
return 1;
/* Search the extent starting at *ptr_block in the catalog file */
if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0))
return 0;
header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC));
leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
bsize = PED_BE16_TO_CPU (header->node_size);
size = bsize / PED_SECTOR_SIZE_DEFAULT;
PED_ASSERT(size < 256);
node = (uint8_t*) ped_malloc(bsize);
if (!node) return 0;
HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node;
for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
if (!hfsplus_file_read (priv_data->attributes_file, node,
(PedSector) leaf_node * size, size)) {
free (node);
return 0;
}
record_number = PED_BE16_TO_CPU (desc->rec_nb);
for (i = 1; i <= record_number; i++) {
unsigned int skip;
uint16_t value;
memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t));
generic_pos = PED_BE16_TO_CPU(value);
generic_key = (HfsPPrivateGenericKey*)(node + generic_pos);
skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length)
+ 1 ) & ~1;
fork_ext_data = (HfsPForkDataAttr*)(node+generic_pos+skip);
/* check for obvious error in FS */
if ((generic_pos < HFS_FIRST_REC)
|| ((uint8_t*)fork_ext_data - node
>= (signed) bsize
- 2 * (signed)(record_number+1))) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("The file system contains errors."));
free (node);
return 0;
}
if (fork_ext_data->record_type
== PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) {
extent = fork_ext_data->fork_res.fork.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU (
extent[j].start_block ),
PED_BE32_TO_CPU (
extent[j].block_count ),
leaf_node,
(uint8_t*)extent-node,
size,
CR_BTREE_ATTR,
j )
) {
free(node);
return 0;
}
}
} else if (fork_ext_data->record_type
== PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) {
extent = fork_ext_data->fork_res.extents;
for (j = 0; j < HFSP_EXT_NB; ++j) {
if (!extent[j].block_count) break;
if (!hfsc_cache_add_extent(
cache,
PED_BE32_TO_CPU (
extent[j].start_block ),
PED_BE32_TO_CPU (
extent[j].block_count ),
leaf_node,
(uint8_t*)extent-node,
size,
CR_BTREE_ATTR,
j )
) {
free(node);
return 0;
}
}
} else continue;
}
}
free (node);
return 1;
}
static HfsCPrivateCache*
hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer)
{
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsCPrivateCache* ret;
unsigned int file_number, block_number;
file_number = PED_BE32_TO_CPU(priv_data->vh->file_count);
block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks);
ret = hfsc_new_cache(block_number, file_number);
if (!ret) return NULL;
if (!hfsplus_cache_from_vh(ret, fs, timer) ||
!hfsplus_cache_from_catalog(ret, fs, timer) ||
!hfsplus_cache_from_extent(ret, fs, timer) ||
!hfsplus_cache_from_attributes(ret, fs, timer)) {
ped_exception_throw(
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Could not cache the file system in memory."));
hfsc_delete_cache(ret);
return NULL;
}
return ret;
}
/* This function moves file's data to compact used and free space,
starting at fblock block */
/* return 0 on error */
int
hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
PedTimer* timer, unsigned int to_free)
{
PedSector bytes_buff;
HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
fs->type_specific;
HfsPVolumeHeader* vh = priv_data->vh;
HfsCPrivateCache* cache;
unsigned int to_fblock = fblock;
unsigned int start = fblock;
unsigned int divisor = PED_BE32_TO_CPU (vh->total_blocks)
+ 1 - start - to_free;
int ret;
PED_ASSERT (!hfsp_block);
cache = hfsplus_cache_extents (fs, timer);
if (!cache)
return 0;
/* Calculate the size of the copy buffer :
* Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
* takes the maximum number of HFS blocks so that the buffer
* will remain smaller than or equal to BYTES_MAX_BUFF, with
* a minimum of 1 HFS block */
bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size)
* (PedSector) BLOCK_MAX_BUFF;
if (bytes_buff > BYTES_MAX_BUFF) {
hfsp_block_count = BYTES_MAX_BUFF
/ PED_BE32_TO_CPU (priv_data->vh->block_size);
if (!hfsp_block_count)
hfsp_block_count = 1;
bytes_buff = (PedSector) hfsp_block_count
* PED_BE32_TO_CPU (priv_data->vh->block_size);
} else
hfsp_block_count = BLOCK_MAX_BUFF;
/* If the cache code requests more space, give it to him */
if (bytes_buff < hfsc_cache_needed_buffer (cache))
bytes_buff = hfsc_cache_needed_buffer (cache);
hfsp_block = (uint8_t*) ped_malloc (bytes_buff);
if (!hfsp_block)
goto error_cache;
if (!hfsplus_read_bad_blocks (fs)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Bad blocks list could not be loaded."));
goto error_alloc;
}
while ( fblock < ( priv_data->plus_geom->length - 2 )
/ ( PED_BE32_TO_CPU (vh->block_size)
/ PED_SECTOR_SIZE_DEFAULT ) ) {
if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock)
&& (!hfsplus_is_bad_block (fs, fblock))) {
if (!(ret = hfsplus_move_extent_starting_at (fs,
&fblock, &to_fblock, cache)))
to_fblock = ++fblock;
else if (ret == -1) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("An error occurred during extent "
"relocation."));
goto error_alloc;
}
} else {
fblock++;
}
ped_timer_update(timer, (float)(to_fblock - start) / divisor);
}
free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0;
hfsc_delete_cache (cache);
return 1;
error_alloc:
free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0;
error_cache:
hfsc_delete_cache (cache);
return 0;
}
#endif /* !DISCOVER_ONLY */

View File

@@ -0,0 +1,37 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 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/>.
*/
#ifndef _RELOC_PLUS_H
#define _RELOC_PLUS_H
#include <parted/parted.h>
#include <parted/endian.h>
#include <parted/debug.h>
#include "hfs.h"
int
hfsplus_update_vh (PedFileSystem *fs);
int
hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
PedTimer* timer, unsigned int to_free);
#endif /* _RELOC_PLUS_H */