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 #include "advfs.h" 37 38 #include "file.h" 39 40 /* Open the data fork of a file with its first three extents and its CNID */ 41 HfsPrivateFile* 42 hfs_file_open (PedFileSystem *fs, uint32_t CNID, 43 HfsExtDataRec ext_desc, PedSector sect_nb) 44 { 45 HfsPrivateFile* file; 46 47 file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile)); 48 if (!file) return NULL; 49 50 file->fs = fs; 51 file->sect_nb = sect_nb; 52 file->CNID = CNID; 53 memcpy(file->first, ext_desc, sizeof (HfsExtDataRec)); 54 file->start_cache = 0; 55 56 return file; 57 } 58 59 /* Close an HFS file */ 60 void 61 hfs_file_close (HfsPrivateFile* file) 62 { 63 ped_free (file); 64 } 65 66 /* warning : only works on data forks */ 67 static int 68 hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block, 69 HfsExtDataRec cache, uint16_t* ptr_start_cache) 70 { 71 uint8_t record[sizeof (HfsExtentKey) 72 + sizeof (HfsExtDataRec)]; 73 HfsExtentKey search; 74 HfsExtentKey* ret_key = (HfsExtentKey*) record; 75 HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) 76 (record + sizeof (HfsExtentKey)); 77 HfsPrivateFSData* priv_data = (HfsPrivateFSData*) 78 file->fs->type_specific; 79 80 search.key_length = sizeof (HfsExtentKey) - 1; 81 search.type = HFS_DATA_FORK; 82 search.file_ID = file->CNID; 83 search.start = PED_CPU_TO_BE16 (block); 84 85 if (!hfs_btree_search (priv_data->extent_file, 86 (HfsPrivateGenericKey*) &search, 87 record, sizeof (record), NULL)) 88 return 0; 89 90 if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) 91 return 0; 92 93 memcpy (cache, ret_cache, sizeof(HfsExtDataRec)); 94 *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start); 95 96 return 1; 97 } 98 99 /* find and return the nth sector of a file */ 100 /* return 0 on error */ 101 static PedSector 102 hfs_file_find_sector (HfsPrivateFile* file, PedSector sector) 103 { 104 HfsPrivateFSData* priv_data = (HfsPrivateFSData*) 105 file->fs->type_specific; 106 unsigned int sect_by_block = PED_BE32_TO_CPU ( 107 priv_data->mdb->block_size) 108 / PED_SECTOR_SIZE_DEFAULT; 109 unsigned int i, s, vol_block; 110 unsigned int block = sector / sect_by_block; 111 unsigned int offset = sector % sect_by_block; 112 113 /* in the three first extent */ 114 for (s = 0, i = 0; i < HFS_EXT_NB; i++) { 115 if ((block >= s) && ( block < s + PED_BE16_TO_CPU ( 116 file->first[i].block_count))) { 117 vol_block = (block - s) + PED_BE16_TO_CPU ( 118 file->first[i].start_block); 119 goto sector_found; 120 } 121 s += PED_BE16_TO_CPU (file->first[i].block_count); 122 } 123 124 /* in the three cached extent */ 125 if (file->start_cache && block >= file->start_cache) 126 for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { 127 if ((block >= s) && (block < s + PED_BE16_TO_CPU ( 128 file->cache[i].block_count))) { 129 vol_block = (block - s) + PED_BE16_TO_CPU ( 130 file->cache[i].start_block); 131 goto sector_found; 132 } 133 s += PED_BE16_TO_CPU (file->cache[i].block_count); 134 } 135 136 /* update cache */ 137 if (!hfs_get_extent_containing (file, block, file->cache, 138 &(file->start_cache))) { 139 ped_exception_throw ( 140 PED_EXCEPTION_WARNING, 141 PED_EXCEPTION_CANCEL, 142 _("Could not update the extent cache for HFS file with " 143 "CNID %X."), 144 PED_BE32_TO_CPU(file->CNID)); 145 return 0; 146 } 147 148 /* in the three cached extent */ 149 PED_ASSERT(file->start_cache && block >= file->start_cache, return 0); 150 for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { 151 if ((block >= s) && (block < s + PED_BE16_TO_CPU ( 152 file->cache[i].block_count))) { 153 vol_block = (block - s) + PED_BE16_TO_CPU ( 154 file->cache[i].start_block); 155 goto sector_found; 156 } 157 s += PED_BE16_TO_CPU (file->cache[i].block_count); 158 } 159 160 return 0; 161 162 sector_found: 163 return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block) 164 + (PedSector) vol_block * sect_by_block 165 + offset; 166 } 167 168 /* Read the nth sector of a file */ 169 /* return 0 on error */ 170 int 171 hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector) 172 { 173 PedSector abs_sector; 174 175 if (sector >= file->sect_nb) { 176 ped_exception_throw ( 177 PED_EXCEPTION_ERROR, 178 PED_EXCEPTION_CANCEL, 179 _("Trying to read HFS file with CNID %X behind EOF."), 180 PED_BE32_TO_CPU(file->CNID)); 181 return 0; 182 } 183 184 abs_sector = hfs_file_find_sector (file, sector); 185 if (!abs_sector) { 186 ped_exception_throw ( 187 PED_EXCEPTION_ERROR, 188 PED_EXCEPTION_CANCEL, 189 _("Could not find sector %lli of HFS file with " 190 "CNID %X."), 191 sector, PED_BE32_TO_CPU(file->CNID)); 192 return 0; 193 } 194 195 return ped_geometry_read (file->fs->geom, buf, abs_sector, 1); 196 } 197 198 /* Write the nth sector of a file */ 199 /* return 0 on error */ 200 int 201 hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector) 202 { 203 PedSector abs_sector; 204 205 if (sector >= file->sect_nb) { 206 ped_exception_throw ( 207 PED_EXCEPTION_ERROR, 208 PED_EXCEPTION_CANCEL, 209 _("Trying to write HFS file with CNID %X behind EOF."), 210 PED_BE32_TO_CPU(file->CNID)); 211 return 0; 212 } 213 214 abs_sector = hfs_file_find_sector (file, sector); 215 if (!abs_sector) { 216 ped_exception_throw ( 217 PED_EXCEPTION_ERROR, 218 PED_EXCEPTION_CANCEL, 219 _("Could not find sector %lli of HFS file with " 220 "CNID %X."), 221 sector, PED_BE32_TO_CPU(file->CNID)); 222 return 0; 223 } 224 225 return ped_geometry_write (file->fs->geom, buf, abs_sector, 1); 226 } 227 228 #endif /* !DISCOVER_ONLY */