pmt: initial 3.0.2 update

This commit is contained in:
2024-12-14 10:25:23 +03:00
parent 686ef38598
commit 7f8090bb1f
1292 changed files with 500876 additions and 2823 deletions

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