1 /* 2 * hfsutils - tools for reading and writing Macintosh HFS volumes 3 * Copyright (C) 1996, 1997 Robert Leslie 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 2 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, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 # include <string.h> 21 # include <errno.h> 22 23 # include "internal.h" 24 # include "data.h" 25 # include "block.h" 26 # include "file.h" 27 # include "btree.h" 28 # include "record.h" 29 # include "volume.h" 30 31 /* #include <stdio.h> */ 32 33 34 /* 35 * NAME: file->selectfork() 36 * DESCRIPTION: choose a fork for file operations 37 */ 38 void f_selectfork(hfsfile *file, int fork) 39 { 40 if (fork == 0) 41 { 42 file->fork = fkData; 43 memcpy(file->ext, file->cat.u.fil.filExtRec, sizeof(ExtDataRec)); 44 } 45 else 46 { 47 file->fork = fkRsrc; 48 memcpy(file->ext, file->cat.u.fil.filRExtRec, sizeof(ExtDataRec)); 49 } 50 51 file->fabn = 0; 52 file->pos = 0; 53 } 54 55 /* 56 * NAME: file->getptrs() 57 * DESCRIPTION: make pointers to the current fork's lengths and extents 58 */ 59 void f_getptrs(hfsfile *file, unsigned long **lglen, 60 unsigned long **pylen, ExtDataRec **extrec) 61 { 62 if (file->fork == fkData) 63 { 64 if (lglen) 65 *lglen = &file->cat.u.fil.filLgLen; 66 if (pylen) 67 *pylen = &file->cat.u.fil.filPyLen; 68 if (extrec) 69 *extrec = &file->cat.u.fil.filExtRec; 70 } 71 else 72 { 73 if (lglen) 74 *lglen = &file->cat.u.fil.filRLgLen; 75 if (pylen) 76 *pylen = &file->cat.u.fil.filRPyLen; 77 if (extrec) 78 *extrec = &file->cat.u.fil.filRExtRec; 79 } 80 } 81 82 /* 83 * NAME: file->doblock() 84 * DESCRIPTION: read or write a numbered block from a file 85 */ 86 int f_doblock(hfsfile *file, unsigned long num, block *bp, 87 int (*func)(hfsvol *, unsigned int, unsigned int, block *)) 88 { 89 unsigned int abnum; 90 unsigned int blnum; 91 unsigned int fabn; 92 int i; 93 94 abnum = num / file->vol->lpa; 95 blnum = num % file->vol->lpa; 96 97 /* locate the appropriate extent record */ 98 99 fabn = file->fabn; 100 101 if (abnum < fabn) 102 { 103 ExtDataRec *extrec; 104 105 f_getptrs(file, 0, 0, &extrec); 106 107 fabn = file->fabn = 0; 108 memcpy(file->ext, extrec, sizeof(ExtDataRec)); 109 } 110 else 111 abnum -= fabn; 112 113 while (1) 114 { 115 unsigned int num; 116 117 for (i = 0; i < 3; ++i) 118 { 119 num = file->ext[i].xdrNumABlks; 120 121 #ifdef APPLE_HYB 122 if (i > 0) { 123 /* SHOULD NOT HAPPEN! - all the files should not be fragmented 124 if this happens, then a serious problem has occured, may be 125 a hard linked file? */ 126 #ifdef DEBUG 127 fprintf(stderr,"file: %s %d\n",file->name, i); */ 128 #endif /* DEBUG */ 129 ERROR(HCE_ERROR, "Possible Catalog file overflow - please report error"); 130 return -1; 131 } 132 #endif /* APPLE_HYB */ 133 if (abnum < num) 134 return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp); 135 136 fabn += num; 137 abnum -= num; 138 } 139 140 if (v_extsearch(file, fabn, &file->ext, 0) <= 0) 141 return -1; 142 143 file->fabn = fabn; 144 } 145 } 146 147 /* 148 * NAME: file->alloc() 149 * DESCRIPTION: reserve disk blocks for a file 150 */ 151 int f_alloc(hfsfile *file) 152 { 153 hfsvol *vol = file->vol; 154 ExtDescriptor blocks; 155 ExtDataRec *extrec; 156 unsigned long *pylen, clumpsz; 157 unsigned int start, end; 158 node n; 159 int i; 160 161 clumpsz = file->clump; 162 if (clumpsz == 0) 163 clumpsz = vol->mdb.drClpSiz; 164 165 blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz; 166 167 if (v_allocblocks(vol, &blocks) < 0) 168 return -1; 169 170 /* update the file's extents */ 171 172 f_getptrs(file, 0, &pylen, &extrec); 173 174 start = file->fabn; 175 end = *pylen / vol->mdb.drAlBlkSiz; 176 177 n.nnum = 0; 178 i = -1; 179 180 while (start < end) 181 { 182 for (i = 0; i < 3; ++i) 183 { 184 unsigned int num; 185 186 num = file->ext[i].xdrNumABlks; 187 start += num; 188 189 if (start == end) 190 break; 191 else if (start > end) 192 { 193 v_freeblocks(vol, &blocks); 194 ERROR(EIO, "file extents exceed file physical length"); 195 return -1; 196 } 197 else if (num == 0) 198 { 199 v_freeblocks(vol, &blocks); 200 ERROR(EIO, "empty file extent"); 201 return -1; 202 } 203 } 204 205 if (start == end) 206 break; 207 208 if (v_extsearch(file, start, &file->ext, &n) <= 0) 209 { 210 v_freeblocks(vol, &blocks); 211 return -1; 212 } 213 214 file->fabn = start; 215 } 216 217 if (i >= 0 && 218 file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks.xdrStABN) 219 file->ext[i].xdrNumABlks += blocks.xdrNumABlks; 220 else 221 { 222 /* create a new extent descriptor */ 223 224 if (++i < 3) 225 file->ext[i] = blocks; 226 else 227 { 228 ExtKeyRec key; 229 unsigned char record[HFS_EXTRECMAXLEN]; 230 int reclen; 231 232 /* record is full; create a new one */ 233 234 file->ext[0] = blocks; 235 236 for (i = 1; i < 3; ++i) 237 { 238 file->ext[i].xdrStABN = 0; 239 file->ext[i].xdrNumABlks = 0; 240 } 241 242 file->fabn = start; 243 244 r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end); 245 r_packextkey(&key, record, &reclen); 246 r_packextdata(&file->ext, HFS_RECDATA(record), &reclen); 247 248 if (bt_insert(&vol->ext, record, reclen) < 0) 249 { 250 v_freeblocks(vol, &blocks); 251 return -1; 252 } 253 254 i = -1; 255 } 256 } 257 258 if (i >= 0) 259 { 260 /* store the modified extent record */ 261 262 if (file->fabn) 263 { 264 if ((n.nnum == 0 && 265 v_extsearch(file, file->fabn, 0, &n) <= 0) || 266 v_putextrec(&file->ext, &n) < 0) 267 { 268 v_freeblocks(vol, &blocks); 269 return -1; 270 } 271 } 272 else 273 memcpy(extrec, file->ext, sizeof(ExtDataRec)); 274 } 275 276 *pylen += blocks.xdrNumABlks * vol->mdb.drAlBlkSiz; 277 278 file->flags |= HFS_UPDATE_CATREC; 279 280 return blocks.xdrNumABlks; 281 } 282 283 /* 284 * NAME: file->trunc() 285 * DESCRIPTION: release disk blocks unneeded by a file 286 */ 287 int f_trunc(hfsfile *file) 288 { 289 ExtDataRec *extrec; 290 unsigned long *lglen, *pylen, alblksz, newpylen; 291 unsigned int dlen, start, end; 292 node n; 293 int i; 294 295 f_getptrs(file, &lglen, &pylen, &extrec); 296 297 alblksz = file->vol->mdb.drAlBlkSiz; 298 newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz; 299 300 if (newpylen > *pylen) 301 { 302 ERROR(EIO, "file size exceeds physical length"); 303 return -1; 304 } 305 else if (newpylen == *pylen) 306 return 0; 307 308 dlen = (*pylen - newpylen) / alblksz; 309 310 start = file->fabn; 311 end = newpylen / alblksz; 312 313 if (start >= end) 314 { 315 start = file->fabn = 0; 316 memcpy(file->ext, extrec, sizeof(ExtDataRec)); 317 } 318 319 n.nnum = 0; 320 i = -1; 321 322 while (start < end) 323 { 324 for (i = 0; i < 3; ++i) 325 { 326 unsigned int num; 327 328 num = file->ext[i].xdrNumABlks; 329 start += num; 330 331 if (start >= end) 332 break; 333 else if (num == 0) 334 { 335 ERROR(EIO, "empty file extent"); 336 return -1; 337 } 338 } 339 340 if (start >= end) 341 break; 342 343 if (v_extsearch(file, start, &file->ext, &n) <= 0) 344 return -1; 345 346 file->fabn = start; 347 } 348 349 if (start > end) 350 { 351 ExtDescriptor blocks; 352 353 file->ext[i].xdrNumABlks -= start - end; 354 dlen -= start - end; 355 356 blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks; 357 blocks.xdrNumABlks = start - end; 358 359 v_freeblocks(file->vol, &blocks); 360 } 361 362 *pylen = newpylen; 363 364 file->flags |= HFS_UPDATE_CATREC; 365 366 do 367 { 368 while (dlen && ++i < 3) 369 { 370 unsigned int num; 371 372 num = file->ext[i].xdrNumABlks; 373 start += num; 374 375 if (num == 0) 376 { 377 ERROR(EIO, "empty file extent"); 378 return -1; 379 } 380 else if (num > dlen) 381 { 382 ERROR(EIO, "file extents exceed physical size"); 383 return -1; 384 } 385 386 dlen -= num; 387 v_freeblocks(file->vol, &file->ext[i]); 388 389 file->ext[i].xdrStABN = 0; 390 file->ext[i].xdrNumABlks = 0; 391 } 392 393 if (file->fabn) 394 { 395 if (n.nnum == 0 && 396 v_extsearch(file, file->fabn, 0, &n) <= 0) 397 return -1; 398 399 if (file->ext[0].xdrNumABlks) 400 { 401 if (v_putextrec(&file->ext, &n) < 0) 402 return -1; 403 } 404 else 405 { 406 if (bt_delete(&file->vol->ext, HFS_NODEREC(n, n.rnum)) < 0) 407 return -1; 408 409 n.nnum = 0; 410 } 411 } 412 else 413 memcpy(extrec, file->ext, sizeof(ExtDataRec)); 414 415 if (dlen) 416 { 417 if (v_extsearch(file, start, &file->ext, &n) <= 0) 418 return -1; 419 420 file->fabn = start; 421 i = -1; 422 } 423 } 424 while (dlen); 425 426 return 0; 427 } 428 429 /* 430 * NAME: file->flush() 431 * DESCRIPTION: flush all pending changes to an open file 432 */ 433 int f_flush(hfsfile *file) 434 { 435 hfsvol *vol = file->vol; 436 437 if (! (vol->flags & HFS_READONLY)) 438 { 439 if (file->flags & HFS_UPDATE_CATREC) 440 { 441 node n; 442 443 file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN; 444 file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN; 445 file->cat.u.fil.filClpSize = file->clump; 446 447 if (v_catsearch(file->vol, file->parid, file->name, 0, 0, &n) <= 0 || 448 v_putcatrec(&file->cat, &n) < 0) 449 return -1; 450 451 file->flags &= ~HFS_UPDATE_CATREC; 452 } 453 } 454 455 return 0; 456 } 457