| /* |
| 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 "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) { ped_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) { ped_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) { |
| ped_free(ret->linked_ref); |
| ped_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) { |
| ped_free (list->table); |
| next = list->next_cache; |
| ped_free (list); |
| list = next; |
| } |
| } |
| |
| void |
| hfsc_delete_cache(HfsCPrivateCache* cache) |
| { |
| hfsc_delete_cachetable(cache->table_list); |
| ped_free(cache->linked_ref); |
| ped_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, return NULL); |
| |
| 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* |
| hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start) |
| { |
| HfsCPrivateExtent* ret; |
| unsigned int idx = start >> CR_SHIFT; |
| |
| PED_ASSERT(idx < cache->linked_ref_size, return NULL); |
| |
| 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, return NULL); |
| PED_ASSERT(idx2 < cache->linked_ref_size, return NULL); |
| |
| 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 Ox%X to block " |
| "Ox%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 */ |