/[pearpc]/src/io/prom/fs/hfs/hfs.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /src/io/prom/fs/hfs/hfs.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Wed Sep 5 17:11:21 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 36150 byte(s)
import upstream CVS
1 dpavlin 1 /*
2     * libhfs - library for reading and writing Macintosh HFS volumes
3     * Copyright (C) 1996-1998 Robert Leslie
4     * Modified for use with PearPC (c) 2004 Stefan Weyergraf <stefan@weyergraf.de>
5     *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19     *
20     */
21    
22     # ifdef HAVE_CONFIG_H
23     # include "config.h"
24     # endif
25    
26     # include <stdlib.h>
27     # include <string.h>
28     # include <time.h>
29     # include <errno.h>
30    
31     # include "libhfs.h"
32     # include "data.h"
33     # include "block.h"
34     # include "medium.h"
35     # include "file.h"
36     # include "btree.h"
37     # include "node.h"
38     # include "record.h"
39     # include "volume.h"
40    
41     const char *hfs_error = "no error"; /* static error string */
42    
43     hfsvol *hfs_mounts; /* linked list of mounted volumes */
44    
45     static
46     hfsvol *curvol; /* current volume */
47    
48     /*
49     * NAME: validvname()
50     * DESCRIPTION: return true if parameter is a valid volume name
51     */
52     static
53     int validvname(const char *name)
54     {
55     int len;
56    
57     len = strlen(name);
58     if (len < 1)
59     ERROR(EINVAL, "volume name cannot be empty");
60     else if (len > HFS_MAX_VLEN)
61     ERROR(ENAMETOOLONG,
62     "volume name can be at most " STR(HFS_MAX_VLEN) " chars");
63    
64     if (strchr(name, '/'))
65     ERROR(EINVAL, "volume name may not contain slashes");
66    
67     return 1;
68    
69     fail:
70     return 0;
71     }
72    
73     /*
74     * NAME: getvol()
75     * DESCRIPTION: validate a volume reference
76     */
77     static
78     int getvol(hfsvol **vol)
79     {
80     if (*vol == 0)
81     {
82     if (curvol == 0)
83     ERROR(EINVAL, "no volume is current");
84    
85     *vol = curvol;
86     }
87    
88     return 0;
89    
90     fail:
91     return -1;
92     }
93    
94     /* High-Level Volume Routines ============================================== */
95    
96     /*
97     * NAME: hfs->mount()
98     * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
99     */
100     hfsvol *hfs_mount(const void *devicehandle, int pnum, int mode)
101     {
102     hfsvol *vol, *check;
103    
104     /* see if the volume is already mounted */
105    
106     for (check = hfs_mounts; check; check = check->next)
107     {
108     if (check->pnum == pnum && v_same(check, devicehandle) == 1)
109     {
110     /* verify compatible read/write mode */
111    
112     if (((check->flags & HFS_VOL_READONLY) &&
113     ! (mode & HFS_MODE_RDWR)) ||
114     (! (check->flags & HFS_VOL_READONLY) &&
115     (mode & (HFS_MODE_RDWR | HFS_MODE_ANY))))
116     {
117     vol = check;
118     goto done;
119     }
120     }
121     }
122    
123     vol = ALLOC(hfsvol, 1);
124     if (vol == 0)
125     ERROR(ENOMEM, 0);
126    
127     v_init(vol, mode);
128    
129     /* open the medium */
130    
131     switch (mode & HFS_MODE_MASK)
132     {
133     case HFS_MODE_RDWR:
134     case HFS_MODE_ANY:
135     if (v_open(vol, devicehandle, HFS_MODE_RDWR) != -1)
136     break;
137    
138     if ((mode & HFS_MODE_MASK) == HFS_MODE_RDWR)
139     goto fail;
140    
141     case HFS_MODE_RDONLY:
142     default:
143     vol->flags |= HFS_VOL_READONLY;
144    
145     if (v_open(vol, devicehandle, HFS_MODE_RDONLY) == -1)
146     goto fail;
147     }
148    
149     /* mount the volume */
150    
151     if (v_geometry(vol, pnum) == -1 ||
152     v_mount(vol) == -1)
153     goto fail;
154    
155     /* add to linked list of volumes */
156    
157     vol->prev = 0;
158     vol->next = hfs_mounts;
159    
160     if (hfs_mounts)
161     hfs_mounts->prev = vol;
162    
163     hfs_mounts = vol;
164    
165     done:
166     ++vol->refs;
167     curvol = vol;
168    
169     return vol;
170    
171     fail:
172     if (vol)
173     {
174     v_close(vol);
175     FREE(vol);
176     }
177    
178     return 0;
179     }
180    
181     /*
182     * NAME: hfs->flush()
183     * DESCRIPTION: flush all pending changes to an HFS volume
184     */
185     int hfs_flush(hfsvol *vol)
186     {
187     hfsfile *file;
188    
189     if (getvol(&vol) == -1)
190     goto fail;
191    
192     for (file = vol->files; file; file = file->next)
193     {
194     if (f_flush(file) == -1)
195     goto fail;
196     }
197    
198     if (v_flush(vol) == -1)
199     goto fail;
200    
201     return 0;
202    
203     fail:
204     return -1;
205     }
206    
207     /*
208     * NAME: hfs->flushall()
209     * DESCRIPTION: flush all pending changes to all mounted HFS volumes
210     */
211     void hfs_flushall(void)
212     {
213     hfsvol *vol;
214    
215     for (vol = hfs_mounts; vol; vol = vol->next)
216     hfs_flush(vol);
217     }
218    
219     /*
220     * NAME: hfs->umount()
221     * DESCRIPTION: close an HFS volume
222     */
223     int hfs_umount(hfsvol *vol)
224     {
225     int result = 0;
226    
227     if (getvol(&vol) == -1)
228     goto fail;
229    
230     if (--vol->refs)
231     {
232     result = v_flush(vol);
233     goto done;
234     }
235    
236     /* close all open files and directories */
237    
238     while (vol->files)
239     {
240     if (hfs_close(vol->files) == -1)
241     result = -1;
242     }
243    
244     while (vol->dirs)
245     {
246     if (hfs_closedir(vol->dirs) == -1)
247     result = -1;
248     }
249    
250     /* close medium */
251    
252     if (v_close(vol) == -1)
253     result = -1;
254    
255     /* remove from linked list of volumes */
256    
257     if (vol->prev)
258     vol->prev->next = vol->next;
259     if (vol->next)
260     vol->next->prev = vol->prev;
261    
262     if (vol == hfs_mounts)
263     hfs_mounts = vol->next;
264     if (vol == curvol)
265     curvol = 0;
266    
267     FREE(vol);
268    
269     done:
270     return result;
271    
272     fail:
273     return -1;
274     }
275    
276     /*
277     * NAME: hfs->umountall()
278     * DESCRIPTION: unmount all mounted volumes
279     */
280     void hfs_umountall(void)
281     {
282     while (hfs_mounts)
283     hfs_umount(hfs_mounts);
284     }
285    
286     /*
287     * NAME: hfs->getvol()
288     * DESCRIPTION: return a pointer to a mounted volume
289     */
290     hfsvol *hfs_getvol(const char *name)
291     {
292     hfsvol *vol;
293    
294     if (name == 0)
295     return curvol;
296    
297     for (vol = hfs_mounts; vol; vol = vol->next)
298     {
299     if (d_relstring(name, vol->mdb.drVN) == 0)
300     return vol;
301     }
302    
303     return 0;
304     }
305    
306     /*
307     * NAME: hfs->setvol()
308     * DESCRIPTION: change the current volume
309     */
310     void hfs_setvol(hfsvol *vol)
311     {
312     curvol = vol;
313     }
314    
315     /*
316     * NAME: hfs->vstat()
317     * DESCRIPTION: return volume statistics
318     */
319     int hfs_vstat(hfsvol *vol, hfsvolent *ent)
320     {
321     if (getvol(&vol) == -1)
322     goto fail;
323    
324     strcpy(ent->name, vol->mdb.drVN);
325    
326     ent->flags = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0;
327    
328     ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
329     ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz;
330    
331     ent->alblocksz = vol->mdb.drAlBlkSiz;
332     ent->clumpsz = vol->mdb.drClpSiz;
333    
334     ent->numfiles = vol->mdb.drFilCnt;
335     ent->numdirs = vol->mdb.drDirCnt;
336    
337     ent->crdate = d_ltime(vol->mdb.drCrDate);
338     ent->mddate = d_ltime(vol->mdb.drLsMod);
339     ent->bkdate = d_ltime(vol->mdb.drVolBkUp);
340    
341     ent->blessed = vol->mdb.drFndrInfo[0];
342    
343     return 0;
344    
345     fail:
346     return -1;
347     }
348    
349     /*
350     * NAME: hfs->vsetattr()
351     * DESCRIPTION: change volume attributes
352     */
353     int hfs_vsetattr(hfsvol *vol, hfsvolent *ent)
354     {
355     if (getvol(&vol) == -1)
356     goto fail;
357    
358     if (ent->clumpsz % vol->mdb.drAlBlkSiz != 0)
359     ERROR(EINVAL, "illegal clump size");
360    
361     /* make sure "blessed" folder exists */
362    
363     if (ent->blessed &&
364     v_getdthread(vol, ent->blessed, 0, 0) <= 0)
365     ERROR(EINVAL, "illegal blessed folder");
366    
367     if (vol->flags & HFS_VOL_READONLY)
368     ERROR(EROFS, 0);
369    
370     vol->mdb.drClpSiz = ent->clumpsz;
371    
372     vol->mdb.drCrDate = d_mtime(ent->crdate);
373     vol->mdb.drLsMod = d_mtime(ent->mddate);
374     vol->mdb.drVolBkUp = d_mtime(ent->bkdate);
375    
376     vol->mdb.drFndrInfo[0] = ent->blessed;
377    
378     vol->flags |= HFS_VOL_UPDATE_MDB;
379    
380     return 0;
381    
382     fail:
383     return -1;
384     }
385    
386     /* High-Level Directory Routines =========================================== */
387    
388     /*
389     * NAME: hfs->chdir()
390     * DESCRIPTION: change current HFS directory
391     */
392     int hfs_chdir(hfsvol *vol, const char *path)
393     {
394     CatDataRec data;
395    
396     if (getvol(&vol) == -1 ||
397     v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
398     goto fail;
399    
400     if (data.cdrType != cdrDirRec)
401     ERROR(ENOTDIR, 0);
402    
403     vol->cwd = data.u.dir.dirDirID;
404    
405     return 0;
406    
407     fail:
408     return -1;
409     }
410    
411     /*
412     * NAME: hfs->getcwd()
413     * DESCRIPTION: return the current working directory ID
414     */
415     unsigned long hfs_getcwd(hfsvol *vol)
416     {
417     if (getvol(&vol) == -1)
418     return 0;
419    
420     return vol->cwd;
421     }
422    
423     /*
424     * NAME: hfs->setcwd()
425     * DESCRIPTION: set the current working directory ID
426     */
427     int hfs_setcwd(hfsvol *vol, unsigned long id)
428     {
429     if (getvol(&vol) == -1)
430     goto fail;
431    
432     if (id == vol->cwd)
433     goto done;
434    
435     /* make sure the directory exists */
436    
437     if (v_getdthread(vol, id, 0, 0) <= 0)
438     goto fail;
439    
440     vol->cwd = id;
441    
442     done:
443     return 0;
444    
445     fail:
446     return -1;
447     }
448    
449     /*
450     * NAME: hfs->dirinfo()
451     * DESCRIPTION: given a directory ID, return its (name and) parent ID
452     */
453     int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name)
454     {
455     CatDataRec thread;
456    
457     if (getvol(&vol) == -1 ||
458     v_getdthread(vol, *id, &thread, 0) <= 0)
459     goto fail;
460    
461     *id = thread.u.dthd.thdParID;
462    
463     if (name)
464     strcpy(name, thread.u.dthd.thdCName);
465    
466     return 0;
467    
468     fail:
469     return -1;
470     }
471    
472     /*
473     * NAME: hfs->opendir()
474     * DESCRIPTION: prepare to read the contents of a directory
475     */
476     hfsdir *hfs_opendir(hfsvol *vol, const char *path)
477     {
478     hfsdir *dir = 0;
479     CatKeyRec key;
480     CatDataRec data;
481     byte pkey[HFS_CATKEYLEN];
482    
483     if (getvol(&vol) == -1)
484     goto fail;
485    
486     dir = ALLOC(hfsdir, 1);
487     if (dir == 0)
488     ERROR(ENOMEM, 0);
489    
490     dir->vol = vol;
491    
492     if (*path == 0)
493     {
494     /* meta-directory containing root dirs from all mounted volumes */
495    
496     dir->dirid = 0;
497     dir->vptr = hfs_mounts;
498     }
499     else
500     {
501     if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
502     goto fail;
503    
504     if (data.cdrType != cdrDirRec)
505     ERROR(ENOTDIR, 0);
506    
507     dir->dirid = data.u.dir.dirDirID;
508     dir->vptr = 0;
509    
510     r_makecatkey(&key, dir->dirid, "");
511     r_packcatkey(&key, pkey, 0);
512    
513     if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
514     goto fail;
515     }
516    
517     dir->prev = 0;
518     dir->next = vol->dirs;
519    
520     if (vol->dirs)
521     vol->dirs->prev = dir;
522    
523     vol->dirs = dir;
524    
525     return dir;
526    
527     fail:
528     FREE(dir);
529     return 0;
530     }
531    
532     /*
533     * NAME: hfs->opendir_by_id()
534     * DESCRIPTION: prepare to read the contents of a directory
535     */
536     hfsdir *hfs_opendir_by_id(hfsvol *vol, int id)
537     {
538     hfsdir *dir = 0;
539     CatKeyRec key;
540     /* CatDataRec data;*/
541     byte pkey[HFS_CATKEYLEN];
542    
543     if (getvol(&vol) == -1)
544     goto fail;
545    
546     dir = ALLOC(hfsdir, 1);
547     if (dir == 0)
548     ERROR(ENOMEM, 0);
549    
550     dir->vol = vol;
551    
552     /* if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
553     goto fail;
554    
555     if (data.cdrType != cdrDirRec)
556     ERROR(ENOTDIR, 0);*/
557    
558     dir->dirid = id;
559     dir->vptr = 0;
560    
561     r_makecatkey(&key, dir->dirid, "");
562     r_packcatkey(&key, pkey, 0);
563    
564     if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
565     goto fail;
566    
567     dir->prev = 0;
568     dir->next = vol->dirs;
569    
570     if (vol->dirs)
571     vol->dirs->prev = dir;
572    
573     vol->dirs = dir;
574    
575     return dir;
576    
577     fail:
578     FREE(dir);
579     return 0;
580     }
581    
582     /*
583     * NAME: hfs->readdir()
584     * DESCRIPTION: return the next entry in the directory
585     */
586     int hfs_readdir(hfsdir *dir, hfsdirent *ent)
587     {
588     CatKeyRec key;
589     CatDataRec data;
590     const byte *ptr;
591    
592     if (dir->dirid == 0)
593     {
594     hfsvol *vol;
595     char cname[HFS_MAX_FLEN + 1];
596    
597     for (vol = hfs_mounts; vol; vol = vol->next)
598     {
599     if (vol == dir->vptr)
600     break;
601     }
602    
603     if (vol == 0)
604     ERROR(ENOENT, "no more entries");
605    
606     if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 ||
607     v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
608     &data, cname, 0) <= 0)
609     goto fail;
610    
611     r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
612    
613     dir->vptr = vol->next;
614    
615     goto done;
616     }
617    
618     if (dir->n.rnum == -1)
619     ERROR(ENOENT, "no more entries");
620    
621     while (1)
622     {
623     ++dir->n.rnum;
624    
625     while (dir->n.rnum >= dir->n.nd.ndNRecs)
626     {
627     if (dir->n.nd.ndFLink == 0)
628     {
629     dir->n.rnum = -1;
630     ERROR(ENOENT, "no more entries");
631     }
632    
633     if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1)
634     {
635     dir->n.rnum = -1;
636     goto fail;
637     }
638    
639     dir->n.rnum = 0;
640     }
641    
642     ptr = HFS_NODEREC(dir->n, dir->n.rnum);
643    
644     r_unpackcatkey(ptr, &key);
645    
646     if (key.ckrParID != dir->dirid)
647     {
648     dir->n.rnum = -1;
649     ERROR(ENOENT, "no more entries");
650     }
651    
652     r_unpackcatdata(HFS_RECDATA(ptr), &data);
653    
654     switch (data.cdrType)
655     {
656     case cdrDirRec:
657     case cdrFilRec:
658     r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
659     goto done;
660    
661     case cdrThdRec:
662     case cdrFThdRec:
663     break;
664    
665     default:
666     dir->n.rnum = -1;
667     ERROR(EIO, "unexpected directory entry found");
668     }
669     }
670    
671     done:
672     return 0;
673    
674     fail:
675     return -1;
676     }
677    
678     /*
679     * NAME: hfs->closedir()
680     * DESCRIPTION: stop reading a directory
681     */
682     int hfs_closedir(hfsdir *dir)
683     {
684     hfsvol *vol = dir->vol;
685    
686     if (dir->prev)
687     dir->prev->next = dir->next;
688     if (dir->next)
689     dir->next->prev = dir->prev;
690     if (dir == vol->dirs)
691     vol->dirs = dir->next;
692    
693     FREE(dir);
694    
695     return 0;
696     }
697    
698     /* High-Level File Routines ================================================ */
699    
700     /*
701     * NAME: hfs->create()
702     * DESCRIPTION: create and open a new file
703     */
704     hfsfile *hfs_create(hfsvol *vol, const char *path,
705     const char *type, const char *creator)
706     {
707     hfsfile *file = 0;
708     unsigned long parid;
709     char name[HFS_MAX_FLEN + 1];
710     CatKeyRec key;
711     byte record[HFS_MAX_CATRECLEN];
712     unsigned reclen;
713     int found;
714    
715     if (getvol(&vol) == -1)
716     goto fail;
717    
718     file = ALLOC(hfsfile, 1);
719     if (file == 0)
720     ERROR(ENOMEM, 0);
721    
722     found = v_resolve(&vol, path, &file->cat, &parid, name, 0);
723     if (found == -1 || parid == 0)
724     goto fail;
725    
726     if (found)
727     ERROR(EEXIST, 0);
728    
729     if (parid == HFS_CNID_ROOTPAR)
730     ERROR(EINVAL, 0);
731    
732     if (vol->flags & HFS_VOL_READONLY)
733     ERROR(EROFS, 0);
734    
735     /* create file `name' in parent `parid' */
736    
737     if (bt_space(&vol->cat, 1) == -1)
738     goto fail;
739    
740     f_init(file, vol, vol->mdb.drNxtCNID++, name);
741     vol->flags |= HFS_VOL_UPDATE_MDB;
742    
743     file->parid = parid;
744    
745     /* create catalog record */
746    
747     file->cat.u.fil.filUsrWds.fdType =
748     d_getsl((const unsigned char *) type);
749     file->cat.u.fil.filUsrWds.fdCreator =
750     d_getsl((const unsigned char *) creator);
751    
752     file->cat.u.fil.filCrDat = d_mtime(time(0));
753     file->cat.u.fil.filMdDat = file->cat.u.fil.filCrDat;
754    
755     r_makecatkey(&key, file->parid, file->name);
756     r_packcatrec(&key, &file->cat, record, &reclen);
757    
758     if (bt_insert(&vol->cat, record, reclen) == -1 ||
759     v_adjvalence(vol, file->parid, 0, 1) == -1)
760     goto fail;
761    
762     /* package file handle for user */
763    
764     file->next = vol->files;
765    
766     if (vol->files)
767     vol->files->prev = file;
768    
769     vol->files = file;
770    
771     return file;
772    
773     fail:
774     FREE(file);
775     return 0;
776     }
777    
778     /*
779     * NAME: hfs->open()
780     * DESCRIPTION: prepare a file for I/O
781     */
782     hfsfile *hfs_open(hfsvol *vol, const char *path)
783     {
784     hfsfile *file = 0;
785    
786     if (getvol(&vol) == -1)
787     goto fail;
788    
789     file = ALLOC(hfsfile, 1);
790     if (file == 0)
791     ERROR(ENOMEM, 0);
792    
793     if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0)
794     goto fail;
795    
796     if (file->cat.cdrType != cdrFilRec)
797     ERROR(EISDIR, 0);
798    
799     /* package file handle for user */
800    
801     file->vol = vol;
802     file->flags = 0;
803    
804     f_selectfork(file, fkData);
805    
806     file->prev = 0;
807     file->next = vol->files;
808    
809     if (vol->files)
810     vol->files->prev = file;
811    
812     vol->files = file;
813    
814     return file;
815    
816     fail:
817     FREE(file);
818     return 0;
819     }
820    
821     /*
822     * NAME: hfs->open_by_id()
823     * DESCRIPTION: prepare a file for I/O
824     */
825     hfsfile *hfs_open_by_dirent(hfsvol *vol, hfsdirent *dirent)
826     {
827     hfsfile *file = 0;
828     int found;
829    
830     if (getvol(&vol) == -1)
831     goto fail;
832    
833     file = ALLOC(hfsfile, 1);
834     if (file == 0)
835     ERROR(ENOMEM, 0);
836    
837     memcpy(file->name, dirent->name, 31);
838     file->name[31] = 0;
839     file->parid = dirent->parid;
840    
841     found = v_catsearch(vol, file->parid, file->name, &file->cat, 0, NULL);
842     if (found != 1)
843     goto fail;
844    
845     if (file->cat.cdrType != cdrFilRec)
846     ERROR(EISDIR, 0);
847    
848     /* package file handle for user */
849    
850     file->vol = vol;
851     file->flags = 0;
852    
853     f_selectfork(file, fkData);
854    
855     file->prev = 0;
856     file->next = vol->files;
857    
858     if (vol->files)
859     vol->files->prev = file;
860    
861     vol->files = file;
862    
863     return file;
864    
865     fail:
866     FREE(file);
867     return 0;
868     }
869    
870     /*
871     * NAME: hfs->setfork()
872     * DESCRIPTION: select file fork for I/O operations
873     */
874     int hfs_setfork(hfsfile *file, int fork)
875     {
876     int result = 0;
877    
878     if (f_trunc(file) == -1)
879     result = -1;
880    
881     f_selectfork(file, fork ? fkRsrc : fkData);
882    
883     return result;
884     }
885    
886     /*
887     * NAME: hfs->getfork()
888     * DESCRIPTION: return the current fork for I/O operations
889     */
890     int hfs_getfork(hfsfile *file)
891     {
892     return file->fork != fkData;
893     }
894    
895     /*
896     * NAME: hfs->read()
897     * DESCRIPTION: read from an open file
898     */
899     unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len)
900     {
901     unsigned long *lglen, count;
902     byte *ptr = buf;
903    
904     f_getptrs(file, 0, &lglen, 0);
905    
906     if (file->pos + len > *lglen)
907     len = *lglen - file->pos;
908    
909     count = len;
910     while (count)
911     {
912     unsigned long bnum, offs, chunk;
913    
914     bnum = file->pos >> HFS_BLOCKSZ_BITS;
915     offs = file->pos & (HFS_BLOCKSZ - 1);
916    
917     chunk = HFS_BLOCKSZ - offs;
918     if (chunk > count)
919     chunk = count;
920    
921     if (offs == 0 && chunk == HFS_BLOCKSZ)
922     {
923     if (f_getblock(file, bnum, (block *) ptr) == -1)
924     goto fail;
925     }
926     else
927     {
928     block b;
929    
930     if (f_getblock(file, bnum, &b) == -1)
931     goto fail;
932    
933     memcpy(ptr, b + offs, chunk);
934     }
935    
936     ptr += chunk;
937    
938     file->pos += chunk;
939     count -= chunk;
940     }
941    
942     return len;
943    
944     fail:
945     return -1;
946     }
947    
948     /*
949     * NAME: hfs->write()
950     * DESCRIPTION: write to an open file
951     */
952     unsigned long hfs_write(hfsfile *file, const void *buf, unsigned long len)
953     {
954     unsigned long *lglen, *pylen, count;
955     const byte *ptr = buf;
956    
957     if (file->vol->flags & HFS_VOL_READONLY)
958     ERROR(EROFS, 0);
959    
960     f_getptrs(file, 0, &lglen, &pylen);
961    
962     count = len;
963    
964     /* set flag to update (at least) the modification time */
965    
966     if (count)
967     {
968     file->cat.u.fil.filMdDat = d_mtime(time(0));
969     file->flags |= HFS_FILE_UPDATE_CATREC;
970     }
971    
972     while (count)
973     {
974     unsigned long bnum, offs, chunk;
975    
976     bnum = file->pos >> HFS_BLOCKSZ_BITS;
977     offs = file->pos & (HFS_BLOCKSZ - 1);
978    
979     chunk = HFS_BLOCKSZ - offs;
980     if (chunk > count)
981     chunk = count;
982    
983     if (file->pos + chunk > *pylen)
984     {
985     if (bt_space(&file->vol->ext, 1) == -1 ||
986     f_alloc(file) == -1)
987     goto fail;
988     }
989    
990     if (offs == 0 && chunk == HFS_BLOCKSZ)
991     {
992     if (f_putblock(file, bnum, (block *) ptr) == -1)
993     goto fail;
994     }
995     else
996     {
997     block b;
998    
999     if (f_getblock(file, bnum, &b) == -1)
1000     goto fail;
1001    
1002     memcpy(b + offs, ptr, chunk);
1003    
1004     if (f_putblock(file, bnum, &b) == -1)
1005     goto fail;
1006     }
1007    
1008     ptr += chunk;
1009    
1010     file->pos += chunk;
1011     count -= chunk;
1012    
1013     if (file->pos > *lglen)
1014     *lglen = file->pos;
1015     }
1016    
1017     return len;
1018    
1019     fail:
1020     return -1;
1021     }
1022    
1023     /*
1024     * NAME: hfs->truncate()
1025     * DESCRIPTION: truncate an open file
1026     */
1027     int hfs_truncate(hfsfile *file, unsigned long len)
1028     {
1029     unsigned long *lglen;
1030    
1031     f_getptrs(file, 0, &lglen, 0);
1032    
1033     if (*lglen > len)
1034     {
1035     if (file->vol->flags & HFS_VOL_READONLY)
1036     ERROR(EROFS, 0);
1037    
1038     *lglen = len;
1039    
1040     file->cat.u.fil.filMdDat = d_mtime(time(0));
1041     file->flags |= HFS_FILE_UPDATE_CATREC;
1042    
1043     if (file->pos > len)
1044     file->pos = len;
1045     }
1046    
1047     return 0;
1048    
1049     fail:
1050     return -1;
1051     }
1052    
1053     /*
1054     * NAME: hfs->seek()
1055     * DESCRIPTION: change file seek pointer
1056     */
1057     unsigned long hfs_seek(hfsfile *file, long offset, int from)
1058     {
1059     unsigned long *lglen, newpos;
1060    
1061     f_getptrs(file, 0, &lglen, 0);
1062    
1063     switch (from)
1064     {
1065     case HFS_SEEK_SET:
1066     newpos = (offset < 0) ? 0 : offset;
1067     break;
1068    
1069     case HFS_SEEK_CUR:
1070     if (offset < 0 && (unsigned long) -offset > file->pos)
1071     newpos = 0;
1072     else
1073     newpos = file->pos + offset;
1074     break;
1075    
1076     case HFS_SEEK_END:
1077     if (offset < 0 && (unsigned long) -offset > *lglen)
1078     newpos = 0;
1079     else
1080     newpos = *lglen + offset;
1081     break;
1082    
1083     default:
1084     ERROR(EINVAL, 0);
1085     }
1086    
1087     if (newpos > *lglen)
1088     newpos = *lglen;
1089    
1090     file->pos = newpos;
1091    
1092     return newpos;
1093    
1094     fail:
1095     return -1;
1096     }
1097    
1098     /*
1099     * NAME: hfs->close()
1100     * DESCRIPTION: close a file
1101     */
1102     int hfs_close(hfsfile *file)
1103     {
1104     hfsvol *vol = file->vol;
1105     int result = 0;
1106    
1107     if (f_trunc(file) == -1 ||
1108     f_flush(file) == -1)
1109     result = -1;
1110    
1111     if (file->prev)
1112     file->prev->next = file->next;
1113     if (file->next)
1114     file->next->prev = file->prev;
1115     if (file == vol->files)
1116     vol->files = file->next;
1117    
1118     FREE(file);
1119    
1120     return result;
1121     }
1122    
1123     /* High-Level Catalog Routines ============================================= */
1124    
1125     /*
1126     * NAME: hfs->stat()
1127     * DESCRIPTION: return catalog information for an arbitrary path
1128     */
1129     int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent)
1130     {
1131     CatDataRec data;
1132     unsigned long parid;
1133     char name[HFS_MAX_FLEN + 1];
1134    
1135     if (getvol(&vol) == -1 ||
1136     v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1137     goto fail;
1138    
1139     r_unpackdirent(parid, name, &data, ent);
1140    
1141     return 0;
1142    
1143     fail:
1144     return -1;
1145     }
1146    
1147     /*
1148     * NAME: hfs->fstat()
1149     * DESCRIPTION: return catalog information for an open file
1150     */
1151     int hfs_fstat(hfsfile *file, hfsdirent *ent)
1152     {
1153     r_unpackdirent(file->parid, file->name, &file->cat, ent);
1154    
1155     return 0;
1156     }
1157    
1158     /*
1159     * NAME: hfs->setattr()
1160     * DESCRIPTION: change a file's attributes
1161     */
1162     int hfs_setattr(hfsvol *vol, const char *path, const hfsdirent *ent)
1163     {
1164     CatDataRec data;
1165     node n;
1166    
1167     if (getvol(&vol) == -1 ||
1168     v_resolve(&vol, path, &data, 0, 0, &n) <= 0)
1169     goto fail;
1170    
1171     if (vol->flags & HFS_VOL_READONLY)
1172     ERROR(EROFS, 0);
1173    
1174     r_packdirent(&data, ent);
1175    
1176     return v_putcatrec(&data, &n);
1177    
1178     fail:
1179     return -1;
1180     }
1181    
1182     /*
1183     * NAME: hfs->fsetattr()
1184     * DESCRIPTION: change an open file's attributes
1185     */
1186     int hfs_fsetattr(hfsfile *file, const hfsdirent *ent)
1187     {
1188     if (file->vol->flags & HFS_VOL_READONLY)
1189     ERROR(EROFS, 0);
1190    
1191     r_packdirent(&file->cat, ent);
1192    
1193     file->flags |= HFS_FILE_UPDATE_CATREC;
1194    
1195     return 0;
1196    
1197     fail:
1198     return -1;
1199     }
1200    
1201     /*
1202     * NAME: hfs->mkdir()
1203     * DESCRIPTION: create a new directory
1204     */
1205     int hfs_mkdir(hfsvol *vol, const char *path)
1206     {
1207     CatDataRec data;
1208     unsigned long parid;
1209     char name[HFS_MAX_FLEN + 1];
1210     int found;
1211    
1212     if (getvol(&vol) == -1)
1213     goto fail;
1214    
1215     found = v_resolve(&vol, path, &data, &parid, name, 0);
1216     if (found == -1 || parid == 0)
1217     goto fail;
1218    
1219     if (found)
1220     ERROR(EEXIST, 0);
1221    
1222     if (parid == HFS_CNID_ROOTPAR)
1223     ERROR(EINVAL, 0);
1224    
1225     if (vol->flags & HFS_VOL_READONLY)
1226     ERROR(EROFS, 0);
1227    
1228     return v_mkdir(vol, parid, name);
1229    
1230     fail:
1231     return -1;
1232     }
1233    
1234     /*
1235     * NAME: hfs->rmdir()
1236     * DESCRIPTION: delete an empty directory
1237     */
1238     int hfs_rmdir(hfsvol *vol, const char *path)
1239     {
1240     CatKeyRec key;
1241     CatDataRec data;
1242     unsigned long parid;
1243     char name[HFS_MAX_FLEN + 1];
1244     byte pkey[HFS_CATKEYLEN];
1245    
1246     if (getvol(&vol) == -1 ||
1247     v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1248     goto fail;
1249    
1250     if (data.cdrType != cdrDirRec)
1251     ERROR(ENOTDIR, 0);
1252    
1253     if (data.u.dir.dirVal != 0)
1254     ERROR(ENOTEMPTY, 0);
1255    
1256     if (parid == HFS_CNID_ROOTPAR)
1257     ERROR(EINVAL, 0);
1258    
1259     if (vol->flags & HFS_VOL_READONLY)
1260     ERROR(EROFS, 0);
1261    
1262     /* delete directory record */
1263    
1264     r_makecatkey(&key, parid, name);
1265     r_packcatkey(&key, pkey, 0);
1266    
1267     if (bt_delete(&vol->cat, pkey) == -1)
1268     goto fail;
1269    
1270     /* delete thread record */
1271    
1272     r_makecatkey(&key, data.u.dir.dirDirID, "");
1273     r_packcatkey(&key, pkey, 0);
1274    
1275     if (bt_delete(&vol->cat, pkey) == -1 ||
1276     v_adjvalence(vol, parid, 1, -1) == -1)
1277     goto fail;
1278    
1279     return 0;
1280    
1281     fail:
1282     return -1;
1283     }
1284    
1285     /*
1286     * NAME: hfs->delete()
1287     * DESCRIPTION: remove both forks of a file
1288     */
1289     int hfs_delete(hfsvol *vol, const char *path)
1290     {
1291     hfsfile file;
1292     CatKeyRec key;
1293     byte pkey[HFS_CATKEYLEN];
1294     int found;
1295    
1296     if (getvol(&vol) == -1 ||
1297     v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0)
1298     goto fail;
1299    
1300     if (file.cat.cdrType != cdrFilRec)
1301     ERROR(EISDIR, 0);
1302    
1303     if (file.parid == HFS_CNID_ROOTPAR)
1304     ERROR(EINVAL, 0);
1305    
1306     if (vol->flags & HFS_VOL_READONLY)
1307     ERROR(EROFS, 0);
1308    
1309     /* free allocation blocks */
1310    
1311     file.vol = vol;
1312     file.flags = 0;
1313    
1314     file.cat.u.fil.filLgLen = 0;
1315     file.cat.u.fil.filRLgLen = 0;
1316    
1317     f_selectfork(&file, fkData);
1318     if (f_trunc(&file) == -1)
1319     goto fail;
1320    
1321     f_selectfork(&file, fkRsrc);
1322     if (f_trunc(&file) == -1)
1323     goto fail;
1324    
1325     /* delete file record */
1326    
1327     r_makecatkey(&key, file.parid, file.name);
1328     r_packcatkey(&key, pkey, 0);
1329    
1330     if (bt_delete(&vol->cat, pkey) == -1 ||
1331     v_adjvalence(vol, file.parid, 0, -1) == -1)
1332     goto fail;
1333    
1334     /* delete file thread, if any */
1335    
1336     found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0);
1337     if (found == -1)
1338     goto fail;
1339    
1340     if (found)
1341     {
1342     r_makecatkey(&key, file.cat.u.fil.filFlNum, "");
1343     r_packcatkey(&key, pkey, 0);
1344    
1345     if (bt_delete(&vol->cat, pkey) == -1)
1346     goto fail;
1347     }
1348    
1349     return 0;
1350    
1351     fail:
1352     return -1;
1353     }
1354    
1355     /*
1356     * NAME: hfs->rename()
1357     * DESCRIPTION: change the name of and/or move a file or directory
1358     */
1359     int hfs_rename(hfsvol *vol, const char *srcpath, const char *dstpath)
1360     {
1361     hfsvol *srcvol;
1362     CatDataRec src, dst;
1363     unsigned long srcid, dstid;
1364     CatKeyRec key;
1365     char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1];
1366     byte record[HFS_MAX_CATRECLEN];
1367     unsigned int reclen;
1368     int found, isdir, moving;
1369     node n;
1370    
1371     if (getvol(&vol) == -1 ||
1372     v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0)
1373     goto fail;
1374    
1375     isdir = (src.cdrType == cdrDirRec);
1376     srcvol = vol;
1377    
1378     found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0);
1379     if (found == -1)
1380     goto fail;
1381    
1382     if (vol != srcvol)
1383     ERROR(EINVAL, "can't move across volumes");
1384    
1385     if (dstid == 0)
1386     ERROR(ENOENT, "bad destination path");
1387    
1388     if (found &&
1389     dst.cdrType == cdrDirRec &&
1390     dst.u.dir.dirDirID != src.u.dir.dirDirID)
1391     {
1392     dstid = dst.u.dir.dirDirID;
1393     strcpy(dstname, srcname);
1394    
1395     found = v_catsearch(vol, dstid, dstname, 0, 0, 0);
1396     if (found == -1)
1397     goto fail;
1398     }
1399    
1400     moving = (srcid != dstid);
1401    
1402     if (found)
1403     {
1404     const char *ptr;
1405    
1406     ptr = strrchr(dstpath, '/');
1407     if (ptr == 0)
1408     ptr = dstpath;
1409     else
1410     ++ptr;
1411    
1412     if (*ptr)
1413     strcpy(dstname, ptr);
1414    
1415     if (! moving && strcmp(srcname, dstname) == 0)
1416     goto done; /* source and destination are identical */
1417    
1418     if (moving || d_relstring(srcname, dstname))
1419     ERROR(EEXIST, "can't use destination name");
1420     }
1421    
1422     /* can't move anything into the root directory's parent */
1423    
1424     if (moving && dstid == HFS_CNID_ROOTPAR)
1425     ERROR(EINVAL, "can't move above root directory");
1426    
1427     if (moving && isdir)
1428     {
1429     unsigned long id;
1430    
1431     /* can't move root directory anywhere */
1432    
1433     if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR)
1434     ERROR(EINVAL, "can't move root directory");
1435    
1436     /* make sure we aren't trying to move a directory inside itself */
1437    
1438     for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID)
1439     {
1440     if (id == src.u.dir.dirDirID)
1441     ERROR(EINVAL, "can't move directory inside itself");
1442    
1443     if (v_getdthread(vol, id, &dst, 0) <= 0)
1444     goto fail;
1445     }
1446     }
1447    
1448     if (vol->flags & HFS_VOL_READONLY)
1449     ERROR(EROFS, 0);
1450    
1451     /* change volume name */
1452    
1453     if (dstid == HFS_CNID_ROOTPAR)
1454     {
1455     if (! validvname(dstname))
1456     goto fail;
1457    
1458     strcpy(vol->mdb.drVN, dstname);
1459     vol->flags |= HFS_VOL_UPDATE_MDB;
1460     }
1461    
1462     /* remove source record */
1463    
1464     r_makecatkey(&key, srcid, srcname);
1465     r_packcatkey(&key, record, 0);
1466    
1467     if (bt_delete(&vol->cat, record) == -1)
1468     goto fail;
1469    
1470     /* insert destination record */
1471    
1472     r_makecatkey(&key, dstid, dstname);
1473     r_packcatrec(&key, &src, record, &reclen);
1474    
1475     if (bt_insert(&vol->cat, record, reclen) == -1)
1476     goto fail;
1477    
1478     /* update thread record */
1479    
1480     if (isdir)
1481     {
1482     if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0)
1483     goto fail;
1484    
1485     dst.u.dthd.thdParID = dstid;
1486     strcpy(dst.u.dthd.thdCName, dstname);
1487    
1488     if (v_putcatrec(&dst, &n) == -1)
1489     goto fail;
1490     }
1491     else
1492     {
1493     found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n);
1494     if (found == -1)
1495     goto fail;
1496    
1497     if (found)
1498     {
1499     dst.u.fthd.fthdParID = dstid;
1500     strcpy(dst.u.fthd.fthdCName, dstname);
1501    
1502     if (v_putcatrec(&dst, &n) == -1)
1503     goto fail;
1504     }
1505     }
1506    
1507     /* update directory valences */
1508    
1509     if (moving)
1510     {
1511     if (v_adjvalence(vol, srcid, isdir, -1) == -1 ||
1512     v_adjvalence(vol, dstid, isdir, 1) == -1)
1513     goto fail;
1514     }
1515    
1516     done:
1517     return 0;
1518    
1519     fail:
1520     return -1;
1521     }
1522    
1523     /* High-Level Media Routines =============================================== */
1524    
1525     /*
1526     * NAME: hfs->mkpart()
1527     * DESCRIPTION: create a new HFS partition
1528     */
1529     int hfs_mkpart(const void *devicehandle, unsigned long len)
1530     {
1531     hfsvol vol;
1532    
1533     v_init(&vol, HFS_OPT_NOCACHE);
1534    
1535     if (v_open(&vol, devicehandle, HFS_MODE_RDWR) == -1)
1536     goto fail;
1537    
1538     if (m_mkpart(&vol, "MacOS", "Apple_HFS", len) == -1)
1539     goto fail;
1540    
1541     if (v_close(&vol) == -1)
1542     goto fail;
1543    
1544     return 0;
1545    
1546     fail:
1547     v_close(&vol);
1548     return -1;
1549     }
1550    
1551     /*
1552     * NAME: hfs->nparts()
1553     * DESCRIPTION: return the number of HFS partitions in the medium
1554     */
1555     int hfs_nparts(const void *devicehandle)
1556     {
1557     hfsvol vol;
1558     int nparts, found;
1559     ApplePartition map;
1560     unsigned long bnum = 0;
1561    
1562     v_init(&vol, HFS_OPT_NOCACHE);
1563    
1564     if (v_open(&vol, devicehandle, HFS_MODE_RDONLY) == -1)
1565     goto fail;
1566    
1567     nparts = 0;
1568     while (1)
1569     {
1570     found = m_findpmentry(&vol, "Apple_HFS", &map, &bnum);
1571     if (found == -1)
1572     goto fail;
1573    
1574     if (! found)
1575     break;
1576    
1577     ++nparts;
1578     }
1579    
1580     if (v_close(&vol) == -1)
1581     goto fail;
1582    
1583     return nparts;
1584    
1585     fail:
1586     v_close(&vol);
1587     return -1;
1588     }
1589    
1590     /*
1591     * NAME: compare()
1592     * DESCRIPTION: comparison function for qsort of blocks to be spared
1593     */
1594     static
1595     int compare(const unsigned int *n1, const unsigned int *n2)
1596     {
1597     return *n1 - *n2;
1598     }
1599    
1600     /*
1601     * NAME: hfs->format()
1602     * DESCRIPTION: write a new filesystem
1603     */
1604     int hfs_format(const void *devicehandle, int pnum, int mode, const char *vname,
1605     unsigned int nbadblocks, const unsigned long badblocks[])
1606     {
1607     hfsvol vol;
1608     btree *ext = &vol.ext;
1609     btree *cat = &vol.cat;
1610     unsigned int i, *badalloc = 0;
1611    
1612     v_init(&vol, mode);
1613    
1614     if (! validvname(vname))
1615     goto fail;
1616    
1617     if (v_open(&vol, devicehandle, HFS_MODE_RDWR) == -1 ||
1618     v_geometry(&vol, pnum) == -1)
1619     goto fail;
1620    
1621     /* initialize volume geometry */
1622    
1623     vol.lpa = 1 + ((vol.vlen - 6) >> 16);
1624    
1625     if (vol.flags & HFS_OPT_2048)
1626     vol.lpa = (vol.lpa + 3) & ~3;
1627    
1628     vol.vbmsz = (vol.vlen / vol.lpa + 0x0fff) >> 12;
1629    
1630     vol.mdb.drSigWord = HFS_SIGWORD;
1631     vol.mdb.drCrDate = d_mtime(time(0));
1632     vol.mdb.drLsMod = vol.mdb.drCrDate;
1633     vol.mdb.drAtrb = 0;
1634     vol.mdb.drNmFls = 0;
1635     vol.mdb.drVBMSt = 3;
1636     vol.mdb.drAllocPtr = 0;
1637    
1638     vol.mdb.drAlBlkSiz = vol.lpa << HFS_BLOCKSZ_BITS;
1639     vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz << 2;
1640     vol.mdb.drAlBlSt = vol.mdb.drVBMSt + vol.vbmsz;
1641    
1642     if (vol.flags & HFS_OPT_2048)
1643     vol.mdb.drAlBlSt = ((vol.vstart & 3) + vol.mdb.drAlBlSt + 3) & ~3;
1644    
1645     vol.mdb.drNmAlBlks = (vol.vlen - 2 - vol.mdb.drAlBlSt) / vol.lpa;
1646    
1647     vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */
1648     vol.mdb.drFreeBks = vol.mdb.drNmAlBlks;
1649    
1650     strcpy(vol.mdb.drVN, vname);
1651    
1652     vol.mdb.drVolBkUp = 0;
1653     vol.mdb.drVSeqNum = 0;
1654     vol.mdb.drWrCnt = 0;
1655    
1656     vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz;
1657     vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz;
1658    
1659     vol.mdb.drNmRtDirs = 0;
1660     vol.mdb.drFilCnt = 0;
1661     vol.mdb.drDirCnt = -1; /* incremented when root directory is created */
1662    
1663     for (i = 0; i < 8; ++i)
1664     vol.mdb.drFndrInfo[i] = 0;
1665    
1666     vol.mdb.drEmbedSigWord = 0x0000;
1667     vol.mdb.drEmbedExtent.xdrStABN = 0;
1668     vol.mdb.drEmbedExtent.xdrNumABlks = 0;
1669    
1670     /* vol.mdb.drXTFlSize */
1671     /* vol.mdb.drCTFlSize */
1672    
1673     /* vol.mdb.drXTExtRec[0..2] */
1674     /* vol.mdb.drCTExtRec[0..2] */
1675    
1676     vol.flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB;
1677    
1678     /* initialize volume bitmap */
1679    
1680     vol.vbm = ALLOC(block, vol.vbmsz);
1681     if (vol.vbm == 0)
1682     ERROR(ENOMEM, 0);
1683    
1684     memset(vol.vbm, 0, vol.vbmsz << HFS_BLOCKSZ_BITS);
1685    
1686     vol.flags |= HFS_VOL_UPDATE_VBM;
1687    
1688     /* perform initial bad block sparing */
1689    
1690     if (nbadblocks > 0)
1691     {
1692     if (nbadblocks * 4 > vol.vlen)
1693     ERROR(EINVAL, "volume contains too many bad blocks");
1694    
1695     badalloc = ALLOC(unsigned int, nbadblocks);
1696     if (badalloc == 0)
1697     ERROR(ENOMEM, 0);
1698    
1699     if (vol.mdb.drNmAlBlks == 1594)
1700     vol.mdb.drFreeBks = --vol.mdb.drNmAlBlks;
1701    
1702     for (i = 0; i < nbadblocks; ++i)
1703     {
1704     unsigned long bnum;
1705     unsigned int anum;
1706    
1707     bnum = badblocks[i];
1708    
1709     if (bnum < vol.mdb.drAlBlSt || bnum == vol.vlen - 2)
1710     ERROR(EINVAL, "can't spare critical bad block");
1711     else if (bnum >= vol.vlen)
1712     ERROR(EINVAL, "bad block not in volume");
1713    
1714     anum = (bnum - vol.mdb.drAlBlSt) / vol.lpa;
1715    
1716     if (anum < vol.mdb.drNmAlBlks)
1717     BMSET(vol.vbm, anum);
1718    
1719     badalloc[i] = anum;
1720     }
1721    
1722     vol.mdb.drAtrb |= HFS_ATRB_BBSPARED;
1723     }
1724    
1725     /* create extents overflow file */
1726    
1727     n_init(&ext->hdrnd, ext, ndHdrNode, 0);
1728    
1729     ext->hdrnd.nnum = 0;
1730     ext->hdrnd.nd.ndNRecs = 3;
1731     ext->hdrnd.roff[1] = 0x078;
1732     ext->hdrnd.roff[2] = 0x0f8;
1733     ext->hdrnd.roff[3] = 0x1f8;
1734    
1735     memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128);
1736    
1737     ext->hdr.bthDepth = 0;
1738     ext->hdr.bthRoot = 0;
1739     ext->hdr.bthNRecs = 0;
1740     ext->hdr.bthFNode = 0;
1741     ext->hdr.bthLNode = 0;
1742     ext->hdr.bthNodeSize = HFS_BLOCKSZ;
1743     ext->hdr.bthKeyLen = 0x07;
1744     ext->hdr.bthNNodes = 0;
1745     ext->hdr.bthFree = 0;
1746     for (i = 0; i < 76; ++i)
1747     ext->hdr.bthResv[i] = 0;
1748    
1749     ext->map = ALLOC(byte, HFS_MAP1SZ);
1750     if (ext->map == 0)
1751     ERROR(ENOMEM, 0);
1752    
1753     memset(ext->map, 0, HFS_MAP1SZ);
1754     BMSET(ext->map, 0);
1755    
1756     ext->mapsz = HFS_MAP1SZ;
1757     ext->flags = HFS_BT_UPDATE_HDR;
1758    
1759     /* create catalog file */
1760    
1761     n_init(&cat->hdrnd, cat, ndHdrNode, 0);
1762    
1763     cat->hdrnd.nnum = 0;
1764     cat->hdrnd.nd.ndNRecs = 3;
1765     cat->hdrnd.roff[1] = 0x078;
1766     cat->hdrnd.roff[2] = 0x0f8;
1767     cat->hdrnd.roff[3] = 0x1f8;
1768    
1769     memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128);
1770    
1771     cat->hdr.bthDepth = 0;
1772     cat->hdr.bthRoot = 0;
1773     cat->hdr.bthNRecs = 0;
1774     cat->hdr.bthFNode = 0;
1775     cat->hdr.bthLNode = 0;
1776     cat->hdr.bthNodeSize = HFS_BLOCKSZ;
1777     cat->hdr.bthKeyLen = 0x25;
1778     cat->hdr.bthNNodes = 0;
1779     cat->hdr.bthFree = 0;
1780     for (i = 0; i < 76; ++i)
1781     cat->hdr.bthResv[i] = 0;
1782    
1783     cat->map = ALLOC(byte, HFS_MAP1SZ);
1784     if (cat->map == 0)
1785     ERROR(ENOMEM, 0);
1786    
1787     memset(cat->map, 0, HFS_MAP1SZ);
1788     BMSET(cat->map, 0);
1789    
1790     cat->mapsz = HFS_MAP1SZ;
1791     cat->flags = HFS_BT_UPDATE_HDR;
1792    
1793     /* allocate space for header nodes (and initial extents) */
1794    
1795     if (bt_space(ext, 1) == -1 ||
1796     bt_space(cat, 1) == -1)
1797     goto fail;
1798    
1799     --ext->hdr.bthFree;
1800     --cat->hdr.bthFree;
1801    
1802     /* create extent records for bad blocks */
1803    
1804     if (nbadblocks > 0)
1805     {
1806     hfsfile bbfile;
1807     ExtDescriptor extent;
1808     ExtDataRec *extrec;
1809     ExtKeyRec key;
1810     byte record[HFS_MAX_EXTRECLEN];
1811     unsigned int reclen;
1812    
1813     f_init(&bbfile, &vol, HFS_CNID_BADALLOC, "bad blocks");
1814    
1815     qsort(badalloc, nbadblocks, sizeof(*badalloc),
1816     (int (*)(const void *, const void *)) compare);
1817    
1818     for (i = 0; i < nbadblocks; ++i)
1819     {
1820     if (i == 0 || badalloc[i] != extent.xdrStABN)
1821     {
1822     extent.xdrStABN = badalloc[i];
1823     extent.xdrNumABlks = 1;
1824    
1825     if (extent.xdrStABN < vol.mdb.drNmAlBlks &&
1826     f_addextent(&bbfile, &extent) == -1)
1827     goto fail;
1828     }
1829     }
1830    
1831     /* flush local extents into extents overflow file */
1832    
1833     f_getptrs(&bbfile, &extrec, 0, 0);
1834    
1835     r_makeextkey(&key, bbfile.fork, bbfile.cat.u.fil.filFlNum, 0);
1836     r_packextrec(&key, extrec, record, &reclen);
1837    
1838     if (bt_insert(&vol.ext, record, reclen) == -1)
1839     goto fail;
1840     }
1841    
1842     vol.flags |= HFS_VOL_MOUNTED;
1843    
1844     /* create root directory */
1845    
1846     if (v_mkdir(&vol, HFS_CNID_ROOTPAR, vname) == -1)
1847     goto fail;
1848    
1849     vol.mdb.drNxtCNID = 16; /* first CNID not reserved by Apple */
1850    
1851     /* write boot blocks */
1852    
1853     if (m_zerobb(&vol) == -1)
1854     goto fail;
1855    
1856     /* zero other unused space, if requested */
1857    
1858     if (vol.flags & HFS_OPT_ZERO)
1859     {
1860     block b;
1861     unsigned long bnum;
1862    
1863     memset(&b, 0, sizeof(b));
1864    
1865     /* between MDB and VBM (never) */
1866    
1867     for (bnum = 3; bnum < vol.mdb.drVBMSt; ++bnum)
1868     b_writelb(&vol, bnum, &b);
1869    
1870     /* between VBM and first allocation block (sometimes if HFS_OPT_2048) */
1871    
1872     for (bnum = vol.mdb.drVBMSt + vol.vbmsz; bnum < vol.mdb.drAlBlSt; ++bnum)
1873     b_writelb(&vol, bnum, &b);
1874    
1875     /* between last allocation block and alternate MDB (sometimes) */
1876    
1877     for (bnum = vol.mdb.drAlBlSt + vol.mdb.drNmAlBlks * vol.lpa;
1878     bnum < vol.vlen - 2; ++bnum)
1879     b_writelb(&vol, bnum, &b);
1880    
1881     /* final block (always) */
1882    
1883     b_writelb(&vol, vol.vlen - 1, &b);
1884     }
1885    
1886     /* flush remaining state and close volume */
1887    
1888     if (v_close(&vol) == -1)
1889     goto fail;
1890    
1891     FREE(badalloc);
1892    
1893     return 0;
1894    
1895     fail:
1896     v_close(&vol);
1897    
1898     FREE(badalloc);
1899    
1900     return -1;
1901     }

  ViewVC Help
Powered by ViewVC 1.1.26