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