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 */
f_selectfork(hfsfile * file,int fork)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 */
f_getptrs(hfsfile * file,unsigned long ** lglen,unsigned long ** pylen,ExtDataRec ** extrec)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 */
f_doblock(hfsfile * file,unsigned long num,block * bp,int (* func)(hfsvol *,unsigned int,unsigned int,block *))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 */
f_alloc(hfsfile * file)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 */
f_trunc(hfsfile * file)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 */
f_flush(hfsfile * file)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