/* libparted - a library for manipulating disk partitions Copyright (C) 2004-2005, 2007, 2009-2014, 2019-2022 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 . */ #ifndef DISCOVER_ONLY #include #include #include #include #include #if ENABLE_NLS # include # 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 + ((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 */