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