2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/systm.h>
27 #include <sys/kernel.h>
28 #include <sys/malloc.h>
30 #include <sys/mount.h>
31 #include <sys/vnode.h>
32 #include <sys/namei.h>
33 #include <sys/dirent.h>
34 #include <vfs/vfs_support.h>
35 #include <libkern/libkern.h>
37 #include <sys/utfconv.h>
40 #include "hfs_catalog.h"
41 #include "hfs_format.h"
42 #include "hfs_endian.h"
44 #include "hfscommon/headers/BTreesInternal.h"
45 #include "hfscommon/headers/CatalogPrivate.h"
46 #include "hfscommon/headers/HFSUnicodeWrappers.h"
48 extern OSErr
PositionIterator(CatalogIterator
*cip
, UInt32 offset
, BTreeIterator
*bip
, UInt16
*op
);
51 * Initialization of an FSBufferDescriptor structure.
53 #define BDINIT(bd, addr) { \
54 (bd).bufferAddress = (addr); \
55 (bd).itemSize = sizeof(*(addr)); \
61 BTreeIterator iterator
;
62 HFSPlusCatalogKey key
;
67 struct cat_desc
* s_desc
;
68 struct cat_attr
* s_attr
;
69 struct cat_fork
* s_datafork
;
70 struct cat_fork
* s_rsrcfork
;
71 struct hfsmount
* s_hfsmp
;
75 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
76 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
78 static int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
79 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
81 extern int mac_roman_to_unicode(const Str31 hfs_str
, UniChar
*uni_str
,
82 UInt32 maxCharLen
, UInt32
*unicodeChars
);
84 extern int unicode_to_hfs(ExtendedVCB
*vcb
, ByteCount srcLen
,
85 const u_int16_t
* srcStr
, Str31 dstStr
, int retry
);
88 /* Internal catalog support routines */
90 int resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
);
92 static int resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
);
94 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
96 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
97 HFSPlusCatalogKey
*key
, int retry
);
99 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
101 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, int *recordSize
);
103 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, u_int16_t reclen
, struct update_state
*state
);
105 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
106 int isdir
, struct cat_desc
*descp
);
108 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
110 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_long
*encoding
);
111 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
112 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
114 static cnid_t
getcnid(const CatalogRecord
*crp
);
115 static u_long
getencoding(const CatalogRecord
*crp
);
116 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
118 static int isadir(const CatalogRecord
*crp
);
120 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
125 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, struct proc
*p
)
130 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
132 /* Lock catalog b-tree */
133 result
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
137 result
= BTReserveSpace(fcb
, ops
, (void*)cookie
);
139 /* Unlock catalog b-tree */
140 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
142 MacToVFSError(result
);
147 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, struct proc
*p
)
152 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
154 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
155 (void) BTReleaseReserve(fcb
, (void*)cookie
);
157 hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
165 struct hfsmount
*hfsmp
,
166 CatalogRecord
* recp
,
167 struct cat_attr
*attrp
,
168 struct cat_fork
*datafp
,
169 struct cat_fork
*rsrcfp
)
171 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
174 struct HFSPlusCatalogFile cnoderec
;
176 promoteattr(hfsmp
, recp
, &cnoderec
);
177 getbsdattr(hfsmp
, &cnoderec
, attrp
);
179 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
183 bzero(datafp
, sizeof(*datafp
));
185 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
186 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
188 /* Convert the data fork. */
189 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
190 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
191 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
192 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
193 datafp
->cf_bytesread
=
194 recp
->hfsPlusFile
.dataFork
.clumpSize
*
195 HFSTOVCB(hfsmp
)->blockSize
;
197 datafp
->cf_bytesread
= 0;
199 datafp
->cf_vblocks
= 0;
200 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
201 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
203 /* Convert the resource fork. */
204 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
205 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
206 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
207 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
208 datafp
->cf_bytesread
=
209 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
210 HFSTOVCB(hfsmp
)->blockSize
;
212 datafp
->cf_bytesread
= 0;
214 rsrcfp
->cf_vblocks
= 0;
215 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
216 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
223 struct hfsmount
*hfsmp
,
225 CatalogRecord
* recp
,
226 struct cat_desc
*descp
)
228 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
229 HFSPlusCatalogKey
* pluskey
= NULL
;
233 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
234 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
237 pluskey
= (HFSPlusCatalogKey
*)key
;
238 encoding
= getencoding(recp
);
241 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
243 FREE(pluskey
, M_TEMP
);
254 cat_releasedesc(struct cat_desc
*descp
)
261 if ((descp
->cd_flags
& CD_HASBUF
) &&
262 (descp
->cd_nameptr
!= NULL
)) {
263 name
= descp
->cd_nameptr
;
264 descp
->cd_nameptr
= NULL
;
265 descp
->cd_namelen
= 0;
266 descp
->cd_flags
&= ~CD_HASBUF
;
269 descp
->cd_nameptr
= NULL
;
270 descp
->cd_namelen
= 0;
274 * These Catalog functions allow access to the HFS Catalog (database).
275 * The catalog b-tree lock must be aquired before calling any of these routines.
279 * cat_lookup - lookup a catalog node using a cnode decriptor
283 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
284 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
285 struct cat_fork
*forkp
)
291 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
293 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
295 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
299 result
= cat_lookupbykey(hfsmp
, keyp
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
);
301 if (result
== ENOENT
) {
303 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
304 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
305 // make MacRoman key from utf-8
306 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
307 // update desc text encoding so that other catalog ops succeed
318 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
320 struct BTreeIterator
*iterator
;
321 struct FSBufferDescriptor file_data
;
322 struct HFSCatalogFile file_rec
;
327 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
330 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
332 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
333 bzero(&iterator
[0], 2* sizeof(*iterator
));
334 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
338 BDINIT(file_data
, &file_rec
);
339 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
343 if (file_rec
.recordType
!= kHFSFileRecord
) {
348 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
349 struct FSBufferDescriptor thread_data
;
350 struct HFSCatalogThread thread_rec
;
352 file_rec
.flags
|= kHFSThreadExistsMask
;
353 BDINIT(thread_data
, &thread_rec
);
354 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
355 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
357 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
361 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
362 (void) BTFlushPath(fcb
);
365 (void) BTFlushPath(fcb
);
366 FREE(iterator
, M_TEMP
);
368 return MacToVFSError(result
);
373 * cat_idlookup - lookup a catalog node using a cnode id
377 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
,
378 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
380 struct BTreeIterator
* iterator
;
381 FSBufferDescriptor btdata
;
384 CatalogRecord
* recp
;
388 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
390 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
391 bzero(iterator
, sizeof(*iterator
));
392 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
394 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
395 BDINIT(btdata
, recp
);
397 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
398 &btdata
, &datasize
, iterator
);
402 /* Turn thread record into a cnode key (in place) */
403 switch (recp
->recordType
) {
404 case kHFSFileThreadRecord
:
405 case kHFSFolderThreadRecord
:
406 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
407 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
410 case kHFSPlusFileThreadRecord
:
411 case kHFSPlusFolderThreadRecord
:
412 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
413 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
414 (keyp
->hfsPlus
.nodeName
.length
* 2);
422 result
= cat_lookupbykey(hfsmp
, keyp
, 0, 0, outdescp
, attrp
, forkp
);
425 FREE(iterator
, M_TEMP
);
427 return MacToVFSError(result
);
432 * cat_lookupmangled - lookup a catalog node using a mangled name
435 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
436 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
445 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
446 if (fileID
< kHFSFirstUserCatalogNodeID
)
449 result
= cat_idlookup(hfsmp
, fileID
, outdescp
, attrp
, forkp
);
453 /* It must be in the correct directory */
454 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
457 if ((outdescp
->cd_namelen
< prefixlen
) ||
458 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
464 cat_releasedesc(outdescp
);
470 * cat_lookupbykey - lookup a catalog node using a cnode key
473 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
474 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
476 struct BTreeIterator
* iterator
;
477 FSBufferDescriptor btdata
;
478 CatalogRecord
* recp
;
486 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
488 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
489 BDINIT(btdata
, recp
);
490 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
491 bzero(iterator
, sizeof(*iterator
));
492 iterator
->hint
.nodeNum
= hint
;
493 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
495 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
496 &btdata
, &datasize
, iterator
);
500 /* Save the cnid now in case there's a hard link */
501 cnid
= getcnid(recp
);
502 encoding
= getencoding(recp
);
503 hint
= iterator
->hint
.nodeNum
;
505 /* Hide the journal files (if any) */
507 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
508 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
515 * When a hardlink link is encountered, auto resolve it
519 && (recp
->recordType
== kHFSPlusFileRecord
)
520 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
521 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)
522 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == HFSTOVCB(hfsmp
)->vcbCrDate
) ||
523 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == hfsmp
->hfs_metadata_createdate
))) {
525 ilink
= recp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
527 (void) resolvelink(hfsmp
, ilink
, (struct HFSPlusCatalogFile
*)recp
);
532 struct HFSPlusCatalogFile cnoderec
;
534 promoteattr(hfsmp
, recp
, &cnoderec
);
535 getbsdattr(hfsmp
, &cnoderec
, attrp
);
537 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
539 attrp
->ca_rdev
= ilink
;
544 bzero(forkp
, sizeof(*forkp
));
545 } else if (std_hfs
) {
546 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
547 } else if (wantrsrc
) {
548 /* Convert the resource fork. */
549 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
550 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
551 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
552 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
553 forkp
->cf_bytesread
=
554 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
555 HFSTOVCB(hfsmp
)->blockSize
;
557 forkp
->cf_bytesread
= 0;
559 forkp
->cf_vblocks
= 0;
560 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
561 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
563 /* Convert the data fork. */
564 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
565 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
566 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
567 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
568 forkp
->cf_bytesread
=
569 recp
->hfsPlusFile
.dataFork
.clumpSize
*
570 HFSTOVCB(hfsmp
)->blockSize
;
572 forkp
->cf_bytesread
= 0;
574 forkp
->cf_vblocks
= 0;
575 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
576 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
580 HFSPlusCatalogKey
* pluskey
= NULL
;
583 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
584 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
587 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
589 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
591 FREE(pluskey
, M_TEMP
);
595 FREE(iterator
, M_TEMP
);
598 return MacToVFSError(result
);
603 * cat_create - create a node in the catalog
607 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
608 struct cat_desc
*out_descp
)
613 FSBufferDescriptor btdata
;
621 modeformat
= attrp
->ca_mode
& S_IFMT
;
623 vcb
= HFSTOVCB(hfsmp
);
624 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
625 nextCNID
= vcb
->vcbNxtCNID
;
626 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
628 if (std_hfs
&& nextCNID
== 0xFFFFFFFF)
631 /* Get space for iterator, key and data */
632 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
633 bzero(bto
, sizeof(struct btobj
));
635 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
640 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
641 bto
->key
.nodeName
.length
);
642 hfs_setencodingbits(hfsmp
, encoding
);
646 * Insert the thread record first
648 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
649 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
650 S_ISDIR(attrp
->ca_mode
));
651 btdata
.bufferAddress
= &bto
->data
;
652 btdata
.itemSize
= datalen
;
653 btdata
.itemCount
= 1;
656 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
658 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
659 if (result
== btExists
&& !std_hfs
) {
661 * Allow CNIDs on HFS Plus volumes to wrap around
664 if (nextCNID
< kHFSFirstUserCatalogNodeID
) {
665 vcb
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
666 vcb
->vcbFlags
|= 0xFF00;
667 nextCNID
= kHFSFirstUserCatalogNodeID
;
673 if (result
) goto exit
;
677 * Now insert the file/directory record
679 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
680 btdata
.bufferAddress
= &bto
->data
;
681 btdata
.itemSize
= datalen
;
682 btdata
.itemCount
= 1;
684 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
686 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
688 if (result
== btExists
)
691 /* Back out the thread record */
692 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
693 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
694 (void) BTDeleteRecord(fcb
, &bto
->iterator
);
700 * Insert was Successfull, update name, parent and volume
704 if (out_descp
!= NULL
) {
705 HFSPlusCatalogKey
* pluskey
= NULL
;
708 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
709 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
712 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
714 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
715 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
717 FREE(pluskey
, M_TEMP
);
720 attrp
->ca_fileid
= nextCNID
;
722 /* Update parent stats */
723 TrashCatalogIterator(vcb
, descp
->cd_parentcnid
);
725 /* Update volume stats */
726 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
727 vcb
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
728 nextCNID
= kHFSFirstUserCatalogNodeID
;
730 vcb
->vcbNxtCNID
= nextCNID
;
731 vcb
->vcbFlags
|= 0xFF00;
734 (void) BTFlushPath(fcb
);
737 return MacToVFSError(result
);
742 * cnode_rename - rename a catalog node
744 * Assumes that the target's directory exists.
746 * Order of B-tree operations:
747 * 1. BTSearchRecord(from_cnode, &data);
748 * 2. BTInsertRecord(to_cnode, &data);
749 * 3. BTDeleteRecord(from_cnode);
750 * 4. BTDeleteRecord(from_thread);
751 * 5. BTInsertRecord(to_thread);
756 struct hfsmount
* hfsmp
,
757 struct cat_desc
* from_cdp
,
758 struct cat_desc
* todir_cdp
,
759 struct cat_desc
* to_cdp
,
760 struct cat_desc
* out_cdp
)
762 struct BTreeIterator
* to_iterator
= NULL
;
763 struct BTreeIterator
* from_iterator
= NULL
;
764 FSBufferDescriptor btdata
;
765 CatalogRecord
* recp
= NULL
;
766 HFSPlusCatalogKey
* to_key
;
773 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
777 vcb
= HFSTOVCB(hfsmp
);
778 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
779 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
781 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
784 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
785 bzero(from_iterator
, sizeof(*from_iterator
));
786 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
789 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
790 bzero(to_iterator
, sizeof(*to_iterator
));
791 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
794 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
795 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
796 BDINIT(btdata
, recp
);
799 * When moving a directory, make sure its a valid move.
801 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
802 struct BTreeIterator iterator
= {0};
803 cnid_t cnid
= from_cdp
->cd_cnid
;
804 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
806 /* First check the obvious ones */
807 if (cnid
== fsRtDirID
||
808 cnid
== to_cdp
->cd_parentcnid
||
815 * Traverese destination path all the way back to the root
816 * making sure that source directory is not encountered.
819 while (pathcnid
> fsRtDirID
) {
820 buildthreadkey(pathcnid
, std_hfs
,
821 (CatalogKey
*)&iterator
.key
);
822 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
824 if (result
) goto exit
;
826 pathcnid
= getparentcnid(recp
);
827 if (pathcnid
== cnid
) {
835 * Step 1: Find cnode data at old location
837 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
838 &datasize
, from_iterator
);
842 /* Update the text encoding (on disk and in descriptor) */
844 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
,
845 to_key
->nodeName
.length
);
846 hfs_setencodingbits(hfsmp
, encoding
);
847 recp
->hfsPlusFile
.textEncoding
= encoding
;
849 out_cdp
->cd_encoding
= encoding
;
852 if (std_hfs
&& !directory
&&
853 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
857 * If the keys are identical then there's nothing left to do!
859 * update the hint and exit
862 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
864 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
868 /* Trash the iterator caches */
869 TrashCatalogIterator(vcb
, from_cdp
->cd_parentcnid
);
870 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
871 TrashCatalogIterator(vcb
, to_cdp
->cd_parentcnid
);
873 /* Step 2: Insert cnode at new location */
874 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
875 if (result
== btExists
) {
876 int fromtype
= recp
->recordType
;
878 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
879 goto exit
; /* EEXIST */
881 /* Find cnode data at new location */
882 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
884 if ((fromtype
!= recp
->recordType
) ||
885 (from_cdp
->cd_cnid
!= getcnid(recp
)))
886 goto exit
; /* EEXIST */
888 /* The old name is a case variant and must be removed */
889 result
= BTDeleteRecord(fcb
, from_iterator
);
893 /* Insert cnode (now that case duplicate is gone) */
894 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
896 /* Try and restore original before leaving */
901 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
903 panic("cat_create: could not undo (BTInsert = %d)", err
);
906 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
915 /* Step 3: Remove cnode from old location */
917 result
= BTDeleteRecord(fcb
, from_iterator
);
919 /* Try and delete new record before leaving */
924 err
= BTDeleteRecord(fcb
, to_iterator
);
926 panic("cat_create: could not undo (BTDelete = %d)", err
);
929 (void) BTDeleteRecord(fcb
, to_iterator
);
935 /* #### POINT OF NO RETURN #### */
938 * Step 4: Remove cnode's old thread record
940 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
941 (void) BTDeleteRecord(fcb
, from_iterator
);
944 * Step 5: Insert cnode's new thread record
945 * (optional for HFS files)
948 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
949 btdata
.itemSize
= datasize
;
950 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
951 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
955 HFSPlusCatalogKey
* pluskey
= NULL
;
958 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
959 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
961 /* Save the real encoding hint in the Finder Info (field 4). */
962 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
965 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
966 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
970 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
972 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
973 encoding
, directory
, out_cdp
);
975 FREE(pluskey
, M_TEMP
);
979 (void) BTFlushPath(fcb
);
981 FREE(from_iterator
, M_TEMP
);
983 FREE(to_iterator
, M_TEMP
);
986 return MacToVFSError(result
);
991 * cat_delete - delete a node from the catalog
993 * Order of B-tree operations:
994 * 1. BTDeleteRecord(cnode);
995 * 2. BTDeleteRecord(thread);
996 * 3. BTUpdateRecord(parent);
1000 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1004 BTreeIterator
*iterator
;
1009 vcb
= HFSTOVCB(hfsmp
);
1010 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1011 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1015 * The root directory cannot be deleted
1016 * A directory must be empty
1017 * A file must be zero length (no blocks)
1019 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1020 descp
->cd_parentcnid
== kRootParID
)
1023 /* XXX Preflight Missing */
1025 /* Get space for iterator */
1026 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1027 bzero(iterator
, sizeof(*iterator
));
1030 * Derive a key from either the file ID (for a virtual inode)
1031 * or the descriptor.
1033 if (descp
->cd_namelen
== 0) {
1034 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1035 cnid
= attrp
->ca_fileid
;
1037 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1038 cnid
= descp
->cd_cnid
;
1044 result
= BTDeleteRecord(fcb
, iterator
);
1048 /* Delete thread record, ignore errors */
1049 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1050 (void) BTDeleteRecord(fcb
, iterator
);
1052 TrashCatalogIterator(vcb
, descp
->cd_parentcnid
);
1055 (void) BTFlushPath(fcb
);
1056 FREE(iterator
, M_TEMP
);
1058 return MacToVFSError(result
);
1063 * cnode_update - update the catalog node described by descp
1064 * using the data from attrp and forkp.
1068 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1069 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1073 BTreeIterator
* iterator
;
1074 struct update_state state
;
1078 vcb
= HFSTOVCB(hfsmp
);
1079 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1080 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1082 state
.s_desc
= descp
;
1083 state
.s_attr
= attrp
;
1084 state
.s_datafork
= dataforkp
;
1085 state
.s_rsrcfork
= rsrcforkp
;
1086 state
.s_hfsmp
= hfsmp
;
1088 /* Get space for iterator */
1089 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1090 bzero(iterator
, sizeof(*iterator
));
1093 * For open-deleted files we need to do a lookup by cnid
1094 * (using thread rec).
1096 * For hard links, the target of the update is the inode
1097 * itself (not the link record) so a lookup by fileid
1098 * (i.e. thread rec) is needed.
1100 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (descp
->cd_namelen
== 0))
1101 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1103 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1107 /* Pass a node hint */
1108 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1110 result
= BTUpdateRecord(fcb
, iterator
,
1111 (IterateCallBackProcPtr
)catrec_update
, &state
);
1115 /* Update the node hint. */
1116 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1119 (void) BTFlushPath(fcb
);
1120 FREE(iterator
, M_TEMP
);
1122 return MacToVFSError(result
);
1126 * catrec_update - Update the fields of a catalog record
1127 * This is called from within BTUpdateRecord.
1130 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, u_int16_t reclen
,
1131 struct update_state
*state
)
1133 struct cat_desc
*descp
;
1134 struct cat_attr
*attrp
;
1135 struct cat_fork
*forkp
;
1136 struct hfsmount
*hfsmp
;
1140 descp
= state
->s_desc
;
1141 attrp
= state
->s_attr
;
1142 hfsmp
= state
->s_hfsmp
;
1143 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1145 switch (crp
->recordType
) {
1146 case kHFSFolderRecord
: {
1147 HFSCatalogFolder
*dir
;
1149 dir
= (struct HFSCatalogFolder
*)crp
;
1150 /* Do a quick sanity check */
1151 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1152 (dir
->folderID
!= descp
->cd_cnid
))
1153 return (btNotFound
);
1154 dir
->valence
= attrp
->ca_entries
;
1155 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1156 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1157 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1158 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1159 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1162 case kHFSFileRecord
: {
1163 HFSCatalogFile
*file
;
1165 file
= (struct HFSCatalogFile
*)crp
;
1166 /* Do a quick sanity check */
1167 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1168 (file
->fileID
!= attrp
->ca_fileid
))
1169 return (btNotFound
);
1170 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1171 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1172 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1173 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1174 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1175 if (state
->s_rsrcfork
) {
1176 forkp
= state
->s_rsrcfork
;
1177 file
->rsrcLogicalSize
= forkp
->cf_size
;
1178 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1179 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1180 file
->rsrcExtents
[i
].startBlock
=
1181 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1182 file
->rsrcExtents
[i
].blockCount
=
1183 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1186 if (state
->s_datafork
) {
1187 forkp
= state
->s_datafork
;
1188 file
->dataLogicalSize
= forkp
->cf_size
;
1189 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1190 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1191 file
->dataExtents
[i
].startBlock
=
1192 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1193 file
->dataExtents
[i
].blockCount
=
1194 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1199 case kHFSPlusFolderRecord
: {
1200 HFSPlusCatalogFolder
*dir
;
1202 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1203 /* Do a quick sanity check */
1204 if ((ckp
->hfsPlus
.parentID
!= descp
->cd_parentcnid
) ||
1205 (dir
->folderID
!= descp
->cd_cnid
))
1206 return (btNotFound
);
1207 dir
->valence
= attrp
->ca_entries
;
1208 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1209 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1210 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1211 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1212 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1213 dir
->textEncoding
= descp
->cd_encoding
;
1214 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1216 * Update the BSD Info if it was already initialized on
1217 * disk or if the runtime values have been modified.
1219 * If the BSD info was already initialized, but
1220 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1221 * probably different than what was on disk. We don't want
1222 * to overwrite the on-disk values (so if we turn off
1223 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1224 * This way, we can still change fields like the mode or
1225 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1227 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1228 * won't change the uid or gid from their defaults. So, if
1229 * the BSD info wasn't set, and the runtime values are not
1230 * default, then what changed was the mode or flags. We
1231 * have to set the uid and gid to something, so use the
1232 * supplied values (which will be default), which has the
1233 * same effect as creating a new file while
1234 * MNT_UNKNOWNPERMISSIONS is set.
1236 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1237 (attrp
->ca_flags
!= 0) ||
1238 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1239 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1240 ((attrp
->ca_mode
& ALLPERMS
) !=
1241 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1242 if ((dir
->bsdInfo
.fileMode
== 0) ||
1243 (HFSTOVFS(hfsmp
)->mnt_flag
&
1244 MNT_UNKNOWNPERMISSIONS
) == 0) {
1245 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1246 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1248 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1249 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1250 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1254 case kHFSPlusFileRecord
: {
1255 HFSPlusCatalogFile
*file
;
1257 file
= (struct HFSPlusCatalogFile
*)crp
;
1258 /* Do a quick sanity check */
1259 if (file
->fileID
!= attrp
->ca_fileid
)
1260 return (btNotFound
);
1261 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1262 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1263 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1264 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1265 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1266 file
->textEncoding
= descp
->cd_encoding
;
1267 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1269 * Update the BSD Info if it was already initialized on
1270 * disk or if the runtime values have been modified.
1272 * If the BSD info was already initialized, but
1273 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1274 * probably different than what was on disk. We don't want
1275 * to overwrite the on-disk values (so if we turn off
1276 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1277 * This way, we can still change fields like the mode or
1278 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1280 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1281 * won't change the uid or gid from their defaults. So, if
1282 * the BSD info wasn't set, and the runtime values are not
1283 * default, then what changed was the mode or flags. We
1284 * have to set the uid and gid to something, so use the
1285 * supplied values (which will be default), which has the
1286 * same effect as creating a new file while
1287 * MNT_UNKNOWNPERMISSIONS is set.
1289 if ((file
->bsdInfo
.fileMode
!= 0) ||
1290 (attrp
->ca_flags
!= 0) ||
1291 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1292 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1293 ((attrp
->ca_mode
& ALLPERMS
) !=
1294 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1295 if ((file
->bsdInfo
.fileMode
== 0) ||
1296 (HFSTOVFS(hfsmp
)->mnt_flag
&
1297 MNT_UNKNOWNPERMISSIONS
) == 0) {
1298 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1299 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1301 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1302 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1303 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1305 if (state
->s_rsrcfork
) {
1306 forkp
= state
->s_rsrcfork
;
1307 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1308 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1309 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1310 sizeof(HFSPlusExtentRecord
));
1311 /* Push blocks read to disk */
1312 file
->resourceFork
.clumpSize
=
1313 howmany(forkp
->cf_bytesread
, blksize
);
1315 if (state
->s_datafork
) {
1316 forkp
= state
->s_datafork
;
1317 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1318 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1319 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1320 sizeof(HFSPlusExtentRecord
));
1321 /* Push blocks read to disk */
1322 file
->resourceFork
.clumpSize
=
1323 howmany(forkp
->cf_bytesread
, blksize
);
1326 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1327 (file
->resourceFork
.extents
[0].startBlock
==
1328 file
->dataFork
.extents
[0].startBlock
))
1329 panic("catrec_update: rsrc fork == data fork");
1331 /* Synchronize the lock state */
1332 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1333 file
->flags
|= kHFSFileLockedMask
;
1335 file
->flags
&= ~kHFSFileLockedMask
;
1337 /* Push out special field if necessary */
1338 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
))
1339 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1340 else if (descp
->cd_cnid
!= attrp
->ca_fileid
1341 || attrp
->ca_nlink
== 2)
1342 file
->bsdInfo
.special
.linkCount
= attrp
->ca_nlink
;
1346 return (btNotFound
);
1353 * This is called from within BTIterateRecords.
1355 struct readattr_state
{
1356 struct hfsmount
*hfsmp
;
1357 struct cat_entrylist
*list
;
1364 catrec_readattr(const CatalogKey
*key
, const CatalogRecord
*rec
,
1365 u_long node
, struct readattr_state
*state
)
1367 struct cat_entrylist
*list
= state
->list
;
1368 struct hfsmount
*hfsmp
= state
->hfsmp
;
1369 struct cat_entry
*cep
;
1372 if (list
->realentries
>= list
->maxentries
)
1373 return (0); /* stop */
1375 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
1377 switch(rec
->recordType
) {
1378 case kHFSPlusFolderRecord
:
1379 case kHFSPlusFileRecord
:
1380 case kHFSFolderRecord
:
1381 case kHFSFileRecord
:
1382 if (parentcnid
!= state
->dir_cnid
) {
1383 state
->error
= ENOENT
;
1384 return (0); /* stop */
1388 state
->error
= ENOENT
;
1389 return (0); /* stop */
1392 /* Hide the private meta data directory and journal files */
1393 if (parentcnid
== kRootDirID
) {
1394 if ((rec
->recordType
== kHFSPlusFolderRecord
) &&
1395 (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1396 return (1); /* continue */
1399 (rec
->recordType
== kHFSPlusFileRecord
) &&
1400 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
1401 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
1403 return (1); /* continue */
1408 cep
= &list
->entry
[list
->realentries
++];
1410 if (state
->stdhfs
) {
1411 struct HFSPlusCatalogFile cnoderec
;
1412 HFSPlusCatalogKey
* pluskey
;
1415 promoteattr(hfsmp
, rec
, &cnoderec
);
1416 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
1418 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1419 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
1420 builddesc(pluskey
, getcnid(rec
), node
, encoding
, isadir(rec
), &cep
->ce_desc
);
1421 FREE(pluskey
, M_TEMP
);
1423 if (rec
->recordType
== kHFSFileRecord
) {
1424 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1426 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
1427 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
1428 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
1429 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
1432 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
1433 builddesc((HFSPlusCatalogKey
*)key
, getcnid(rec
), node
, getencoding(rec
),
1434 isadir(rec
), &cep
->ce_desc
);
1436 if (rec
->recordType
== kHFSPlusFileRecord
) {
1437 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
1438 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
1439 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
1440 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
1442 /* Save link reference for later processing. */
1443 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
1444 && (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
))
1445 cep
->ce_attr
.ca_rdev
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1449 return (list
->realentries
< list
->maxentries
);
1453 * Note: index is zero relative
1457 cat_getentriesattr(struct hfsmount
*hfsmp
, struct cat_desc
*prevdesc
, int index
,
1458 struct cat_entrylist
*ce_list
)
1462 BTreeIterator
* iterator
;
1463 struct readattr_state state
;
1469 ce_list
->realentries
= 0;
1471 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
1472 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
1473 parentcnid
= prevdesc
->cd_parentcnid
;
1475 state
.hfsmp
= hfsmp
;
1476 state
.list
= ce_list
;
1477 state
.dir_cnid
= parentcnid
;
1478 state
.stdhfs
= std_hfs
;
1481 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1482 bzero(iterator
, sizeof(*iterator
));
1483 key
= (CatalogKey
*)&iterator
->key
;
1484 iterator
->hint
.nodeNum
= prevdesc
->cd_hint
;
1487 * If the last entry wasn't cached then establish the iterator
1490 (prevdesc
->cd_namelen
== 0) ||
1491 (buildkey(hfsmp
, prevdesc
, (HFSPlusCatalogKey
*)key
, 0) != 0)) {
1494 * Position the iterator at the directory thread.
1495 * (ie just before the first entry)
1497 buildthreadkey(parentcnid
, std_hfs
, key
);
1498 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1500 goto exit
; /* bad news */
1502 * Iterate until we reach the entry just
1503 * before the one we want to start with.
1505 for (i
= 0; i
< index
; ++i
) {
1506 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1508 goto exit
; /* bad news */
1512 /* Fill list with entries. */
1513 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1514 (IterateCallBackProcPtr
)catrec_readattr
, &state
);
1517 result
= state
.error
;
1518 else if (ce_list
->realentries
== 0)
1521 result
= MacToVFSError(result
);
1527 * Resolve any hard links.
1529 for (i
= 0; i
< ce_list
->realentries
; ++i
) {
1530 struct FndrFileInfo
*fip
;
1531 struct cat_entry
*cep
;
1532 struct HFSPlusCatalogFile filerec
;
1534 cep
= &ce_list
->entry
[i
];
1535 if (!S_ISREG(cep
->ce_attr
.ca_mode
))
1538 /* Note: Finder info is still in Big Endian */
1539 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
1541 /* Check for hard link signature. */
1542 if ((cep
->ce_attr
.ca_rdev
!= 0)
1543 && (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
)
1544 && (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)
1545 && ((cep
->ce_attr
.ca_itime
== HFSTOVCB(hfsmp
)->vcbCrDate
) ||
1546 (cep
->ce_attr
.ca_itime
== hfsmp
->hfs_metadata_createdate
))) {
1548 if (resolvelink(hfsmp
, cep
->ce_attr
.ca_rdev
, &filerec
) != 0)
1550 /* Repack entry from inode record. */
1551 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
1552 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
1553 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
1554 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
1555 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
1559 FREE(iterator
, M_TEMP
);
1561 return MacToVFSError(result
);
1570 u_int32_t cbs_parentID
;
1571 u_int32_t cbs_hiddenDirID
;
1572 u_int32_t cbs_hiddenJournalID
;
1573 u_int32_t cbs_hiddenInfoBlkID
;
1574 off_t cbs_lastoffset
;
1575 struct uio
* cbs_uio
;
1576 ExtendedVCB
* cbs_vcb
;
1578 int8_t cbs_case_sensitive
;
1580 int32_t cbs_numresults
;
1581 u_long
*cbs_cookies
;
1582 int32_t cbs_ncookies
;
1584 int32_t cbs_maxlinks
;
1585 struct linkinfo
*cbs_linkinfo
;
1588 /* Map file mode type to directory entry types */
1589 u_char modetodirtype
[16] = {
1590 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
1591 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
1592 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
1593 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
1596 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
1599 catrec_read(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
1600 u_int16_t recordLen
, struct read_state
*state
)
1602 struct hfsmount
*hfsmp
;
1607 struct dirent catent
;
1609 u_long ilinkref
= 0;
1612 if (state
->cbs_hfsPlus
)
1613 curID
= ckp
->hfsPlus
.parentID
;
1615 curID
= ckp
->hfs
.parentID
;
1617 /* We're done when parent directory changes */
1618 if (state
->cbs_parentID
!= curID
) {
1621 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
1622 * so remove padding for now...
1626 * Pad the end of list with an empty record.
1627 * This eliminates an extra call by readdir(3c).
1629 catent
.d_fileno
= 0;
1630 catent
.d_reclen
= 0;
1632 catent
.d_namlen
= 0;
1633 *(int32_t*)&catent
.d_name
[0] = 0;
1635 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1637 state
->cbs_result
= uiomove((caddr_t
) &catent
, 12, state
->cbs_uio
);
1638 if (state
->cbs_result
== 0)
1639 state
->cbs_result
= ENOENT
;
1641 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1642 state
->cbs_result
= ENOENT
;
1644 return (0); /* stop */
1647 if (state
->cbs_hfsPlus
) {
1648 switch(crp
->recordType
) {
1649 case kHFSPlusFolderRecord
:
1650 catent
.d_type
= DT_DIR
;
1651 catent
.d_fileno
= crp
->hfsPlusFolder
.folderID
;
1653 case kHFSPlusFileRecord
:
1654 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
1655 hfsmp
= VCBTOHFS(state
->cbs_vcb
);
1657 * When a hardlink link is encountered save its link ref.
1659 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
1660 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
1661 ((itime
== state
->cbs_vcb
->vcbCrDate
) ||
1662 (itime
== hfsmp
->hfs_metadata_createdate
))) {
1663 ilinkref
= crp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1665 catent
.d_type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
1666 catent
.d_fileno
= crp
->hfsPlusFile
.fileID
;
1669 return (0); /* stop */
1672 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
1673 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
1674 catent
.d_name
, &utf8chars
, kdirentMaxNameBytes
+ 1, ':', 0);
1675 if (result
== ENAMETOOLONG
) {
1676 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
1677 cnp
->ustr
.unicode
, kdirentMaxNameBytes
+ 1, (ByteCount
*)&utf8chars
, catent
.d_name
, catent
.d_fileno
);
1680 switch(crp
->recordType
) {
1681 case kHFSFolderRecord
:
1682 catent
.d_type
= DT_DIR
;
1683 catent
.d_fileno
= crp
->hfsFolder
.folderID
;
1685 case kHFSFileRecord
:
1686 catent
.d_type
= DT_REG
;
1687 catent
.d_fileno
= crp
->hfsFile
.fileID
;
1690 return (0); /* stop */
1693 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
1694 result
= hfs_to_utf8(state
->cbs_vcb
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
1695 (ByteCount
*)&utf8chars
, catent
.d_name
);
1697 * When an HFS name cannot be encoded with the current
1698 * volume encoding we use MacRoman as a fallback.
1701 result
= mac_roman_to_utf8(cnp
->pstr
, kdirentMaxNameBytes
+ 1,
1702 (ByteCount
*)&utf8chars
, catent
.d_name
);
1705 catent
.d_namlen
= utf8chars
;
1706 catent
.d_reclen
= DIRENTRY_SIZE(utf8chars
);
1708 /* hide our private meta data directory */
1709 if (curID
== kRootDirID
&&
1710 catent
.d_fileno
== state
->cbs_hiddenDirID
&&
1711 catent
.d_type
== DT_DIR
) {
1712 if (state
->cbs_case_sensitive
) {
1713 // This is how we skip over these entries. The next
1714 // time we fill in a real item the uio_offset will
1715 // point to the correct place in the "virtual" directory
1716 // so that PositionIterator() will do the right thing
1717 // when scanning to get to a particular position in the
1719 state
->cbs_uio
->uio_offset
+= catent
.d_reclen
;
1720 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1722 return (1); /* skip and continue */
1727 /* Hide the journal files */
1728 if ((curID
== kRootDirID
) &&
1729 (catent
.d_type
== DT_REG
) &&
1730 ((catent
.d_fileno
== state
->cbs_hiddenJournalID
) ||
1731 (catent
.d_fileno
== state
->cbs_hiddenInfoBlkID
))) {
1733 // see comment up above for why this is here
1734 state
->cbs_uio
->uio_offset
+= catent
.d_reclen
;
1735 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1737 return (1); /* skip and continue */
1740 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1741 uiobase
= state
->cbs_uio
->uio_iov
->iov_base
;
1743 /* if this entry won't fit then we're done */
1744 if (catent
.d_reclen
> state
->cbs_uio
->uio_resid
||
1745 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
) ||
1746 (state
->cbs_ncookies
!= 0 && state
->cbs_numresults
>= state
->cbs_ncookies
))
1747 return (0); /* stop */
1749 state
->cbs_result
= uiomove((caddr_t
) &catent
, catent
.d_reclen
, state
->cbs_uio
);
1752 * Record any hard links for post processing.
1754 if ((ilinkref
!= 0) &&
1755 (state
->cbs_result
== 0) &&
1756 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
1757 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
1758 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
1759 state
->cbs_nlinks
++;
1762 if (state
->cbs_cookies
) {
1763 state
->cbs_cookies
[state
->cbs_numresults
++] = state
->cbs_uio
->uio_offset
;
1765 state
->cbs_numresults
++;
1768 /* continue iteration if there's room */
1769 return (state
->cbs_result
== 0 &&
1770 state
->cbs_uio
->uio_resid
>= AVERAGE_HFSDIRENTRY_SIZE
);
1773 #define SMALL_DIRENTRY_SIZE (sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1779 cat_getdirentries(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int entrycnt
,
1780 struct uio
*uio
, int *eofflag
, u_long
*cookies
, int ncookies
)
1782 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
1783 BTreeIterator
* iterator
;
1784 CatalogIterator
*cip
;
1785 u_int32_t diroffset
;
1787 struct read_state state
;
1788 u_int32_t dirID
= descp
->cd_cnid
;
1794 diroffset
= uio
->uio_offset
;
1796 maxdirentries
= MIN(entrycnt
, uio
->uio_resid
/ SMALL_DIRENTRY_SIZE
);
1798 /* Get a buffer for collecting link info and for a btree iterator */
1799 bufsize
= (maxdirentries
* sizeof(struct linkinfo
)) + sizeof(*iterator
);
1800 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
1801 bzero(buffer
, bufsize
);
1803 state
.cbs_nlinks
= 0;
1804 state
.cbs_maxlinks
= maxdirentries
;
1805 state
.cbs_linkinfo
= (struct linkinfo
*) buffer
;
1806 iterator
= (BTreeIterator
*) ((char *)buffer
+ (maxdirentries
* sizeof(struct linkinfo
)));
1808 /* get an iterator and position it */
1809 cip
= GetCatalogIterator(vcb
, dirID
, diroffset
);
1811 result
= PositionIterator(cip
, diroffset
, iterator
, &op
);
1812 if (result
== cmNotFound
) {
1815 AgeCatalogIterator(cip
);
1817 } else if ((result
= MacToVFSError(result
)))
1820 state
.cbs_hiddenDirID
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1822 state
.cbs_hiddenJournalID
= hfsmp
->hfs_jnlfileid
;
1823 state
.cbs_hiddenInfoBlkID
= hfsmp
->hfs_jnlinfoblkid
;
1826 state
.cbs_lastoffset
= cip
->currentOffset
;
1827 state
.cbs_vcb
= vcb
;
1828 state
.cbs_uio
= uio
;
1829 state
.cbs_result
= 0;
1830 state
.cbs_parentID
= dirID
;
1831 if (diroffset
<= 2*sizeof(struct hfsdotentry
)) {
1832 state
.cbs_numresults
= diroffset
/sizeof(struct hfsdotentry
);
1834 state
.cbs_numresults
= 0;
1836 state
.cbs_cookies
= cookies
;
1837 state
.cbs_ncookies
= ncookies
;
1839 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1840 state
.cbs_hfsPlus
= 1;
1842 state
.cbs_hfsPlus
= 0;
1844 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
1845 state
.cbs_case_sensitive
= 1;
1847 state
.cbs_case_sensitive
= 0;
1849 /* process as many entries as possible... */
1850 result
= BTIterateRecords(GetFileControlBlock(vcb
->catalogRefNum
), op
,
1851 iterator
, (IterateCallBackProcPtr
)catrec_read
, &state
);
1854 * Post process any hard links to get the real file id.
1856 if (state
.cbs_nlinks
> 0) {
1863 auio
.uio_iov
= &aiov
;
1864 auio
.uio_iovcnt
= 1;
1865 auio
.uio_segflg
= uio
->uio_segflg
;
1866 auio
.uio_rw
= UIO_READ
; /* read kernel memory into user memory */
1867 auio
.uio_procp
= uio
->uio_procp
;
1869 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
1872 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
1875 /* Update the file id in the user's buffer */
1876 aiov
.iov_base
= (char *) state
.cbs_linkinfo
[i
].dirent_addr
;
1877 aiov
.iov_len
= sizeof(fileid
);
1878 auio
.uio_offset
= 0;
1879 auio
.uio_resid
= aiov
.iov_len
;
1880 (void) uiomove((caddr_t
)&fileid
, sizeof(fileid
), &auio
);
1883 if (state
.cbs_result
)
1884 result
= state
.cbs_result
;
1886 result
= MacToVFSError(result
);
1888 if (result
== ENOENT
) {
1894 cip
->currentOffset
= state
.cbs_lastoffset
;
1895 cip
->nextOffset
= uio
->uio_offset
;
1896 UpdateCatalogIterator(iterator
, cip
);
1903 AgeCatalogIterator(cip
);
1906 (void) ReleaseCatalogIterator(cip
);
1907 FREE(buffer
, M_TEMP
);
1914 * cat_binarykeycompare - compare two HFS Plus catalog keys.
1916 * The name portion of the key is comapred using a 16-bit binary comparison.
1917 * This is called from the b-tree code.
1921 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
1923 u_int32_t searchParentID
, trialParentID
;
1926 searchParentID
= searchKey
->parentID
;
1927 trialParentID
= trialKey
->parentID
;
1930 if (searchParentID
> trialParentID
) {
1932 } else if (searchParentID
< trialParentID
) {
1935 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
1936 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
1937 int length1
= searchKey
->nodeName
.length
;
1938 int length2
= trialKey
->nodeName
.length
;
1942 if (length1
< length2
) {
1945 } else if (length1
> length2
) {
1972 * buildkey - build a Catalog b-tree key from a cnode descriptor
1975 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
1976 HFSPlusCatalogKey
*key
, int retry
)
1980 size_t unicodeBytes
= 0;
1982 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
1983 return (EINVAL
); /* invalid name */
1985 key
->parentID
= descp
->cd_parentcnid
;
1986 key
->nodeName
.length
= 0;
1988 * Convert filename from UTF-8 into Unicode
1991 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
1992 utf8_flags
|= UTF_DECOMPOSED
;
1993 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
1994 key
->nodeName
.unicode
, &unicodeBytes
,
1995 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
1996 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
1997 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
1999 if (result
!= ENAMETOOLONG
)
2000 result
= EINVAL
; /* name has invalid characters */
2005 * For HFS volumes convert to an HFS compatible key
2007 * XXX need to save the encoding that succeeded
2009 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
2010 HFSCatalogKey hfskey
;
2012 bzero(&hfskey
, sizeof(hfskey
));
2013 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
2014 hfskey
.parentID
= key
->parentID
;
2015 hfskey
.nodeName
[0] = 0;
2016 if (key
->nodeName
.length
> 0) {
2017 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
2018 key
->nodeName
.length
* 2,
2019 key
->nodeName
.unicode
,
2020 &hfskey
.nodeName
[0], retry
) != 0) {
2023 hfskey
.keyLength
+= hfskey
.nodeName
[0];
2025 bcopy(&hfskey
, key
, sizeof(hfskey
));
2032 * Resolve hard link reference to obtain the inode record.
2036 resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
)
2038 FSBufferDescriptor btdata
;
2039 struct BTreeIterator
*iterator
;
2040 struct cat_desc idesc
;
2044 BDINIT(btdata
, recp
);
2045 MAKE_INODE_NAME(inodename
, linkref
);
2047 /* Get space for iterator */
2048 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2049 bzero(iterator
, sizeof(*iterator
));
2051 /* Build a descriptor for private dir. */
2052 idesc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
2053 idesc
.cd_nameptr
= inodename
;
2054 idesc
.cd_namelen
= strlen(inodename
);
2057 idesc
.cd_encoding
= 0;
2058 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
2060 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2061 &btdata
, NULL
, NULL
);
2064 /* Make sure there's a reference */
2065 if (recp
->bsdInfo
.special
.linkCount
== 0)
2066 recp
->bsdInfo
.special
.linkCount
= 2;
2068 printf("HFS resolvelink: can't find %s\n", inodename
);
2071 FREE(iterator
, M_TEMP
);
2073 return (result
? ENOENT
: 0);
2077 * Resolve hard link reference to obtain the inode number.
2080 resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
)
2082 struct HFSPlusCatalogFile record
;
2085 error
= resolvelink(hfsmp
, linkref
, &record
);
2087 if (record
.fileID
== 0)
2090 *ino
= record
.fileID
;
2096 * getkey - get a key from id by doing a thread lookup
2099 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
2101 struct BTreeIterator
* iterator
;
2102 FSBufferDescriptor btdata
;
2105 CatalogRecord
* recp
;
2109 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2111 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2112 bzero(iterator
, sizeof(*iterator
));
2113 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
2115 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
2116 BDINIT(btdata
, recp
);
2118 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2119 &btdata
, &datasize
, iterator
);
2123 /* Turn thread record into a cnode key (in place) */
2124 switch (recp
->recordType
) {
2125 case kHFSFileThreadRecord
:
2126 case kHFSFolderThreadRecord
:
2127 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
2128 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
2129 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
2132 case kHFSPlusFileThreadRecord
:
2133 case kHFSPlusFolderThreadRecord
:
2134 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
2135 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
2136 (keyp
->hfsPlus
.nodeName
.length
* 2);
2137 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
2146 FREE(iterator
, M_TEMP
);
2149 return MacToVFSError(result
);
2154 * buildrecord - build a default catalog directory or file record
2157 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
2158 CatalogRecord
*crp
, int *recordSize
)
2160 int type
= attrp
->ca_mode
& S_IFMT
;
2161 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
2164 createtime
= UTCToLocal(createtime
);
2165 if (type
== S_IFDIR
) {
2166 bzero(crp
, sizeof(HFSCatalogFolder
));
2167 crp
->recordType
= kHFSFolderRecord
;
2168 crp
->hfsFolder
.folderID
= cnid
;
2169 crp
->hfsFolder
.createDate
= createtime
;
2170 crp
->hfsFolder
.modifyDate
= createtime
;
2171 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
2172 *recordSize
= sizeof(HFSCatalogFolder
);
2174 bzero(crp
, sizeof(HFSCatalogFile
));
2175 crp
->recordType
= kHFSFileRecord
;
2176 crp
->hfsFile
.fileID
= cnid
;
2177 crp
->hfsFile
.createDate
= createtime
;
2178 crp
->hfsFile
.modifyDate
= createtime
;
2179 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
2180 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
2181 *recordSize
= sizeof(HFSCatalogFile
);
2184 struct HFSPlusBSDInfo
* bsdp
= NULL
;
2185 struct FndrFileInfo
* fip
= NULL
;
2187 if (type
== S_IFDIR
) {
2188 bzero(crp
, sizeof(HFSPlusCatalogFolder
));
2189 crp
->recordType
= kHFSPlusFolderRecord
;
2190 crp
->hfsPlusFolder
.folderID
= cnid
;
2191 crp
->hfsPlusFolder
.createDate
= createtime
;
2192 crp
->hfsPlusFolder
.contentModDate
= createtime
;
2193 crp
->hfsPlusFolder
.accessDate
= createtime
;
2194 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
2195 crp
->hfsPlusFolder
.textEncoding
= encoding
;
2196 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
2197 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
2198 *recordSize
= sizeof(HFSPlusCatalogFolder
);
2200 bzero(crp
, sizeof(HFSPlusCatalogFile
));
2201 crp
->recordType
= kHFSPlusFileRecord
;
2202 crp
->hfsPlusFile
.fileID
= cnid
;
2203 crp
->hfsPlusFile
.createDate
= createtime
;
2204 crp
->hfsPlusFile
.contentModDate
= createtime
;
2205 crp
->hfsPlusFile
.accessDate
= createtime
;
2206 crp
->hfsPlusFile
.attributeModDate
= createtime
;
2207 crp
->hfsPlusFile
.flags
|= kHFSThreadExistsMask
;
2208 crp
->hfsPlusFile
.textEncoding
= encoding
;
2209 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
2213 /* BLK/CHR need to save the device info */
2214 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
2217 /* Hardlink links need to save the linkref */
2218 fip
= (FndrFileInfo
*)&attrp
->ca_finderinfo
;
2219 if ((SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2220 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2221 bsdp
->special
.iNodeNum
= attrp
->ca_rdev
;
2223 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2226 /* Symlinks also have a type and creator */
2227 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2230 *recordSize
= sizeof(HFSPlusCatalogFile
);
2232 bsdp
->ownerID
= attrp
->ca_uid
;
2233 bsdp
->groupID
= attrp
->ca_gid
;
2234 bsdp
->fileMode
= attrp
->ca_mode
;
2235 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
2236 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
2242 * builddesc - build a cnode descriptor from an HFS+ key
2245 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
2246 int isdir
, struct cat_desc
*descp
)
2254 /* guess a size... */
2255 bufsize
= (3 * key
->nodeName
.length
) + 1;
2256 if (bufsize
>= sizeof(tmpbuff
)-1) {
2257 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2259 nameptr
= &tmpbuff
[0];
2262 result
= utf8_encodestr(key
->nodeName
.unicode
,
2263 key
->nodeName
.length
* sizeof(UniChar
),
2264 nameptr
, (size_t *)&utf8len
,
2267 if (result
== ENAMETOOLONG
) {
2268 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
2269 key
->nodeName
.length
* sizeof(UniChar
),
2271 FREE(nameptr
, M_TEMP
);
2272 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2274 result
= utf8_encodestr(key
->nodeName
.unicode
,
2275 key
->nodeName
.length
* sizeof(UniChar
),
2276 nameptr
, (size_t *)&utf8len
,
2279 descp
->cd_parentcnid
= key
->parentID
;
2280 descp
->cd_nameptr
= add_name(nameptr
, utf8len
, 0, 0);
2281 descp
->cd_namelen
= utf8len
;
2282 descp
->cd_cnid
= cnid
;
2283 descp
->cd_hint
= hint
;
2284 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
2286 descp
->cd_flags
|= CD_ISDIR
;
2287 descp
->cd_encoding
= encoding
;
2288 if (nameptr
!= &tmpbuff
[0]) {
2289 FREE(nameptr
, M_TEMP
);
2296 * getbsdattr - get attributes in bsd format
2300 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
2302 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
2303 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
2305 attrp
->ca_nlink
= 1;
2306 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
2307 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
2308 attrp
->ca_mtime_nsec
= 0;
2309 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
2310 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
2311 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
2313 if ((bsd
->fileMode
& S_IFMT
) == 0) {
2314 attrp
->ca_flags
= 0;
2315 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2316 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2318 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
2320 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
2324 attrp
->ca_uid
= bsd
->ownerID
;
2325 attrp
->ca_gid
= bsd
->groupID
;
2326 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
2327 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
2328 switch (attrp
->ca_mode
& S_IFMT
) {
2329 case S_IFCHR
: /* fall through */
2331 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
2334 /* Pick up the hard link count */
2335 if (bsd
->special
.linkCount
> 0)
2336 attrp
->ca_nlink
= bsd
->special
.linkCount
;
2340 if (HFSTOVFS(hfsmp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
2342 * Override the permissions as determined by the mount auguments
2343 * in ALMOST the same way unset permissions are treated but keep
2344 * track of whether or not the file or folder is hfs locked
2345 * by leaving the h_pflags field unchanged from what was unpacked
2346 * out of the catalog.
2348 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2349 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2354 if (!S_ISDIR(attrp
->ca_mode
)) {
2355 attrp
->ca_mode
&= ~S_IFMT
;
2356 attrp
->ca_mode
|= S_IFDIR
;
2358 attrp
->ca_nlink
= 2 + ((HFSPlusCatalogFolder
*)crp
)->valence
;
2359 attrp
->ca_entries
= ((HFSPlusCatalogFolder
*)crp
)->valence
;
2361 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2362 if (crp
->flags
& kHFSFileLockedMask
) {
2363 /* The file's supposed to be locked:
2364 Make sure at least one of the IMMUTABLE bits is set: */
2365 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
2366 attrp
->ca_flags
|= UF_IMMUTABLE
;
2368 /* The file's supposed to be unlocked: */
2369 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
2371 /* get total blocks (both forks) */
2372 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
2375 attrp
->ca_fileid
= crp
->fileID
;
2377 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
2381 * promotekey - promote hfs key to hfs plus key
2385 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
2386 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
2388 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
2392 *encoding
= hfsmp
->hfs_encoding
;
2394 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
2395 kHFSPlusMaxFileNameChars
, &uniCount
);
2397 * When an HFS name cannot be encoded with the current
2398 * encoding use MacRoman as a fallback.
2400 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
2402 (void) mac_roman_to_unicode(hfskey
->nodeName
,
2403 keyp
->nodeName
.unicode
,
2404 kHFSPlusMaxFileNameChars
,
2408 keyp
->nodeName
.length
= uniCount
;
2409 keyp
->parentID
= hfskey
->parentID
;
2413 * promotefork - promote hfs fork info to hfs plus
2417 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
2418 int resource
, struct cat_fork
* forkp
)
2420 struct HFSPlusExtentDescriptor
*xp
;
2421 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2423 bzero(forkp
, sizeof(*forkp
));
2424 xp
= &forkp
->cf_extents
[0];
2426 forkp
->cf_size
= filep
->rsrcLogicalSize
;
2427 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
2428 forkp
->cf_bytesread
= 0;
2429 forkp
->cf_vblocks
= 0;
2430 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
2431 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
2432 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
2433 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
2434 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
2435 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
2437 forkp
->cf_size
= filep
->dataLogicalSize
;
2438 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
2439 forkp
->cf_bytesread
= 0;
2440 forkp
->cf_vblocks
= 0;
2441 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
2442 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
2443 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
2444 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
2445 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
2446 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
2451 * promoteattr - promote hfs catalog attributes to hfs plus
2455 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
2457 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2459 if (dataPtr
->recordType
== kHFSFolderRecord
) {
2460 struct HFSCatalogFolder
* folder
;
2462 folder
= (struct HFSCatalogFolder
*) dataPtr
;
2463 crp
->recordType
= kHFSPlusFolderRecord
;
2464 crp
->flags
= folder
->flags
;
2465 crp
->fileID
= folder
->folderID
;
2466 crp
->createDate
= LocalToUTC(folder
->createDate
);
2467 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
2468 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
2469 crp
->reserved1
= folder
->valence
;
2470 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
2472 struct HFSCatalogFile
* file
;
2474 file
= (struct HFSCatalogFile
*) dataPtr
;
2475 crp
->recordType
= kHFSPlusFileRecord
;
2476 crp
->flags
= file
->flags
;
2477 crp
->fileID
= file
->fileID
;
2478 crp
->createDate
= LocalToUTC(file
->createDate
);
2479 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
2480 crp
->backupDate
= LocalToUTC(file
->backupDate
);
2482 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
2483 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
2484 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
2485 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
2487 crp
->textEncoding
= 0;
2488 crp
->attributeModDate
= crp
->contentModDate
;
2489 crp
->accessDate
= crp
->contentModDate
;
2490 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
2495 * Build a catalog node thread record from a catalog key
2496 * and return the size of the record.
2499 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
2504 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
2505 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
2507 size
= sizeof(HFSCatalogThread
);
2510 rec
->recordType
= kHFSFolderThreadRecord
;
2512 rec
->recordType
= kHFSFileThreadRecord
;
2513 rec
->parentID
= key
->parentID
;
2514 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
2517 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
2518 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
2520 size
= sizeof(HFSPlusCatalogThread
);
2522 rec
->recordType
= kHFSPlusFolderThreadRecord
;
2524 rec
->recordType
= kHFSPlusFileThreadRecord
;
2526 rec
->parentID
= key
->parentID
;
2527 bcopy(&key
->nodeName
, &rec
->nodeName
,
2528 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
2530 /* HFS Plus has varaible sized thread records */
2531 size
-= (sizeof(rec
->nodeName
.unicode
) -
2532 (rec
->nodeName
.length
* sizeof(UniChar
)));
2539 * Build a catalog node thread key.
2542 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
2545 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
2546 key
->hfs
.reserved
= 0;
2547 key
->hfs
.parentID
= parentID
;
2548 key
->hfs
.nodeName
[0] = 0;
2550 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
2551 key
->hfsPlus
.parentID
= parentID
;
2552 key
->hfsPlus
.nodeName
.length
= 0;
2557 * Extract the text encoding from a catalog node record.
2560 getencoding(const CatalogRecord
*crp
)
2564 if (crp
->recordType
== kHFSPlusFolderRecord
)
2565 encoding
= crp
->hfsPlusFolder
.textEncoding
;
2566 else if (crp
->recordType
== kHFSPlusFileRecord
)
2567 encoding
= crp
->hfsPlusFile
.textEncoding
;
2575 * Extract the CNID from a catalog node record.
2578 getcnid(const CatalogRecord
*crp
)
2582 switch (crp
->recordType
) {
2583 case kHFSFolderRecord
:
2584 cnid
= crp
->hfsFolder
.folderID
;
2586 case kHFSFileRecord
:
2587 cnid
= crp
->hfsFile
.fileID
;
2589 case kHFSPlusFolderRecord
:
2590 cnid
= crp
->hfsPlusFolder
.folderID
;
2592 case kHFSPlusFileRecord
:
2593 cnid
= crp
->hfsPlusFile
.fileID
;
2596 panic("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp
);
2604 * Extract the parent ID from a catalog node record.
2607 getparentcnid(const CatalogRecord
*recp
)
2611 switch (recp
->recordType
) {
2612 case kHFSFileThreadRecord
:
2613 case kHFSFolderThreadRecord
:
2614 cnid
= recp
->hfsThread
.parentID
;
2617 case kHFSPlusFileThreadRecord
:
2618 case kHFSPlusFolderThreadRecord
:
2619 cnid
= recp
->hfsPlusThread
.parentID
;
2622 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp
);
2630 * Determine if a catalog node record is a directory.
2633 isadir(const CatalogRecord
*crp
)
2635 return (crp
->recordType
== kHFSFolderRecord
||
2636 crp
->recordType
== kHFSPlusFolderRecord
);