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