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_plus.h" 37 38 #include "file_plus.h" 39 40 /* Open the data fork of a file with its first eight extents and its CNID */ 41 /* CNID and ext_desc must be in disc order, sect_nb in CPU order */ 42 /* return null on failure */ 43 HfsPPrivateFile* 44 hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, 45 HfsPExtDataRec ext_desc, PedSector sect_nb) 46 { 47 HfsPPrivateFile* file; 48 49 file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile)); 50 if (!file) return NULL; 51 52 file->fs = fs; 53 file->sect_nb = sect_nb; 54 file->CNID = CNID; 55 memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec)); 56 file->start_cache = 0; 57 58 return file; 59 } 60 61 /* Close an HFS+ file */ 62 void 63 hfsplus_file_close (HfsPPrivateFile* file) 64 { 65 ped_free (file); 66 } 67 68 /* warning : only works on data forks */ 69 static int 70 hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block, 71 HfsPExtDataRec cache, uint32_t* ptr_start_cache) 72 { 73 uint8_t record[sizeof (HfsPExtentKey) 74 + sizeof (HfsPExtDataRec)]; 75 HfsPExtentKey search; 76 HfsPExtentKey* ret_key = (HfsPExtentKey*) record; 77 HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) 78 (record + sizeof (HfsPExtentKey)); 79 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 80 file->fs->type_specific; 81 82 search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2); 83 search.type = HFS_DATA_FORK; 84 search.pad = 0; 85 search.file_ID = file->CNID; 86 search.start = PED_CPU_TO_BE32 (block); 87 88 if (!hfsplus_btree_search (priv_data->extents_file, 89 (HfsPPrivateGenericKey*) &search, 90 record, sizeof (record), NULL)) 91 return 0; 92 93 if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) 94 return 0; 95 96 memcpy (cache, ret_cache, sizeof(HfsPExtDataRec)); 97 *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start); 98 99 return 1; 100 } 101 102 /* find a sub extent contained in the desired area */ 103 /* and with the same starting point */ 104 /* return 0 in sector_count on error, or the physical area */ 105 /* on the volume corresponding to the logical area in the file */ 106 static HfsPPrivateExtent 107 hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector, 108 unsigned int nb) 109 { 110 HfsPPrivateExtent ret = {0,0}; 111 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 112 file->fs->type_specific; 113 unsigned int sect_by_block = PED_BE32_TO_CPU ( 114 priv_data->vh->block_size) 115 / PED_SECTOR_SIZE_DEFAULT; 116 unsigned int i, s, vol_block, size; 117 PedSector sect_size; 118 unsigned int block = sector / sect_by_block; 119 unsigned int offset = sector % sect_by_block; 120 121 /* in the 8 first extent */ 122 for (s = 0, i = 0; i < HFSP_EXT_NB; i++) { 123 if ((block >= s) && (block < s + PED_BE32_TO_CPU ( 124 file->first[i].block_count))) { 125 vol_block = (block - s) 126 + PED_BE32_TO_CPU (file->first[i] 127 .start_block); 128 size = PED_BE32_TO_CPU (file->first[i].block_count) 129 + s - block; 130 goto plus_sector_found; 131 } 132 s += PED_BE32_TO_CPU (file->first[i].block_count); 133 } 134 135 /* in the 8 cached extent */ 136 if (file->start_cache && block >= file->start_cache) 137 for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { 138 if ((block >= s) && (block < s + PED_BE32_TO_CPU ( 139 file->cache[i].block_count))) { 140 vol_block = (block - s) 141 + PED_BE32_TO_CPU (file->cache[i] 142 .start_block); 143 size = PED_BE32_TO_CPU (file->cache[i].block_count) 144 + s - block; 145 goto plus_sector_found; 146 } 147 s += PED_BE32_TO_CPU (file->cache[i].block_count); 148 } 149 150 /* update cache */ 151 if (!hfsplus_get_extent_containing (file, block, file->cache, 152 &(file->start_cache))) { 153 ped_exception_throw ( 154 PED_EXCEPTION_WARNING, 155 PED_EXCEPTION_CANCEL, 156 _("Could not update the extent cache for HFS+ file " 157 "with CNID %X."), 158 PED_BE32_TO_CPU(file->CNID)); 159 return ret; /* ret == {0,0} */ 160 } 161 162 /* ret == {0,0} */ 163 PED_ASSERT(file->start_cache && block >= file->start_cache, return ret); 164 165 for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { 166 if ((block >= s) && (block < s + PED_BE32_TO_CPU ( 167 file->cache[i].block_count))) { 168 vol_block = (block - s) 169 + PED_BE32_TO_CPU (file->cache[i] 170 .start_block); 171 size = PED_BE32_TO_CPU (file->cache[i].block_count) 172 + s - block; 173 goto plus_sector_found; 174 } 175 s += PED_BE32_TO_CPU (file->cache[i].block_count); 176 } 177 178 return ret; 179 180 plus_sector_found: 181 sect_size = (PedSector) size * sect_by_block - offset; 182 ret.start_sector = vol_block * sect_by_block + offset; 183 ret.sector_count = (sect_size < nb) ? sect_size : nb; 184 return ret; 185 } 186 187 int 188 hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector, 189 unsigned int nb) 190 { 191 HfsPPrivateExtent phy_area; 192 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 193 file->fs->type_specific; 194 char *b = buf; 195 196 if (sector+nb < sector /* detect overflow */ 197 || sector+nb > file->sect_nb) /* out of file */ { 198 ped_exception_throw ( 199 PED_EXCEPTION_ERROR, 200 PED_EXCEPTION_CANCEL, 201 _("Trying to read HFS+ file with CNID %X behind EOF."), 202 PED_BE32_TO_CPU(file->CNID)); 203 return 0; 204 } 205 206 while (nb) { 207 phy_area = hfsplus_file_find_extent(file, sector, nb); 208 if (phy_area.sector_count == 0) { 209 ped_exception_throw ( 210 PED_EXCEPTION_ERROR, 211 PED_EXCEPTION_CANCEL, 212 _("Could not find sector %lli of HFS+ file " 213 "with CNID %X."), 214 sector, PED_BE32_TO_CPU(file->CNID)); 215 return 0; 216 } 217 if (!ped_geometry_read(priv_data->plus_geom, b, 218 phy_area.start_sector, 219 phy_area.sector_count)) 220 return 0; 221 222 nb -= phy_area.sector_count; /* < nb anyway ... */ 223 sector += phy_area.sector_count; 224 b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; 225 } 226 227 return 1; 228 } 229 230 int 231 hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector, 232 unsigned int nb) 233 { 234 HfsPPrivateExtent phy_area; 235 HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) 236 file->fs->type_specific; 237 char *b = buf; 238 239 if (sector+nb < sector /* detect overflow */ 240 || sector+nb > file->sect_nb) /* out of file */ { 241 ped_exception_throw ( 242 PED_EXCEPTION_ERROR, 243 PED_EXCEPTION_CANCEL, 244 _("Trying to write HFS+ file with CNID %X behind EOF."), 245 PED_BE32_TO_CPU(file->CNID)); 246 return 0; 247 } 248 249 while (nb) { 250 phy_area = hfsplus_file_find_extent(file, sector, nb); 251 if (phy_area.sector_count == 0) { 252 ped_exception_throw ( 253 PED_EXCEPTION_ERROR, 254 PED_EXCEPTION_CANCEL, 255 _("Could not find sector %lli of HFS+ file " 256 "with CNID %X."), 257 sector, PED_BE32_TO_CPU(file->CNID)); 258 return 0; 259 } 260 if (!ped_geometry_write(priv_data->plus_geom, b, 261 phy_area.start_sector, 262 phy_area.sector_count)) 263 return 0; 264 265 nb -= phy_area.sector_count; /* < nb anyway ... */ 266 sector += phy_area.sector_count; 267 b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; 268 } 269 270 return 1; 271 } 272 273 #endif /* !DISCOVER_ONLY */