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