1 /* @(#)hfs.c 1.15 09/11/07 joerg */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)hfs.c 1.15 09/11/07 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 /* APPLE_HYB James Pearson j.pearson@ps.ucl.ac.uk 16/7/97 */
27
28 #include <schily/stdlib.h>
29 #include <schily/unistd.h>
30 #include <schily/fcntl.h>
31 #include <schily/errno.h>
32 #include <schily/string.h>
33 #include <schily/ctype.h>
34 #include <schily/stat.h>
35
36 #include "internal.h"
37 #include "data.h"
38 #include "block.h"
39 #include "low.h"
40 #include "file.h"
41 #include "btree.h"
42 #include "node.h"
43 #include "record.h"
44 #include "volume.h"
45
46 /* High-Level Volume Routines ============================================== */
47
48 /*
49 * NAME: hfs->mount()
50 * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
51 */
52 #ifdef APPLE_HYB
hfs_mount(hce,pnum,flags)53 hfsvol *hfs_mount(hce, pnum, flags)
54 hce_mem *hce;
55 int pnum;
56 int flags;
57 #else
58 hfsvol *hfs_mount(path, pnum, flags)
59 char *path;
60 int pnum;
61 int flags;
62 #endif /* APPLE_HYB */
63 {
64 #ifndef APPLE_HYB
65 struct stat dev;
66 #endif
67 hfsvol *vol = 0;
68
69 #ifndef APPLE_HYB
70 /* see if the volume is already mounted */
71
72 if (stat(path, &dev) >= 0)
73 {
74 struct stat mdev;
75 hfsvol *check;
76
77 for (check = hfs_mounts; check; check = check->next)
78 {
79 if (fstat(check->fd, &mdev) >= 0 &&
80 mdev.st_dev == dev.st_dev &&
81 mdev.st_ino == dev.st_ino &&
82 (check->pnum == 0 || check->pnum == pnum))
83 {
84 /* verify compatible read/write mode */
85
86 if (((check->flags & HFS_READONLY) &&
87 ! (flags & O_WRONLY)) ||
88 (! (check->flags & HFS_READONLY) &&
89 (flags & (O_WRONLY | O_RDWR))))
90 {
91 vol = check;
92 break;
93 }
94 }
95 }
96 }
97 #endif /* APPLE_HYB */
98 if (vol == 0)
99 {
100 vol = ALLOC(hfsvol, 1);
101 if (vol == 0)
102 {
103 ERROR(ENOMEM, 0);
104 return 0;
105 }
106
107 vol->flags = 0;
108 vol->pnum = pnum;
109 vol->vstart = 0;
110 vol->vlen = 0;
111 vol->lpa = 0;
112 vol->vbm = 0;
113 vol->cwd = HFS_CNID_ROOTDIR;
114
115 vol->refs = 0;
116 vol->files = 0;
117 vol->dirs = 0;
118 vol->prev = 0;
119 vol->next = 0;
120
121 vol->ext.map = 0;
122 vol->ext.mapsz = 0;
123 vol->ext.flags = 0;
124 vol->ext.compare = r_compareextkeys;
125
126 vol->cat.map = 0;
127 vol->cat.mapsz = 0;
128 vol->cat.flags = 0;
129 vol->cat.compare = r_comparecatkeys;
130
131 /* open and lock the device */
132
133 #ifdef APPLE_HYB
134 vol->fd = 3; /* any +ve number will do? */
135 vol->hce = hce; /* store the extra with the vol info */
136 #else
137 if (flags & (O_WRONLY | O_RDWR))
138 {
139 vol->fd = open(path, O_RDWR);
140 if (vol->fd >= 0 && l_lockvol(vol) < 0)
141 {
142 close(vol->fd);
143 vol->fd = -2;
144 }
145 }
146
147 if (! (flags & (O_WRONLY | O_RDWR)) ||
148 (vol->fd < 0 &&
149 (errno == EROFS || errno == EACCES || errno == EAGAIN) &&
150 (flags & O_RDWR)))
151 {
152 vol->flags |= HFS_READONLY;
153 vol->fd = open(path, O_RDONLY);
154 if (vol->fd >= 0 && l_lockvol(vol) < 0)
155 {
156 close(vol->fd);
157 vol->fd = -2;
158 }
159 }
160
161 if (vol->fd < 0)
162 {
163 if (vol->fd != -2)
164 ERROR(errno, "error opening device");
165
166 v_destruct(vol);
167
168 return 0;
169 }
170 #endif /* APPLE_HYB */
171
172 /* find out what kind of media this is and read the MDB */
173
174 if (l_readblock0(vol) < 0 ||
175 l_readmdb(vol) < 0)
176 {
177 #ifndef APPLE_HYB
178 close(vol->fd);
179 v_destruct(vol);
180 #endif /* APPLE_HYB */
181 return 0;
182 }
183
184 /* verify this is an HFS volume */
185
186 if (vol->mdb.drSigWord != 0x4244)
187 {
188 #ifndef APPLE_HYB
189 close(vol->fd);
190 #endif /* APPLE_HYB */
191 v_destruct(vol);
192
193 ERROR(EINVAL, "not a Macintosh HFS volume");
194 return 0;
195 }
196
197 /* do minimal consistency checks */
198
199 if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
200 {
201 #ifndef APPLE_HYB
202 close(vol->fd);
203 #endif /* APPLE_HYB */
204 v_destruct(vol);
205
206 ERROR(EINVAL, "bad volume allocation block size");
207 return 0;
208 }
209
210 if (vol->vlen == 0)
211 vol->vlen = vol->mdb.drAlBlSt +
212 vol->mdb.drNmAlBlks * (vol->mdb.drAlBlkSiz / HFS_BLOCKSZ) + 2;
213
214 /* read the volume bitmap and extents/catalog B*-tree headers */
215
216 if (l_readvbm(vol) < 0 ||
217 bt_readhdr(&vol->ext) < 0 ||
218 bt_readhdr(&vol->cat) < 0)
219 {
220 #ifndef APPLE_HYB
221 close(vol->fd);
222 #endif /* APPLE_HYB */
223 v_destruct(vol);
224 return 0;
225 }
226
227 if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
228 {
229 /* volume was not cleanly unmounted; scavenge free-space */
230
231 if (v_scavenge(vol) < 0)
232 {
233 #ifndef APPLE_HYB
234 close(vol->fd);
235 #endif /* APPLE_HYB */
236 v_destruct(vol);
237 return 0;
238 }
239 }
240
241 if (vol->flags & HFS_READONLY)
242 vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
243 else
244 vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
245
246 vol->prev = 0;
247 vol->next = hfs_mounts;
248
249 if (hfs_mounts)
250 hfs_mounts->prev = vol;
251
252 hfs_mounts = vol;
253 }
254
255 ++vol->refs;
256
257 return hfs_curvol = vol;
258 }
259
260 /*
261 * NAME: hfs->flush()
262 * DESCRIPTION: flush all pending changes to an HFS volume
263 */
hfs_flush(vol)264 int hfs_flush(vol)
265 hfsvol *vol;
266 {
267 hfsfile *file;
268
269 if (v_getvol(&vol) < 0)
270 return -1;
271
272 for (file = vol->files; file; file = file->next)
273 {
274 if (f_flush(file) < 0)
275 return -1;
276 }
277
278 if (v_flush(vol, 0) < 0)
279 return -1;
280
281 return 0;
282 }
283
284 /*
285 * NAME: hfs->flushall()
286 * DESCRIPTION: flush all pending changes to all mounted HFS volumes
287 */
hfs_flushall()288 void hfs_flushall()
289 {
290 hfsvol *vol;
291
292 for (vol = hfs_mounts; vol; vol = vol->next)
293 hfs_flush(vol);
294 }
295
296 /*
297 * NAME: hfs->umount()
298 * DESCRIPTION: close an HFS volume
299 */
300 #ifdef APPLE_HYB
301 /* extra argument used to alter the position of the extents/catalog files */
hfs_umount(vol,end,locked)302 int hfs_umount(vol, end, locked)
303 hfsvol *vol;
304 long end;
305 long locked;
306 #else
307 int hfs_umount(vol)
308 hfsvol *vol;
309 #endif /* APPLE_HYB */
310 {
311 int result = 0;
312
313 if (v_getvol(&vol) < 0)
314 return -1;
315
316 if (--vol->refs)
317 return v_flush(vol, 0);
318
319 /* close all open files and directories */
320
321 while (vol->files)
322 #ifdef APPLE_HYB
323 hfs_close(vol->files, 0, 0);
324 #else
325 hfs_close(vol->files);
326 #endif /* APPLE_HYB */
327
328 while (vol->dirs)
329 hfs_closedir(vol->dirs);
330
331 #ifdef APPLE_HYB
332 if (end)
333 {
334 /* move extents and catalog to end of volume ... */
335 long vbmsz = (vol->vlen / vol->lpa + 4095) / 4096;
336
337 /* we are adding this "files" to the end of the ISO volume,
338 so calculate this address in HFS speak ... */
339 /* end -= vol->mdb.drAlBlSt; */
340 end -= (vol->mdb.drAlBlSt + vol->hce->hfs_map_size);
341 end /= vol->lpa;
342
343 /* catalog file ... */
344 vol->ext.f.cat.u.fil.filExtRec[0].xdrStABN = end;
345 vol->mdb.drXTExtRec[0].xdrStABN = end;
346
347 /* move postition to start of extents file */
348 end += vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN;
349
350 /* extents file ... */
351 vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN = end;
352 vol->mdb.drCTExtRec[0].xdrStABN = end;
353
354 /* the volume bitmap is wrong as we have "moved" files
355 about - simple just set the whole lot (it's a readonly volume
356 anyway!) */
357 memset(vol->vbm, 0xff, vbmsz*HFS_BLOCKSZ);
358
359 /* set the free blocks to zero */
360 vol->mdb.drFreeBks = 0;
361
362 /* flag changes for flushing later */
363 vol->flags |= HFS_UPDATE_VBM;
364 vol->flags |= HFS_UPDATE_MDB;
365 vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
366 if (locked) {
367 vol->mdb.drAtrb |= HFS_ATRB_SLOCKED;
368 }
369 vol->ext.flags |= HFS_UPDATE_BTHDR;
370 vol->cat.flags |= HFS_UPDATE_BTHDR;
371 }
372 #endif /* APPLE_HYB */
373
374 if (v_flush(vol, 1) < 0)
375 result = -1;
376
377 #ifndef APPLE_HYB
378 if (close(vol->fd) < 0 && result == 0)
379 {
380 ERROR(errno, "error closing device");
381 result = -1;
382 }
383 #endif /* APPLE_HYB */
384
385 if (vol->prev)
386 vol->prev->next = vol->next;
387 if (vol->next)
388 vol->next->prev = vol->prev;
389
390 if (vol == hfs_mounts)
391 hfs_mounts = vol->next;
392 if (vol == hfs_curvol)
393 hfs_curvol = 0;
394
395 v_destruct(vol);
396
397 return result;
398 }
399
400 /*
401 * NAME: hfs->umountall()
402 * DESCRIPTION: unmount all mounted volumes
403 */
hfs_umountall()404 void hfs_umountall()
405 {
406 while (hfs_mounts)
407 #ifdef APPLE_HYB
408 hfs_umount(hfs_mounts, 0L, 0L);
409 #else
410 hfs_umount(hfs_mounts);
411 #endif /* APPLE_HYB */
412 }
413
414 /*
415 * NAME: hfs->getvol()
416 * DESCRIPTION: return a pointer to a mounted volume
417 */
hfs_getvol(name)418 hfsvol *hfs_getvol(name)
419 char *name;
420 {
421 hfsvol *vol;
422
423 if (name == 0)
424 return hfs_curvol;
425
426 for (vol = hfs_mounts; vol; vol = vol->next)
427 {
428 if (d_relstring(name, vol->mdb.drVN) == 0)
429 return vol;
430 }
431
432 return 0;
433 }
434
435 /*
436 * NAME: hfs->setvol()
437 * DESCRIPTION: change the current volume
438 */
hfs_setvol(vol)439 void hfs_setvol(vol)
440 hfsvol *vol;
441 {
442 hfs_curvol = vol;
443 }
444
445 /*
446 * NAME: hfs->vstat()
447 * DESCRIPTION: return volume statistics
448 */
hfs_vstat(vol,ent)449 int hfs_vstat(vol, ent)
450 hfsvol *vol;
451 hfsvolent *ent;
452 {
453 if (v_getvol(&vol) < 0)
454 return -1;
455
456 strcpy(ent->name, vol->mdb.drVN);
457
458 ent->flags = (vol->flags & HFS_READONLY) ? HFS_ISLOCKED : 0;
459 ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
460 ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz;
461 ent->crdate = d_toutime(vol->mdb.drCrDate);
462 ent->mddate = d_toutime(vol->mdb.drLsMod);
463
464 return 0;
465 }
466
467 /*
468 * NAME: hfs->format()
469 * DESCRIPTION: write a new filesystem
470 */
471 #ifdef APPLE_HYB
hfs_format(hce,pnum,vname)472 int hfs_format(hce, pnum, vname)
473 hce_mem *hce;
474 int pnum;
475 char *vname;
476 #else
477 int hfs_format(path, pnum, vname)
478 char *path;
479 int pnum;
480 char *vname;
481 #endif /* APPLE_HYB */
482 {
483 hfsvol vol;
484 btree *ext = &vol.ext;
485 btree *cat = &vol.cat;
486 unsigned int vbmsz;
487 int i, result = 0;
488 block vbm[16];
489 char *map;
490
491 if (strchr(vname, ':'))
492 {
493 ERROR(EINVAL, "volume name may not contain colons");
494 return -1;
495 }
496
497 i = strlen(vname);
498 if (i < 1 || i > HFS_MAX_VLEN)
499 {
500 ERROR(EINVAL, "volume name must be 1-27 chars");
501 return -1;
502 }
503
504 vol.flags = 0;
505 vol.pnum = pnum;
506 vol.vstart = 0;
507 vol.vlen = 0;
508 vol.lpa = 0;
509 vol.vbm = vbm;
510 vol.cwd = HFS_CNID_ROOTDIR;
511
512 vol.refs = 0;
513 vol.files = 0;
514 vol.dirs = 0;
515 vol.prev = 0;
516 vol.next = 0;
517
518 #ifndef APPLE_HYB
519 vol.fd = open(path, O_RDWR);
520 if (vol.fd < 0)
521 {
522 ERROR(errno, "error opening device for writing");
523 return -1;
524 }
525
526 if (l_lockvol(&vol) < 0)
527 {
528 close(vol.fd);
529 return -1;
530 }
531 #endif /* APPLE_HYB */
532 if (pnum > 0)
533 {
534 if (l_readpm(&vol) < 0)
535 {
536 close(vol.fd);
537 return -1;
538 }
539 }
540 else /* determine size of entire device */
541 {
542 #ifdef APPLE_HYB
543 vol.vlen = hce->hfs_vol_size;
544 #else
545 unsigned long low, high, mid;
546 block b;
547
548 for (low = 0, high = 2879; b_readlb(&vol, high, &b) >= 0; high *= 2)
549 low = high;
550
551 while (low < high - 1)
552 {
553 mid = (low + high) / 2;
554
555 if (b_readlb(&vol, mid, &b) < 0)
556 high = mid;
557 else
558 low = mid;
559 }
560
561 vol.vlen = low + 1;
562 #endif /* APPLE_HYB */
563 }
564
565 if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ)
566 {
567 #ifndef APPLE_HYB
568 close(vol.fd);
569 #endif /* APPLE_HYB */
570
571 ERROR(EINVAL, "volume size must be >= 800K");
572 return -1;
573 }
574
575 /* initialize volume geometry */
576
577 #ifdef APPLE_HYB
578 /* force lpa to be a multiple of 4 (i.e. 2048/512) - as calculated
579 earlier */
580 vol.lpa = hce->Csize/HFS_BLOCKSZ;
581 #else
582 vol.lpa = 1 + vol.vlen / 65536;
583 #endif /* APPLE_HYB */
584
585 vbmsz = (vol.vlen / vol.lpa + 4095) / 4096;
586
587 vol.mdb.drSigWord = 0x4244;
588 vol.mdb.drCrDate = d_tomtime(time(0));
589 vol.mdb.drLsMod = vol.mdb.drCrDate;
590 vol.mdb.drAtrb = 0;
591 vol.mdb.drNmFls = 0;
592 vol.mdb.drVBMSt = 3;
593 vol.mdb.drAllocPtr = 0;
594 vol.mdb.drNmAlBlks = (vol.vlen - 5 - vbmsz) / vol.lpa;
595 vol.mdb.drAlBlkSiz = vol.lpa * HFS_BLOCKSZ;
596 vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz * 4;
597 vol.mdb.drAlBlSt = 3 + vbmsz;
598 #ifdef APPLE_HYB
599 /* round up start block to a muliple of lpa - important later */
600 /*vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + vol.lpa - 1) / vol.lpa) * vol.lpa;
601 */
602 /* take in accout alignment of files wrt HFS volume start i.e we want
603 drAlBlSt plus hfs_map_size to me a multiple of lpa */
604 vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + hce->hfs_map_size + vol.lpa - 1) / vol.lpa) * vol.lpa;
605 vol.mdb.drAlBlSt -= hce->hfs_map_size;
606 #endif /* APPLE_HYB */
607 vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */
608 vol.mdb.drFreeBks = vol.mdb.drNmAlBlks;
609
610 strcpy(vol.mdb.drVN, vname);
611
612 vol.mdb.drVolBkUp = 0;
613 vol.mdb.drVSeqNum = 0;
614 vol.mdb.drWrCnt = 0;
615 vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz;
616 #ifdef APPLE_HYB
617 /* adjust size of extents/catalog upwards as we may have rounded up
618 allocation size */
619 i = 1 + vol.vlen / 65536;
620
621 vol.mdb.drXTClpSiz = (vol.mdb.drXTClpSiz * vol.lpa) / i;
622
623 /* round up to lpa size */
624 vol.mdb.drXTClpSiz = ((vol.mdb.drXTClpSiz + vol.mdb.drAlBlkSiz - 1) /
625 vol.mdb.drAlBlkSiz) * vol.mdb.drAlBlkSiz;
626
627 /* ignore above, use what we have already calculated ... */
628 vol.mdb.drXTClpSiz = hce->XTCsize;
629
630 /* make Catalog file CTC (default twice) as big - prevents further allocation
631 later which we don't want - this seems to work OK ... */
632 /*vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * CTC; */
633 vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * hce->ctc_size;
634
635 /* we want to put things at the end of the volume later, so we'll
636 cheat here ... shouldn't matter, as we only need the volume read
637 only anyway (we won't be adding files later!) - leave some extra
638 space for the alternative MDB (in the last allocation block) */
639
640 vol.mdb.drNmAlBlks = vol.mdb.drFreeBks = vol.vlen / vol.lpa - 1;
641 #else
642 vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz;
643 #endif /* APPLE_HYB */
644 vol.mdb.drNmRtDirs = 0;
645 vol.mdb.drFilCnt = 0;
646 vol.mdb.drDirCnt = -1; /* incremented when root folder is created */
647
648 for (i = 0; i < 8; ++i)
649 vol.mdb.drFndrInfo[i] = 0;
650
651 vol.mdb.drVCSize = 0;
652 vol.mdb.drVBMCSize = 0;
653 vol.mdb.drCtlCSize = 0;
654
655 vol.mdb.drXTFlSize = 0;
656 vol.mdb.drCTFlSize = 0;
657
658 for (i = 0; i < 3; ++i)
659 {
660 vol.mdb.drXTExtRec[i].xdrStABN = 0;
661 vol.mdb.drXTExtRec[i].xdrNumABlks = 0;
662
663 vol.mdb.drCTExtRec[i].xdrStABN = 0;
664 vol.mdb.drCTExtRec[i].xdrNumABlks = 0;
665 }
666
667 /* initialize volume bitmap */
668
669 memset(vol.vbm, 0, sizeof(vbm));
670
671 #ifdef APPLE_HYB
672 /* We don't want to write anything out at the moment, so we allocate
673 memory to hold the HFS "header" info and extents/catalog files.
674 Any reads/writes from/to these parts of the volume are trapped and
675 stored in memory. */
676
677 /* blocks up to the first unallocated block == HFS "header" info
678 This will be placed in the first 32kb of the ISO volume later */
679 hce->hfs_hdr_size = vol.mdb.drAlBlSt;
680
681 /* size of the extents and catalog files. This will be added
682 to the end of the ISO volume later */
683 hce->hfs_ce_size = vol.mdb.drXTClpSiz + vol.mdb.drCTClpSiz;
684
685 /* we also allocate space for the Desktop file and the alternative
686 MDB while we're here */
687 FREE(hce->hfs_ce);
688 hce->hfs_ce = ALLOC(unsigned char, (hce->hfs_ce_size + vol.mdb.drClpSiz
689 + vol.mdb.drAlBlkSiz));
690
691 /* allocate memory for the map and hdr */
692 FREE(hce->hfs_map);
693 hce->hfs_map = ALLOC(unsigned char, ((hce->hfs_hdr_size + hce->hfs_map_size)
694 *HFS_BLOCKSZ));
695
696 if (hce->hfs_ce == 0 || hce->hfs_map == 0)
697 {
698 ERROR(ENOMEM, 0);
699 result = -1;
700 }
701
702 /* hfs_hdr is immediately after the hfs_map */
703 hce->hfs_hdr = hce->hfs_map + hce->hfs_map_size*HFS_BLOCKSZ;
704
705 /* size needed in HFS_BLOCKSZ blocks for later use */
706 hce->hfs_ce_size /= HFS_BLOCKSZ;
707
708 /* note size of Desktop file */
709 hce->hfs_dt_size = vol.mdb.drClpSiz/HFS_BLOCKSZ;
710
711 /* total size of catalog/extents and desktop */
712 hce->hfs_tot_size = hce->hfs_ce_size + hce->hfs_dt_size;
713
714 /* alternative MDB in the last alocation block */
715 hce->hfs_alt_mdb = hce->hfs_ce + hce->hfs_tot_size*HFS_BLOCKSZ;
716
717 /* add the MDB to the total size */
718 hce->hfs_tot_size += vol.lpa;
719
720 /* store this info in the volume info */
721 vol.hce = hce;
722
723 #endif /* APPLE_HYB */
724
725 /* create extents overflow file */
726
727 ext->f.vol = &vol;
728 ext->f.parid = 0;
729 strcpy(ext->f.name, "extents overflow");
730
731 ext->f.cat.cdrType = cdrFilRec;
732 /* ext->f.cat.cdrResrv2 */
733 ext->f.cat.u.fil.filFlags = 0;
734 ext->f.cat.u.fil.filTyp = 0;
735 /* ext->f.cat.u.fil.filUsrWds */
736 ext->f.cat.u.fil.filFlNum = HFS_CNID_EXT;
737 ext->f.cat.u.fil.filStBlk = 0;
738 ext->f.cat.u.fil.filLgLen = 0;
739 ext->f.cat.u.fil.filPyLen = 0;
740 ext->f.cat.u.fil.filRStBlk = 0;
741 ext->f.cat.u.fil.filRLgLen = 0;
742 ext->f.cat.u.fil.filRPyLen = 0;
743 ext->f.cat.u.fil.filCrDat = vol.mdb.drCrDate;
744 ext->f.cat.u.fil.filMdDat = vol.mdb.drLsMod;
745 ext->f.cat.u.fil.filBkDat = 0;
746 /* ext->f.cat.u.fil.filFndrInfo */
747 ext->f.cat.u.fil.filClpSize = 0;
748
749 for (i = 0; i < 3; ++i)
750 {
751 ext->f.cat.u.fil.filExtRec[i].xdrStABN = 0;
752 ext->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0;
753
754 ext->f.cat.u.fil.filRExtRec[i].xdrStABN = 0;
755 ext->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
756 }
757 /* ext->f.cat.u.fil.filResrv */
758 f_selectfork(&ext->f, 0);
759
760 ext->f.clump = vol.mdb.drXTClpSiz;
761 ext->f.flags = 0;
762
763 ext->f.prev = ext->f.next = 0;
764
765 n_init(&ext->hdrnd, ext, ndHdrNode, 0);
766
767 ext->hdrnd.nnum = 0;
768 ext->hdrnd.nd.ndNRecs = 3;
769 ext->hdrnd.roff[1] = 0x078;
770 ext->hdrnd.roff[2] = 0x0f8;
771 ext->hdrnd.roff[3] = 0x1f8;
772
773 memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128);
774
775 ext->hdr.bthDepth = 0;
776 ext->hdr.bthRoot = 0;
777 ext->hdr.bthNRecs = 0;
778 ext->hdr.bthFNode = 0;
779 ext->hdr.bthLNode = 0;
780 ext->hdr.bthNodeSize = HFS_BLOCKSZ;
781 ext->hdr.bthKeyLen = 0x07;
782 ext->hdr.bthNNodes = 0;
783 ext->hdr.bthFree = 0;
784 for (i = 0; i < 76; ++i)
785 ext->hdr.bthResv[i] = 0;
786
787 map = ALLOC(char, HFS_MAP1SZ);
788 if (map == 0)
789 {
790 if (result == 0)
791 {
792 ERROR(ENOMEM, 0);
793 result = -1;
794 }
795 }
796 else
797 {
798 memset(map, 0, HFS_MAP1SZ);
799 BMSET(map, 0);
800 }
801
802 ext->map = map;
803 ext->mapsz = HFS_MAP1SZ;
804 ext->flags = HFS_UPDATE_BTHDR;
805 ext->compare = r_compareextkeys;
806
807 if (result == 0 && bt_space(ext, 1) < 0)
808 result = -1;
809
810 --ext->hdr.bthFree;
811
812 /* create catalog file */
813
814 cat->f.vol = &vol;
815 cat->f.parid = 0;
816 strcpy(cat->f.name, "catalog");
817
818 cat->f.cat.cdrType = cdrFilRec;
819 /* cat->f.cat.cdrResrv2 */
820 cat->f.cat.u.fil.filFlags = 0;
821 cat->f.cat.u.fil.filTyp = 0;
822 /* cat->f.cat.u.fil.filUsrWds */
823 cat->f.cat.u.fil.filFlNum = HFS_CNID_CAT;
824 cat->f.cat.u.fil.filStBlk = 0;
825 cat->f.cat.u.fil.filLgLen = 0;
826 cat->f.cat.u.fil.filPyLen = 0;
827 cat->f.cat.u.fil.filRStBlk = 0;
828 cat->f.cat.u.fil.filRLgLen = 0;
829 cat->f.cat.u.fil.filRPyLen = 0;
830 cat->f.cat.u.fil.filCrDat = vol.mdb.drCrDate;
831 cat->f.cat.u.fil.filMdDat = vol.mdb.drLsMod;
832 cat->f.cat.u.fil.filBkDat = 0;
833 /* cat->f.cat.u.fil.filFndrInfo */
834 cat->f.cat.u.fil.filClpSize = 0;
835
836 for (i = 0; i < 3; ++i)
837 {
838 cat->f.cat.u.fil.filExtRec[i].xdrStABN = 0;
839 cat->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0;
840
841 cat->f.cat.u.fil.filRExtRec[i].xdrStABN = 0;
842 cat->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
843 }
844 /* cat->f.cat.u.fil.filResrv */
845 f_selectfork(&cat->f, 0);
846
847 cat->f.clump = vol.mdb.drCTClpSiz;
848 cat->f.flags = 0;
849
850 cat->f.prev = cat->f.next = 0;
851
852 n_init(&cat->hdrnd, cat, ndHdrNode, 0);
853
854 cat->hdrnd.nnum = 0;
855 cat->hdrnd.nd.ndNRecs = 3;
856 cat->hdrnd.roff[1] = 0x078;
857 cat->hdrnd.roff[2] = 0x0f8;
858 cat->hdrnd.roff[3] = 0x1f8;
859
860 memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128);
861
862 cat->hdr.bthDepth = 0;
863 cat->hdr.bthRoot = 0;
864 cat->hdr.bthNRecs = 0;
865 cat->hdr.bthFNode = 0;
866 cat->hdr.bthLNode = 0;
867 cat->hdr.bthNodeSize = HFS_BLOCKSZ;
868 cat->hdr.bthKeyLen = 0x25;
869 cat->hdr.bthNNodes = 0;
870 cat->hdr.bthFree = 0;
871 for (i = 0; i < 76; ++i)
872 cat->hdr.bthResv[i] = 0;
873
874 map = ALLOC(char, HFS_MAP1SZ);
875 if (map == 0)
876 {
877 if (result == 0)
878 {
879 ERROR(ENOMEM, 0);
880 result = -1;
881 }
882 }
883 else
884 {
885 memset(map, 0, HFS_MAP1SZ);
886 BMSET(map, 0);
887 }
888
889 cat->map = map;
890 cat->mapsz = HFS_MAP1SZ;
891 cat->flags = HFS_UPDATE_BTHDR;
892 cat->compare = r_comparecatkeys;
893
894 if (result == 0 && bt_space(cat, 1) < 0)
895 result = -1;
896
897 --cat->hdr.bthFree;
898
899 /* create root folder */
900
901 if (result == 0 && v_newfolder(&vol, HFS_CNID_ROOTPAR, vname) < 0)
902 result = -1;
903
904 vol.mdb.drNxtCNID = 16;
905
906 /* finish up */
907
908 if (result == 0)
909 {
910 block b;
911
912 /* write boot blocks */
913
914 memset(b, 0, sizeof(b));
915 b_writelb(&vol, 0, &b);
916 b_writelb(&vol, 1, &b);
917
918 /* flush other disk state */
919
920 vol.flags |= HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB | HFS_UPDATE_VBM;
921
922 if (v_flush(&vol, 1) < 0)
923 result = -1;
924 }
925 #ifndef APPLE_HYB
926 if (close(vol.fd) < 0 && result == 0)
927 {
928 ERROR(errno, "error closing device");
929 result = -1;
930 }
931 #endif /* APPLE_HYB */
932 FREE(vol.ext.map);
933 FREE(vol.cat.map);
934
935 return result;
936 }
937
938 /* High-Level Directory Routines =========================================== */
939
940 /*
941 * NAME: hfs->chdir()
942 * DESCRIPTION: change current HFS directory
943 */
hfs_chdir(vol,path)944 int hfs_chdir(vol, path)
945 hfsvol *vol;
946 char *path;
947 {
948 CatDataRec data;
949
950 if (v_getvol(&vol) < 0 ||
951 v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
952 return -1;
953
954 if (data.cdrType != cdrDirRec)
955 {
956 ERROR(ENOTDIR, 0);
957 return -1;
958 }
959
960 vol->cwd = data.u.dir.dirDirID;
961
962 return 0;
963 }
964
965 /*
966 * NAME: hfs->getcwd()
967 * DESCRIPTION: return the current working directory ID
968 */
hfs_getcwd(vol)969 long hfs_getcwd(vol)
970 hfsvol *vol;
971 {
972 if (v_getvol(&vol) < 0)
973 return 0;
974
975 return vol->cwd;
976 }
977
978 /*
979 * NAME: hfs->setcwd()
980 * DESCRIPTION: set the current working directory ID
981 */
hfs_setcwd(vol,id)982 int hfs_setcwd(vol, id)
983 hfsvol *vol;
984 long id;
985 {
986 if (v_getvol(&vol) < 0)
987 return -1;
988
989 if (id == vol->cwd)
990 return 0;
991
992 /* make sure the directory exists */
993
994 if (v_getdthread(vol, id, 0, 0) <= 0)
995 return -1;
996
997 vol->cwd = id;
998
999 return 0;
1000 }
1001
1002 /*
1003 * NAME: hfs->dirinfo()
1004 * DESCRIPTION: given a directory ID, return its (name and) parent ID
1005 */
hfs_dirinfo(vol,id,name)1006 int hfs_dirinfo(vol, id, name)
1007 hfsvol *vol;
1008 long *id;
1009 char *name;
1010 {
1011 CatDataRec thread;
1012
1013 if (v_getvol(&vol) < 0 ||
1014 v_getdthread(vol, *id, &thread, 0) <= 0)
1015 return -1;
1016
1017 *id = thread.u.dthd.thdParID;
1018
1019 if (name)
1020 strcpy(name, thread.u.dthd.thdCName);
1021
1022 return 0;
1023 }
1024
1025 /*
1026 * NAME: hfs->opendir()
1027 * DESCRIPTION: prepare to read the contents of a directory
1028 */
hfs_opendir(vol,path)1029 hfsdir *hfs_opendir(vol, path)
1030 hfsvol *vol;
1031 char *path;
1032 {
1033 hfsdir *dir;
1034 CatKeyRec key;
1035 CatDataRec data;
1036 unsigned char pkey[HFS_CATKEYLEN];
1037
1038 if (v_getvol(&vol) < 0)
1039 return 0;
1040
1041 dir = ALLOC(hfsdir, 1);
1042 if (dir == 0)
1043 {
1044 ERROR(ENOMEM, 0);
1045 return 0;
1046 }
1047
1048 dir->vol = vol;
1049
1050 if (*path == 0)
1051 {
1052 /* meta-directory containing root dirs from all mounted volumes */
1053
1054 dir->dirid = 0;
1055 dir->vptr = hfs_mounts;
1056 }
1057 else
1058 {
1059 if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0)
1060 {
1061 FREE(dir);
1062 return 0;
1063 }
1064
1065 if (data.cdrType != cdrDirRec)
1066 {
1067 FREE(dir);
1068 ERROR(ENOTDIR, 0);
1069 return 0;
1070 }
1071
1072 dir->dirid = data.u.dir.dirDirID;
1073 dir->vptr = 0;
1074
1075 r_makecatkey(&key, dir->dirid, "");
1076 r_packcatkey(&key, pkey, 0);
1077
1078 if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
1079 {
1080 FREE(dir);
1081 return 0;
1082 }
1083 }
1084
1085 dir->prev = 0;
1086 dir->next = vol->dirs;
1087
1088 if (vol->dirs)
1089 vol->dirs->prev = dir;
1090
1091 vol->dirs = dir;
1092
1093 return dir;
1094 }
1095
1096 /*
1097 * NAME: hfs->readdir()
1098 * DESCRIPTION: return the next entry in the directory
1099 */
hfs_readdir(dir,ent)1100 int hfs_readdir(dir, ent)
1101 hfsdir *dir;
1102 hfsdirent *ent;
1103 {
1104 CatKeyRec key;
1105 CatDataRec data;
1106 unsigned char *ptr;
1107
1108 if (dir->dirid == 0)
1109 {
1110 hfsvol *vol;
1111 char cname[HFS_MAX_FLEN + 1];
1112
1113 for (vol = hfs_mounts; vol; vol = vol->next)
1114 {
1115 if (vol == dir->vptr)
1116 break;
1117 }
1118
1119 if (vol == 0)
1120 {
1121 ERROR(ENOENT, "no more entries");
1122 return -1;
1123 }
1124
1125 if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 ||
1126 v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
1127 &data, cname, 0) < 0)
1128 return -1;
1129
1130 r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
1131
1132 dir->vptr = vol->next;
1133
1134 return 0;
1135 }
1136
1137 if (dir->n.rnum == -1)
1138 {
1139 ERROR(ENOENT, "no more entries");
1140 return -1;
1141 }
1142
1143 for (;;)
1144 {
1145 ++dir->n.rnum;
1146
1147 while (dir->n.rnum >= (int)dir->n.nd.ndNRecs)
1148 {
1149 dir->n.nnum = dir->n.nd.ndFLink;
1150 if (dir->n.nnum == 0)
1151 {
1152 dir->n.rnum = -1;
1153 ERROR(ENOENT, "no more entries");
1154 return -1;
1155 }
1156
1157 if (bt_getnode(&dir->n) < 0)
1158 {
1159 dir->n.rnum = -1;
1160 return -1;
1161 }
1162
1163 dir->n.rnum = 0;
1164 }
1165
1166 ptr = HFS_NODEREC(dir->n, dir->n.rnum);
1167
1168 r_unpackcatkey(ptr, &key);
1169
1170 if (key.ckrParID != dir->dirid)
1171 {
1172 dir->n.rnum = -1;
1173 ERROR(ENOENT, "no more entries");
1174 return -1;
1175 }
1176
1177 r_unpackcatdata(HFS_RECDATA(ptr), &data);
1178
1179 switch (data.cdrType)
1180 {
1181 case cdrDirRec:
1182 case cdrFilRec:
1183 r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
1184 return 0;
1185
1186 case cdrThdRec:
1187 case cdrFThdRec:
1188 break;
1189
1190 default:
1191 dir->n.rnum = -1;
1192
1193 ERROR(EIO, "unexpected directory entry found");
1194 return -1;
1195 }
1196 }
1197 }
1198
1199 /*
1200 * NAME: hfs->closedir()
1201 * DESCRIPTION: stop reading a directory
1202 */
hfs_closedir(dir)1203 int hfs_closedir(dir)
1204 hfsdir *dir;
1205 {
1206 hfsvol *vol = dir->vol;
1207
1208 if (dir->prev)
1209 dir->prev->next = dir->next;
1210 if (dir->next)
1211 dir->next->prev = dir->prev;
1212 if (dir == vol->dirs)
1213 vol->dirs = dir->next;
1214
1215 FREE(dir);
1216
1217 return 0;
1218 }
1219
1220 /* High-Level File Routines ================================================ */
1221
1222 /*
1223 * NAME: hfs->open()
1224 * DESCRIPTION: prepare a file for I/O
1225 */
hfs_open(vol,path)1226 hfsfile *hfs_open(vol, path)
1227 hfsvol *vol;
1228 char *path;
1229 {
1230 hfsfile *file;
1231
1232 if (v_getvol(&vol) < 0)
1233 return 0;
1234
1235 file = ALLOC(hfsfile, 1);
1236 if (file == 0)
1237 {
1238 ERROR(ENOMEM, 0);
1239 return 0;
1240 }
1241
1242 if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0)
1243 {
1244 FREE(file);
1245 return 0;
1246 }
1247
1248 if (file->cat.cdrType != cdrFilRec)
1249 {
1250 FREE(file);
1251 ERROR(EISDIR, 0);
1252 return 0;
1253 }
1254
1255 file->vol = vol;
1256 file->clump = file->cat.u.fil.filClpSize;
1257 file->flags = 0;
1258
1259 f_selectfork(file, 0);
1260
1261 file->prev = 0;
1262 file->next = vol->files;
1263
1264 if (vol->files)
1265 vol->files->prev = file;
1266
1267 vol->files = file;
1268
1269 return file;
1270 }
1271
1272 /*
1273 * NAME: hfs->setfork()
1274 * DESCRIPTION: select file fork for I/O operations
1275 */
hfs_setfork(file,ffork)1276 int hfs_setfork(file, ffork)
1277 hfsfile *file;
1278 int ffork;
1279 {
1280 int result = 0;
1281
1282 if (! (file->vol->flags & HFS_READONLY) &&
1283 f_trunc(file) < 0)
1284 result = -1;
1285
1286 f_selectfork(file, ffork);
1287
1288 return result;
1289 }
1290
1291 /*
1292 * NAME: hfs->getfork()
1293 * DESCRIPTION: return the current fork for I/O operations
1294 */
hfs_getfork(file)1295 int hfs_getfork(file)
1296 hfsfile *file;
1297 {
1298 return file->fork != fkData;
1299 }
1300
1301 /*
1302 * NAME: hfs->read()
1303 * DESCRIPTION: read from an open file
1304 */
hfs_read(file,buf,len)1305 long hfs_read(file, buf, len)
1306 hfsfile *file;
1307 void *buf;
1308 unsigned long len;
1309 {
1310 unsigned long *lglen, count;
1311 unsigned char *ptr = buf;
1312
1313 f_getptrs(file, &lglen, 0, 0);
1314
1315 if (file->pos + len > *lglen)
1316 len = *lglen - file->pos;
1317
1318 count = len;
1319 while (count)
1320 {
1321 block b;
1322 unsigned long bnum, offs, chunk;
1323
1324 bnum = file->pos / HFS_BLOCKSZ;
1325 offs = file->pos % HFS_BLOCKSZ;
1326
1327 chunk = HFS_BLOCKSZ - offs;
1328 if (chunk > count)
1329 chunk = count;
1330
1331 if (f_getblock(file, bnum, &b) < 0)
1332 return -1;
1333
1334 memcpy(ptr, b + offs, chunk);
1335 ptr += chunk;
1336
1337 file->pos += chunk;
1338 count -= chunk;
1339 }
1340
1341 return len;
1342 }
1343
1344 /*
1345 * NAME: hfs->write()
1346 * DESCRIPTION: write to an open file
1347 */
hfs_write(file,buf,len)1348 long hfs_write(file, buf, len)
1349 hfsfile *file;
1350 void *buf;
1351 unsigned long len;
1352 {
1353 unsigned long *lglen, *pylen, count;
1354 unsigned char *ptr = buf;
1355
1356 if (file->vol->flags & HFS_READONLY)
1357 {
1358 ERROR(EROFS, 0);
1359 return -1;
1360 }
1361
1362 f_getptrs(file, &lglen, &pylen, 0);
1363
1364 count = len;
1365
1366 /* set flag to update (at least) the modification time */
1367
1368 if (count)
1369 {
1370 file->cat.u.fil.filMdDat = d_tomtime(time(0));
1371 file->flags |= HFS_UPDATE_CATREC;
1372 }
1373
1374 while (count)
1375 {
1376 block b;
1377 unsigned long bnum, offs, chunk;
1378
1379 bnum = file->pos / HFS_BLOCKSZ;
1380 offs = file->pos % HFS_BLOCKSZ;
1381
1382 chunk = HFS_BLOCKSZ - offs;
1383 if (chunk > count)
1384 chunk = count;
1385
1386 if (file->pos + chunk > *pylen)
1387 {
1388 if (bt_space(&file->vol->ext, 1) < 0 ||
1389 f_alloc(file) < 0)
1390 return -1;
1391 }
1392 #ifndef APPLE_HYB
1393 /* Ignore this part as we are always writing new files to an empty disk
1394 i.e. offs will always be 0 */
1395
1396 if (offs > 0 || chunk < HFS_BLOCKSZ)
1397 {
1398 if (f_getblock(file, bnum, &b) < 0)
1399 return -1;
1400 }
1401 #endif /* APPLE_HYB */
1402 memcpy(b + offs, ptr, chunk);
1403 ptr += chunk;
1404
1405 if (f_putblock(file, bnum, &b) < 0)
1406 return -1;
1407
1408 file->pos += chunk;
1409 count -= chunk;
1410
1411 if (file->pos > *lglen)
1412 *lglen = file->pos;
1413 }
1414
1415 return len;
1416 }
1417
1418 /*
1419 * NAME: hfs->truncate()
1420 * DESCRIPTION: truncate an open file
1421 */
hfs_truncate(file,len)1422 int hfs_truncate(file, len)
1423 hfsfile *file;
1424 unsigned long len;
1425 {
1426 unsigned long *lglen;
1427
1428 f_getptrs(file, &lglen, 0, 0);
1429
1430 if (*lglen > len)
1431 {
1432 if (file->vol->flags & HFS_READONLY)
1433 {
1434 ERROR(EROFS, 0);
1435 return -1;
1436 }
1437
1438 *lglen = len;
1439
1440 file->cat.u.fil.filMdDat = d_tomtime(time(0));
1441 file->flags |= HFS_UPDATE_CATREC;
1442
1443 if (file->pos > len)
1444 file->pos = len;
1445 }
1446
1447 return 0;
1448 }
1449
1450 /*
1451 * NAME: hfs->lseek()
1452 * DESCRIPTION: change file seek pointer
1453 */
hfs_lseek(file,offset,from)1454 long hfs_lseek(file, offset, from)
1455 hfsfile *file;
1456 long offset;
1457 int from;
1458 {
1459 unsigned long *lglen;
1460 long newpos;
1461
1462 f_getptrs(file, &lglen, 0, 0);
1463
1464 switch (from)
1465 {
1466 case SEEK_SET:
1467 newpos = offset;
1468 break;
1469
1470 case SEEK_CUR:
1471 newpos = file->pos + offset;
1472 break;
1473
1474 case SEEK_END:
1475 newpos = *lglen + offset;
1476 break;
1477
1478 default:
1479 ERROR(EINVAL, 0);
1480 return -1;
1481 }
1482
1483 if (newpos < 0)
1484 newpos = 0;
1485 else if (newpos > *lglen)
1486 newpos = *lglen;
1487
1488 file->pos = newpos;
1489
1490 return newpos;
1491 }
1492
1493 /*
1494 * NAME: hfs->close()
1495 * DESCRIPTION: close a file
1496 */
1497 #ifdef APPLE_HYB
1498 /* extra args are used to set the start of the forks in the ISO volume */
hfs_close(file,dext,rext)1499 int hfs_close(file, dext, rext)
1500 hfsfile *file;
1501 long dext;
1502 long rext;
1503 {
1504 int offset;
1505 #else
1506 int hfs_close(hfsfile *file)
1507 {
1508 #endif /* APPLE_HYB */
1509 hfsvol *vol = file->vol;
1510 int result = 0;
1511
1512 if (f_trunc(file) < 0 ||
1513 f_flush(file) < 0)
1514 result = -1;
1515
1516 #ifdef APPLE_HYB
1517 /* "start" of file is relative to the first available block */
1518 offset = vol->hce->hfs_hdr_size + vol->hce->hfs_map_size;
1519 /* update the "real" starting extent and re-flush the file */
1520 if (dext)
1521 file->cat.u.fil.filExtRec[0].xdrStABN = (dext - offset)/vol->lpa;
1522
1523 if (rext)
1524 file->cat.u.fil.filRExtRec[0].xdrStABN = (rext - offset)/vol->lpa;
1525
1526 if (dext || rext)
1527 file->flags |= HFS_UPDATE_CATREC;
1528
1529 if (f_flush(file) < 0)
1530 result = -1;
1531 #endif /*APPLE_HYB */
1532
1533 if (file->prev)
1534 file->prev->next = file->next;
1535 if (file->next)
1536 file->next->prev = file->prev;
1537 if (file == vol->files)
1538 vol->files = file->next;
1539
1540 FREE(file);
1541
1542 return result;
1543 }
1544
1545 /* High-Level Catalog Routines ============================================= */
1546
1547 /*
1548 * NAME: hfs->stat()
1549 * DESCRIPTION: return catalog information for an arbitrary path
1550 */
hfs_stat(vol,path,ent)1551 int hfs_stat(vol, path, ent)
1552 hfsvol *vol;
1553 char *path;
1554 hfsdirent *ent;
1555 {
1556 CatDataRec data;
1557 long parid;
1558 char name[HFS_MAX_FLEN + 1];
1559
1560 if (v_getvol(&vol) < 0 ||
1561 v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1562 return -1;
1563
1564 r_unpackdirent(parid, name, &data, ent);
1565
1566 return 0;
1567 }
1568
1569 /*
1570 * NAME: hfs->fstat()
1571 * DESCRIPTION: return catalog information for an open file
1572 */
hfs_fstat(file,ent)1573 int hfs_fstat(file, ent)
1574 hfsfile *file;
1575 hfsdirent *ent;
1576 {
1577 r_unpackdirent(file->parid, file->name, &file->cat, ent);
1578
1579 return 0;
1580 }
1581
1582 /*
1583 * NAME: hfs->setattr()
1584 * DESCRIPTION: change a file's attributes
1585 */
hfs_setattr(vol,path,ent)1586 int hfs_setattr(vol, path, ent)
1587 hfsvol *vol;
1588 char *path;
1589 hfsdirent *ent;
1590 {
1591 CatDataRec data;
1592 node n;
1593
1594 if (v_getvol(&vol) < 0 ||
1595 v_resolve(&vol, path, &data, 0, 0, &n) <= 0)
1596 return -1;
1597
1598 if (vol->flags & HFS_READONLY)
1599 {
1600 ERROR(EROFS, 0);
1601 return -1;
1602 }
1603
1604 r_packdirent(&data, ent);
1605
1606 if (v_putcatrec(&data, &n) < 0)
1607 return -1;
1608
1609 return 0;
1610 }
1611
1612 /*
1613 * NAME: hfs->fsetattr()
1614 * DESCRIPTION: change an open file's attributes
1615 */
hfs_fsetattr(file,ent)1616 int hfs_fsetattr(file, ent)
1617 hfsfile *file;
1618 hfsdirent *ent;
1619 {
1620 if (file->vol->flags & HFS_READONLY)
1621 {
1622 ERROR(EROFS, 0);
1623 return -1;
1624 }
1625
1626 r_packdirent(&file->cat, ent);
1627
1628 file->flags |= HFS_UPDATE_CATREC;
1629
1630 return 0;
1631 }
1632
1633 /*
1634 * NAME: hfs->mkdir()
1635 * DESCRIPTION: create a new directory
1636 */
hfs_mkdir(vol,path)1637 int hfs_mkdir(vol, path)
1638 hfsvol *vol;
1639 char *path;
1640 {
1641 CatDataRec data;
1642 long parid;
1643 char name[HFS_MAX_FLEN + 1];
1644 int found;
1645
1646 if (v_getvol(&vol) < 0)
1647 return -1;
1648
1649 found = v_resolve(&vol, path, &data, &parid, name, 0);
1650 if (found < 0 || parid == 0)
1651 return -1;
1652 else if (found)
1653 {
1654 ERROR(EEXIST, 0);
1655 return -1;
1656 }
1657
1658 if (parid == HFS_CNID_ROOTPAR)
1659 {
1660 ERROR(EINVAL, 0);
1661 return -1;
1662 }
1663
1664 if (vol->flags & HFS_READONLY)
1665 {
1666 ERROR(EROFS, 0);
1667 return -1;
1668 }
1669
1670 if (v_newfolder(vol, parid, name) < 0)
1671 return -1;
1672
1673 return 0;
1674 }
1675
1676 /*
1677 * NAME: hfs->rmdir()
1678 * DESCRIPTION: delete an empty directory
1679 */
hfs_rmdir(vol,path)1680 int hfs_rmdir(vol, path)
1681 hfsvol *vol;
1682 char *path;
1683 {
1684 CatKeyRec key;
1685 CatDataRec data;
1686 long parid;
1687 char name[HFS_MAX_FLEN + 1];
1688 unsigned char pkey[HFS_CATKEYLEN];
1689
1690 if (v_getvol(&vol) < 0 ||
1691 v_resolve(&vol, path, &data, &parid, name, 0) <= 0)
1692 return -1;
1693
1694 if (data.cdrType != cdrDirRec)
1695 {
1696 ERROR(ENOTDIR, 0);
1697 return -1;
1698 }
1699
1700 if (data.u.dir.dirVal != 0)
1701 {
1702 ERROR(ENOTEMPTY, 0);
1703 return -1;
1704 }
1705
1706 if (parid == HFS_CNID_ROOTPAR)
1707 {
1708 ERROR(EINVAL, 0);
1709 return -1;
1710 }
1711
1712 if (vol->flags & HFS_READONLY)
1713 {
1714 ERROR(EROFS, 0);
1715 return -1;
1716 }
1717
1718 /* delete directory record */
1719
1720 r_makecatkey(&key, parid, name);
1721 r_packcatkey(&key, pkey, 0);
1722
1723 if (bt_delete(&vol->cat, pkey) < 0)
1724 return -1;
1725
1726 /* delete thread record */
1727
1728 r_makecatkey(&key, data.u.dir.dirDirID, "");
1729 r_packcatkey(&key, pkey, 0);
1730
1731 if (bt_delete(&vol->cat, pkey) < 0 ||
1732 v_adjvalence(vol, parid, 1, -1) < 0)
1733 return -1;
1734
1735 return 0;
1736 }
1737
1738 /*
1739 * NAME: hfs->create()
1740 * DESCRIPTION: create a new file
1741 */
hfs_create(vol,path,type,creator)1742 int hfs_create(vol, path, type, creator)
1743 hfsvol *vol;
1744 char *path;
1745 char *type;
1746 char *creator;
1747 {
1748 CatKeyRec key;
1749 CatDataRec data;
1750 long id, parid;
1751 char name[HFS_MAX_FLEN + 1];
1752 unsigned char record[HFS_CATRECMAXLEN];
1753 int found, i, reclen;
1754
1755 if (v_getvol(&vol) < 0)
1756 return -1;
1757
1758 found = v_resolve(&vol, path, &data, &parid, name, 0);
1759 if (found < 0 || parid == 0)
1760 return -1;
1761 else if (found)
1762 {
1763 ERROR(EEXIST, 0);
1764 return -1;
1765 }
1766
1767 if (parid == HFS_CNID_ROOTPAR)
1768 {
1769 ERROR(EINVAL, 0);
1770 return -1;
1771 }
1772
1773 if (vol->flags & HFS_READONLY)
1774 {
1775 ERROR(EROFS, 0);
1776 return -1;
1777 }
1778
1779 /* create file `name' in parent `parid' */
1780
1781 if (bt_space(&vol->cat, 1) < 0)
1782 return -1;
1783
1784 id = vol->mdb.drNxtCNID++;
1785 vol->flags |= HFS_UPDATE_MDB;
1786
1787 /* create file record */
1788
1789 data.cdrType = cdrFilRec;
1790 data.cdrResrv2 = 0;
1791
1792 data.u.fil.filFlags = 0;
1793 data.u.fil.filTyp = 0;
1794
1795 memset(&data.u.fil.filUsrWds, 0, sizeof(data.u.fil.filUsrWds));
1796
1797 data.u.fil.filUsrWds.fdType = d_getl((unsigned char *) type);
1798 data.u.fil.filUsrWds.fdCreator = d_getl((unsigned char *) creator);
1799
1800 data.u.fil.filFlNum = id;
1801 data.u.fil.filStBlk = 0;
1802 data.u.fil.filLgLen = 0;
1803 data.u.fil.filPyLen = 0;
1804 data.u.fil.filRStBlk = 0;
1805 data.u.fil.filRLgLen = 0;
1806 data.u.fil.filRPyLen = 0;
1807 data.u.fil.filCrDat = d_tomtime(time(0));
1808 data.u.fil.filMdDat = data.u.fil.filCrDat;
1809 data.u.fil.filBkDat = 0;
1810
1811 memset(&data.u.fil.filFndrInfo, 0, sizeof(data.u.fil.filFndrInfo));
1812
1813 data.u.fil.filClpSize = 0;
1814
1815 for (i = 0; i < 3; ++i)
1816 {
1817 data.u.fil.filExtRec[i].xdrStABN = 0;
1818 data.u.fil.filExtRec[i].xdrNumABlks = 0;
1819
1820 data.u.fil.filRExtRec[i].xdrStABN = 0;
1821 data.u.fil.filRExtRec[i].xdrNumABlks = 0;
1822 }
1823
1824 data.u.fil.filResrv = 0;
1825
1826 r_makecatkey(&key, parid, name);
1827 r_packcatkey(&key, record, &reclen);
1828 r_packcatdata(&data, HFS_RECDATA(record), &reclen);
1829
1830 if (bt_insert(&vol->cat, record, reclen) < 0 ||
1831 v_adjvalence(vol, parid, 0, 1) < 0)
1832 return -1;
1833
1834 return 0;
1835 }
1836
1837 /*
1838 * NAME: hfs->delete()
1839 * DESCRIPTION: remove both forks of a file
1840 */
hfs_delete(vol,path)1841 int hfs_delete(vol, path)
1842 hfsvol *vol;
1843 char *path;
1844 {
1845 hfsfile file;
1846 CatKeyRec key;
1847 unsigned char pkey[HFS_CATKEYLEN];
1848 int found;
1849
1850 if (v_getvol(&vol) < 0 ||
1851 v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0)
1852 return -1;
1853
1854 if (file.cat.cdrType != cdrFilRec)
1855 {
1856 ERROR(EISDIR, 0);
1857 return -1;
1858 }
1859
1860 if (file.parid == HFS_CNID_ROOTPAR)
1861 {
1862 ERROR(EINVAL, 0);
1863 return -1;
1864 }
1865
1866 if (vol->flags & HFS_READONLY)
1867 {
1868 ERROR(EROFS, 0);
1869 return -1;
1870 }
1871
1872 /* free disk blocks */
1873
1874 file.vol = vol;
1875 file.flags = 0;
1876
1877 file.cat.u.fil.filLgLen = 0;
1878 file.cat.u.fil.filRLgLen = 0;
1879
1880 f_selectfork(&file, 0);
1881 if (f_trunc(&file) < 0)
1882 return -1;
1883
1884 f_selectfork(&file, 1);
1885 if (f_trunc(&file) < 0)
1886 return -1;
1887
1888 /* delete file record */
1889
1890 r_makecatkey(&key, file.parid, file.name);
1891 r_packcatkey(&key, pkey, 0);
1892
1893 if (bt_delete(&vol->cat, pkey) < 0 ||
1894 v_adjvalence(vol, file.parid, 0, -1) < 0)
1895 return -1;
1896
1897 /* delete file thread, if any */
1898
1899 found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0);
1900 if (found < 0)
1901 return -1;
1902
1903 if (found)
1904 {
1905 r_makecatkey(&key, file.cat.u.fil.filFlNum, "");
1906 r_packcatkey(&key, pkey, 0);
1907
1908 if (bt_delete(&vol->cat, pkey) < 0)
1909 return -1;
1910 }
1911
1912 return 0;
1913 }
1914
1915 /*
1916 * NAME: hfs->rename()
1917 * DESCRIPTION: change the name of and/or move a file or directory
1918 */
hfs_rename(vol,srcpath,dstpath)1919 int hfs_rename(vol, srcpath, dstpath)
1920 hfsvol *vol;
1921 char *srcpath;
1922 char *dstpath;
1923 {
1924 hfsvol *srcvol;
1925 CatDataRec src, dst;
1926 long srcid, dstid;
1927 CatKeyRec key;
1928 char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1];
1929 unsigned char record[HFS_CATRECMAXLEN];
1930 int found, isdir, moving, reclen;
1931 node n;
1932
1933 if (v_getvol(&vol) < 0 ||
1934 v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0)
1935 return -1;
1936
1937 isdir = (src.cdrType == cdrDirRec);
1938 srcvol = vol;
1939
1940 found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0);
1941 if (found < 0)
1942 return -1;
1943
1944 if (vol != srcvol)
1945 {
1946 ERROR(EINVAL, "can't move across volumes");
1947 return -1;
1948 }
1949
1950 if (dstid == 0)
1951 {
1952 ERROR(ENOENT, "bad destination path");
1953 return -1;
1954 }
1955
1956 if (found &&
1957 dst.cdrType == cdrDirRec &&
1958 dst.u.dir.dirDirID != src.u.dir.dirDirID)
1959 {
1960 dstid = dst.u.dir.dirDirID;
1961 strcpy(dstname, srcname);
1962
1963 found = v_catsearch(vol, dstid, dstname, 0, 0, 0);
1964 if (found < 0)
1965 return -1;
1966 }
1967
1968 moving = (srcid != dstid);
1969
1970 if (found)
1971 {
1972 char *ptr;
1973
1974 ptr = strrchr(dstpath, ':');
1975 if (ptr == 0)
1976 ptr = dstpath;
1977 else
1978 ++ptr;
1979
1980 if (*ptr)
1981 strcpy(dstname, ptr);
1982
1983 if (! moving && strcmp(srcname, dstname) == 0)
1984 return 0; /* source and destination are the same */
1985
1986 if (moving || d_relstring(srcname, dstname))
1987 {
1988 ERROR(EEXIST, "can't use destination name");
1989 return -1;
1990 }
1991 }
1992
1993 /* can't move anything into the root directory's parent */
1994
1995 if (moving && dstid == HFS_CNID_ROOTPAR)
1996 {
1997 ERROR(EINVAL, "can't move above root directory");
1998 return -1;
1999 }
2000
2001 if (moving && isdir)
2002 {
2003 long id;
2004
2005 /* can't move root directory anywhere */
2006
2007 if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR)
2008 {
2009 ERROR(EINVAL, "can't move root directory");
2010 return -1;
2011 }
2012
2013 /* make sure we aren't trying to move a directory inside itself */
2014
2015 for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID)
2016 {
2017 if (id == src.u.dir.dirDirID)
2018 {
2019 ERROR(EINVAL, "can't move directory inside itself");
2020 return -1;
2021 }
2022
2023 if (v_getdthread(vol, id, &dst, 0) <= 0)
2024 return -1;
2025 }
2026 }
2027
2028 if (vol->flags & HFS_READONLY)
2029 {
2030 ERROR(EROFS, 0);
2031 return -1;
2032 }
2033
2034 /* change volume name */
2035
2036 if (dstid == HFS_CNID_ROOTPAR)
2037 {
2038 if (strlen(dstname) > HFS_MAX_VLEN)
2039 {
2040 ERROR(ENAMETOOLONG, 0);
2041 return -1;
2042 }
2043
2044 strcpy(vol->mdb.drVN, dstname);
2045 vol->flags |= HFS_UPDATE_MDB;
2046 }
2047
2048 /* remove source record */
2049
2050 r_makecatkey(&key, srcid, srcname);
2051 r_packcatkey(&key, record, 0);
2052
2053 if (bt_delete(&vol->cat, record) < 0)
2054 return -1;
2055
2056 /* insert destination record */
2057
2058 r_makecatkey(&key, dstid, dstname);
2059 r_packcatkey(&key, record, &reclen);
2060 r_packcatdata(&src, HFS_RECDATA(record), &reclen);
2061
2062 if (bt_insert(&vol->cat, record, reclen) < 0)
2063 return -1;
2064
2065 /* update thread record */
2066
2067 if (isdir)
2068 {
2069 if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0)
2070 return -1;
2071
2072 dst.u.dthd.thdParID = dstid;
2073 strcpy(dst.u.dthd.thdCName, dstname);
2074
2075 if (v_putcatrec(&dst, &n) < 0)
2076 return -1;
2077 }
2078 else
2079 {
2080 found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n);
2081 if (found < 0)
2082 return -1;
2083
2084 if (found)
2085 {
2086 dst.u.fthd.fthdParID = dstid;
2087 strcpy(dst.u.fthd.fthdCName, dstname);
2088
2089 if (v_putcatrec(&dst, &n) < 0)
2090 return -1;
2091 }
2092 }
2093
2094 /* update directory valences */
2095
2096 if (moving)
2097 {
2098 if (v_adjvalence(vol, srcid, isdir, -1) < 0 ||
2099 v_adjvalence(vol, dstid, isdir, 1) < 0)
2100 return -1;
2101 }
2102
2103 return 0;
2104 }
2105 #ifdef APPLE_HYB
2106 /*
2107 * NAME: hfs->hfs_get_drAllocPtr()
2108 * DESCRIPTION: get the current start of next allocation search
2109 */
2110 unsigned short
hfs_get_drAllocPtr(file)2111 hfs_get_drAllocPtr(file)
2112 hfsfile *file;
2113 {
2114 return(file->vol->mdb.drAllocPtr);
2115 }
2116
2117 /*
2118 * NAME: hfs->hfs_set_drAllocPtr()
2119 * DESCRIPTION: set the current start of next allocation search
2120 */
2121 #ifdef PROTOTYPES
2122 int
hfs_set_drAllocPtr(hfsfile * file,unsigned short drAllocPtr,int size)2123 hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size)
2124 #else
2125 int
2126 hfs_set_drAllocPtr(file, drAllocPtr, size)
2127 hfsfile *file;
2128 unsigned short drAllocPtr;
2129 int size;
2130 #endif
2131 {
2132 hfsvol *vol = file->vol;
2133 int result = 0;
2134
2135 /* truncate the current fork */
2136 if (f_trunc(file) < 0 ||
2137 f_flush(file) < 0)
2138 result = -1;
2139
2140 /* convert the fork size into allocation blocks */
2141 size = (size + vol->mdb.drAlBlkSiz - 1)/vol->mdb.drAlBlkSiz;
2142
2143 /* set the start of next allocation search to be after this fork */
2144 vol->mdb.drAllocPtr = drAllocPtr + size;
2145
2146 vol->flags |= HFS_UPDATE_MDB;
2147
2148 return result;
2149 }
2150
2151 /*
2152 * NAME: hfs->vsetbless()
2153 * DESCRIPTION: set blessed folder
2154 *
2155 * adapted from vsetattr() from v3.2.6
2156 */
2157 #ifdef PROTOTYPES
2158 void
hfs_vsetbless(hfsvol * vol,unsigned long cnid)2159 hfs_vsetbless(hfsvol *vol, unsigned long cnid)
2160 #else
2161 void
2162 hfs_vsetbless(vol, cnid)
2163 hfsvol *vol;
2164 unsigned long cnid;
2165 #endif
2166 {
2167 vol->mdb.drFndrInfo[0] = cnid;
2168
2169 vol->flags |= HFS_UPDATE_MDB;
2170 }
2171 #endif /* APPLE_HYB */
2172