1 /* 2 libparted - a library for manipulating disk partitions 3 Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #ifndef DISCOVER_ONLY 20 21 #include <config.h> 22 23 #include <parted/parted.h> 24 #include <parted/endian.h> 25 #include <parted/debug.h> 26 #include <stdint.h> 27 28 #if ENABLE_NLS 29 # include <libintl.h> 30 # define _(String) dgettext (PACKAGE, String) 31 #else 32 # define _(String) (String) 33 #endif /* ENABLE_NLS */ 34 35 #include "hfs.h" 36 37 #include "cache.h" 38 39 static HfsCPrivateCacheTable* 40 hfsc_new_cachetable(unsigned int size) 41 { 42 HfsCPrivateCacheTable* ret; 43 44 ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret)); 45 if (!ret) return NULL; 46 47 ret->next_cache = NULL; 48 ret->table_size = size; 49 ret->table_first_free = 0; 50 51 ret->table = ped_malloc(sizeof(*ret->table)*size); 52 if (!ret->table) { ped_free(ret); return NULL; } 53 memset(ret->table, 0, sizeof(*ret->table)*size); 54 55 return ret; 56 } 57 58 HfsCPrivateCache* 59 hfsc_new_cache(unsigned int block_number, unsigned int file_number) 60 { 61 unsigned int cachetable_size, i; 62 HfsCPrivateCache* ret; 63 64 ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret)); 65 if (!ret) return NULL; 66 ret->block_number = block_number; 67 /* following code avoid integer overflow */ 68 ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ? 69 ( block_number >> CR_SHIFT ) + 1 : 70 ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT 71 ; 72 73 ret->linked_ref = (HfsCPrivateExtent**) 74 ped_malloc( sizeof(*ret->linked_ref) 75 * ret->linked_ref_size ); 76 if (!ret->linked_ref) { ped_free(ret); return NULL; } 77 78 cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST; 79 if (cachetable_size < file_number) cachetable_size = (unsigned) -1; 80 ret->first_cachetable_size = cachetable_size; 81 ret->table_list = hfsc_new_cachetable(cachetable_size); 82 if (!ret->table_list) { 83 ped_free(ret->linked_ref); 84 ped_free(ret); 85 return NULL; 86 } 87 ret->last_table = ret->table_list; 88 89 for (i = 0; i < ret->linked_ref_size; ++i) 90 ret->linked_ref[i] = NULL; 91 92 ret->needed_alloc_size = 0; 93 94 return ret; 95 } 96 97 static void 98 hfsc_delete_cachetable(HfsCPrivateCacheTable* list) 99 { 100 HfsCPrivateCacheTable* next; 101 102 while (list) { 103 ped_free (list->table); 104 next = list->next_cache; 105 ped_free (list); 106 list = next; 107 } 108 } 109 110 void 111 hfsc_delete_cache(HfsCPrivateCache* cache) 112 { 113 hfsc_delete_cachetable(cache->table_list); 114 ped_free(cache->linked_ref); 115 ped_free(cache); 116 } 117 118 HfsCPrivateExtent* 119 hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, 120 uint32_t block, uint16_t offset, uint8_t sbb, 121 uint8_t where, uint8_t ref_index) 122 { 123 HfsCPrivateExtent* ext; 124 unsigned int idx = start >> CR_SHIFT; 125 126 PED_ASSERT(idx < cache->linked_ref_size, return NULL); 127 128 for (ext = cache->linked_ref[idx]; 129 ext && start != ext->ext_start; 130 ext = ext->next); 131 132 if (ext) { 133 ped_exception_throw ( 134 PED_EXCEPTION_ERROR, 135 PED_EXCEPTION_CANCEL, 136 _("Trying to register an extent starting at block " 137 "0x%X, but another one already exists at this " 138 "position. You should check the file system!"), 139 start); 140 return NULL; 141 } 142 143 if ( cache->last_table->table_first_free 144 == cache->last_table->table_size ) { 145 cache->last_table->next_cache = 146 hfsc_new_cachetable( ( cache->first_cachetable_size 147 / CR_NEW_ALLOC_DIV ) 148 + CR_ADD_CST ); 149 if (!cache->last_table->next_cache) 150 return NULL; 151 cache->last_table = cache->last_table->next_cache; 152 } 153 154 ext = cache->last_table->table+(cache->last_table->table_first_free++); 155 156 ext->ext_start = start; 157 ext->ext_length = length; 158 ext->ref_block = block; 159 ext->ref_offset = offset; 160 ext->sect_by_block = sbb; 161 ext->where = where; 162 ext->ref_index = ref_index; 163 164 ext->next = cache->linked_ref[idx]; 165 cache->linked_ref[idx] = ext; 166 167 cache->needed_alloc_size = cache->needed_alloc_size > 168 (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ? 169 cache->needed_alloc_size : 170 (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb; 171 172 return ext; 173 } 174 175 HfsCPrivateExtent* 176 hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start) 177 { 178 HfsCPrivateExtent* ret; 179 unsigned int idx = start >> CR_SHIFT; 180 181 PED_ASSERT(idx < cache->linked_ref_size, return NULL); 182 183 for (ret = cache->linked_ref[idx]; 184 ret && start != ret->ext_start; 185 ret = ret->next); 186 187 return ret; 188 } 189 190 /* Can't fail if extent begining at old_start exists */ 191 /* Returns 0 if no such extent, or on error */ 192 HfsCPrivateExtent* 193 hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, 194 uint32_t new_start) 195 { 196 HfsCPrivateExtent** ppext; 197 HfsCPrivateExtent* pext; 198 199 unsigned int idx1 = old_start >> CR_SHIFT; 200 unsigned int idx2 = new_start >> CR_SHIFT; 201 202 PED_ASSERT(idx1 < cache->linked_ref_size, return NULL); 203 PED_ASSERT(idx2 < cache->linked_ref_size, return NULL); 204 205 for (pext = cache->linked_ref[idx2]; 206 pext && new_start != pext->ext_start; 207 pext = pext->next); 208 209 if (pext) { 210 ped_exception_throw ( 211 PED_EXCEPTION_BUG, 212 PED_EXCEPTION_CANCEL, 213 _("Trying to move an extent from block Ox%X to block " 214 "Ox%X, but another one already exists at this " 215 "position. This should not happen!"), 216 old_start, new_start); 217 return NULL; 218 } 219 220 for (ppext = &(cache->linked_ref[idx1]); 221 (*ppext) && old_start != (*ppext)->ext_start; 222 ppext = &((*ppext)->next)); 223 224 if (!(*ppext)) return NULL; 225 226 /* removing the extent from the cache */ 227 pext = *ppext; 228 (*ppext) = pext->next; 229 230 /* change ext_start and insert the extent again */ 231 pext->ext_start = new_start; 232 pext->next = cache->linked_ref[idx2]; 233 cache->linked_ref[idx2] = pext; 234 235 return pext; 236 } 237 238 #endif /* DISCOVER_ONLY */