]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_catalog.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
26 #include <sys/stat.h>
27 #include <sys/mount.h>
28 #include <sys/vnode.h>
29 #include <sys/dirent.h>
30 #include <vfs/vfs_support.h>
31 #include <libkern/libkern.h>
32
33 #include <sys/utfconv.h>
34
35 #include "hfs.h"
36 #include "hfs_catalog.h"
37 #include "hfs_format.h"
38 #include "hfs_endian.h"
39
40 #include "hfscommon/headers/BTreesInternal.h"
41 #include "hfscommon/headers/HFSUnicodeWrappers.h"
42
43
44 /*
45 * Initialization of an FSBufferDescriptor structure.
46 */
47 #define BDINIT(bd, addr) { \
48 (bd).bufferAddress = (addr); \
49 (bd).itemSize = sizeof(*(addr)); \
50 (bd).itemCount = 1; \
51 }
52
53
54 struct btobj {
55 BTreeIterator iterator;
56 HFSPlusCatalogKey key;
57 CatalogRecord data;
58 };
59
60 struct update_state {
61 struct cat_desc * s_desc;
62 struct cat_attr * s_attr;
63 struct cat_fork * s_datafork;
64 struct cat_fork * s_rsrcfork;
65 struct hfsmount * s_hfsmp;
66 };
67
68 struct position_state {
69 int error;
70 u_int32_t count;
71 u_int32_t index;
72 u_int32_t parentID;
73 struct hfsmount *hfsmp;
74 };
75
76 /* Map file mode type to directory entry types */
77 u_char modetodirtype[16] = {
78 DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
79 DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
80 DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
81 DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
82 };
83 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
84
85
86 static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
87 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
88
89 static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
90 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
91
92 extern int mac_roman_to_unicode(const Str31 hfs_str, UniChar *uni_str,
93 UInt32 maxCharLen, UInt32 *unicodeChars);
94
95 extern int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen,
96 const u_int16_t* srcStr, Str31 dstStr, int retry);
97
98
99 /* Internal catalog support routines */
100
101 static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
102 struct position_state *state);
103
104 static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino);
105
106 static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
107
108 static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
109 HFSPlusCatalogKey *key, int retry);
110
111 static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key);
112
113 static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, int *recordSize);
114
115 static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
116
117 static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
118 int isdir, struct cat_desc *descp);
119
120 static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
121
122 static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
123 static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
124 static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
125
126 static cnid_t getcnid(const CatalogRecord *crp);
127 static u_long getencoding(const CatalogRecord *crp);
128 static cnid_t getparentcnid(const CatalogRecord *recp);
129
130 static int isadir(const CatalogRecord *crp);
131
132 static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
133
134
135 __private_extern__
136 int
137 cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, struct proc *p)
138 {
139 FCB *fcb;
140 int lockflags;
141 int result;
142
143 fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
144
145 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
146
147 result = BTReserveSpace(fcb, ops, (void*)cookie);
148
149 hfs_systemfile_unlock(hfsmp, lockflags);
150
151 return MacToVFSError(result);
152 }
153
154 __private_extern__
155 void
156 cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, struct proc *p)
157 {
158 FCB *fcb;
159 int lockflags;
160
161 fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
162
163 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
164
165 (void) BTReleaseReserve(fcb, (void*)cookie);
166
167 hfs_systemfile_unlock(hfsmp, lockflags);
168 }
169
170
171 __private_extern__
172 void
173 cat_convertattr(
174 struct hfsmount *hfsmp,
175 CatalogRecord * recp,
176 struct cat_attr *attrp,
177 struct cat_fork *datafp,
178 struct cat_fork *rsrcfp)
179 {
180 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
181
182 if (std_hfs) {
183 struct HFSPlusCatalogFile cnoderec;
184
185 promoteattr(hfsmp, recp, &cnoderec);
186 getbsdattr(hfsmp, &cnoderec, attrp);
187 } else {
188 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
189 }
190
191 if (isadir(recp))
192 bzero(datafp, sizeof(*datafp));
193 else if (std_hfs) {
194 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
195 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
196 } else {
197 /* Convert the data fork. */
198 datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
199 datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
200 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
201 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
202 datafp->cf_bytesread =
203 recp->hfsPlusFile.dataFork.clumpSize *
204 HFSTOVCB(hfsmp)->blockSize;
205 } else {
206 datafp->cf_bytesread = 0;
207 }
208 datafp->cf_vblocks = 0;
209 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
210 &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
211
212 /* Convert the resource fork. */
213 rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
214 rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
215 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
216 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
217 datafp->cf_bytesread =
218 recp->hfsPlusFile.resourceFork.clumpSize *
219 HFSTOVCB(hfsmp)->blockSize;
220 } else {
221 datafp->cf_bytesread = 0;
222 }
223 rsrcfp->cf_vblocks = 0;
224 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
225 &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
226 }
227 }
228
229 __private_extern__
230 int
231 cat_convertkey(
232 struct hfsmount *hfsmp,
233 CatalogKey *key,
234 CatalogRecord * recp,
235 struct cat_desc *descp)
236 {
237 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
238 HFSPlusCatalogKey * pluskey = NULL;
239 u_long encoding;
240
241 if (std_hfs) {
242 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
243 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
244
245 } else {
246 pluskey = (HFSPlusCatalogKey *)key;
247 encoding = getencoding(recp);
248 }
249
250 builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp);
251 if (std_hfs) {
252 FREE(pluskey, M_TEMP);
253 }
254 return (0);
255 }
256
257
258 /*
259 * cat_releasedesc
260 */
261 __private_extern__
262 void
263 cat_releasedesc(struct cat_desc *descp)
264 {
265 char * name;
266
267 if (descp == NULL)
268 return;
269
270 if ((descp->cd_flags & CD_HASBUF) &&
271 (descp->cd_nameptr != NULL)) {
272 name = descp->cd_nameptr;
273 descp->cd_nameptr = NULL;
274 descp->cd_namelen = 0;
275 descp->cd_flags &= ~CD_HASBUF;
276 vfs_removename(name);
277 }
278 descp->cd_nameptr = NULL;
279 descp->cd_namelen = 0;
280 }
281
282 /*
283 * These Catalog functions allow access to the HFS Catalog (database).
284 * The catalog b-tree lock must be aquired before calling any of these routines.
285 */
286
287 /*
288 * cat_lookup - lookup a catalog node using a cnode decriptor
289 */
290 __private_extern__
291 int
292 cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
293 struct cat_desc *outdescp, struct cat_attr *attrp,
294 struct cat_fork *forkp, cnid_t *desc_cnid)
295 {
296 CatalogKey * keyp;
297 int std_hfs;
298 int result;
299
300 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
301
302 MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
303
304 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1);
305 if (result)
306 goto exit;
307
308 result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
309
310 if (result == ENOENT) {
311 if (!std_hfs) {
312 struct cat_desc temp_desc;
313 if (outdescp == NULL) {
314 bzero(&temp_desc, sizeof(temp_desc));
315 outdescp = &temp_desc;
316 }
317 result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
318 if (desc_cnid) {
319 *desc_cnid = outdescp->cd_cnid;
320 }
321 if (outdescp == &temp_desc) {
322 /* Release the local copy of desc */
323 cat_releasedesc(outdescp);
324 }
325 } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
326 // make MacRoman key from utf-8
327 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
328 // update desc text encoding so that other catalog ops succeed
329 }
330 }
331 exit:
332 FREE(keyp, M_TEMP);
333
334 return (result);
335 }
336
337 __private_extern__
338 int
339 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
340 {
341 struct BTreeIterator *iterator;
342 struct FSBufferDescriptor file_data;
343 struct HFSCatalogFile file_rec;
344 UInt16 datasize;
345 FCB *fcb;
346 int result;
347
348 if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord)
349 return (EINVAL);
350
351 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
352
353 MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
354 bzero(&iterator[0], 2* sizeof(*iterator));
355 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0);
356 if (result)
357 goto exit;
358
359 BDINIT(file_data, &file_rec);
360 result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
361 if (result)
362 goto exit;
363
364 if (file_rec.recordType != kHFSFileRecord) {
365 result = EISDIR;
366 goto exit;
367 }
368
369 if ((file_rec.flags & kHFSThreadExistsMask) == 0) {
370 struct FSBufferDescriptor thread_data;
371 struct HFSCatalogThread thread_rec;
372
373 file_rec.flags |= kHFSThreadExistsMask;
374 BDINIT(thread_data, &thread_rec);
375 thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0);
376 buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key);
377
378 result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize);
379 if (result)
380 goto exit;
381
382 (void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize);
383 (void) BTFlushPath(fcb);
384 }
385 exit:
386 (void) BTFlushPath(fcb);
387 FREE(iterator, M_TEMP);
388
389 return MacToVFSError(result);
390 }
391
392
393 /*
394 * cat_findname - obtain a descriptor from cnid
395 *
396 * Only a thread lookup is performed.
397 */
398 __private_extern__
399 int
400 cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
401 {
402 struct BTreeIterator * iterator;
403 FSBufferDescriptor btdata;
404 CatalogKey * keyp;
405 CatalogRecord * recp;
406 int isdir;
407 int result;
408 int std_hfs;
409
410 isdir = 0;
411 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
412
413 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
414 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
415 iterator->hint.nodeNum = 0;
416
417 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
418 BDINIT(btdata, recp);
419
420 result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
421 if (result)
422 goto exit;
423
424 /* Turn thread record into a cnode key (in place). */
425 switch (recp->recordType) {
426 case kHFSFolderThreadRecord:
427 isdir = 1;
428 /* fall through */
429 case kHFSFileThreadRecord:
430 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
431 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
432 break;
433
434 case kHFSPlusFolderThreadRecord:
435 isdir = 1;
436 /* fall through */
437 case kHFSPlusFileThreadRecord:
438 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
439 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
440 (keyp->hfsPlus.nodeName.length * 2);
441 break;
442 default:
443 result = ENOENT;
444 goto exit;
445 }
446 if (std_hfs) {
447 HFSPlusCatalogKey * pluskey = NULL;
448 u_long encoding;
449
450 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
451 promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
452 builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
453 FREE(pluskey, M_TEMP);
454
455 } else {
456 builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
457 }
458 exit:
459 FREE(recp, M_TEMP);
460 FREE(iterator, M_TEMP);
461
462 return MacToVFSError(result);
463 }
464
465 /*
466 * cat_idlookup - lookup a catalog node using a cnode id
467 */
468 __private_extern__
469 int
470 cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
471 struct cat_attr *attrp, struct cat_fork *forkp)
472 {
473 struct BTreeIterator * iterator;
474 FSBufferDescriptor btdata;
475 UInt16 datasize;
476 CatalogKey * keyp;
477 CatalogRecord * recp;
478 int result;
479 int std_hfs;
480
481 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
482
483 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
484 bzero(iterator, sizeof(*iterator));
485 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
486
487 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
488 BDINIT(btdata, recp);
489
490 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
491 &btdata, &datasize, iterator);
492 if (result)
493 goto exit;
494
495 /* Turn thread record into a cnode key (in place) */
496 switch (recp->recordType) {
497 case kHFSFileThreadRecord:
498 case kHFSFolderThreadRecord:
499 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
500 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
501 break;
502
503 case kHFSPlusFileThreadRecord:
504 case kHFSPlusFolderThreadRecord:
505 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
506 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
507 (keyp->hfsPlus.nodeName.length * 2);
508 break;
509
510 default:
511 result = ENOENT;
512 goto exit;
513 }
514
515 result = cat_lookupbykey(hfsmp, keyp, 0, 0, outdescp, attrp, forkp, NULL);
516 exit:
517 FREE(recp, M_TEMP);
518 FREE(iterator, M_TEMP);
519
520 return MacToVFSError(result);
521 }
522
523
524 /*
525 * cat_lookupmangled - lookup a catalog node using a mangled name
526 */
527 static int
528 cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
529 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
530 {
531 cnid_t fileID;
532 int prefixlen;
533 int result;
534
535 if (wantrsrc)
536 return (ENOENT);
537
538 fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
539 if (fileID < kHFSFirstUserCatalogNodeID)
540 return (ENOENT);
541
542 result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
543 if (result)
544 return (ENOENT);
545
546 /* It must be in the correct directory */
547 if (descp->cd_parentcnid != outdescp->cd_parentcnid)
548 goto falsematch;
549
550 if ((outdescp->cd_namelen < prefixlen) ||
551 bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
552 goto falsematch;
553
554 return (0);
555
556 falsematch:
557 cat_releasedesc(outdescp);
558 return (ENOENT);
559 }
560
561
562 /*
563 * cat_lookupbykey - lookup a catalog node using a cnode key
564 */
565 static int
566 cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
567 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
568 {
569 struct BTreeIterator * iterator;
570 FSBufferDescriptor btdata;
571 CatalogRecord * recp;
572 UInt16 datasize;
573 int result;
574 int std_hfs;
575 u_long ilink = 0;
576 cnid_t cnid = 0;
577 u_long encoding = 0;
578
579 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
580
581 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
582 BDINIT(btdata, recp);
583 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
584 bzero(iterator, sizeof(*iterator));
585 iterator->hint.nodeNum = hint;
586 bcopy(keyp, &iterator->key, sizeof(CatalogKey));
587
588 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
589 &btdata, &datasize, iterator);
590 if (result)
591 goto exit;
592
593 /* Save the cnid now in case there's a hard link */
594 cnid = getcnid(recp);
595 encoding = getencoding(recp);
596 hint = iterator->hint.nodeNum;
597
598 /* Hide the journal files (if any) */
599 if (hfsmp->jnl &&
600 ((cnid == hfsmp->hfs_jnlfileid) ||
601 (cnid == hfsmp->hfs_jnlinfoblkid))) {
602
603 result = ENOENT;
604 goto exit;
605 }
606
607 /*
608 * When a hardlink link is encountered, auto resolve it
609 */
610 if (!std_hfs
611 && (attrp || forkp)
612 && (recp->recordType == kHFSPlusFileRecord)
613 && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
614 && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
615 && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
616 (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
617
618 ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
619
620 (void) resolvelink(hfsmp, ilink, (struct HFSPlusCatalogFile *)recp);
621 }
622
623 if (attrp != NULL) {
624 if (std_hfs) {
625 struct HFSPlusCatalogFile cnoderec;
626
627 promoteattr(hfsmp, recp, &cnoderec);
628 getbsdattr(hfsmp, &cnoderec, attrp);
629 } else {
630 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
631 if (ilink)
632 attrp->ca_rdev = ilink;
633 }
634 }
635 if (forkp != NULL) {
636 if (isadir(recp)) {
637 bzero(forkp, sizeof(*forkp));
638 } else if (std_hfs) {
639 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
640 } else if (wantrsrc) {
641 /* Convert the resource fork. */
642 forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
643 forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
644 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
645 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
646 forkp->cf_bytesread =
647 recp->hfsPlusFile.resourceFork.clumpSize *
648 HFSTOVCB(hfsmp)->blockSize;
649 } else {
650 forkp->cf_bytesread = 0;
651 }
652 forkp->cf_vblocks = 0;
653 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
654 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
655 } else {
656 /* Convert the data fork. */
657 forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
658 forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
659 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
660 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
661 forkp->cf_bytesread =
662 recp->hfsPlusFile.dataFork.clumpSize *
663 HFSTOVCB(hfsmp)->blockSize;
664 } else {
665 forkp->cf_bytesread = 0;
666 }
667 forkp->cf_vblocks = 0;
668 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
669 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
670 }
671 }
672 if (descp != NULL) {
673 HFSPlusCatalogKey * pluskey = NULL;
674
675 if (std_hfs) {
676 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
677 promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
678
679 } else
680 pluskey = (HFSPlusCatalogKey *)&iterator->key;
681
682 builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
683 if (std_hfs) {
684 FREE(pluskey, M_TEMP);
685 }
686 }
687
688 if (desc_cnid != NULL) {
689 *desc_cnid = cnid;
690 }
691 exit:
692 FREE(iterator, M_TEMP);
693 FREE(recp, M_TEMP);
694
695 return MacToVFSError(result);
696 }
697
698
699 /*
700 * cat_create - create a node in the catalog
701 */
702 __private_extern__
703 int
704 cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
705 struct cat_desc *out_descp)
706 {
707 ExtendedVCB * vcb;
708 FCB * fcb;
709 struct btobj * bto;
710 FSBufferDescriptor btdata;
711 u_int32_t nextCNID;
712 u_int32_t datalen;
713 int std_hfs;
714 int result = 0;
715 u_long encoding;
716 int modeformat;
717 int mntlock = 0;
718
719 modeformat = attrp->ca_mode & S_IFMT;
720
721 vcb = HFSTOVCB(hfsmp);
722 fcb = GetFileControlBlock(vcb->catalogRefNum);
723 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
724
725 /*
726 * Atomically get the next CNID. If we have wrapped the CNIDs
727 * then keep the hfsmp lock held until we have found a CNID.
728 */
729 HFS_MOUNT_LOCK(hfsmp, TRUE);
730 mntlock = 1;
731 nextCNID = hfsmp->vcbNxtCNID;
732 if (nextCNID == 0xFFFFFFFF) {
733 if (std_hfs) {
734 result = ENOSPC;
735 } else {
736 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
737 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
738 }
739 } else {
740 hfsmp->vcbNxtCNID++;
741 }
742 hfsmp->vcbFlags |= 0xFF00;
743 /* OK to drop lock if CNIDs are not wrapping */
744 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask) == 0) {
745 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
746 mntlock = 0;
747 if (result)
748 return (result); /* HFS only exit */
749 }
750
751 /* Get space for iterator, key and data */
752 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
753 bto->iterator.hint.nodeNum = 0;
754
755 result = buildkey(hfsmp, descp, &bto->key, 0);
756 if (result)
757 goto exit;
758
759 if (!std_hfs) {
760 encoding = hfs_pickencoding(bto->key.nodeName.unicode,
761 bto->key.nodeName.length);
762 hfs_setencodingbits(hfsmp, encoding);
763 }
764
765 /*
766 * Insert the thread record first
767 */
768 if (!std_hfs || (modeformat == S_IFDIR)) {
769 datalen = buildthread((void*)&bto->key, &bto->data, std_hfs,
770 S_ISDIR(attrp->ca_mode));
771 btdata.bufferAddress = &bto->data;
772 btdata.itemSize = datalen;
773 btdata.itemCount = 1;
774
775 for (;;) {
776 buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
777
778 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
779 if ((result == btExists) && !std_hfs && mntlock) {
780 /*
781 * Allow CNIDs on HFS Plus volumes to wrap around
782 */
783 if (++nextCNID < kHFSFirstUserCatalogNodeID) {
784 nextCNID = kHFSFirstUserCatalogNodeID;
785 }
786 continue;
787 }
788 break;
789 }
790 if (result) goto exit;
791 }
792
793 /*
794 * CNID is now established. If we have wrapped then
795 * update the vcbNxtCNID and drop the vcb lock.
796 */
797 if (mntlock) {
798 hfsmp->vcbNxtCNID = nextCNID + 1;
799 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
800 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
801 }
802 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
803 mntlock = 0;
804 }
805
806 /*
807 * Now insert the file/directory record
808 */
809 buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen);
810 btdata.bufferAddress = &bto->data;
811 btdata.itemSize = datalen;
812 btdata.itemCount = 1;
813
814 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
815
816 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
817 if (result) {
818 if (result == btExists)
819 result = EEXIST;
820
821 /* Back out the thread record */
822 if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
823 buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key);
824 (void) BTDeleteRecord(fcb, &bto->iterator);
825 }
826 goto exit;
827 }
828
829 /*
830 * Insert was Successfull, update name, parent and volume
831 */
832
833
834 if (out_descp != NULL) {
835 HFSPlusCatalogKey * pluskey = NULL;
836
837 if (std_hfs) {
838 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
839 promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
840
841 } else
842 pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
843
844 builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum,
845 encoding, S_ISDIR(attrp->ca_mode), out_descp);
846 if (std_hfs) {
847 FREE(pluskey, M_TEMP);
848 }
849 }
850 attrp->ca_fileid = nextCNID;
851
852 exit:
853 if (mntlock)
854 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
855
856 (void) BTFlushPath(fcb);
857 FREE(bto, M_TEMP);
858
859 return MacToVFSError(result);
860 }
861
862
863 /*
864 * cnode_rename - rename a catalog node
865 *
866 * Assumes that the target's directory exists.
867 *
868 * Order of B-tree operations:
869 * 1. BTSearchRecord(from_cnode, &data);
870 * 2. BTInsertRecord(to_cnode, &data);
871 * 3. BTDeleteRecord(from_cnode);
872 * 4. BTDeleteRecord(from_thread);
873 * 5. BTInsertRecord(to_thread);
874 */
875 __private_extern__
876 int
877 cat_rename (
878 struct hfsmount * hfsmp,
879 struct cat_desc * from_cdp,
880 struct cat_desc * todir_cdp,
881 struct cat_desc * to_cdp,
882 struct cat_desc * out_cdp )
883 {
884 struct BTreeIterator * to_iterator = NULL;
885 struct BTreeIterator * from_iterator = NULL;
886 FSBufferDescriptor btdata;
887 CatalogRecord * recp = NULL;
888 HFSPlusCatalogKey * to_key;
889 ExtendedVCB * vcb;
890 FCB * fcb;
891 UInt16 datasize;
892 int result = 0;
893 int sourcegone = 0;
894 int skipthread = 0;
895 int directory = from_cdp->cd_flags & CD_ISDIR;
896 int std_hfs;
897 u_long encoding = 0;
898
899 vcb = HFSTOVCB(hfsmp);
900 fcb = GetFileControlBlock(vcb->catalogRefNum);
901 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
902
903 if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
904 return (EINVAL);
905
906 MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK);
907 bzero(from_iterator, sizeof(*from_iterator));
908 if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0)))
909 goto exit;
910
911 MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK);
912 bzero(to_iterator, sizeof(*to_iterator));
913 if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
914 goto exit;
915
916 to_key = (HFSPlusCatalogKey *)&to_iterator->key;
917 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
918 BDINIT(btdata, recp);
919
920 /*
921 * When moving a directory, make sure its a valid move.
922 */
923 if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
924 struct BTreeIterator iterator;
925 cnid_t cnid = from_cdp->cd_cnid;
926 cnid_t pathcnid = todir_cdp->cd_parentcnid;
927
928 /* First check the obvious ones */
929 if (cnid == fsRtDirID ||
930 cnid == to_cdp->cd_parentcnid ||
931 cnid == pathcnid) {
932 result = EINVAL;
933 goto exit;
934 }
935 bzero(&iterator, sizeof(iterator));
936 /*
937 * Traverese destination path all the way back to the root
938 * making sure that source directory is not encountered.
939 *
940 */
941 while (pathcnid > fsRtDirID) {
942 buildthreadkey(pathcnid, std_hfs,
943 (CatalogKey *)&iterator.key);
944 result = BTSearchRecord(fcb, &iterator, &btdata,
945 &datasize, NULL);
946 if (result) goto exit;
947
948 pathcnid = getparentcnid(recp);
949 if (pathcnid == cnid) {
950 result = EINVAL;
951 goto exit;
952 }
953 }
954 }
955
956 /*
957 * Step 1: Find cnode data at old location
958 */
959 result = BTSearchRecord(fcb, from_iterator, &btdata,
960 &datasize, from_iterator);
961 if (result) {
962 if (std_hfs || (result != btNotFound))
963 goto exit;
964
965 struct cat_desc temp_desc;
966
967 /* Probably the node has mangled name */
968 result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL);
969 if (result)
970 goto exit;
971
972 /* The file has mangled name. Search the cnode data using full name */
973 bzero(from_iterator, sizeof(*from_iterator));
974 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
975 if (result) {
976 cat_releasedesc(&temp_desc);
977 goto exit;
978 }
979
980 result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
981 if (result) {
982 cat_releasedesc(&temp_desc);
983 goto exit;
984 }
985
986 cat_releasedesc(&temp_desc);
987 }
988
989 /* Update the text encoding (on disk and in descriptor) */
990 if (!std_hfs) {
991 encoding = hfs_pickencoding(to_key->nodeName.unicode,
992 to_key->nodeName.length);
993 hfs_setencodingbits(hfsmp, encoding);
994 recp->hfsPlusFile.textEncoding = encoding;
995 if (out_cdp)
996 out_cdp->cd_encoding = encoding;
997 }
998
999 if (std_hfs && !directory &&
1000 !(recp->hfsFile.flags & kHFSThreadExistsMask))
1001 skipthread = 1;
1002 #if 0
1003 /*
1004 * If the keys are identical then there's nothing left to do!
1005 *
1006 * update the hint and exit
1007 *
1008 */
1009 if (std_hfs && hfskeycompare(to_key, iter->key) == 0)
1010 goto exit;
1011 if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0)
1012 goto exit;
1013 #endif
1014
1015 /* Step 2: Insert cnode at new location */
1016 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1017 if (result == btExists) {
1018 int fromtype = recp->recordType;
1019
1020 if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
1021 goto exit; /* EEXIST */
1022
1023 /* Find cnode data at new location */
1024 result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
1025
1026 if ((fromtype != recp->recordType) ||
1027 (from_cdp->cd_cnid != getcnid(recp)))
1028 goto exit; /* EEXIST */
1029
1030 /* The old name is a case variant and must be removed */
1031 result = BTDeleteRecord(fcb, from_iterator);
1032 if (result)
1033 goto exit;
1034
1035 /* Insert cnode (now that case duplicate is gone) */
1036 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1037 if (result) {
1038 /* Try and restore original before leaving */
1039 // XXXdbg
1040 #if 1
1041 {
1042 int err;
1043 err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1044 if (err)
1045 panic("cat_create: could not undo (BTInsert = %d)", err);
1046 }
1047 #else
1048 (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1049 #endif
1050 goto exit;
1051 }
1052 sourcegone = 1;
1053 }
1054 if (result)
1055 goto exit;
1056
1057 /* Step 3: Remove cnode from old location */
1058 if (!sourcegone) {
1059 result = BTDeleteRecord(fcb, from_iterator);
1060 if (result) {
1061 /* Try and delete new record before leaving */
1062 // XXXdbg
1063 #if 1
1064 {
1065 int err;
1066 err = BTDeleteRecord(fcb, to_iterator);
1067 if (err)
1068 panic("cat_create: could not undo (BTDelete = %d)", err);
1069 }
1070 #else
1071 (void) BTDeleteRecord(fcb, to_iterator);
1072 #endif
1073 goto exit;
1074 }
1075 }
1076
1077 /* #### POINT OF NO RETURN #### */
1078
1079 /*
1080 * Step 4: Remove cnode's old thread record
1081 */
1082 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1083 (void) BTDeleteRecord(fcb, from_iterator);
1084
1085 /*
1086 * Step 5: Insert cnode's new thread record
1087 * (optional for HFS files)
1088 */
1089 if (!skipthread) {
1090 datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
1091 btdata.itemSize = datasize;
1092 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1093 result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1094 }
1095
1096 if (out_cdp) {
1097 HFSPlusCatalogKey * pluskey = NULL;
1098
1099 if (std_hfs) {
1100 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1101 promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
1102
1103 /* Save the real encoding hint in the Finder Info (field 4). */
1104 if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
1105 u_long realhint;
1106
1107 realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
1108 vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
1109 }
1110
1111 } else
1112 pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
1113
1114 builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
1115 encoding, directory, out_cdp);
1116 if (std_hfs) {
1117 FREE(pluskey, M_TEMP);
1118 }
1119 }
1120 exit:
1121 (void) BTFlushPath(fcb);
1122 if (from_iterator)
1123 FREE(from_iterator, M_TEMP);
1124 if (to_iterator)
1125 FREE(to_iterator, M_TEMP);
1126 if (recp)
1127 FREE(recp, M_TEMP);
1128 return MacToVFSError(result);
1129 }
1130
1131
1132 /*
1133 * cat_delete - delete a node from the catalog
1134 *
1135 * Order of B-tree operations:
1136 * 1. BTDeleteRecord(cnode);
1137 * 2. BTDeleteRecord(thread);
1138 * 3. BTUpdateRecord(parent);
1139 */
1140 __private_extern__
1141 int
1142 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
1143 {
1144 ExtendedVCB * vcb;
1145 FCB * fcb;
1146 BTreeIterator *iterator;
1147 cnid_t cnid;
1148 int std_hfs;
1149 int result;
1150
1151 vcb = HFSTOVCB(hfsmp);
1152 fcb = GetFileControlBlock(vcb->catalogRefNum);
1153 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1154
1155 /* Preflight check:
1156 *
1157 * The root directory cannot be deleted
1158 * A directory must be empty
1159 * A file must be zero length (no blocks)
1160 */
1161 if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
1162 descp->cd_parentcnid == kHFSRootParentID)
1163 return (EINVAL);
1164
1165 /* XXX Preflight Missing */
1166
1167 /* Get space for iterator */
1168 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1169 iterator->hint.nodeNum = 0;
1170
1171 /*
1172 * Derive a key from either the file ID (for a virtual inode)
1173 * or the descriptor.
1174 */
1175 if (descp->cd_namelen == 0) {
1176 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1177 cnid = attrp->ca_fileid;
1178 } else {
1179 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1180 cnid = descp->cd_cnid;
1181 }
1182 if (result)
1183 goto exit;
1184
1185 /* Delete record */
1186 result = BTDeleteRecord(fcb, iterator);
1187 if (result) {
1188 if (std_hfs || (result != btNotFound))
1189 goto exit;
1190
1191 struct cat_desc temp_desc;
1192
1193 /* Probably the node has mangled name */
1194 result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL);
1195 if (result)
1196 goto exit;
1197
1198 /* The file has mangled name. Delete the file using full name */
1199 bzero(iterator, sizeof(*iterator));
1200 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
1201 cnid = temp_desc.cd_cnid;
1202 if (result) {
1203 cat_releasedesc(&temp_desc);
1204 goto exit;
1205 }
1206
1207 result = BTDeleteRecord(fcb, iterator);
1208 if (result) {
1209 cat_releasedesc(&temp_desc);
1210 goto exit;
1211 }
1212
1213 cat_releasedesc(&temp_desc);
1214 }
1215
1216 /* Delete thread record, ignore errors */
1217 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
1218 (void) BTDeleteRecord(fcb, iterator);
1219
1220 exit:
1221 (void) BTFlushPath(fcb);
1222 FREE(iterator, M_TEMP);
1223
1224 return MacToVFSError(result);
1225 }
1226
1227
1228 /*
1229 * cnode_update - update the catalog node described by descp
1230 * using the data from attrp and forkp.
1231 */
1232 __private_extern__
1233 int
1234 cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
1235 struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
1236 {
1237 ExtendedVCB * vcb;
1238 FCB * fcb;
1239 BTreeIterator * iterator;
1240 struct update_state state;
1241 int std_hfs;
1242 int result;
1243
1244 vcb = HFSTOVCB(hfsmp);
1245 fcb = GetFileControlBlock(vcb->catalogRefNum);
1246 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1247
1248 state.s_desc = descp;
1249 state.s_attr = attrp;
1250 state.s_datafork = dataforkp;
1251 state.s_rsrcfork = rsrcforkp;
1252 state.s_hfsmp = hfsmp;
1253
1254 /* Get space for iterator */
1255 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1256
1257 /*
1258 * For open-deleted files we need to do a lookup by cnid
1259 * (using thread rec).
1260 *
1261 * For hard links, the target of the update is the inode
1262 * itself (not the link record) so a lookup by fileid
1263 * (i.e. thread rec) is needed.
1264 */
1265 if ((descp->cd_cnid != attrp->ca_fileid) || (descp->cd_namelen == 0))
1266 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1267 else
1268 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1269 if (result)
1270 goto exit;
1271
1272 /* Pass a node hint */
1273 iterator->hint.nodeNum = descp->cd_hint;
1274
1275 result = BTUpdateRecord(fcb, iterator,
1276 (IterateCallBackProcPtr)catrec_update, &state);
1277 if (result)
1278 goto exit;
1279
1280 /* Update the node hint. */
1281 descp->cd_hint = iterator->hint.nodeNum;
1282
1283 exit:
1284 (void) BTFlushPath(fcb);
1285 FREE(iterator, M_TEMP);
1286
1287 return MacToVFSError(result);
1288 }
1289
1290 /*
1291 * catrec_update - Update the fields of a catalog record
1292 * This is called from within BTUpdateRecord.
1293 */
1294 static int
1295 catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
1296 {
1297 struct cat_desc *descp;
1298 struct cat_attr *attrp;
1299 struct cat_fork *forkp;
1300 struct hfsmount *hfsmp;
1301 long blksize;
1302 int i;
1303
1304 descp = state->s_desc;
1305 attrp = state->s_attr;
1306 hfsmp = state->s_hfsmp;
1307 blksize = HFSTOVCB(hfsmp)->blockSize;
1308
1309 switch (crp->recordType) {
1310 case kHFSFolderRecord: {
1311 HFSCatalogFolder *dir;
1312
1313 dir = (struct HFSCatalogFolder *)crp;
1314 /* Do a quick sanity check */
1315 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1316 (dir->folderID != descp->cd_cnid))
1317 return (btNotFound);
1318 dir->valence = attrp->ca_entries;
1319 dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1320 dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1321 dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1322 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16);
1323 bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16);
1324 break;
1325 }
1326 case kHFSFileRecord: {
1327 HFSCatalogFile *file;
1328
1329 file = (struct HFSCatalogFile *)crp;
1330 /* Do a quick sanity check */
1331 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1332 (file->fileID != attrp->ca_fileid))
1333 return (btNotFound);
1334 file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1335 file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1336 file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1337 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16);
1338 bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16);
1339 if (state->s_rsrcfork) {
1340 forkp = state->s_rsrcfork;
1341 file->rsrcLogicalSize = forkp->cf_size;
1342 file->rsrcPhysicalSize = forkp->cf_blocks * blksize;
1343 for (i = 0; i < kHFSExtentDensity; ++i) {
1344 file->rsrcExtents[i].startBlock =
1345 (u_int16_t)forkp->cf_extents[i].startBlock;
1346 file->rsrcExtents[i].blockCount =
1347 (u_int16_t)forkp->cf_extents[i].blockCount;
1348 }
1349 }
1350 if (state->s_datafork) {
1351 forkp = state->s_datafork;
1352 file->dataLogicalSize = forkp->cf_size;
1353 file->dataPhysicalSize = forkp->cf_blocks * blksize;
1354 for (i = 0; i < kHFSExtentDensity; ++i) {
1355 file->dataExtents[i].startBlock =
1356 (u_int16_t)forkp->cf_extents[i].startBlock;
1357 file->dataExtents[i].blockCount =
1358 (u_int16_t)forkp->cf_extents[i].blockCount;
1359 }
1360 }
1361 break;
1362 }
1363 case kHFSPlusFolderRecord: {
1364 HFSPlusCatalogFolder *dir;
1365
1366 dir = (struct HFSPlusCatalogFolder *)crp;
1367 /* Do a quick sanity check */
1368 if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
1369 (dir->folderID != descp->cd_cnid))
1370 return (btNotFound);
1371 dir->flags = attrp->ca_recflags;
1372 dir->valence = attrp->ca_entries;
1373 dir->createDate = to_hfs_time(attrp->ca_itime);
1374 dir->contentModDate = to_hfs_time(attrp->ca_mtime);
1375 dir->backupDate = to_hfs_time(attrp->ca_btime);
1376 dir->accessDate = to_hfs_time(attrp->ca_atime);
1377 attrp->ca_atimeondisk = attrp->ca_atime;
1378 dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
1379 dir->textEncoding = descp->cd_encoding;
1380 dir->attrBlocks = attrp->ca_attrblks;
1381 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
1382 /*
1383 * Update the BSD Info if it was already initialized on
1384 * disk or if the runtime values have been modified.
1385 *
1386 * If the BSD info was already initialized, but
1387 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1388 * probably different than what was on disk. We don't want
1389 * to overwrite the on-disk values (so if we turn off
1390 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1391 * This way, we can still change fields like the mode or
1392 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1393 *
1394 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1395 * won't change the uid or gid from their defaults. So, if
1396 * the BSD info wasn't set, and the runtime values are not
1397 * default, then what changed was the mode or flags. We
1398 * have to set the uid and gid to something, so use the
1399 * supplied values (which will be default), which has the
1400 * same effect as creating a new file while
1401 * MNT_UNKNOWNPERMISSIONS is set.
1402 */
1403 if ((dir->bsdInfo.fileMode != 0) ||
1404 (attrp->ca_flags != 0) ||
1405 (attrp->ca_uid != hfsmp->hfs_uid) ||
1406 (attrp->ca_gid != hfsmp->hfs_gid) ||
1407 ((attrp->ca_mode & ALLPERMS) !=
1408 (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
1409 if ((dir->bsdInfo.fileMode == 0) ||
1410 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1411 dir->bsdInfo.ownerID = attrp->ca_uid;
1412 dir->bsdInfo.groupID = attrp->ca_gid;
1413 }
1414 dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1415 dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1416 dir->bsdInfo.fileMode = attrp->ca_mode;
1417 }
1418 break;
1419 }
1420 case kHFSPlusFileRecord: {
1421 HFSPlusCatalogFile *file;
1422
1423 file = (struct HFSPlusCatalogFile *)crp;
1424 /* Do a quick sanity check */
1425 if (file->fileID != attrp->ca_fileid)
1426 return (btNotFound);
1427 file->flags = attrp->ca_recflags;
1428 file->createDate = to_hfs_time(attrp->ca_itime);
1429 file->contentModDate = to_hfs_time(attrp->ca_mtime);
1430 file->backupDate = to_hfs_time(attrp->ca_btime);
1431 file->accessDate = to_hfs_time(attrp->ca_atime);
1432 attrp->ca_atimeondisk = attrp->ca_atime;
1433 file->attributeModDate = to_hfs_time(attrp->ca_ctime);
1434 file->textEncoding = descp->cd_encoding;
1435 file->attrBlocks = attrp->ca_attrblks;
1436 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
1437 /*
1438 * Update the BSD Info if it was already initialized on
1439 * disk or if the runtime values have been modified.
1440 *
1441 * If the BSD info was already initialized, but
1442 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1443 * probably different than what was on disk. We don't want
1444 * to overwrite the on-disk values (so if we turn off
1445 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1446 * This way, we can still change fields like the mode or
1447 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1448 *
1449 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1450 * won't change the uid or gid from their defaults. So, if
1451 * the BSD info wasn't set, and the runtime values are not
1452 * default, then what changed was the mode or flags. We
1453 * have to set the uid and gid to something, so use the
1454 * supplied values (which will be default), which has the
1455 * same effect as creating a new file while
1456 * MNT_UNKNOWNPERMISSIONS is set.
1457 */
1458 if ((file->bsdInfo.fileMode != 0) ||
1459 (attrp->ca_flags != 0) ||
1460 (attrp->ca_uid != hfsmp->hfs_uid) ||
1461 (attrp->ca_gid != hfsmp->hfs_gid) ||
1462 ((attrp->ca_mode & ALLPERMS) !=
1463 (hfsmp->hfs_file_mask & ACCESSPERMS))) {
1464 if ((file->bsdInfo.fileMode == 0) ||
1465 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1466 file->bsdInfo.ownerID = attrp->ca_uid;
1467 file->bsdInfo.groupID = attrp->ca_gid;
1468 }
1469 file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1470 file->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1471 file->bsdInfo.fileMode = attrp->ca_mode;
1472 }
1473 if (state->s_rsrcfork) {
1474 forkp = state->s_rsrcfork;
1475 file->resourceFork.logicalSize = forkp->cf_size;
1476 file->resourceFork.totalBlocks = forkp->cf_blocks;
1477 bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
1478 sizeof(HFSPlusExtentRecord));
1479 /* Push blocks read to disk */
1480 file->resourceFork.clumpSize =
1481 howmany(forkp->cf_bytesread, blksize);
1482 }
1483 if (state->s_datafork) {
1484 forkp = state->s_datafork;
1485 file->dataFork.logicalSize = forkp->cf_size;
1486 file->dataFork.totalBlocks = forkp->cf_blocks;
1487 bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
1488 sizeof(HFSPlusExtentRecord));
1489 /* Push blocks read to disk */
1490 file->dataFork.clumpSize =
1491 howmany(forkp->cf_bytesread, blksize);
1492 }
1493
1494 if ((file->resourceFork.extents[0].startBlock != 0) &&
1495 (file->resourceFork.extents[0].startBlock ==
1496 file->dataFork.extents[0].startBlock))
1497 panic("catrec_update: rsrc fork == data fork");
1498
1499 /* Synchronize the lock state */
1500 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
1501 file->flags |= kHFSFileLockedMask;
1502 else
1503 file->flags &= ~kHFSFileLockedMask;
1504
1505 /* Push out special field if necessary */
1506 if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode))
1507 file->bsdInfo.special.rawDevice = attrp->ca_rdev;
1508 else if (descp->cd_cnid != attrp->ca_fileid
1509 || attrp->ca_nlink == 2)
1510 file->bsdInfo.special.linkCount = attrp->ca_nlink;
1511 break;
1512 }
1513 default:
1514 return (btNotFound);
1515 }
1516 return (0);
1517 }
1518
1519 /*
1520 * Callback to collect directory entries.
1521 * Called with readattr_state for each item in a directory.
1522 */
1523 struct readattr_state {
1524 struct hfsmount *hfsmp;
1525 struct cat_entrylist *list;
1526 cnid_t dir_cnid;
1527 int stdhfs;
1528 int error;
1529 };
1530
1531 static int
1532 cat_readattr(const CatalogKey *key, const CatalogRecord *rec,
1533 struct readattr_state *state)
1534 {
1535 struct cat_entrylist *list = state->list;
1536 struct hfsmount *hfsmp = state->hfsmp;
1537 struct cat_entry *cep;
1538 cnid_t parentcnid;
1539
1540 if (list->realentries >= list->maxentries)
1541 return (0); /* stop */
1542
1543 parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
1544
1545 switch(rec->recordType) {
1546 case kHFSPlusFolderRecord:
1547 case kHFSPlusFileRecord:
1548 case kHFSFolderRecord:
1549 case kHFSFileRecord:
1550 if (parentcnid != state->dir_cnid) {
1551 state->error = ENOENT;
1552 return (0); /* stop */
1553 }
1554 break;
1555 default:
1556 state->error = ENOENT;
1557 return (0); /* stop */
1558 }
1559
1560 /* Hide the private meta data directory and journal files */
1561 if (parentcnid == kHFSRootFolderID) {
1562 if ((rec->recordType == kHFSPlusFolderRecord) &&
1563 (rec->hfsPlusFolder.folderID == hfsmp->hfs_privdir_desc.cd_cnid)) {
1564 return (1); /* continue */
1565 }
1566 if (hfsmp->jnl &&
1567 (rec->recordType == kHFSPlusFileRecord) &&
1568 ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
1569 (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
1570
1571 return (1); /* continue */
1572 }
1573 }
1574
1575 cep = &list->entry[list->realentries++];
1576
1577 if (state->stdhfs) {
1578 struct HFSPlusCatalogFile cnoderec;
1579 HFSPlusCatalogKey * pluskey;
1580 long encoding;
1581
1582 promoteattr(hfsmp, rec, &cnoderec);
1583 getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
1584
1585 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1586 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
1587 builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
1588 FREE(pluskey, M_TEMP);
1589
1590 if (rec->recordType == kHFSFileRecord) {
1591 int blksize = HFSTOVCB(hfsmp)->blockSize;
1592
1593 cep->ce_datasize = rec->hfsFile.dataLogicalSize;
1594 cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
1595 cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
1596 cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
1597 }
1598 } else {
1599 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
1600 builddesc((HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
1601 isadir(rec), &cep->ce_desc);
1602
1603 if (rec->recordType == kHFSPlusFileRecord) {
1604 cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
1605 cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
1606 cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
1607 cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
1608
1609 /* Save link reference for later processing. */
1610 if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
1611 && (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator))
1612 cep->ce_attr.ca_rdev = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
1613 }
1614 }
1615
1616 return (list->realentries < list->maxentries);
1617 }
1618
1619 /*
1620 * Pack a cat_entrylist buffer with attributes from the catalog
1621 *
1622 * Note: index is zero relative
1623 */
1624 __private_extern__
1625 int
1626 cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
1627 {
1628 FCB* fcb;
1629 CatalogKey * key;
1630 BTreeIterator * iterator;
1631 struct readattr_state state;
1632 cnid_t parentcnid;
1633 int i;
1634 int std_hfs;
1635 int index;
1636 int have_key;
1637 int result = 0;
1638
1639 ce_list->realentries = 0;
1640
1641 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
1642 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
1643 parentcnid = dirhint->dh_desc.cd_parentcnid;
1644
1645 state.hfsmp = hfsmp;
1646 state.list = ce_list;
1647 state.dir_cnid = parentcnid;
1648 state.stdhfs = std_hfs;
1649 state.error = 0;
1650
1651 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1652 bzero(iterator, sizeof(*iterator));
1653 key = (CatalogKey *)&iterator->key;
1654 have_key = 0;
1655 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
1656 index = dirhint->dh_index + 1;
1657
1658 /*
1659 * Attempt to build a key from cached filename
1660 */
1661 if (dirhint->dh_desc.cd_namelen != 0) {
1662 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
1663 have_key = 1;
1664 }
1665 }
1666
1667 /*
1668 * If the last entry wasn't cached then position the btree iterator
1669 */
1670 if ((index == 0) || !have_key) {
1671 /*
1672 * Position the iterator at the directory's thread record.
1673 * (i.e. just before the first entry)
1674 */
1675 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
1676 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
1677 if (result) {
1678 result = MacToVFSError(result);
1679 goto exit;
1680 }
1681
1682 /*
1683 * Iterate until we reach the entry just
1684 * before the one we want to start with.
1685 */
1686 if (index > 0) {
1687 struct position_state ps;
1688
1689 ps.error = 0;
1690 ps.count = 0;
1691 ps.index = index;
1692 ps.parentID = dirhint->dh_desc.cd_parentcnid;
1693 ps.hfsmp = hfsmp;
1694
1695 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
1696 (IterateCallBackProcPtr)cat_findposition, &ps);
1697 if (ps.error)
1698 result = ps.error;
1699 else
1700 result = MacToVFSError(result);
1701 if (result) {
1702 result = MacToVFSError(result);
1703 goto exit;
1704 }
1705 }
1706 }
1707
1708 /* Fill list with entries starting at iterator->key. */
1709 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
1710 (IterateCallBackProcPtr)cat_readattr, &state);
1711
1712 if (state.error)
1713 result = state.error;
1714 else if (ce_list->realentries == 0)
1715 result = ENOENT;
1716 else
1717 result = MacToVFSError(result);
1718
1719 if (std_hfs)
1720 goto exit;
1721
1722 /*
1723 * Resolve any hard links.
1724 */
1725 for (i = 0; i < (int)ce_list->realentries; ++i) {
1726 struct FndrFileInfo *fip;
1727 struct cat_entry *cep;
1728 struct HFSPlusCatalogFile filerec;
1729
1730 cep = &ce_list->entry[i];
1731 if (!S_ISREG(cep->ce_attr.ca_mode))
1732 continue;
1733
1734 /* Note: Finder info is still in Big Endian */
1735 fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
1736
1737 /* Check for hard link signature. */
1738 if ((cep->ce_attr.ca_rdev != 0)
1739 && (SWAP_BE32(fip->fdType) == kHardLinkFileType)
1740 && (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
1741 && ((cep->ce_attr.ca_itime == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
1742 (cep->ce_attr.ca_itime == (time_t)hfsmp->hfs_metadata_createdate))) {
1743
1744 if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &filerec) != 0)
1745 continue;
1746 /* Repack entry from inode record. */
1747 getbsdattr(hfsmp, &filerec, &cep->ce_attr);
1748 cep->ce_datasize = filerec.dataFork.logicalSize;
1749 cep->ce_datablks = filerec.dataFork.totalBlocks;
1750 cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
1751 cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
1752 }
1753 }
1754 exit:
1755 FREE(iterator, M_TEMP);
1756
1757 return MacToVFSError(result);
1758 }
1759
1760 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1761
1762 /*
1763 * Callback to pack directory entries.
1764 * Called with packdirentry_state for each item in a directory.
1765 */
1766
1767 /* Hard link information collected during cat_getdirentries. */
1768 struct linkinfo {
1769 u_long link_ref;
1770 user_addr_t dirent_addr;
1771 };
1772 typedef struct linkinfo linkinfo_t;
1773
1774 /* State information for the cat_packdirentry callback function. */
1775 struct packdirentry_state {
1776 int cbs_extended;
1777 u_int32_t cbs_parentID;
1778 u_int32_t cbs_index;
1779 uio_t cbs_uio;
1780 ExtendedVCB * cbs_hfsmp;
1781 int cbs_result;
1782 int32_t cbs_nlinks;
1783 int32_t cbs_maxlinks;
1784 linkinfo_t * cbs_linkinfo;
1785 struct cat_desc * cbs_desc;
1786 // struct dirent * cbs_stdentry;
1787 // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
1788 struct direntry * cbs_direntry;
1789 struct direntry * cbs_prevdirentry;
1790 u_int32_t cbs_previlinkref;
1791 Boolean cbs_hasprevdirentry;
1792 Boolean cbs_eof;
1793 };
1794
1795 static int
1796 cat_packdirentry(const CatalogKey *ckp, const CatalogRecord *crp,
1797 struct packdirentry_state *state)
1798 {
1799 struct hfsmount *hfsmp;
1800 CatalogName *cnp;
1801 cnid_t curID;
1802 OSErr result;
1803 struct dirent catent;
1804 struct direntry * entry = NULL;
1805 time_t itime;
1806 u_int32_t ilinkref = 0;
1807 u_int32_t curlinkref = 0;
1808 cnid_t cnid;
1809 int hide = 0;
1810 u_int8_t type;
1811 u_int8_t is_mangled = 0;
1812 char *nameptr;
1813 user_addr_t uiobase;
1814 size_t namelen = 0;
1815 size_t maxnamelen;
1816 size_t uiosize = 0;
1817 caddr_t uioaddr;
1818 Boolean stop_after_pack = false;
1819
1820 hfsmp = state->cbs_hfsmp;
1821
1822 if (hfsmp->hfs_flags & HFS_STANDARD)
1823 curID = ckp->hfs.parentID;
1824 else
1825 curID = ckp->hfsPlus.parentID;
1826
1827 /* We're done when parent directory changes */
1828 if (state->cbs_parentID != curID) {
1829 if (state->cbs_extended) {
1830 if (state->cbs_hasprevdirentry) { /* the last record haven't been returned yet, so we want to stop after
1831 * packing the last item */
1832 stop_after_pack = true;
1833 } else {
1834 state->cbs_result = ENOENT;
1835 return (0); /* stop */
1836 }
1837 } else {
1838 state->cbs_result = ENOENT;
1839 return (0); /* stop */
1840 }
1841 }
1842
1843 if (state->cbs_extended) {
1844 entry = state->cbs_direntry;
1845 nameptr = &entry->d_name[0];
1846 maxnamelen = NAME_MAX;
1847 } else {
1848 nameptr = &catent.d_name[0];
1849 maxnamelen = NAME_MAX;
1850 }
1851
1852 if (state->cbs_extended && stop_after_pack) {
1853 cnid = INT_MAX; /* the last item returns a non-zero invalid cookie */
1854 } else {
1855 if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
1856 switch(crp->recordType) {
1857 case kHFSPlusFolderRecord:
1858 type = DT_DIR;
1859 cnid = crp->hfsPlusFolder.folderID;
1860 /* Hide our private meta data directory */
1861 if ((curID == kHFSRootFolderID) &&
1862 (cnid == hfsmp->hfs_privdir_desc.cd_cnid)) {
1863 hide = 1;
1864 }
1865
1866 break;
1867 case kHFSPlusFileRecord:
1868 itime = to_bsd_time(crp->hfsPlusFile.createDate);
1869 /*
1870 * When a hardlink link is encountered save its link ref.
1871 */
1872 if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
1873 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
1874 ((itime == (time_t)hfsmp->hfs_itime) ||
1875 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
1876 ilinkref = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
1877 }
1878 type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
1879 cnid = crp->hfsPlusFile.fileID;
1880 /* Hide the journal files */
1881 if ((curID == kHFSRootFolderID) &&
1882 (hfsmp->jnl) &&
1883 ((cnid == hfsmp->hfs_jnlfileid) ||
1884 (cnid == hfsmp->hfs_jnlinfoblkid))) {
1885 hide = 1;
1886 }
1887 break;
1888 default:
1889 return (0); /* stop */
1890 };
1891
1892 cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
1893 result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
1894 nameptr, &namelen, maxnamelen + 1, ':', 0);
1895 if (result == ENAMETOOLONG) {
1896 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
1897 cnp->ustr.unicode, maxnamelen + 1,
1898 (ByteCount*)&namelen, nameptr,
1899 cnid);
1900 is_mangled = 1;
1901 }
1902 } else { /* hfs */
1903 switch(crp->recordType) {
1904 case kHFSFolderRecord:
1905 type = DT_DIR;
1906 cnid = crp->hfsFolder.folderID;
1907 break;
1908 case kHFSFileRecord:
1909 type = DT_REG;
1910 cnid = crp->hfsFile.fileID;
1911 break;
1912 default:
1913 return (0); /* stop */
1914 };
1915
1916 cnp = (CatalogName*) ckp->hfs.nodeName;
1917 result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1,
1918 (ByteCount *)&namelen, nameptr);
1919 /*
1920 * When an HFS name cannot be encoded with the current
1921 * volume encoding we use MacRoman as a fallback.
1922 */
1923 if (result)
1924 result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1,
1925 (ByteCount *)&namelen, nameptr);
1926 }
1927 }
1928
1929 if (state->cbs_extended) {
1930 /*
1931 * The index is 1 relative and includes "." and ".."
1932 *
1933 * Also stuff the cnid in the upper 32 bits of the cookie. The cookie is stored to the previous entry, which
1934 * will be packed and copied this time
1935 */
1936 state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
1937 uiosize = state->cbs_prevdirentry->d_reclen;
1938 uioaddr = (caddr_t) state->cbs_prevdirentry;
1939 } else {
1940 catent.d_type = type;
1941 catent.d_namlen = namelen;
1942 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
1943 if (hide)
1944 catent.d_fileno = 0; /* file number = 0 means skip entry */
1945 else
1946 catent.d_fileno = cnid;
1947 uioaddr = (caddr_t) &catent;
1948 }
1949
1950 /* Save current base address for post processing of hard-links. */
1951 uiobase = uio_curriovbase(state->cbs_uio);
1952
1953 /* If this entry won't fit then we're done */
1954 if ((uiosize > uio_resid(state->cbs_uio)) ||
1955 (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
1956 return (0); /* stop */
1957 }
1958
1959 if (!state->cbs_extended || state->cbs_hasprevdirentry) {
1960 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
1961 if (state->cbs_result == 0) {
1962 ++state->cbs_index;
1963
1964 /* Remember previous entry */
1965 state->cbs_desc->cd_cnid = cnid;
1966 if (type == DT_DIR) {
1967 state->cbs_desc->cd_flags |= CD_ISDIR;
1968 } else {
1969 state->cbs_desc->cd_flags &= ~CD_ISDIR;
1970 }
1971 if (state->cbs_desc->cd_nameptr != NULL) {
1972 vfs_removename(state->cbs_desc->cd_nameptr);
1973 }
1974 #if 0
1975 state->cbs_desc->cd_encoding = xxxx;
1976 #endif
1977 if (!is_mangled) {
1978 state->cbs_desc->cd_namelen = namelen;
1979 state->cbs_desc->cd_nameptr = vfs_addname(nameptr, namelen, 0, 0);
1980 } else {
1981 /* Store unmangled name for the directory hint else it will
1982 * restart readdir at the last location again
1983 */
1984 char *new_nameptr;
1985 size_t bufsize;
1986 size_t tmp_namelen = 0;
1987
1988 cnp = (CatalogName *)&ckp->hfsPlus.nodeName;
1989 bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
1990 cnp->ustr.length * sizeof(UniChar),
1991 ':', 0);
1992 MALLOC(new_nameptr, char *, bufsize, M_TEMP, M_WAITOK);
1993 result = utf8_encodestr(cnp->ustr.unicode,
1994 cnp->ustr.length * sizeof(UniChar),
1995 new_nameptr, &tmp_namelen,
1996 bufsize, ':', 0);
1997
1998 state->cbs_desc->cd_namelen = tmp_namelen;
1999 state->cbs_desc->cd_nameptr = vfs_addname(new_nameptr, tmp_namelen, 0, 0);
2000
2001 FREE(new_nameptr, M_TEMP);
2002 }
2003 }
2004 if (state->cbs_hasprevdirentry) {
2005 curlinkref = ilinkref; /* save current */
2006 ilinkref = state->cbs_previlinkref; /* use previous */
2007 }
2008 /*
2009 * Record any hard links for post processing.
2010 */
2011 if ((ilinkref != 0) &&
2012 (state->cbs_result == 0) &&
2013 (state->cbs_nlinks < state->cbs_maxlinks)) {
2014 state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
2015 state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
2016 state->cbs_nlinks++;
2017 }
2018 if (state->cbs_hasprevdirentry) {
2019 ilinkref = curlinkref; /* restore current */
2020 }
2021 }
2022
2023 if (state->cbs_extended) { /* fill the direntry to be used the next time */
2024 if (stop_after_pack) {
2025 state->cbs_eof = true;
2026 return (0); /* stop */
2027 }
2028 entry->d_type = type;
2029 entry->d_namlen = namelen;
2030 entry->d_reclen = EXT_DIRENT_LEN(namelen);
2031 if (hide)
2032 entry->d_fileno = 0; /* file number = 0 means skip entry */
2033 else
2034 entry->d_fileno = cnid;
2035 /* swap the current and previous entry */
2036 struct direntry * tmp;
2037 tmp = state->cbs_direntry;
2038 state->cbs_direntry = state->cbs_prevdirentry;
2039 state->cbs_prevdirentry = tmp;
2040 state->cbs_hasprevdirentry = true;
2041 state->cbs_previlinkref = ilinkref;
2042 }
2043
2044 /* Continue iteration if there's room */
2045 return (state->cbs_result == 0 &&
2046 uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
2047 }
2048
2049
2050 /*
2051 * Pack a uio buffer with directory entries from the catalog
2052 */
2053 __private_extern__
2054 int
2055 cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
2056 uio_t uio, int extended, int * items, int * eofflag)
2057 {
2058 FCB* fcb;
2059 BTreeIterator * iterator;
2060 CatalogKey * key;
2061 struct packdirentry_state state;
2062 void * buffer;
2063 int bufsize;
2064 int maxlinks;
2065 int result;
2066 int index;
2067 int have_key;
2068
2069 fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
2070
2071 /*
2072 * Get a buffer for link info array, btree iterator and a direntry:
2073 */
2074 maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
2075 bufsize = (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
2076 if (extended) {
2077 bufsize += 2*sizeof(struct direntry);
2078 }
2079 MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
2080 bzero(buffer, bufsize);
2081
2082 state.cbs_extended = extended;
2083 state.cbs_hasprevdirentry = false;
2084 state.cbs_previlinkref = 0;
2085 state.cbs_nlinks = 0;
2086 state.cbs_maxlinks = maxlinks;
2087 state.cbs_linkinfo = (linkinfo_t *) buffer;
2088
2089 iterator = (BTreeIterator *) ((char *)buffer + (maxlinks * sizeof(linkinfo_t)));
2090 key = (CatalogKey *)&iterator->key;
2091 have_key = 0;
2092 index = dirhint->dh_index + 1;
2093 if (extended) {
2094 state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
2095 state.cbs_prevdirentry = state.cbs_direntry + 1;
2096 state.cbs_eof = false;
2097 }
2098 /*
2099 * Attempt to build a key from cached filename
2100 */
2101 if (dirhint->dh_desc.cd_namelen != 0) {
2102 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2103 have_key = 1;
2104 }
2105 }
2106
2107 /*
2108 * If the last entry wasn't cached then position the btree iterator
2109 */
2110 if ((index == 0) || !have_key) {
2111 /*
2112 * Position the iterator at the directory's thread record.
2113 * (i.e. just before the first entry)
2114 */
2115 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
2116 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
2117 if (result) {
2118 result = MacToVFSError(result);
2119 goto cleanup;
2120 }
2121
2122 /*
2123 * Iterate until we reach the entry just
2124 * before the one we want to start with.
2125 */
2126 if (index > 0) {
2127 struct position_state ps;
2128
2129 ps.error = 0;
2130 ps.count = 0;
2131 ps.index = index;
2132 ps.parentID = dirhint->dh_desc.cd_parentcnid;
2133 ps.hfsmp = hfsmp;
2134
2135 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2136 (IterateCallBackProcPtr)cat_findposition, &ps);
2137 if (ps.error)
2138 result = ps.error;
2139 else
2140 result = MacToVFSError(result);
2141 if (result) {
2142 result = MacToVFSError(result);
2143 goto cleanup;
2144 }
2145 }
2146 }
2147
2148 state.cbs_index = index;
2149 state.cbs_hfsmp = hfsmp;
2150 state.cbs_uio = uio;
2151 state.cbs_desc = &dirhint->dh_desc;
2152 state.cbs_result = 0;
2153 state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
2154
2155 enum BTreeIterationOperations op;
2156 if (extended && index != 0 && have_key)
2157 op = kBTreeCurrentRecord;
2158 else
2159 op = kBTreeNextRecord;
2160
2161 /*
2162 * Process as many entries as possible starting at iterator->key.
2163 */
2164 result = BTIterateRecords(fcb, op, iterator,
2165 (IterateCallBackProcPtr)cat_packdirentry, &state);
2166
2167 /* Note that state.cbs_index is still valid on errors */
2168 *items = state.cbs_index - index;
2169 index = state.cbs_index;
2170
2171 if (state.cbs_eof) {
2172 *eofflag = 1;
2173 }
2174
2175 /* Finish updating the catalog iterator. */
2176 dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
2177 dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
2178 dirhint->dh_index = index - 1;
2179
2180 /*
2181 * Post process any hard links to get the real file id.
2182 */
2183 if (state.cbs_nlinks > 0) {
2184 u_int32_t fileid = 0;
2185 user_addr_t address;
2186 int i;
2187
2188 for (i = 0; i < state.cbs_nlinks; ++i) {
2189 if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
2190 continue;
2191 /* This assumes that d_ino is always first field. */
2192 address = state.cbs_linkinfo[i].dirent_addr;
2193 if (address == (user_addr_t)0)
2194 continue;
2195 if (uio_isuserspace(uio)) {
2196 (void) copyout(&fileid, address,
2197 extended ? sizeof(ino64_t) : sizeof(ino_t));
2198 } else /* system space */ {
2199 ino64_t *inoptr = (ino64_t *)CAST_DOWN(caddr_t, address);
2200 *inoptr = fileid;
2201 }
2202 }
2203 }
2204
2205 if (state.cbs_result)
2206 result = state.cbs_result;
2207 else
2208 result = MacToVFSError(result);
2209
2210 if (result == ENOENT) {
2211 result = 0;
2212 }
2213
2214 cleanup:
2215 FREE(buffer, M_TEMP);
2216
2217 return (result);
2218 }
2219
2220
2221 /*
2222 * Callback to establish directory position.
2223 * Called with position_state for each item in a directory.
2224 */
2225 static int
2226 cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
2227 struct position_state *state)
2228 {
2229 cnid_t curID;
2230
2231 if (state->hfsmp->hfs_flags & HFS_STANDARD)
2232 curID = ckp->hfs.parentID;
2233 else
2234 curID = ckp->hfsPlus.parentID;
2235
2236 /* Make sure parent directory didn't change */
2237 if (state->parentID != curID) {
2238 state->error = EINVAL;
2239 return (0); /* stop */
2240 }
2241
2242 /* Count this entry */
2243 switch(crp->recordType) {
2244 case kHFSPlusFolderRecord:
2245 case kHFSPlusFileRecord:
2246 case kHFSFolderRecord:
2247 case kHFSFileRecord:
2248 ++state->count;
2249 break;
2250 default:
2251 printf("cat_findposition: invalid record type %d in dir %d\n",
2252 crp->recordType, curID);
2253 state->error = EINVAL;
2254 return (0); /* stop */
2255 };
2256
2257 return (state->count < state->index);
2258 }
2259
2260
2261 /*
2262 * cat_binarykeycompare - compare two HFS Plus catalog keys.
2263
2264 * The name portion of the key is compared using a 16-bit binary comparison.
2265 * This is called from the b-tree code.
2266 */
2267 __private_extern__
2268 int
2269 cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
2270 {
2271 u_int32_t searchParentID, trialParentID;
2272 int result;
2273
2274 searchParentID = searchKey->parentID;
2275 trialParentID = trialKey->parentID;
2276 result = 0;
2277
2278 if (searchParentID > trialParentID) {
2279 ++result;
2280 } else if (searchParentID < trialParentID) {
2281 --result;
2282 } else {
2283 u_int16_t * str1 = &searchKey->nodeName.unicode[0];
2284 u_int16_t * str2 = &trialKey->nodeName.unicode[0];
2285 int length1 = searchKey->nodeName.length;
2286 int length2 = trialKey->nodeName.length;
2287 u_int16_t c1, c2;
2288 int length;
2289
2290 if (length1 < length2) {
2291 length = length1;
2292 --result;
2293 } else if (length1 > length2) {
2294 length = length2;
2295 ++result;
2296 } else {
2297 length = length1;
2298 }
2299
2300 while (length--) {
2301 c1 = *(str1++);
2302 c2 = *(str2++);
2303
2304 if (c1 > c2) {
2305 result = 1;
2306 break;
2307 }
2308 if (c1 < c2) {
2309 result = -1;
2310 break;
2311 }
2312 }
2313 }
2314
2315 return result;
2316 }
2317
2318
2319 /*
2320 * Compare two standard HFS catalog keys
2321 *
2322 * Result: +n search key > trial key
2323 * 0 search key = trial key
2324 * -n search key < trial key
2325 */
2326 int
2327 CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
2328 {
2329 cnid_t searchParentID, trialParentID;
2330 int result;
2331
2332 searchParentID = searchKey->parentID;
2333 trialParentID = trialKey->parentID;
2334
2335 if (searchParentID > trialParentID)
2336 result = 1;
2337 else if (searchParentID < trialParentID)
2338 result = -1;
2339 else /* parent dirID's are equal, compare names */
2340 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
2341
2342 return result;
2343 }
2344
2345
2346 /*
2347 * Compare two HFS+ catalog keys
2348 *
2349 * Result: +n search key > trial key
2350 * 0 search key = trial key
2351 * -n search key < trial key
2352 */
2353 int
2354 CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
2355 {
2356 cnid_t searchParentID, trialParentID;
2357 int result;
2358
2359 searchParentID = searchKey->parentID;
2360 trialParentID = trialKey->parentID;
2361
2362 if (searchParentID > trialParentID) {
2363 result = 1;
2364 }
2365 else if (searchParentID < trialParentID) {
2366 result = -1;
2367 } else {
2368 /* parent node ID's are equal, compare names */
2369 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
2370 result = searchKey->nodeName.length - trialKey->nodeName.length;
2371 else
2372 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
2373 searchKey->nodeName.length,
2374 &trialKey->nodeName.unicode[0],
2375 trialKey->nodeName.length);
2376 }
2377
2378 return result;
2379 }
2380
2381
2382 /*
2383 * buildkey - build a Catalog b-tree key from a cnode descriptor
2384 */
2385 static int
2386 buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
2387 HFSPlusCatalogKey *key, int retry)
2388 {
2389 int utf8_flags = 0;
2390 int result = 0;
2391 size_t unicodeBytes = 0;
2392
2393 if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
2394 return (EINVAL); /* invalid name */
2395
2396 key->parentID = descp->cd_parentcnid;
2397 key->nodeName.length = 0;
2398 /*
2399 * Convert filename from UTF-8 into Unicode
2400 */
2401
2402 if ((descp->cd_flags & CD_DECOMPOSED) == 0)
2403 utf8_flags |= UTF_DECOMPOSED;
2404 result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
2405 key->nodeName.unicode, &unicodeBytes,
2406 sizeof(key->nodeName.unicode), ':', utf8_flags);
2407 key->nodeName.length = unicodeBytes / sizeof(UniChar);
2408 key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
2409 if (result) {
2410 if (result != ENAMETOOLONG)
2411 result = EINVAL; /* name has invalid characters */
2412 return (result);
2413 }
2414
2415 /*
2416 * For HFS volumes convert to an HFS compatible key
2417 *
2418 * XXX need to save the encoding that succeeded
2419 */
2420 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
2421 HFSCatalogKey hfskey;
2422
2423 bzero(&hfskey, sizeof(hfskey));
2424 hfskey.keyLength = kHFSCatalogKeyMinimumLength;
2425 hfskey.parentID = key->parentID;
2426 hfskey.nodeName[0] = 0;
2427 if (key->nodeName.length > 0) {
2428 if (unicode_to_hfs(HFSTOVCB(hfsmp),
2429 key->nodeName.length * 2,
2430 key->nodeName.unicode,
2431 &hfskey.nodeName[0], retry) != 0) {
2432 return (EINVAL);
2433 }
2434 hfskey.keyLength += hfskey.nodeName[0];
2435 }
2436 bcopy(&hfskey, key, sizeof(hfskey));
2437 }
2438 return (0);
2439 }
2440
2441
2442 /*
2443 * Resolve hard link reference to obtain the inode record.
2444 */
2445 __private_extern__
2446 int
2447 resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp)
2448 {
2449 FSBufferDescriptor btdata;
2450 struct BTreeIterator *iterator;
2451 struct cat_desc idesc;
2452 char inodename[32];
2453 int result = 0;
2454
2455 BDINIT(btdata, recp);
2456 MAKE_INODE_NAME(inodename, linkref);
2457
2458 /* Get space for iterator */
2459 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2460 bzero(iterator, sizeof(*iterator));
2461
2462 /* Build a descriptor for private dir. */
2463 idesc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
2464 idesc.cd_nameptr = inodename;
2465 idesc.cd_namelen = strlen(inodename);
2466 idesc.cd_flags = 0;
2467 idesc.cd_hint = 0;
2468 idesc.cd_encoding = 0;
2469 (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
2470
2471 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
2472 &btdata, NULL, NULL);
2473
2474 if (result == 0) {
2475 /* Make sure there's a reference */
2476 if (recp->bsdInfo.special.linkCount == 0)
2477 recp->bsdInfo.special.linkCount = 2;
2478 } else {
2479 printf("HFS resolvelink: can't find %s\n", inodename);
2480 }
2481
2482 FREE(iterator, M_TEMP);
2483
2484 return (result ? ENOENT : 0);
2485 }
2486
2487 /*
2488 * Resolve hard link reference to obtain the inode number.
2489 */
2490 static int
2491 resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
2492 {
2493 struct HFSPlusCatalogFile record;
2494 int error;
2495
2496 error = resolvelink(hfsmp, linkref, &record);
2497 if (error == 0) {
2498 if (record.fileID == 0)
2499 error = ENOENT;
2500 else
2501 *ino = record.fileID;
2502 }
2503 return (error);
2504 }
2505
2506 /*
2507 * getkey - get a key from id by doing a thread lookup
2508 */
2509 static int
2510 getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
2511 {
2512 struct BTreeIterator * iterator;
2513 FSBufferDescriptor btdata;
2514 UInt16 datasize;
2515 CatalogKey * keyp;
2516 CatalogRecord * recp;
2517 int result;
2518 int std_hfs;
2519
2520 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
2521
2522 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2523 bzero(iterator, sizeof(*iterator));
2524 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
2525
2526 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
2527 BDINIT(btdata, recp);
2528
2529 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
2530 &btdata, &datasize, iterator);
2531 if (result)
2532 goto exit;
2533
2534 /* Turn thread record into a cnode key (in place) */
2535 switch (recp->recordType) {
2536 case kHFSFileThreadRecord:
2537 case kHFSFolderThreadRecord:
2538 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
2539 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
2540 bcopy(keyp, key, keyp->hfs.keyLength + 1);
2541 break;
2542
2543 case kHFSPlusFileThreadRecord:
2544 case kHFSPlusFolderThreadRecord:
2545 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
2546 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
2547 (keyp->hfsPlus.nodeName.length * 2);
2548 bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
2549 break;
2550
2551 default:
2552 result = ENOENT;
2553 break;
2554 }
2555
2556 exit:
2557 FREE(iterator, M_TEMP);
2558 FREE(recp, M_TEMP);
2559
2560 return MacToVFSError(result);
2561 }
2562
2563 /*
2564 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
2565 * null arguments to cat_idlookup instead, but we save around 10% by not building the
2566 * cat_desc here). Both key and attrp must point to real structures.
2567 */
2568 __private_extern__
2569 int
2570 cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
2571 {
2572 int result;
2573
2574 result = getkey(hfsmp, cnid, key);
2575
2576 if (result == 0) {
2577 result = cat_lookupbykey(hfsmp, key, 0, 0, NULL, attrp, NULL, NULL);
2578 }
2579
2580 return MacToVFSError(result);
2581 }
2582
2583
2584 /*
2585 * buildrecord - build a default catalog directory or file record
2586 */
2587 static void
2588 buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
2589 CatalogRecord *crp, int *recordSize)
2590 {
2591 int type = attrp->ca_mode & S_IFMT;
2592 u_int32_t createtime = to_hfs_time(attrp->ca_itime);
2593
2594 if (std_hfs) {
2595 createtime = UTCToLocal(createtime);
2596 if (type == S_IFDIR) {
2597 bzero(crp, sizeof(HFSCatalogFolder));
2598 crp->recordType = kHFSFolderRecord;
2599 crp->hfsFolder.folderID = cnid;
2600 crp->hfsFolder.createDate = createtime;
2601 crp->hfsFolder.modifyDate = createtime;
2602 bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
2603 *recordSize = sizeof(HFSCatalogFolder);
2604 } else {
2605 bzero(crp, sizeof(HFSCatalogFile));
2606 crp->recordType = kHFSFileRecord;
2607 crp->hfsFile.fileID = cnid;
2608 crp->hfsFile.createDate = createtime;
2609 crp->hfsFile.modifyDate = createtime;
2610 bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
2611 bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
2612 *recordSize = sizeof(HFSCatalogFile);
2613 }
2614 } else {
2615 struct HFSPlusBSDInfo * bsdp = NULL;
2616 struct FndrFileInfo * fip = NULL;
2617
2618 if (type == S_IFDIR) {
2619 crp->recordType = kHFSPlusFolderRecord;
2620 crp->hfsPlusFolder.flags = 0;
2621 crp->hfsPlusFolder.valence = 0;
2622 crp->hfsPlusFolder.folderID = cnid;
2623 crp->hfsPlusFolder.createDate = createtime;
2624 crp->hfsPlusFolder.contentModDate = createtime;
2625 crp->hfsPlusFolder.attributeModDate = createtime;
2626 crp->hfsPlusFolder.accessDate = createtime;
2627 crp->hfsPlusFolder.backupDate = 0;
2628 crp->hfsPlusFolder.textEncoding = encoding;
2629 crp->hfsPlusFolder.attrBlocks = 0;
2630 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
2631 bsdp = &crp->hfsPlusFolder.bsdInfo;
2632 bsdp->special.rawDevice = 0;
2633 *recordSize = sizeof(HFSPlusCatalogFolder);
2634 } else {
2635 crp->recordType = kHFSPlusFileRecord;
2636 crp->hfsPlusFile.flags = kHFSThreadExistsMask;
2637 crp->hfsPlusFile.reserved1 = 0;
2638 crp->hfsPlusFile.fileID = cnid;
2639 crp->hfsPlusFile.createDate = createtime;
2640 crp->hfsPlusFile.contentModDate = createtime;
2641 crp->hfsPlusFile.accessDate = createtime;
2642 crp->hfsPlusFile.attributeModDate = createtime;
2643 crp->hfsPlusFile.backupDate = 0;
2644 crp->hfsPlusFile.textEncoding = encoding;
2645 crp->hfsPlusFile.attrBlocks = 0;
2646 bsdp = &crp->hfsPlusFile.bsdInfo;
2647 bsdp->special.rawDevice = 0;
2648 switch(type) {
2649 case S_IFBLK:
2650 case S_IFCHR:
2651 /* BLK/CHR need to save the device info */
2652 bsdp->special.rawDevice = attrp->ca_rdev;
2653 bzero(&crp->hfsPlusFile.userInfo, 32);
2654 break;
2655 case S_IFREG:
2656 /* Hardlink links need to save the linkref */
2657 fip = (FndrFileInfo *)&attrp->ca_finderinfo;
2658 if ((SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
2659 (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
2660 bsdp->special.iNodeNum = attrp->ca_rdev;
2661 }
2662 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
2663 break;
2664 case S_IFLNK:
2665 /* Symlinks also have a type and creator */
2666 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
2667 break;
2668 }
2669 bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
2670 *recordSize = sizeof(HFSPlusCatalogFile);
2671 }
2672 bsdp->ownerID = attrp->ca_uid;
2673 bsdp->groupID = attrp->ca_gid;
2674 bsdp->fileMode = attrp->ca_mode;
2675 bsdp->adminFlags = attrp->ca_flags >> 16;
2676 bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
2677 }
2678 }
2679
2680
2681 /*
2682 * builddesc - build a cnode descriptor from an HFS+ key
2683 */
2684 static int
2685 builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
2686 int isdir, struct cat_desc *descp)
2687 {
2688 int result = 0;
2689 char * nameptr;
2690 size_t bufsize;
2691 size_t utf8len;
2692 char tmpbuff[128];
2693
2694 /* guess a size... */
2695 bufsize = (3 * key->nodeName.length) + 1;
2696 if (bufsize >= sizeof(tmpbuff) - 1) {
2697 MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2698 } else {
2699 nameptr = &tmpbuff[0];
2700 }
2701
2702 result = utf8_encodestr(key->nodeName.unicode,
2703 key->nodeName.length * sizeof(UniChar),
2704 nameptr, (size_t *)&utf8len,
2705 bufsize, ':', 0);
2706
2707 if (result == ENAMETOOLONG) {
2708 bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
2709 key->nodeName.length * sizeof(UniChar),
2710 ':', 0);
2711 FREE(nameptr, M_TEMP);
2712 MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2713
2714 result = utf8_encodestr(key->nodeName.unicode,
2715 key->nodeName.length * sizeof(UniChar),
2716 nameptr, (size_t *)&utf8len,
2717 bufsize, ':', 0);
2718 }
2719 descp->cd_parentcnid = key->parentID;
2720 descp->cd_nameptr = vfs_addname(nameptr, utf8len, 0, 0);
2721 descp->cd_namelen = utf8len;
2722 descp->cd_cnid = cnid;
2723 descp->cd_hint = hint;
2724 descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
2725 if (isdir)
2726 descp->cd_flags |= CD_ISDIR;
2727 descp->cd_encoding = encoding;
2728 if (nameptr != &tmpbuff[0]) {
2729 FREE(nameptr, M_TEMP);
2730 }
2731 return result;
2732 }
2733
2734
2735 /*
2736 * getbsdattr - get attributes in bsd format
2737 *
2738 */
2739 static void
2740 getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
2741 {
2742 int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
2743 const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
2744
2745 attrp->ca_recflags = crp->flags;
2746 attrp->ca_nlink = 1;
2747 attrp->ca_atime = to_bsd_time(crp->accessDate);
2748 attrp->ca_atimeondisk = attrp->ca_atime;
2749 attrp->ca_mtime = to_bsd_time(crp->contentModDate);
2750 attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
2751 attrp->ca_itime = to_bsd_time(crp->createDate);
2752 attrp->ca_btime = to_bsd_time(crp->backupDate);
2753
2754 if ((bsd->fileMode & S_IFMT) == 0) {
2755 attrp->ca_flags = 0;
2756 attrp->ca_uid = hfsmp->hfs_uid;
2757 attrp->ca_gid = hfsmp->hfs_gid;
2758 if (isDirectory)
2759 attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
2760 else
2761 attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
2762 attrp->ca_rdev = 0;
2763 } else {
2764 attrp->ca_rdev = 0;
2765 attrp->ca_uid = bsd->ownerID;
2766 attrp->ca_gid = bsd->groupID;
2767 attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
2768 attrp->ca_mode = (mode_t)bsd->fileMode;
2769 switch (attrp->ca_mode & S_IFMT) {
2770 case S_IFCHR: /* fall through */
2771 case S_IFBLK:
2772 attrp->ca_rdev = bsd->special.rawDevice;
2773 break;
2774 case S_IFREG:
2775 /* Pick up the hard link count */
2776 if (bsd->special.linkCount > 0)
2777 attrp->ca_nlink = bsd->special.linkCount;
2778 break;
2779 }
2780
2781 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
2782 /*
2783 * Override the permissions as determined by the mount auguments
2784 * in ALMOST the same way unset permissions are treated but keep
2785 * track of whether or not the file or folder is hfs locked
2786 * by leaving the h_pflags field unchanged from what was unpacked
2787 * out of the catalog.
2788 */
2789 attrp->ca_uid = hfsmp->hfs_uid;
2790 attrp->ca_gid = hfsmp->hfs_gid;
2791 }
2792 }
2793
2794 if (isDirectory) {
2795 if (!S_ISDIR(attrp->ca_mode)) {
2796 attrp->ca_mode &= ~S_IFMT;
2797 attrp->ca_mode |= S_IFDIR;
2798 }
2799 attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
2800 attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
2801 attrp->ca_attrblks = ((HFSPlusCatalogFolder *)crp)->attrBlocks;
2802 } else {
2803 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2804 if (crp->flags & kHFSFileLockedMask) {
2805 /* The file's supposed to be locked:
2806 Make sure at least one of the IMMUTABLE bits is set: */
2807 if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
2808 attrp->ca_flags |= UF_IMMUTABLE;
2809 } else {
2810 /* The file's supposed to be unlocked: */
2811 attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
2812 }
2813 /* get total blocks (both forks) */
2814 attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
2815 attrp->ca_attrblks = crp->attrBlocks;
2816 /* On HFS+ the ThreadExists flag must always be set. */
2817 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
2818 attrp->ca_recflags |= kHFSThreadExistsMask;
2819 }
2820
2821 attrp->ca_fileid = crp->fileID;
2822
2823 bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
2824 }
2825
2826 /*
2827 * promotekey - promote hfs key to hfs plus key
2828 *
2829 */
2830 static void
2831 promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
2832 HFSPlusCatalogKey *keyp, u_long *encoding)
2833 {
2834 hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
2835 UInt32 uniCount;
2836 int error;
2837
2838 *encoding = hfsmp->hfs_encoding;
2839
2840 error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
2841 kHFSPlusMaxFileNameChars, &uniCount);
2842 /*
2843 * When an HFS name cannot be encoded with the current
2844 * encoding use MacRoman as a fallback.
2845 */
2846 if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
2847 *encoding = 0;
2848 (void) mac_roman_to_unicode(hfskey->nodeName,
2849 keyp->nodeName.unicode,
2850 kHFSPlusMaxFileNameChars,
2851 &uniCount);
2852 }
2853
2854 keyp->nodeName.length = uniCount;
2855 keyp->parentID = hfskey->parentID;
2856 }
2857
2858 /*
2859 * promotefork - promote hfs fork info to hfs plus
2860 *
2861 */
2862 static void
2863 promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
2864 int resource, struct cat_fork * forkp)
2865 {
2866 struct HFSPlusExtentDescriptor *xp;
2867 u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
2868
2869 bzero(forkp, sizeof(*forkp));
2870 xp = &forkp->cf_extents[0];
2871 if (resource) {
2872 forkp->cf_size = filep->rsrcLogicalSize;
2873 forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
2874 forkp->cf_bytesread = 0;
2875 forkp->cf_vblocks = 0;
2876 xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
2877 xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
2878 xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
2879 xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
2880 xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
2881 xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
2882 } else {
2883 forkp->cf_size = filep->dataLogicalSize;
2884 forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
2885 forkp->cf_bytesread = 0;
2886 forkp->cf_vblocks = 0;
2887 xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
2888 xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
2889 xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
2890 xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
2891 xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
2892 xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
2893 }
2894 }
2895
2896 /*
2897 * promoteattr - promote hfs catalog attributes to hfs plus
2898 *
2899 */
2900 static void
2901 promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
2902 {
2903 u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
2904
2905 if (dataPtr->recordType == kHFSFolderRecord) {
2906 struct HFSCatalogFolder * folder;
2907
2908 folder = (struct HFSCatalogFolder *) dataPtr;
2909 crp->recordType = kHFSPlusFolderRecord;
2910 crp->flags = folder->flags;
2911 crp->fileID = folder->folderID;
2912 crp->createDate = LocalToUTC(folder->createDate);
2913 crp->contentModDate = LocalToUTC(folder->modifyDate);
2914 crp->backupDate = LocalToUTC(folder->backupDate);
2915 crp->reserved1 = folder->valence;
2916 bcopy(&folder->userInfo, &crp->userInfo, 32);
2917 } else /* file */ {
2918 struct HFSCatalogFile * file;
2919
2920 file = (struct HFSCatalogFile *) dataPtr;
2921 crp->recordType = kHFSPlusFileRecord;
2922 crp->flags = file->flags;
2923 crp->fileID = file->fileID;
2924 crp->createDate = LocalToUTC(file->createDate);
2925 crp->contentModDate = LocalToUTC(file->modifyDate);
2926 crp->backupDate = LocalToUTC(file->backupDate);
2927 crp->reserved1 = 0;
2928 bcopy(&file->userInfo, &crp->userInfo, 16);
2929 bcopy(&file->finderInfo, &crp->finderInfo, 16);
2930 crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
2931 crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
2932 }
2933 crp->textEncoding = 0;
2934 crp->attributeModDate = crp->contentModDate;
2935 crp->accessDate = crp->contentModDate;
2936 bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
2937 crp->attrBlocks = 0;
2938 }
2939
2940 /*
2941 * Build a catalog node thread record from a catalog key
2942 * and return the size of the record.
2943 */
2944 static int
2945 buildthread(void *keyp, void *recp, int std_hfs, int directory)
2946 {
2947 int size = 0;
2948
2949 if (std_hfs) {
2950 HFSCatalogKey *key = (HFSCatalogKey *)keyp;
2951 HFSCatalogThread *rec = (HFSCatalogThread *)recp;
2952
2953 size = sizeof(HFSCatalogThread);
2954 bzero(rec, size);
2955 if (directory)
2956 rec->recordType = kHFSFolderThreadRecord;
2957 else
2958 rec->recordType = kHFSFileThreadRecord;
2959 rec->parentID = key->parentID;
2960 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
2961
2962 } else /* HFS+ */ {
2963 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
2964 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
2965
2966 size = sizeof(HFSPlusCatalogThread);
2967 if (directory)
2968 rec->recordType = kHFSPlusFolderThreadRecord;
2969 else
2970 rec->recordType = kHFSPlusFileThreadRecord;
2971 rec->reserved = 0;
2972 rec->parentID = key->parentID;
2973 bcopy(&key->nodeName, &rec->nodeName,
2974 sizeof(UniChar) * (key->nodeName.length + 1));
2975
2976 /* HFS Plus has varaible sized thread records */
2977 size -= (sizeof(rec->nodeName.unicode) -
2978 (rec->nodeName.length * sizeof(UniChar)));
2979 }
2980
2981 return (size);
2982 }
2983
2984 /*
2985 * Build a catalog node thread key.
2986 */
2987 static void
2988 buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
2989 {
2990 if (std_hfs) {
2991 key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
2992 key->hfs.reserved = 0;
2993 key->hfs.parentID = parentID;
2994 key->hfs.nodeName[0] = 0;
2995 } else {
2996 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
2997 key->hfsPlus.parentID = parentID;
2998 key->hfsPlus.nodeName.length = 0;
2999 }
3000 }
3001
3002 /*
3003 * Extract the text encoding from a catalog node record.
3004 */
3005 static u_long
3006 getencoding(const CatalogRecord *crp)
3007 {
3008 u_long encoding;
3009
3010 if (crp->recordType == kHFSPlusFolderRecord)
3011 encoding = crp->hfsPlusFolder.textEncoding;
3012 else if (crp->recordType == kHFSPlusFileRecord)
3013 encoding = crp->hfsPlusFile.textEncoding;
3014 else
3015 encoding = 0;
3016
3017 return (encoding);
3018 }
3019
3020 /*
3021 * Extract the CNID from a catalog node record.
3022 */
3023 static cnid_t
3024 getcnid(const CatalogRecord *crp)
3025 {
3026 cnid_t cnid = 0;
3027
3028 switch (crp->recordType) {
3029 case kHFSFolderRecord:
3030 cnid = crp->hfsFolder.folderID;
3031 break;
3032 case kHFSFileRecord:
3033 cnid = crp->hfsFile.fileID;
3034 break;
3035 case kHFSPlusFolderRecord:
3036 cnid = crp->hfsPlusFolder.folderID;
3037 break;
3038 case kHFSPlusFileRecord:
3039 cnid = crp->hfsPlusFile.fileID;
3040 break;
3041 default:
3042 printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp);
3043 break;
3044 }
3045
3046 return (cnid);
3047 }
3048
3049 /*
3050 * Extract the parent ID from a catalog node record.
3051 */
3052 static cnid_t
3053 getparentcnid(const CatalogRecord *recp)
3054 {
3055 cnid_t cnid = 0;
3056
3057 switch (recp->recordType) {
3058 case kHFSFileThreadRecord:
3059 case kHFSFolderThreadRecord:
3060 cnid = recp->hfsThread.parentID;
3061 break;
3062
3063 case kHFSPlusFileThreadRecord:
3064 case kHFSPlusFolderThreadRecord:
3065 cnid = recp->hfsPlusThread.parentID;
3066 break;
3067 default:
3068 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp);
3069 break;
3070 }
3071
3072 return (cnid);
3073 }
3074
3075 /*
3076 * Determine if a catalog node record is a directory.
3077 */
3078 static int
3079 isadir(const CatalogRecord *crp)
3080 {
3081 return (crp->recordType == kHFSFolderRecord ||
3082 crp->recordType == kHFSPlusFolderRecord);
3083 }
3084