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