Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libhfs_iso / volume.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 /* @(#)volume.c 1.4 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 <stdxlib.h>
35 #include <strdefs.h>
36 #include <errno.h>
37
38 #include "internal.h"
39 #include "data.h"
40 #include "low.h"
41 #include "btree.h"
42 #include "record.h"
43 #include "volume.h"
44
45 static  void    markexts(block *vbm, ExtDataRec *exts);
46
47
48 /*
49  * NAME:        vol->catsearch()
50  * DESCRIPTION: search catalog tree
51  */
52 int v_catsearch(hfsvol *vol, long parid, char *name, CatDataRec *data, 
53                 char *cname, node *np)
54 {
55   CatKeyRec key;
56   unsigned char pkey[HFS_CATKEYLEN];
57   node n;
58   unsigned char *ptr;
59   int found;
60
61   if (np == 0)
62     np = &n;
63
64   r_makecatkey(&key, parid, name);
65   r_packcatkey(&key, pkey, 0);
66
67   found = bt_search(&vol->cat, pkey, np);
68   if (found <= 0)
69     return found;
70
71   ptr = HFS_NODEREC(*np, np->rnum);
72
73   if (cname)
74     {
75       r_unpackcatkey(ptr, &key);
76       strcpy(cname, key.ckrCName);
77     }
78
79   if (data)
80     r_unpackcatdata(HFS_RECDATA(ptr), data);
81
82   return 1;
83 }
84
85 /*
86  * NAME:        vol->extsearch()
87  * DESCRIPTION: search extents tree
88  */
89 int v_extsearch(hfsfile *file, unsigned int fabn, ExtDataRec *data, node *np)
90 {
91   ExtKeyRec key;
92   ExtDataRec extsave;
93   unsigned int fabnsave;
94   unsigned char pkey[HFS_EXTKEYLEN];
95   node n;
96   unsigned char *ptr;
97   int found;
98
99   if (np == 0)
100     np = &n;
101
102   r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
103   r_packextkey(&key, pkey, 0);
104
105   /* in case bt_search() clobbers these */
106
107   memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
108   fabnsave = file->fabn;
109
110   found = bt_search(&file->vol->ext, pkey, np);
111
112   memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
113   file->fabn = fabnsave;
114
115   if (found <= 0)
116     return found;
117
118   if (data)
119     {
120       ptr = HFS_NODEREC(*np, np->rnum);
121       r_unpackextdata(HFS_RECDATA(ptr), data);
122     }
123
124   return 1;
125 }
126
127 /*
128  * NAME:        vol->getthread()
129  * DESCRIPTION: retrieve catalog thread information for a file or directory
130  */
131 int v_getthread(hfsvol *vol, long id, CatDataRec *thread, node *np, int type)
132 {
133   CatDataRec rec;
134   int found;
135
136   if (thread == 0)
137     thread = &rec;
138
139   found = v_catsearch(vol, id, "", thread, 0, np);
140   if (found <= 0)
141     return found;
142
143   if (thread->cdrType != type)
144     {
145       ERROR(EIO, "bad thread record");
146       return -1;
147     }
148
149   return 1;
150 }
151
152 /*
153  * NAME:        vol->putcatrec()
154  * DESCRIPTION: store catalog information
155  */
156 int v_putcatrec(CatDataRec *data, node *np)
157 {
158   unsigned char pdata[HFS_CATDATALEN], *ptr;
159   int len = 0;
160
161   r_packcatdata(data, pdata, &len);
162
163   ptr = HFS_NODEREC(*np, np->rnum);
164   memcpy(HFS_RECDATA(ptr), pdata, len);
165
166   return bt_putnode(np);
167 }
168
169 /*
170  * NAME:        vol->putextrec()
171  * DESCRIPTION: store extent information
172  */
173 int v_putextrec(ExtDataRec *data, node *np)
174 {
175   unsigned char pdata[HFS_EXTDATALEN], *ptr;
176   int len = 0;
177
178   r_packextdata(data, pdata, &len);
179
180   ptr = HFS_NODEREC(*np, np->rnum);
181   memcpy(HFS_RECDATA(ptr), pdata, len);
182
183   return bt_putnode(np);
184 }
185
186 /*
187  * NAME:        vol->allocblocks()
188  * DESCRIPTION: allocate a contiguous range of blocks
189  */
190 int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks)
191 {
192   unsigned int request, found, foundat, start, end, pt;
193   block *vbm;
194   int wrap = 0;
195
196   if (vol->mdb.drFreeBks == 0)
197     {
198       ERROR(ENOSPC, "volume full");
199       return -1;
200     }
201
202   request = blocks->xdrNumABlks;
203   found   = 0;
204   foundat = 0;
205   start   = vol->mdb.drAllocPtr;
206   end     = vol->mdb.drNmAlBlks;
207   pt      = start;
208   vbm     = vol->vbm;
209
210   if (request == 0)
211     abort();
212
213   for (;;)
214     {
215       unsigned int mark;
216
217       /* skip blocks in use */
218
219       while (pt < end && BMTST(vbm, pt))
220         ++pt;
221
222       if (wrap && pt >= start)
223         break;
224
225       /* count blocks not in use */
226
227       mark = pt;
228       while (pt < end && pt - mark < request && ! BMTST(vbm, pt))
229         ++pt;
230
231       if (pt - mark > found)
232         {
233           found   = pt - mark;
234           foundat = mark;
235         }
236
237       if (pt == end)
238         pt = 0, wrap = 1;
239
240       if (found == request)
241         break;
242     }
243
244   if (found == 0 || found > vol->mdb.drFreeBks)
245     {
246       ERROR(EIO, "bad volume bitmap or free block count");
247       return -1;
248     }
249
250   blocks->xdrStABN    = foundat;
251   blocks->xdrNumABlks = found;
252
253   vol->mdb.drAllocPtr = pt;
254   vol->mdb.drFreeBks -= found;
255
256   for (pt = foundat; pt < foundat + found; ++pt)
257     BMSET(vbm, pt);
258
259   vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
260
261   return 0;
262 }
263
264 /*
265  * NAME:        vol->freeblocks()
266  * DESCRIPTION: deallocate a contiguous range of blocks
267  */
268 void v_freeblocks(hfsvol *vol, ExtDescriptor *blocks)
269 {
270   unsigned int start, len, pt;
271   block *vbm;
272
273   start = blocks->xdrStABN;
274   len   = blocks->xdrNumABlks;
275   vbm   = vol->vbm;
276
277   vol->mdb.drFreeBks += len;
278
279   for (pt = start; pt < start + len; ++pt)
280     BMCLR(vbm, pt);
281
282   vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
283 }
284
285 /*
286  * NAME:        vol->resolve()
287  * DESCRIPTION: translate a pathname; return catalog information
288  */
289 int v_resolve(hfsvol **vol, char *path, CatDataRec *data, long *parid, 
290               char *fname, node *np)
291 {
292   long dirid;
293   char name[HFS_MAX_FLEN + 1], *nptr;
294   int found;
295
296   if (*path == 0)
297     {
298       ERROR(ENOENT, "empty path");
299       return -1;
300     }
301
302   if (parid)
303     *parid = 0;
304
305   nptr = strchr(path, ':');
306
307   if (*path == ':' || nptr == 0)
308     {
309       dirid = (*vol)->cwd;  /* relative path */
310
311       if (*path == ':')
312         ++path;
313
314       if (*path == 0)
315         {
316           found = v_getdthread(*vol, dirid, data, 0);
317           if (found <= 0)
318             return found;
319
320           if (parid)
321             *parid = data->u.dthd.thdParID;
322
323           return v_catsearch(*vol, data->u.dthd.thdParID,
324                              data->u.dthd.thdCName, data, fname, np);
325         }
326     }
327   else
328     {
329       hfsvol *check;
330
331       dirid = HFS_CNID_ROOTPAR;  /* absolute path */
332
333       if (nptr - path > HFS_MAX_VLEN)
334         {
335           ERROR(ENAMETOOLONG, 0);
336           return -1;
337         }
338
339       strncpy(name, path, nptr - path);
340       name[nptr - path] = 0;
341
342       for (check = hfs_mounts; check; check = check->next)
343         {
344           if (d_relstring(check->mdb.drVN, name) == 0)
345             {
346               *vol = check;
347               break;
348             }
349         }
350     }
351
352   for (;;)
353     {
354       while (*path == ':')
355         {
356           ++path;
357
358           found = v_getdthread(*vol, dirid, data, 0);
359           if (found <= 0)
360             return found;
361
362           dirid = data->u.dthd.thdParID;
363         }
364
365       if (*path == 0)
366         {
367           found = v_getdthread(*vol, dirid, data, 0);
368           if (found <= 0)
369             return found;
370
371           if (parid)
372             *parid = data->u.dthd.thdParID;
373
374           return v_catsearch(*vol, data->u.dthd.thdParID,
375                              data->u.dthd.thdCName, data, fname, np);
376         }
377
378       nptr = name;
379       while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
380         *nptr++ = *path++;
381
382       if (*path && *path != ':')
383         {
384           ERROR(ENAMETOOLONG, 0);
385           return -1;
386         }
387
388       *nptr = 0;
389       if (*path == ':')
390         ++path;
391
392       if (parid)
393         *parid = dirid;
394
395       found = v_catsearch(*vol, dirid, name, data, fname, np);
396       if (found < 0)
397         return -1;
398
399       if (found == 0)
400         {
401           if (*path && parid)
402             *parid = 0;
403
404           if (*path == 0 && fname)
405             strcpy(fname, name);
406
407           return 0;
408         }
409
410       switch (data->cdrType)
411         {
412         case cdrDirRec:
413           if (*path == 0)
414             return 1;
415
416           dirid = data->u.dir.dirDirID;
417           break;
418
419         case cdrFilRec:
420           if (*path == 0)
421             return 1;
422
423           ERROR(ENOTDIR, "invalid pathname");
424           return -1;
425
426         default:
427           ERROR(EIO, "unexpected catalog record");
428           return -1;
429         }
430     }
431 }
432
433 /*
434  * NAME:        vol->destruct()
435  * DESCRIPTION: free memory consumed by a volume descriptor
436  */
437 void v_destruct(hfsvol *vol)
438 {
439   FREE(vol->vbm);
440
441   FREE(vol->ext.map);
442   FREE(vol->cat.map);
443
444   FREE(vol);
445 }
446
447 /*
448  * NAME:        vol->getvol()
449  * DESCRIPTION: validate a volume reference
450  */
451 int v_getvol(hfsvol **vol)
452 {
453   if (*vol == 0)
454     {
455       if (hfs_curvol == 0)
456         {
457           ERROR(EINVAL, "no volume is current");
458           return -1;
459         }
460
461       *vol = hfs_curvol;
462     }
463
464   return 0;
465 }
466
467 /*
468  * NAME:        vol->flush()
469  * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to disk
470  */
471 int v_flush(hfsvol *vol, int umounting)
472 {
473   if (! (vol->flags & HFS_READONLY))
474     {
475       if ((vol->ext.flags & HFS_UPDATE_BTHDR) &&
476           bt_writehdr(&vol->ext) < 0)
477         return -1;
478
479       if ((vol->cat.flags & HFS_UPDATE_BTHDR) &&
480           bt_writehdr(&vol->cat) < 0)
481         return -1;
482
483       if ((vol->flags & HFS_UPDATE_VBM) &&
484           l_writevbm(vol) < 0)
485         return -1;
486
487       if (umounting &&
488           ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
489         {
490           vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED;
491           vol->flags |= HFS_UPDATE_MDB;
492         }
493
494       if ((vol->flags & (HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB)) &&
495           l_writemdb(vol) < 0)
496         return -1;
497     }
498
499   return 0;
500 }
501
502 /*
503  * NAME:        vol->adjvalence()
504  * DESCRIPTION: update a volume's valence counts
505  */
506 int v_adjvalence(hfsvol *vol, long parid, int isdir, int adj)
507 {
508   node n;
509   CatDataRec data;
510
511   if (isdir)
512     vol->mdb.drDirCnt += adj;
513   else
514     vol->mdb.drFilCnt += adj;
515
516   vol->flags |= HFS_UPDATE_MDB;
517
518   if (parid == HFS_CNID_ROOTDIR)
519     {
520       if (isdir)
521         vol->mdb.drNmRtDirs += adj;
522       else
523         vol->mdb.drNmFls    += adj;
524     }
525   else if (parid == HFS_CNID_ROOTPAR)
526     return 0;
527
528   if (v_getdthread(vol, parid, &data, 0) <= 0 ||
529       v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName,
530                   &data, 0, &n) <= 0 ||
531       data.cdrType != cdrDirRec)
532     {
533       ERROR(EIO, "can't find parent directory");
534       return -1;
535     }
536
537   data.u.dir.dirVal  += adj;
538   data.u.dir.dirMdDat = d_tomtime(time(0));
539
540   return v_putcatrec(&data, &n);
541 }
542
543 /*
544  * NAME:        vol->newfolder()
545  * DESCRIPTION: create a new HFS folder
546  */
547 int v_newfolder(hfsvol *vol, long parid, char *name)
548 {
549   CatKeyRec key;
550   CatDataRec data;
551   long id;
552   unsigned char record[HFS_CATRECMAXLEN];
553   int i, reclen;
554
555   if (bt_space(&vol->cat, 2) < 0)
556     return -1;
557
558   id = vol->mdb.drNxtCNID++;
559   vol->flags |= HFS_UPDATE_MDB;
560
561   /* create directory record */
562
563   data.cdrType   = cdrDirRec;
564   data.cdrResrv2 = 0;
565
566   data.u.dir.dirFlags = 0;
567   data.u.dir.dirVal   = 0;
568   data.u.dir.dirDirID = id;
569   data.u.dir.dirCrDat = d_tomtime(time(0));
570   data.u.dir.dirMdDat = data.u.dir.dirCrDat;
571   data.u.dir.dirBkDat = 0;
572
573   memset(&data.u.dir.dirUsrInfo,  0, sizeof(data.u.dir.dirUsrInfo));
574   memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo));
575   for (i = 0; i < 4; ++i)
576     data.u.dir.dirResrv[i] = 0;
577
578   r_makecatkey(&key, parid, name);
579   r_packcatkey(&key, record, &reclen);
580   r_packcatdata(&data, HFS_RECDATA(record), &reclen);
581
582   if (bt_insert(&vol->cat, record, reclen) < 0)
583     return -1;
584
585   /* create thread record */
586
587   data.cdrType   = cdrThdRec;
588   data.cdrResrv2 = 0;
589
590   data.u.dthd.thdResrv[0] = 0;
591   data.u.dthd.thdResrv[1] = 0;
592   data.u.dthd.thdParID    = parid;
593   strcpy(data.u.dthd.thdCName, name);
594
595   r_makecatkey(&key, id, "");
596   r_packcatkey(&key, record, &reclen);
597   r_packcatdata(&data, HFS_RECDATA(record), &reclen);
598
599   if (bt_insert(&vol->cat, record, reclen) < 0 ||
600       v_adjvalence(vol, parid, 1, 1) < 0)
601     return -1;
602
603   return 0;
604 }
605
606 /*
607  * NAME:        markexts()
608  * DESCRIPTION: set bits from an extent record in the volume bitmap
609  */
610 static
611 void markexts(block *vbm, ExtDataRec *exts)
612 {
613   int i;
614   unsigned int start, len;
615
616   for (i = 0; i < 3; ++i)
617     {
618       for (start = (*exts)[i].xdrStABN,
619              len = (*exts)[i].xdrNumABlks; len--; ++start)
620         BMSET(vbm, start);
621     }
622 }
623
624 /*
625  * NAME:        vol->scavenge()
626  * DESCRIPTION: safeguard blocks in the volume bitmap
627  */
628 int v_scavenge(hfsvol *vol)
629 {
630   block *vbm = vol->vbm;
631   node n;
632   unsigned int pt, blks;
633
634   if (vbm == 0)
635     return 0;
636
637   markexts(vbm, &vol->mdb.drXTExtRec);
638   markexts(vbm, &vol->mdb.drCTExtRec);
639
640   vol->flags |= HFS_UPDATE_VBM;
641
642   /* scavenge the extents overflow file */
643
644   n.bt   = &vol->ext;
645   n.nnum = vol->ext.hdr.bthFNode;
646
647   if (n.nnum > 0)
648     {
649       if (bt_getnode(&n) < 0)
650         return -1;
651
652       n.rnum = 0;
653
654       for (;;)
655         {
656           ExtDataRec data;
657           unsigned char *ptr;
658
659           while (n.rnum >= (int)n.nd.ndNRecs)
660             {
661               n.nnum = n.nd.ndFLink;
662               if (n.nnum == 0)
663                 break;
664
665               if (bt_getnode(&n) < 0)
666                 return -1;
667
668               n.rnum = 0;
669             }
670
671           if (n.nnum == 0)
672             break;
673
674           ptr = HFS_NODEREC(n, n.rnum);
675           r_unpackextdata(HFS_RECDATA(ptr), &data);
676
677           markexts(vbm, &data);
678
679           ++n.rnum;
680         }
681     }
682
683   /* scavenge the catalog file */
684
685   n.bt   = &vol->cat;
686   n.nnum = vol->cat.hdr.bthFNode;
687
688   if (n.nnum > 0)
689     {
690       if (bt_getnode(&n) < 0)
691         return -1;
692
693       n.rnum = 0;
694
695       for (;;)
696         {
697           CatDataRec data;
698           unsigned char *ptr;
699
700           while (n.rnum >= (int)n.nd.ndNRecs)
701             {
702               n.nnum = n.nd.ndFLink;
703               if (n.nnum == 0)
704                 break;
705
706               if (bt_getnode(&n) < 0)
707                 return -1;
708
709               n.rnum = 0;
710             }
711
712           if (n.nnum == 0)
713             break;
714
715           ptr = HFS_NODEREC(n, n.rnum);
716           r_unpackcatdata(HFS_RECDATA(ptr), &data);
717
718           if (data.cdrType == cdrFilRec)
719             {
720               markexts(vbm, &data.u.fil.filExtRec);
721               markexts(vbm, &data.u.fil.filRExtRec);
722             }
723
724           ++n.rnum;
725         }
726     }
727
728   for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; )
729     {
730       if (! BMTST(vbm, pt))
731         ++blks;
732     }
733
734   if (vol->mdb.drFreeBks != blks)
735     {
736       vol->mdb.drFreeBks = blks;
737       vol->flags |= HFS_UPDATE_MDB;
738     }
739
740   return 0;
741 }