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