2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
33 #include <sys/mount.h>
34 #include <sys/vnode.h>
35 #include <sys/dirent.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
39 #include <sys/utfconv.h>
42 #include "hfs_catalog.h"
43 #include "hfs_format.h"
44 #include "hfs_endian.h"
46 #include "hfscommon/headers/BTreesInternal.h"
47 #include "hfscommon/headers/BTreesPrivate.h"
48 #include "hfscommon/headers/HFSUnicodeWrappers.h"
52 * Initialization of an FSBufferDescriptor structure.
54 #define BDINIT(bd, addr) { \
55 (bd).bufferAddress = (addr); \
56 (bd).itemSize = sizeof(*(addr)); \
62 BTreeIterator iterator
;
63 HFSPlusCatalogKey key
;
68 struct cat_desc
* s_desc
;
69 struct cat_attr
* s_attr
;
70 struct cat_fork
* s_datafork
;
71 struct cat_fork
* s_rsrcfork
;
72 struct hfsmount
* s_hfsmp
;
75 struct position_state
{
80 struct hfsmount
*hfsmp
;
83 /* Map file mode type to directory entry types */
84 u_char modetodirtype
[16] = {
85 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
86 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
87 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
88 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
90 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
93 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int allow_system_files
, u_long hint
, int wantrsrc
,
94 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
96 static int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
97 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
99 /* Internal catalog support routines */
101 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
102 struct position_state
*state
);
104 static int resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
);
106 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
108 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
109 HFSPlusCatalogKey
*key
, int retry
);
111 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
113 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, u_int32_t
*recordSize
);
115 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
117 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
118 int isdir
, struct cat_desc
*descp
);
120 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
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
);
126 static cnid_t
getcnid(const CatalogRecord
*crp
);
127 static u_long
getencoding(const CatalogRecord
*crp
);
128 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
130 static int isadir(const CatalogRecord
*crp
);
132 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
134 static int cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
);
139 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, __unused proc_t p
)
144 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
145 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
147 result
= BTReserveSpace(hfsmp
->hfs_catalog_cp
->c_datafork
, ops
, (void*)cookie
);
150 hfs_systemfile_unlock(hfsmp
, lockflags
);
152 return MacToVFSError(result
);
157 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, __unused proc_t p
)
161 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
162 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
164 (void) BTReleaseReserve(hfsmp
->hfs_catalog_cp
->c_datafork
, (void*)cookie
);
167 hfs_systemfile_unlock(hfsmp
, lockflags
);
174 struct hfsmount
*hfsmp
,
175 CatalogRecord
* recp
,
176 struct cat_attr
*attrp
,
177 struct cat_fork
*datafp
,
178 struct cat_fork
*rsrcfp
)
180 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
183 struct HFSPlusCatalogFile cnoderec
;
185 promoteattr(hfsmp
, recp
, &cnoderec
);
186 getbsdattr(hfsmp
, &cnoderec
, attrp
);
188 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
192 bzero(datafp
, sizeof(*datafp
));
194 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
195 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
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
;
206 datafp
->cf_bytesread
= 0;
208 datafp
->cf_vblocks
= 0;
209 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
210 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
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
;
221 datafp
->cf_bytesread
= 0;
223 rsrcfp
->cf_vblocks
= 0;
224 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
225 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
230 * Convert a raw catalog key and record into an in-core catalog descriptor.
232 * Note: The caller is responsible for releasing the catalog descriptor.
237 struct hfsmount
*hfsmp
,
239 CatalogRecord
* recp
,
240 struct cat_desc
*descp
)
242 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
243 HFSPlusCatalogKey
* pluskey
= NULL
;
247 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
248 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
251 pluskey
= (HFSPlusCatalogKey
*)key
;
252 encoding
= getencoding(recp
);
255 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
257 FREE(pluskey
, M_TEMP
);
268 cat_releasedesc(struct cat_desc
*descp
)
270 const u_int8_t
* name
;
275 if ((descp
->cd_flags
& CD_HASBUF
) &&
276 (descp
->cd_nameptr
!= NULL
)) {
277 name
= descp
->cd_nameptr
;
278 descp
->cd_nameptr
= NULL
;
279 descp
->cd_namelen
= 0;
280 vfs_removename((const char *)name
);
282 descp
->cd_nameptr
= NULL
;
283 descp
->cd_namelen
= 0;
284 descp
->cd_flags
&= ~CD_HASBUF
;
288 * These Catalog functions allow access to the HFS Catalog (database).
289 * The catalog b-tree lock must be aquired before calling any of these routines.
293 * cat_lookup - lookup a catalog node using a cnode decriptor
295 * Note: The caller is responsible for releasing the output
296 * catalog descriptor (when supplied outdescp is non-null).
300 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
301 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
302 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
308 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
310 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
312 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
316 result
= cat_lookupbykey(hfsmp
, keyp
, 0, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
318 if (result
== ENOENT
) {
320 struct cat_desc temp_desc
;
321 if (outdescp
== NULL
) {
322 bzero(&temp_desc
, sizeof(temp_desc
));
323 outdescp
= &temp_desc
;
325 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
327 *desc_cnid
= outdescp
->cd_cnid
;
329 if (outdescp
== &temp_desc
) {
330 /* Release the local copy of desc */
331 cat_releasedesc(outdescp
);
333 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
334 // make MacRoman key from utf-8
335 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
336 // update desc text encoding so that other catalog ops succeed
347 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
349 struct BTreeIterator
*iterator
;
350 struct FSBufferDescriptor file_data
;
351 struct HFSCatalogFile file_rec
;
356 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
359 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
361 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
362 bzero(&iterator
[0], 2* sizeof(*iterator
));
363 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
367 BDINIT(file_data
, &file_rec
);
368 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
372 if (file_rec
.recordType
!= kHFSFileRecord
) {
377 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
378 struct FSBufferDescriptor thread_data
;
379 struct HFSCatalogThread thread_rec
;
381 file_rec
.flags
|= kHFSThreadExistsMask
;
382 BDINIT(thread_data
, &thread_rec
);
383 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
384 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
386 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
390 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
391 (void) BTFlushPath(fcb
);
394 (void) BTFlushPath(fcb
);
395 FREE(iterator
, M_TEMP
);
397 return MacToVFSError(result
);
402 * cat_findname - obtain a descriptor from cnid
404 * Only a thread lookup is performed.
406 * Note: The caller is responsible for releasing the output
407 * catalog descriptor (when supplied outdescp is non-null).
412 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
414 struct BTreeIterator
* iterator
;
415 FSBufferDescriptor btdata
;
417 CatalogRecord
* recp
;
423 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
425 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
426 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
427 iterator
->hint
.nodeNum
= 0;
429 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
430 BDINIT(btdata
, recp
);
432 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
436 /* Turn thread record into a cnode key (in place). */
437 switch (recp
->recordType
) {
438 case kHFSFolderThreadRecord
:
441 case kHFSFileThreadRecord
:
442 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
443 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
446 case kHFSPlusFolderThreadRecord
:
449 case kHFSPlusFileThreadRecord
:
450 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
451 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
452 (keyp
->hfsPlus
.nodeName
.length
* 2);
459 HFSPlusCatalogKey
* pluskey
= NULL
;
462 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
463 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
464 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
465 FREE(pluskey
, M_TEMP
);
468 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
472 FREE(iterator
, M_TEMP
);
474 return MacToVFSError(result
);
478 * cat_idlookup - lookup a catalog node using a cnode id
480 * Note: The caller is responsible for releasing the output
481 * catalog descriptor (when supplied outdescp is non-null).
485 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
,
486 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
488 struct BTreeIterator
* iterator
;
489 FSBufferDescriptor btdata
;
492 CatalogRecord
* recp
;
496 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
498 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
499 bzero(iterator
, sizeof(*iterator
));
500 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
502 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
503 BDINIT(btdata
, recp
);
505 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
506 &btdata
, &datasize
, iterator
);
510 /* Turn thread record into a cnode key (in place) */
511 switch (recp
->recordType
) {
512 case kHFSFileThreadRecord
:
513 case kHFSFolderThreadRecord
:
514 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
515 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
518 case kHFSPlusFileThreadRecord
:
519 case kHFSPlusFolderThreadRecord
:
520 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
521 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
522 (keyp
->hfsPlus
.nodeName
.length
* 2);
530 result
= cat_lookupbykey(hfsmp
, keyp
, allow_system_files
, 0, 0, outdescp
, attrp
, forkp
, NULL
);
531 /* No corresponding file/folder record found for a thread record,
532 * mark the volume inconsistent.
534 if (result
== 0 && outdescp
) {
535 cnid_t dcnid
= outdescp
->cd_cnid
;
537 * Just for sanity's case, let's make sure that
538 * the key in the thread matches the key in the record.
541 printf("Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
547 FREE(iterator
, M_TEMP
);
549 return MacToVFSError(result
);
554 * cat_lookupmangled - lookup a catalog node using a mangled name
557 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
558 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
567 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
568 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
571 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
572 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
573 fileID
== hfsmp
->hfs_jnlfileid
||
574 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
578 result
= cat_idlookup(hfsmp
, fileID
, 0, outdescp
, attrp
, forkp
);
581 /* It must be in the correct directory */
582 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
585 if (((u_int16_t
)outdescp
->cd_namelen
< prefixlen
) ||
586 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
592 cat_releasedesc(outdescp
);
598 * cat_lookupbykey - lookup a catalog node using a cnode key
601 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int allow_system_files
, u_long hint
, int wantrsrc
,
602 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
604 struct BTreeIterator
* iterator
;
605 FSBufferDescriptor btdata
;
606 CatalogRecord
* recp
;
614 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
616 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
617 BDINIT(btdata
, recp
);
618 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
619 bzero(iterator
, sizeof(*iterator
));
620 iterator
->hint
.nodeNum
= hint
;
621 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
623 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
624 &btdata
, &datasize
, iterator
);
628 /* Save the cnid and encoding now in case there's a hard link */
629 cnid
= getcnid(recp
);
630 encoding
= getencoding(recp
);
631 hint
= iterator
->hint
.nodeNum
;
633 /* Hide the journal files (if any) */
634 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
635 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
636 !allow_system_files
) {
643 * When a hardlink link is encountered, auto resolve it.
645 * The catalog record will change, and possibly its type.
649 && (recp
->recordType
== kHFSPlusFileRecord
)
650 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->vcbCrDate
) ||
651 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
655 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
656 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
658 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
659 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
660 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
663 if (isfilelink
|| isdirlink
) {
664 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
665 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
671 struct HFSPlusCatalogFile cnoderec
;
673 promoteattr(hfsmp
, recp
, &cnoderec
);
674 getbsdattr(hfsmp
, &cnoderec
, attrp
);
676 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
678 attrp
->ca_linkref
= ilink
;
683 bzero(forkp
, sizeof(*forkp
));
684 } else if (std_hfs
) {
685 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
686 } else if (wantrsrc
) {
687 /* Convert the resource fork. */
688 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
689 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
690 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
691 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
692 forkp
->cf_bytesread
=
693 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
694 HFSTOVCB(hfsmp
)->blockSize
;
696 forkp
->cf_bytesread
= 0;
698 forkp
->cf_vblocks
= 0;
699 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
700 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
705 /* Convert the data fork. */
706 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
707 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
708 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
709 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
710 forkp
->cf_bytesread
=
711 recp
->hfsPlusFile
.dataFork
.clumpSize
*
712 HFSTOVCB(hfsmp
)->blockSize
;
714 forkp
->cf_bytesread
= 0;
716 forkp
->cf_vblocks
= 0;
717 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
718 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
720 /* Validate the fork's resident extents. */
722 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
723 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
724 /* Suppress any bad extents so a remove can succeed. */
725 forkp
->cf_extents
[i
].startBlock
= 0;
726 forkp
->cf_extents
[i
].blockCount
= 0;
729 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
732 validblks
+= forkp
->cf_extents
[i
].blockCount
;
735 /* Adjust for any missing blocks. */
736 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
739 forkp
->cf_blocks
= validblks
;
741 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
743 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
744 if (psize
< forkp
->cf_size
) {
745 forkp
->cf_size
= psize
;
752 HFSPlusCatalogKey
* pluskey
= NULL
;
755 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
756 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
759 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
761 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
763 FREE(pluskey
, M_TEMP
);
767 if (desc_cnid
!= NULL
) {
771 FREE(iterator
, M_TEMP
);
774 return MacToVFSError(result
);
779 * cat_create - create a node in the catalog
781 * NOTE: both the catalog file and attribute file locks must
782 * be held before calling this function.
784 * The caller is responsible for releasing the output
785 * catalog descriptor (when supplied outdescp is non-null).
789 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
790 struct cat_desc
*out_descp
)
794 FSBufferDescriptor btdata
;
799 u_long encoding
= kTextEncodingMacRoman
;
802 modeformat
= attrp
->ca_mode
& S_IFMT
;
804 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
805 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
808 * Get the next CNID. We can change it since we hold the catalog lock.
810 nextCNID
= hfsmp
->vcbNxtCNID
;
811 if (nextCNID
== 0xFFFFFFFF) {
815 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
816 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
817 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
818 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
825 /* Get space for iterator, key and data */
826 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
827 bto
->iterator
.hint
.nodeNum
= 0;
829 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
834 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
835 bto
->key
.nodeName
.length
);
836 hfs_setencodingbits(hfsmp
, encoding
);
840 * Insert the thread record first
842 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
843 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
844 S_ISDIR(attrp
->ca_mode
));
845 btdata
.bufferAddress
= &bto
->data
;
846 btdata
.itemSize
= datalen
;
847 btdata
.itemCount
= 1;
850 // this call requires the attribute file lock to be held
851 result
= file_attribute_exist(hfsmp
, nextCNID
);
852 if (result
== EEXIST
) {
853 // that cnid has orphaned attributes so just skip it.
854 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
855 nextCNID
= kHFSFirstUserCatalogNodeID
;
859 if (result
) goto exit
;
861 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
863 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
864 if ((result
== btExists
) && !std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
866 * Allow CNIDs on HFS Plus volumes to wrap around
868 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
869 nextCNID
= kHFSFirstUserCatalogNodeID
;
875 if (result
) goto exit
;
879 * CNID is now established. If we have wrapped then
880 * update the vcbNxtCNID.
882 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
883 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
884 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
885 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
890 * Now insert the file/directory record
892 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
893 btdata
.bufferAddress
= &bto
->data
;
894 btdata
.itemSize
= datalen
;
895 btdata
.itemCount
= 1;
897 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
899 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
901 if (result
== btExists
)
904 /* Back out the thread record */
905 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
906 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
907 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
908 /* Error on deleting extra thread record, mark
909 * volume inconsistent
911 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
912 hfs_mark_volume_inconsistent(hfsmp
);
919 * Insert was successful, update name, parent and volume
921 if (out_descp
!= NULL
) {
922 HFSPlusCatalogKey
* pluskey
= NULL
;
925 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
926 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
929 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
931 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
932 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
934 FREE(pluskey
, M_TEMP
);
937 attrp
->ca_fileid
= nextCNID
;
940 (void) BTFlushPath(fcb
);
943 return MacToVFSError(result
);
948 * cnode_rename - rename a catalog node
950 * Assumes that the target's directory exists.
952 * Order of B-tree operations:
953 * 1. BTSearchRecord(from_cnode, &data);
954 * 2. BTInsertRecord(to_cnode, &data);
955 * 3. BTDeleteRecord(from_cnode);
956 * 4. BTDeleteRecord(from_thread);
957 * 5. BTInsertRecord(to_thread);
959 * Note: The caller is responsible for releasing the output
960 * catalog descriptor (when supplied out_cdp is non-null).
965 struct hfsmount
* hfsmp
,
966 struct cat_desc
* from_cdp
,
967 struct cat_desc
* todir_cdp
,
968 struct cat_desc
* to_cdp
,
969 struct cat_desc
* out_cdp
)
971 struct BTreeIterator
* to_iterator
= NULL
;
972 struct BTreeIterator
* from_iterator
= NULL
;
973 FSBufferDescriptor btdata
;
974 CatalogRecord
* recp
= NULL
;
975 HFSPlusCatalogKey
* to_key
;
982 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
987 vcb
= HFSTOVCB(hfsmp
);
988 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
989 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
991 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
994 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
995 bzero(from_iterator
, sizeof(*from_iterator
));
996 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
999 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1000 bzero(to_iterator
, sizeof(*to_iterator
));
1001 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1004 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1005 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1006 BDINIT(btdata
, recp
);
1009 * When moving a directory, make sure its a valid move.
1011 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1012 struct BTreeIterator iterator
;
1013 cnid_t cnid
= from_cdp
->cd_cnid
;
1014 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1016 /* First check the obvious ones */
1017 if (cnid
== fsRtDirID
||
1018 cnid
== to_cdp
->cd_parentcnid
||
1023 bzero(&iterator
, sizeof(iterator
));
1025 * Traverse destination path all the way back to the root
1026 * making sure that source directory is not encountered.
1029 while (pathcnid
> fsRtDirID
) {
1030 buildthreadkey(pathcnid
, std_hfs
,
1031 (CatalogKey
*)&iterator
.key
);
1032 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
1034 if (result
) goto exit
;
1036 pathcnid
= getparentcnid(recp
);
1037 if (pathcnid
== cnid
|| pathcnid
== 0) {
1045 * Step 1: Find cnode data at old location
1047 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1048 &datasize
, from_iterator
);
1050 if (std_hfs
|| (result
!= btNotFound
))
1053 struct cat_desc temp_desc
;
1055 /* Probably the node has mangled name */
1056 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1060 /* The file has mangled name. Search the cnode data using full name */
1061 bzero(from_iterator
, sizeof(*from_iterator
));
1062 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1064 cat_releasedesc(&temp_desc
);
1068 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1070 cat_releasedesc(&temp_desc
);
1074 cat_releasedesc(&temp_desc
);
1077 /* Check if the source is directory hard link. We do not change
1078 * directory flag because it is later used to initialize result descp
1082 (recp
->recordType
== kHFSPlusFileRecord
) &&
1083 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1088 * Update the text encoding (on disk and in descriptor).
1090 * Note that hardlink inodes don't require a text encoding hint.
1093 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1094 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1095 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1096 hfs_setencodingbits(hfsmp
, encoding
);
1097 recp
->hfsPlusFile
.textEncoding
= encoding
;
1099 out_cdp
->cd_encoding
= encoding
;
1102 if (std_hfs
&& !directory
&&
1103 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1107 * If the keys are identical then there's nothing left to do!
1109 * update the hint and exit
1112 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1114 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1118 /* Step 2: Insert cnode at new location */
1119 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1120 if (result
== btExists
) {
1121 int fromtype
= recp
->recordType
;
1123 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1124 goto exit
; /* EEXIST */
1126 /* Find cnode data at new location */
1127 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1131 if ((fromtype
!= recp
->recordType
) ||
1132 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1134 goto exit
; /* EEXIST */
1136 /* The old name is a case variant and must be removed */
1137 result
= BTDeleteRecord(fcb
, from_iterator
);
1141 /* Insert cnode (now that case duplicate is gone) */
1142 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1144 /* Try and restore original before leaving */
1149 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1151 printf("cat_create: could not undo (BTInsert = %d)", err
);
1152 hfs_mark_volume_inconsistent(hfsmp
);
1158 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1167 /* Step 3: Remove cnode from old location */
1169 result
= BTDeleteRecord(fcb
, from_iterator
);
1171 /* Try and delete new record before leaving */
1176 err
= BTDeleteRecord(fcb
, to_iterator
);
1178 printf("cat_create: could not undo (BTDelete = %d)", err
);
1179 hfs_mark_volume_inconsistent(hfsmp
);
1185 (void) BTDeleteRecord(fcb
, to_iterator
);
1191 /* #### POINT OF NO RETURN #### */
1194 * Step 4: Remove cnode's old thread record
1196 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1197 (void) BTDeleteRecord(fcb
, from_iterator
);
1200 * Step 5: Insert cnode's new thread record
1201 * (optional for HFS files)
1204 /* For directory hard links, always create a file thread
1205 * record. For everything else, use the directory flag.
1208 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1210 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1212 btdata
.itemSize
= datasize
;
1213 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1214 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1218 HFSPlusCatalogKey
* pluskey
= NULL
;
1221 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1222 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1224 /* Save the real encoding hint in the Finder Info (field 4). */
1225 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1228 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1229 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1233 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1235 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1236 encoding
, directory
, out_cdp
);
1238 FREE(pluskey
, M_TEMP
);
1242 (void) BTFlushPath(fcb
);
1244 FREE(from_iterator
, M_TEMP
);
1246 FREE(to_iterator
, M_TEMP
);
1249 return MacToVFSError(result
);
1254 * cat_delete - delete a node from the catalog
1256 * Order of B-tree operations:
1257 * 1. BTDeleteRecord(cnode);
1258 * 2. BTDeleteRecord(thread);
1259 * 3. BTUpdateRecord(parent);
1263 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1266 BTreeIterator
*iterator
;
1271 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1272 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1276 * The root directory cannot be deleted
1277 * A directory must be empty
1278 * A file must be zero length (no blocks)
1280 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1281 descp
->cd_parentcnid
== kHFSRootParentID
)
1284 /* XXX Preflight Missing */
1286 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1287 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1288 iterator
->hint
.nodeNum
= 0;
1291 * Derive a key from either the file ID (for a virtual inode)
1292 * or the descriptor.
1294 if (descp
->cd_namelen
== 0) {
1295 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1296 cnid
= attrp
->ca_fileid
;
1298 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1299 cnid
= descp
->cd_cnid
;
1305 result
= BTDeleteRecord(fcb
, iterator
);
1307 if (std_hfs
|| (result
!= btNotFound
))
1310 struct cat_desc temp_desc
;
1312 /* Probably the node has mangled name */
1313 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1317 /* The file has mangled name. Delete the file using full name */
1318 bzero(iterator
, sizeof(*iterator
));
1319 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1320 cnid
= temp_desc
.cd_cnid
;
1322 cat_releasedesc(&temp_desc
);
1326 result
= BTDeleteRecord(fcb
, iterator
);
1328 cat_releasedesc(&temp_desc
);
1332 cat_releasedesc(&temp_desc
);
1335 /* Delete thread record. On error, mark volume inconsistent */
1336 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1337 if (BTDeleteRecord(fcb
, iterator
)) {
1339 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1340 hfs_mark_volume_inconsistent(hfsmp
);
1345 (void) BTFlushPath(fcb
);
1347 return MacToVFSError(result
);
1352 * cnode_update - update the catalog node described by descp
1353 * using the data from attrp and forkp.
1357 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1358 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1361 BTreeIterator
* iterator
;
1362 struct update_state state
;
1366 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1367 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1369 state
.s_desc
= descp
;
1370 state
.s_attr
= attrp
;
1371 state
.s_datafork
= dataforkp
;
1372 state
.s_rsrcfork
= rsrcforkp
;
1373 state
.s_hfsmp
= hfsmp
;
1375 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1376 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1379 * For open-deleted files we need to do a lookup by cnid
1380 * (using thread rec).
1382 * For hard links, the target of the update is the inode
1383 * itself (not the link record) so a lookup by fileid
1384 * (i.e. thread rec) is needed.
1386 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1387 (descp
->cd_namelen
== 0) ||
1388 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
1389 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1391 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1396 /* Pass a node hint */
1397 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1399 result
= BTUpdateRecord(fcb
, iterator
,
1400 (IterateCallBackProcPtr
)catrec_update
, &state
);
1404 /* Update the node hint. */
1405 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1408 (void) BTFlushPath(fcb
);
1410 return MacToVFSError(result
);
1414 * catrec_update - Update the fields of a catalog record
1415 * This is called from within BTUpdateRecord.
1418 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1420 struct cat_desc
*descp
;
1421 struct cat_attr
*attrp
;
1422 struct cat_fork
*forkp
;
1423 struct hfsmount
*hfsmp
;
1427 descp
= state
->s_desc
;
1428 attrp
= state
->s_attr
;
1429 hfsmp
= state
->s_hfsmp
;
1430 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1432 switch (crp
->recordType
) {
1433 case kHFSFolderRecord
: {
1434 HFSCatalogFolder
*dir
;
1436 dir
= (struct HFSCatalogFolder
*)crp
;
1437 /* Do a quick sanity check */
1438 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1439 (dir
->folderID
!= descp
->cd_cnid
))
1440 return (btNotFound
);
1441 dir
->valence
= attrp
->ca_entries
;
1442 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1443 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1444 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1445 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1446 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1449 case kHFSFileRecord
: {
1450 HFSCatalogFile
*file
;
1452 file
= (struct HFSCatalogFile
*)crp
;
1453 /* Do a quick sanity check */
1454 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1455 (file
->fileID
!= attrp
->ca_fileid
))
1456 return (btNotFound
);
1457 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1458 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1459 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1460 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1461 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1462 if (state
->s_rsrcfork
) {
1463 forkp
= state
->s_rsrcfork
;
1464 file
->rsrcLogicalSize
= forkp
->cf_size
;
1465 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1466 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1467 file
->rsrcExtents
[i
].startBlock
=
1468 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1469 file
->rsrcExtents
[i
].blockCount
=
1470 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1473 if (state
->s_datafork
) {
1474 forkp
= state
->s_datafork
;
1475 file
->dataLogicalSize
= forkp
->cf_size
;
1476 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1477 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1478 file
->dataExtents
[i
].startBlock
=
1479 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1480 file
->dataExtents
[i
].blockCount
=
1481 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1485 /* Synchronize the lock state */
1486 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1487 file
->flags
|= kHFSFileLockedMask
;
1489 file
->flags
&= ~kHFSFileLockedMask
;
1492 case kHFSPlusFolderRecord
: {
1493 HFSPlusCatalogFolder
*dir
;
1495 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1496 /* Do a quick sanity check */
1497 if (dir
->folderID
!= attrp
->ca_fileid
) {
1498 printf("catrec_update: id %d != %d\n", dir
->folderID
, attrp
->ca_fileid
);
1499 return (btNotFound
);
1501 dir
->flags
= attrp
->ca_recflags
;
1502 dir
->valence
= attrp
->ca_entries
;
1503 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1504 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1505 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1506 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1507 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1508 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1509 /* Note: directory hardlink inodes don't require a text encoding hint. */
1510 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1511 dir
->textEncoding
= descp
->cd_encoding
;
1513 dir
->folderCount
= attrp
->ca_dircount
;
1514 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1516 * Update the BSD Info if it was already initialized on
1517 * disk or if the runtime values have been modified.
1519 * If the BSD info was already initialized, but
1520 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1521 * probably different than what was on disk. We don't want
1522 * to overwrite the on-disk values (so if we turn off
1523 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1524 * This way, we can still change fields like the mode or
1525 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1527 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1528 * won't change the uid or gid from their defaults. So, if
1529 * the BSD info wasn't set, and the runtime values are not
1530 * default, then what changed was the mode or flags. We
1531 * have to set the uid and gid to something, so use the
1532 * supplied values (which will be default), which has the
1533 * same effect as creating a new file while
1534 * MNT_UNKNOWNPERMISSIONS is set.
1536 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1537 (attrp
->ca_flags
!= 0) ||
1538 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1539 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1540 ((attrp
->ca_mode
& ALLPERMS
) !=
1541 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1542 if ((dir
->bsdInfo
.fileMode
== 0) ||
1543 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1544 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1545 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1547 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1548 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1549 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1550 /* A directory hardlink has a link count. */
1551 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1552 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1557 case kHFSPlusFileRecord
: {
1558 HFSPlusCatalogFile
*file
;
1560 file
= (struct HFSPlusCatalogFile
*)crp
;
1561 /* Do a quick sanity check */
1562 if (file
->fileID
!= attrp
->ca_fileid
)
1563 return (btNotFound
);
1564 file
->flags
= attrp
->ca_recflags
;
1565 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1566 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1567 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1568 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1569 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1570 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1572 * Note: file hardlink inodes don't require a text encoding
1573 * hint, but they do have a first link value.
1575 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1576 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1578 file
->textEncoding
= descp
->cd_encoding
;
1580 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1582 * Update the BSD Info if it was already initialized on
1583 * disk or if the runtime values have been modified.
1585 * If the BSD info was already initialized, but
1586 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1587 * probably different than what was on disk. We don't want
1588 * to overwrite the on-disk values (so if we turn off
1589 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1590 * This way, we can still change fields like the mode or
1591 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1593 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1594 * won't change the uid or gid from their defaults. So, if
1595 * the BSD info wasn't set, and the runtime values are not
1596 * default, then what changed was the mode or flags. We
1597 * have to set the uid and gid to something, so use the
1598 * supplied values (which will be default), which has the
1599 * same effect as creating a new file while
1600 * MNT_UNKNOWNPERMISSIONS is set.
1602 if ((file
->bsdInfo
.fileMode
!= 0) ||
1603 (attrp
->ca_flags
!= 0) ||
1604 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1605 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1606 ((attrp
->ca_mode
& ALLPERMS
) !=
1607 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1608 if ((file
->bsdInfo
.fileMode
== 0) ||
1609 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1610 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1611 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1613 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1614 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1615 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1617 if (state
->s_rsrcfork
) {
1618 forkp
= state
->s_rsrcfork
;
1619 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1620 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1621 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1622 sizeof(HFSPlusExtentRecord
));
1623 /* Push blocks read to disk */
1624 file
->resourceFork
.clumpSize
=
1625 howmany(forkp
->cf_bytesread
, blksize
);
1627 if (state
->s_datafork
) {
1628 forkp
= state
->s_datafork
;
1629 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1630 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1631 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1632 sizeof(HFSPlusExtentRecord
));
1633 /* Push blocks read to disk */
1634 file
->dataFork
.clumpSize
=
1635 howmany(forkp
->cf_bytesread
, blksize
);
1638 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1639 (file
->resourceFork
.extents
[0].startBlock
==
1640 file
->dataFork
.extents
[0].startBlock
)) {
1641 panic("catrec_update: rsrc fork == data fork");
1644 /* Synchronize the lock state */
1645 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1646 file
->flags
|= kHFSFileLockedMask
;
1648 file
->flags
&= ~kHFSFileLockedMask
;
1650 /* Push out special field if necessary */
1651 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
1652 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1653 } else if (descp
->cd_cnid
!= attrp
->ca_fileid
|| attrp
->ca_linkcount
== 2) {
1654 file
->hl_linkCount
= attrp
->ca_linkcount
;
1659 return (btNotFound
);
1664 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
1665 * catalog btree of given cnid by walking up the parent chain till it reaches
1666 * either the root folder, or the private metadata directory for storing
1667 * directory hard links. This function updates the corresponding in-core
1668 * cnode, if any, and the directory record in the catalog btree.
1669 * On success, returns zero. On failure, returns non-zero value.
1673 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
1677 struct cat_desc desc
;
1678 struct cat_attr attr
;
1680 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
1681 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
1682 /* Update the bit in corresponding cnode, if any, in the hash.
1683 * If the cnode has the bit already set, stop the traversal.
1685 retval
= hfs_chash_set_childlinkbit(hfsmp
->hfs_raw_dev
, cnid
);
1690 /* Update the catalog record on disk if either cnode was not
1691 * found in the hash, or if a cnode was found and the cnode
1692 * did not have the bit set previously.
1694 retval
= hfs_start_transaction(hfsmp
);
1698 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1700 /* Look up our catalog folder record */
1701 retval
= cat_idlookup(hfsmp
, cnid
, 0, &desc
, &attr
, NULL
);
1703 hfs_systemfile_unlock(hfsmp
, lockflags
);
1704 hfs_end_transaction(hfsmp
);
1708 /* Update the bit in the catalog record */
1709 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
1710 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
1712 hfs_systemfile_unlock(hfsmp
, lockflags
);
1713 hfs_end_transaction(hfsmp
);
1714 cat_releasedesc(&desc
);
1718 hfs_systemfile_unlock(hfsmp
, lockflags
);
1719 hfs_end_transaction(hfsmp
);
1721 cnid
= desc
.cd_parentcnid
;
1722 cat_releasedesc(&desc
);
1728 /* This function traverses the parent directory hierarchy from the given
1729 * directory to one level below root directory and checks if any of its
1731 * 1. A directory hard link.
1732 * 2. The 'pointed at' directory.
1733 * If any of these conditions fail or an internal error is encountered
1734 * during look up of the catalog record, this function returns non-zero value.
1738 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
1740 HFSPlusCatalogKey
*keyp
;
1742 FSBufferDescriptor btdata
;
1743 HFSPlusCatalogFolder folder
;
1749 BDINIT(btdata
, &folder
);
1750 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
1751 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
1752 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1754 while (cnid
!= kHFSRootParentID
) {
1755 /* Check if the 'pointed at' directory is an ancestor */
1756 if (pointed_at_cnid
== cnid
) {
1760 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
1761 printf("cat_check_link_ancestry: getkey for %u failed\n", cnid
);
1762 invalid
= 1; /* On errors, assume an invalid parent */
1765 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
1766 printf("cat_check_link_ancestry: cannot find %u\n", cnid
);
1767 invalid
= 1; /* On errors, assume an invalid parent */
1770 /* Check if this ancestor is a directory hard link */
1771 if (folder
.flags
& kHFSHasLinkChainMask
) {
1775 cnid
= keyp
->parentID
;
1783 * updatelink_callback - update a link's chain
1786 struct linkupdate_state
{
1793 updatelink_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
1795 HFSPlusCatalogFile
*file
;
1797 if (crp
->recordType
!= kHFSPlusFileRecord
) {
1798 printf("updatelink_callback: unexpected rec type %d\n", crp
->recordType
);
1799 return (btNotFound
);
1802 file
= (struct HFSPlusCatalogFile
*)crp
;
1803 if (file
->flags
& kHFSHasLinkChainMask
) {
1804 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
1805 file
->hl_prevLinkID
= state
->prevlinkid
;
1807 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
1808 file
->hl_nextLinkID
= state
->nextlinkid
;
1811 printf("updatelink_callback: file %d isn't a chain\n", file
->fileID
);
1817 * cat_updatelink - update a link's chain
1821 cat_updatelink(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
1824 BTreeIterator
* iterator
;
1825 struct linkupdate_state state
;
1828 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1829 state
.filelinkid
= linkfileid
;
1830 state
.prevlinkid
= prevlinkid
;
1831 state
.nextlinkid
= nextlinkid
;
1833 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1834 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1835 iterator
->hint
.nodeNum
= 0;
1837 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
1839 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)updatelink_callback
, &state
);
1840 (void) BTFlushPath(fcb
);
1842 printf("cat_updatelink: couldn't resolve cnid %d\n", linkfileid
);
1844 return MacToVFSError(result
);
1848 * cat_lookuplink - lookup a link by it's name
1852 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1855 BTreeIterator
* iterator
;
1856 struct FSBufferDescriptor btdata
;
1857 struct HFSPlusCatalogFile file
;
1860 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1862 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1863 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1864 iterator
->hint
.nodeNum
= 0;
1866 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
1869 BDINIT(btdata
, &file
);
1871 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1874 if (file
.recordType
!= kHFSPlusFileRecord
) {
1878 *linkfileid
= file
.fileID
;
1880 if (file
.flags
& kHFSHasLinkChainMask
) {
1881 *prevlinkid
= file
.hl_prevLinkID
;
1882 *nextlinkid
= file
.hl_nextLinkID
;
1888 return MacToVFSError(result
);
1893 * cat_lookuplink - lookup a link by its cnid
1897 cat_lookuplinkbyid(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1900 BTreeIterator
* iterator
;
1901 struct FSBufferDescriptor btdata
;
1902 struct HFSPlusCatalogFile file
;
1905 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1907 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1908 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1909 iterator
->hint
.nodeNum
= 0;
1911 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
1912 printf("cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid
, result
);
1915 BDINIT(btdata
, &file
);
1917 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1918 printf("cat_lookuplinkbyid: cannot find %d\n", linkfileid
);
1921 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
1922 if (file
.flags
& kHFSHasLinkChainMask
) {
1925 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
1927 /* ADL inodes don't have a chain (its in an EA) */
1928 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1929 result
= ENOLINK
; /* signal to caller to get head of list */
1930 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1932 *nextlinkid
= file
.hl_firstLinkID
;
1934 *prevlinkid
= file
.hl_prevLinkID
;
1935 *nextlinkid
= file
.hl_nextLinkID
;
1942 return MacToVFSError(result
);
1947 * cat_createlink - create a link in the catalog
1949 * The following cat_attr fields are expected to be set:
1955 * ca_finderinfo (type and creator)
1959 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1960 cnid_t nextlinkid
, cnid_t
*linkfileid
)
1964 FSBufferDescriptor btdata
;
1965 HFSPlusForkData
*rsrcforkp
;
1969 int thread_inserted
= 0;
1970 int alias_allocated
= 0;
1973 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1976 * Get the next CNID. We can change it since we hold the catalog lock.
1978 nextCNID
= hfsmp
->vcbNxtCNID
;
1979 if (nextCNID
== 0xFFFFFFFF) {
1980 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
1981 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
1982 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
1983 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1985 hfsmp
->vcbNxtCNID
++;
1987 MarkVCBDirty(hfsmp
);
1989 /* Get space for iterator, key and data */
1990 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
1991 bto
->iterator
.hint
.nodeNum
= 0;
1992 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
1994 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
1996 printf("cat_createlink: err %d from buildkey\n", result
);
2000 /* This is our only chance to set the encoding (other than a rename). */
2001 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2003 /* Insert the thread record first. */
2004 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2005 btdata
.bufferAddress
= &bto
->data
;
2006 btdata
.itemSize
= datalen
;
2007 btdata
.itemCount
= 1;
2010 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2012 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2013 if ((result
== btExists
) && (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2015 * Allow CNIDs on HFS Plus volumes to wrap around
2017 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
2018 nextCNID
= kHFSFirstUserCatalogNodeID
;
2023 thread_inserted
= 1;
2031 * CNID is now established. If we have wrapped then
2032 * update the vcbNxtCNID.
2034 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2035 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
2036 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
2037 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2042 * Now insert the link record.
2044 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2046 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2047 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2048 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2050 /* For directory hard links, create alias in resource fork */
2051 if (descp
->cd_flags
& CD_ISDIR
) {
2052 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2055 alias_allocated
= 1;
2057 btdata
.bufferAddress
= &bto
->data
;
2058 btdata
.itemSize
= datalen
;
2059 btdata
.itemCount
= 1;
2061 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2063 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2065 if (result
== btExists
)
2069 if (linkfileid
!= NULL
) {
2070 *linkfileid
= nextCNID
;
2074 if (thread_inserted
) {
2075 printf("cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result
));
2077 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2078 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2079 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2080 hfs_mark_volume_inconsistent(hfsmp
);
2083 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2084 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2085 rsrcforkp
->extents
[0].blockCount
);
2086 rsrcforkp
->extents
[0].startBlock
= 0;
2087 rsrcforkp
->extents
[0].blockCount
= 0;
2090 (void) BTFlushPath(fcb
);
2093 return MacToVFSError(result
);
2096 /* Directory hard links are visible as aliases on pre-Leopard systems and
2097 * as normal directories on Leopard or later. All directory hard link aliases
2098 * have the same resource fork content except for the three uniquely
2099 * identifying values that are updated in the resource fork data when the alias
2100 * is created. The following array is the constant resource fork data used
2101 * only for creating directory hard link aliases.
2103 static const char hfs_dirlink_alias_rsrc
[] = {
2104 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2120 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2130 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2132 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2135 /* Constants for directory hard link alias */
2137 /* Size of resource fork data array for directory hard link alias */
2138 kHFSAliasSize
= 0x1d0,
2140 /* Volume type for ejectable devices like disk image */
2141 kHFSAliasVolTypeEjectable
= 0x5,
2143 /* Offset for volume create date, in Mac OS local time */
2144 kHFSAliasVolCreateDateOffset
= 0x12a,
2146 /* Offset for the type of volume */
2147 kHFSAliasVolTypeOffset
= 0x130,
2149 /* Offset for folder ID of the parent directory of the directory inode */
2150 kHFSAliasParentIDOffset
= 0x132,
2152 /* Offset for folder ID of the directory inode */
2153 kHFSAliasTargetIDOffset
= 0x176,
2156 /* Create and write an alias that points at the directory represented by given
2157 * inode number on the same volume. Directory hard links are visible as
2158 * aliases in pre-Leopard systems and this function creates these aliases.
2160 * Note: This code is very specific to creating alias for the purpose
2161 * of directory hard links only, and should not be generalized.
2164 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2172 HFSPlusForkData
*rsrcforkp
;
2176 rsrcforkp
= &(crp
->resourceFork
);
2178 blksize
= hfsmp
->blockSize
;
2179 blkcount
= howmany(kHFSAliasSize
, blksize
);
2180 sectorsize
= hfsmp
->hfs_phys_block_size
;
2181 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2183 /* Allocate some disk space for the alias content. */
2184 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
, 1, 1,
2185 &rsrcforkp
->extents
[0].startBlock
,
2186 &rsrcforkp
->extents
[0].blockCount
);
2188 rsrcforkp
->extents
[0].startBlock
= 0;
2192 /* Acquire a buffer cache block for our block. */
2193 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2194 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2196 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_phys_block_size
), 0, 0, BLK_META
);
2198 journal_modify_block_start(hfsmp
->jnl
, bp
);
2201 /* Generate alias content */
2202 alias
= (char *)buf_dataptr(bp
);
2203 bzero(alias
, buf_size(bp
));
2204 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2206 /* Set the volume create date, local time in Mac OS format */
2207 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2208 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2210 /* If the file system is on a virtual device like disk image,
2211 * update the volume type to be ejectable device.
2213 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2214 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2215 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2218 /* Set id of the parent of the target directory */
2219 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2220 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2222 /* Set id of the target directory */
2223 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2224 *valptr
= OSSwapHostToBigInt32(inode_num
);
2226 /* Write alias content to disk. */
2228 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2229 } else if ((result
= buf_bwrite(bp
))) {
2233 /* Finish initializing the fork data. */
2234 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2235 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2238 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2239 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
);
2240 rsrcforkp
->extents
[0].startBlock
= 0;
2241 rsrcforkp
->extents
[0].blockCount
= 0;
2242 rsrcforkp
->logicalSize
= 0;
2243 rsrcforkp
->totalBlocks
= 0;
2249 * cat_deletelink - delete a link from the catalog
2253 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2255 struct HFSPlusCatalogFile file
;
2256 struct cat_attr cattr
;
2257 uint32_t totalBlocks
;
2261 bzero(&file
, sizeof (file
));
2262 bzero(&cattr
, sizeof (cattr
));
2263 cattr
.ca_fileid
= descp
->cd_cnid
;
2265 /* Directory links have alias content to remove. */
2266 if (descp
->cd_flags
& CD_ISDIR
) {
2268 BTreeIterator
* iterator
;
2269 struct FSBufferDescriptor btdata
;
2271 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2273 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2274 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2275 iterator
->hint
.nodeNum
= 0;
2277 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2280 BDINIT(btdata
, &file
);
2282 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2287 result
= cat_delete(hfsmp
, descp
, &cattr
);
2289 if ((result
== 0) &&
2290 (descp
->cd_flags
& CD_ISDIR
) &&
2291 (file
.recordType
== kHFSPlusFileRecord
)) {
2293 totalBlocks
= file
.resourceFork
.totalBlocks
;
2295 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2296 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2297 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2301 (void) BlockDeallocate(hfsmp
,
2302 file
.resourceFork
.extents
[i
].startBlock
,
2303 file
.resourceFork
.extents
[i
].blockCount
);
2305 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2306 file
.resourceFork
.extents
[i
].startBlock
= 0;
2307 file
.resourceFork
.extents
[i
].blockCount
= 0;
2316 * Callback to collect directory entries.
2317 * Called with readattr_state for each item in a directory.
2319 struct readattr_state
{
2320 struct hfsmount
*hfsmp
;
2321 struct cat_entrylist
*list
;
2328 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2329 struct readattr_state
*state
)
2331 struct cat_entrylist
*list
= state
->list
;
2332 struct hfsmount
*hfsmp
= state
->hfsmp
;
2333 struct cat_entry
*cep
;
2336 if (list
->realentries
>= list
->maxentries
)
2337 return (0); /* stop */
2339 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2341 switch(rec
->recordType
) {
2342 case kHFSPlusFolderRecord
:
2343 case kHFSPlusFileRecord
:
2344 case kHFSFolderRecord
:
2345 case kHFSFileRecord
:
2346 if (parentcnid
!= state
->dir_cnid
) {
2347 state
->error
= ENOENT
;
2348 return (0); /* stop */
2352 state
->error
= ENOENT
;
2353 return (0); /* stop */
2356 /* Hide the private system directories and journal files */
2357 if (parentcnid
== kHFSRootFolderID
) {
2358 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2359 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2360 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2361 list
->skipentries
++;
2362 return (1); /* continue */
2365 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2366 (rec
->recordType
== kHFSPlusFileRecord
) &&
2367 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2368 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2369 list
->skipentries
++;
2370 return (1); /* continue */
2374 cep
= &list
->entry
[list
->realentries
++];
2376 if (state
->stdhfs
) {
2377 struct HFSPlusCatalogFile cnoderec
;
2378 HFSPlusCatalogKey
* pluskey
;
2381 promoteattr(hfsmp
, rec
, &cnoderec
);
2382 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2384 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2385 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2386 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2387 FREE(pluskey
, M_TEMP
);
2389 if (rec
->recordType
== kHFSFileRecord
) {
2390 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2392 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2393 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2394 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2395 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2398 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2399 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2400 isadir(rec
), &cep
->ce_desc
);
2402 if (rec
->recordType
== kHFSPlusFileRecord
) {
2403 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2404 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2405 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2406 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2408 /* Save link reference for later processing. */
2409 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2410 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2411 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2412 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2413 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2414 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2415 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2420 return (list
->realentries
< list
->maxentries
);
2424 * Pack a cat_entrylist buffer with attributes from the catalog
2426 * Note: index is zero relative
2430 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
2434 BTreeIterator
* iterator
;
2435 struct readattr_state state
;
2443 ce_list
->realentries
= 0;
2445 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2446 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2447 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2449 state
.hfsmp
= hfsmp
;
2450 state
.list
= ce_list
;
2451 state
.dir_cnid
= parentcnid
;
2452 state
.stdhfs
= std_hfs
;
2455 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2456 bzero(iterator
, sizeof(*iterator
));
2457 key
= (CatalogKey
*)&iterator
->key
;
2459 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2460 index
= dirhint
->dh_index
+ 1;
2463 * Attempt to build a key from cached filename
2465 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2466 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2472 * If the last entry wasn't cached then position the btree iterator
2474 if ((index
== 0) || !have_key
) {
2476 * Position the iterator at the directory's thread record.
2477 * (i.e. just before the first entry)
2479 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2480 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2482 result
= MacToVFSError(result
);
2487 * Iterate until we reach the entry just
2488 * before the one we want to start with.
2491 struct position_state ps
;
2496 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2499 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2500 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2504 result
= MacToVFSError(result
);
2506 result
= MacToVFSError(result
);
2512 /* Fill list with entries starting at iterator->key. */
2513 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2514 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2517 result
= state
.error
;
2518 else if (ce_list
->realentries
== 0)
2521 result
= MacToVFSError(result
);
2527 * Resolve any hard links.
2529 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2530 struct FndrFileInfo
*fip
;
2531 struct cat_entry
*cep
;
2532 struct HFSPlusCatalogFile filerec
;
2536 cep
= &ce_list
->entry
[i
];
2537 if (cep
->ce_attr
.ca_linkref
== 0)
2540 /* Note: Finder info is still in Big Endian */
2541 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
2543 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2544 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2545 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2548 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2549 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
2550 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
2551 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
2554 if (isfilelink
|| isdirlink
) {
2555 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
2557 /* Repack entry from inode record. */
2558 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
2559 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
2560 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
2561 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
2562 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
2566 FREE(iterator
, M_TEMP
);
2568 return MacToVFSError(result
);
2571 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
2574 * Callback to pack directory entries.
2575 * Called with packdirentry_state for each item in a directory.
2578 /* Hard link information collected during cat_getdirentries. */
2581 user_addr_t dirent_addr
;
2583 typedef struct linkinfo linkinfo_t
;
2585 /* State information for the getdirentries_callback function. */
2586 struct packdirentry_state
{
2588 u_int32_t cbs_parentID
;
2589 u_int32_t cbs_index
;
2591 ExtendedVCB
* cbs_hfsmp
;
2594 int32_t cbs_maxlinks
;
2595 linkinfo_t
* cbs_linkinfo
;
2596 struct cat_desc
* cbs_desc
;
2597 u_int8_t
* cbs_namebuf
;
2599 * The following fields are only used for NFS readdir, which
2600 * uses the next file id as the seek offset of each entry.
2602 struct direntry
* cbs_direntry
;
2603 struct direntry
* cbs_prevdirentry
;
2604 u_int32_t cbs_previlinkref
;
2605 Boolean cbs_hasprevdirentry
;
2610 * getdirentries callback for HFS Plus directories.
2613 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2614 struct packdirentry_state
*state
)
2616 struct hfsmount
*hfsmp
;
2617 const CatalogName
*cnp
;
2620 struct dirent catent
;
2621 struct direntry
* entry
= NULL
;
2623 u_int32_t ilinkref
= 0;
2624 u_int32_t curlinkref
= 0;
2627 u_int8_t type
= DT_UNKNOWN
;
2628 u_int8_t is_mangled
= 0;
2630 user_addr_t uiobase
= USER_ADDR_NULL
;
2635 Boolean stop_after_pack
= false;
2637 hfsmp
= state
->cbs_hfsmp
;
2638 curID
= ckp
->hfsPlus
.parentID
;
2640 /* We're done when parent directory changes */
2641 if (state
->cbs_parentID
!= curID
) {
2642 if (state
->cbs_extended
) {
2643 /* The last record has not been returned yet, so we
2644 * want to stop after packing the last item
2646 if (state
->cbs_hasprevdirentry
) {
2647 stop_after_pack
= true;
2649 state
->cbs_result
= ENOENT
;
2650 return (0); /* stop */
2653 state
->cbs_result
= ENOENT
;
2654 return (0); /* stop */
2658 if (state
->cbs_extended
) {
2659 entry
= state
->cbs_direntry
;
2660 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
2661 maxnamelen
= NAME_MAX
;
2663 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2664 maxnamelen
= NAME_MAX
;
2667 if (state
->cbs_extended
&& stop_after_pack
) {
2668 /* The last item returns a non-zero invalid cookie */
2671 switch(crp
->recordType
) {
2672 case kHFSPlusFolderRecord
:
2674 cnid
= crp
->hfsPlusFolder
.folderID
;
2675 /* Hide our private system directories. */
2676 if (curID
== kHFSRootFolderID
) {
2677 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2678 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2683 case kHFSPlusFileRecord
:
2684 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
2685 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
2686 cnid
= crp
->hfsPlusFile
.fileID
;
2688 * When a hardlink link is encountered save its link ref.
2690 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2691 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
2692 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2693 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2694 /* If link ref is inode's file id then use it directly. */
2695 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
2696 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2698 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
2700 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2701 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
2702 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2703 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
2704 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2705 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2706 /* A directory's link resolves to a directory. */
2708 /* A directory's link ref is always inode's file id. */
2709 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2711 /* Hide the journal files */
2712 if ((curID
== kHFSRootFolderID
) &&
2713 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
2714 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
2715 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
2720 return (0); /* stop */
2723 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
2725 namelen
= cnp
->ustr
.length
;
2727 * For MacRoman encoded names, assume that its ascii and
2728 * convert it directly in an attempt to avoid the more
2729 * expensive utf8_encodestr conversion.
2731 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
2734 const u_int16_t
*chp
;
2736 chp
= &cnp
->ustr
.unicode
[0];
2737 for (i
= 0; i
< (int)namelen
; ++i
) {
2739 if (ch
> 0x007f || ch
== 0x0000) {
2740 /* Perform expensive utf8_encodestr conversion */
2743 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
2745 nameptr
[namelen
] = '\0';
2749 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
2750 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
2753 /* Check result returned from encoding the filename to utf8 */
2754 if (result
== ENAMETOOLONG
) {
2755 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
2756 cnp
->ustr
.unicode
, maxnamelen
+ 1,
2757 (ByteCount
*)&namelen
, nameptr
, cnid
);
2762 if (state
->cbs_extended
) {
2764 * The index is 1 relative and includes "." and ".."
2766 * Also stuff the cnid in the upper 32 bits of the cookie.
2767 * The cookie is stored to the previous entry, which will
2768 * be packed and copied this time
2770 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
2771 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
2772 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
2774 catent
.d_type
= type
;
2775 catent
.d_namlen
= namelen
;
2776 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2778 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
2780 catent
.d_fileno
= cnid
;
2781 uioaddr
= (caddr_t
) &catent
;
2784 /* Save current base address for post processing of hard-links. */
2785 if (ilinkref
|| state
->cbs_previlinkref
) {
2786 uiobase
= uio_curriovbase(state
->cbs_uio
);
2788 /* If this entry won't fit then we're done */
2789 if ((uiosize
> uio_resid(state
->cbs_uio
)) ||
2790 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
2791 return (0); /* stop */
2794 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
2795 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2796 if (state
->cbs_result
== 0) {
2799 /* Remember previous entry */
2800 state
->cbs_desc
->cd_cnid
= cnid
;
2801 if (type
== DT_DIR
) {
2802 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2804 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2806 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2807 state
->cbs_desc
->cd_namelen
= 0;
2810 state
->cbs_desc
->cd_encoding
= xxxx
;
2813 state
->cbs_desc
->cd_namelen
= namelen
;
2814 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
2816 /* Store unmangled name for the directory hint else it will
2817 * restart readdir at the last location again
2819 u_int8_t
*new_nameptr
;
2821 size_t tmp_namelen
= 0;
2823 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
2824 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
2825 cnp
->ustr
.length
* sizeof(UniChar
),
2827 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
2828 result
= utf8_encodestr(cnp
->ustr
.unicode
,
2829 cnp
->ustr
.length
* sizeof(UniChar
),
2830 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
2832 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2833 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
2835 FREE(new_nameptr
, M_TEMP
);
2838 if (state
->cbs_hasprevdirentry
) {
2839 curlinkref
= ilinkref
; /* save current */
2840 ilinkref
= state
->cbs_previlinkref
; /* use previous */
2843 * Record any hard links for post processing.
2845 if ((ilinkref
!= 0) &&
2846 (state
->cbs_result
== 0) &&
2847 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
2848 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
2849 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
2850 state
->cbs_nlinks
++;
2852 if (state
->cbs_hasprevdirentry
) {
2853 ilinkref
= curlinkref
; /* restore current */
2857 /* Fill the direntry to be used the next time */
2858 if (state
->cbs_extended
) {
2859 if (stop_after_pack
) {
2860 state
->cbs_eof
= true;
2861 return (0); /* stop */
2863 entry
->d_type
= type
;
2864 entry
->d_namlen
= namelen
;
2865 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
2867 /* File number = 0 means skip entry */
2868 entry
->d_fileno
= 0;
2870 entry
->d_fileno
= cnid
;
2872 /* swap the current and previous entry */
2873 struct direntry
* tmp
;
2874 tmp
= state
->cbs_direntry
;
2875 state
->cbs_direntry
= state
->cbs_prevdirentry
;
2876 state
->cbs_prevdirentry
= tmp
;
2877 state
->cbs_hasprevdirentry
= true;
2878 state
->cbs_previlinkref
= ilinkref
;
2881 /* Continue iteration if there's room */
2882 return (state
->cbs_result
== 0 &&
2883 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2887 * getdirentries callback for standard HFS (non HFS+) directories.
2890 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2891 struct packdirentry_state
*state
)
2893 struct hfsmount
*hfsmp
;
2894 const CatalogName
*cnp
;
2897 struct dirent catent
;
2899 u_int8_t type
= DT_UNKNOWN
;
2906 hfsmp
= state
->cbs_hfsmp
;
2908 curID
= ckp
->hfs
.parentID
;
2910 /* We're done when parent directory changes */
2911 if (state
->cbs_parentID
!= curID
) {
2912 state
->cbs_result
= ENOENT
;
2913 return (0); /* stop */
2916 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2917 maxnamelen
= NAME_MAX
;
2919 switch(crp
->recordType
) {
2920 case kHFSFolderRecord
:
2922 cnid
= crp
->hfsFolder
.folderID
;
2924 case kHFSFileRecord
:
2926 cnid
= crp
->hfsFile
.fileID
;
2929 return (0); /* stop */
2932 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
2933 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
2935 * When an HFS name cannot be encoded with the current
2936 * volume encoding we use MacRoman as a fallback.
2939 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
2941 catent
.d_type
= type
;
2942 catent
.d_namlen
= namelen
;
2943 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2944 catent
.d_fileno
= cnid
;
2945 uioaddr
= (caddr_t
) &catent
;
2947 /* If this entry won't fit then we're done */
2948 if (uiosize
> uio_resid(state
->cbs_uio
)) {
2949 return (0); /* stop */
2952 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2953 if (state
->cbs_result
== 0) {
2956 /* Remember previous entry */
2957 state
->cbs_desc
->cd_cnid
= cnid
;
2958 if (type
== DT_DIR
) {
2959 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2961 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2963 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2964 state
->cbs_desc
->cd_namelen
= 0;
2966 state
->cbs_desc
->cd_namelen
= namelen
;
2967 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
2970 /* Continue iteration if there's room */
2971 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2975 * Pack a uio buffer with directory entries from the catalog
2979 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
2980 uio_t uio
, int extended
, int * items
, int * eofflag
)
2983 BTreeIterator
* iterator
;
2985 struct packdirentry_state state
;
2993 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
2996 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2999 * Get a buffer for link info array, btree iterator and a direntry:
3001 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
3002 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3004 bufsize
+= 2*sizeof(struct direntry
);
3006 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3007 bzero(buffer
, bufsize
);
3009 state
.cbs_extended
= extended
;
3010 state
.cbs_hasprevdirentry
= false;
3011 state
.cbs_previlinkref
= 0;
3012 state
.cbs_nlinks
= 0;
3013 state
.cbs_maxlinks
= maxlinks
;
3014 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3016 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3017 key
= (CatalogKey
*)&iterator
->key
;
3019 index
= dirhint
->dh_index
+ 1;
3021 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3022 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3023 state
.cbs_eof
= false;
3026 * Attempt to build a key from cached filename
3028 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3029 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3030 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3035 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3037 * Position the iterator at the directory's thread record.
3038 * (i.e. just before the first entry)
3040 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3041 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3042 iterator
->hint
.index
= 0;
3047 * If the last entry wasn't cached then position the btree iterator
3051 * Position the iterator at the directory's thread record.
3052 * (i.e. just before the first entry)
3054 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3055 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3057 result
= MacToVFSError(result
);
3061 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3064 * Iterate until we reach the entry just
3065 * before the one we want to start with.
3068 struct position_state ps
;
3073 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3076 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3077 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3081 result
= MacToVFSError(result
);
3083 result
= MacToVFSError(result
);
3089 state
.cbs_index
= index
;
3090 state
.cbs_hfsmp
= hfsmp
;
3091 state
.cbs_uio
= uio
;
3092 state
.cbs_desc
= &dirhint
->dh_desc
;
3093 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3094 state
.cbs_result
= 0;
3095 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3097 /* Use a temporary buffer to hold intermediate descriptor names. */
3098 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3099 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3100 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3101 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3102 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3105 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3107 enum BTreeIterationOperations op
;
3108 if (extended
&& index
!= 0 && have_key
)
3109 op
= kBTreeCurrentRecord
;
3111 op
= kBTreeNextRecord
;
3114 * Process as many entries as possible starting at iterator->key.
3116 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
3117 result
= BTIterateRecords(fcb
, op
, iterator
,
3118 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3120 result
= BTIterateRecords(fcb
, op
, iterator
,
3121 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3123 /* For extended calls, every call to getdirentries_callback()
3124 * transfers the previous directory entry found to the user
3125 * buffer. Therefore when BTIterateRecords reaches the end of
3126 * Catalog BTree, call getdirentries_callback() again with
3127 * dummy values to copy the last directory entry stored in
3128 * packdirentry_state
3130 if (state
.cbs_extended
&& (result
== fsBTRecordNotFoundErr
)) {
3134 bzero(&ckp
, sizeof(ckp
));
3135 bzero(&crp
, sizeof(crp
));
3137 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3141 /* Note that state.cbs_index is still valid on errors */
3142 *items
= state
.cbs_index
- index
;
3143 index
= state
.cbs_index
;
3145 if (state
.cbs_eof
) {
3149 /* Finish updating the catalog iterator. */
3150 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3151 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3152 dirhint
->dh_index
= index
- 1;
3154 /* Fix up the name. */
3155 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3156 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3157 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3159 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3160 dirhint
->dh_desc
.cd_namelen
= 0;
3164 * Post process any hard links to get the real file id.
3166 if (state
.cbs_nlinks
> 0) {
3167 u_int32_t fileid
= 0;
3168 user_addr_t address
;
3171 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3172 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3174 /* This assumes that d_ino is always first field. */
3175 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3176 if (address
== (user_addr_t
)0)
3178 if (uio_isuserspace(uio
)) {
3180 ino64_t fileid_64
= (ino64_t
)fileid
;
3181 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3183 (void) copyout(&fileid
, address
, sizeof(fileid
));
3185 } else /* system space */ {
3187 ino64_t fileid_64
= (ino64_t
)fileid
;
3188 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3190 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3196 if (state
.cbs_result
)
3197 result
= state
.cbs_result
;
3199 result
= MacToVFSError(result
);
3201 if (result
== ENOENT
) {
3206 FREE(buffer
, M_TEMP
);
3213 * Callback to establish directory position.
3214 * Called with position_state for each item in a directory.
3217 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3218 struct position_state
*state
)
3222 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
3223 curID
= ckp
->hfs
.parentID
;
3225 curID
= ckp
->hfsPlus
.parentID
;
3227 /* Make sure parent directory didn't change */
3228 if (state
->parentID
!= curID
) {
3229 state
->error
= EINVAL
;
3230 return (0); /* stop */
3233 /* Count this entry */
3234 switch(crp
->recordType
) {
3235 case kHFSPlusFolderRecord
:
3236 case kHFSPlusFileRecord
:
3237 case kHFSFolderRecord
:
3238 case kHFSFileRecord
:
3242 printf("cat_findposition: invalid record type %d in dir %d\n",
3243 crp
->recordType
, curID
);
3244 state
->error
= EINVAL
;
3245 return (0); /* stop */
3248 return (state
->count
< state
->index
);
3253 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3255 * The name portion of the key is compared using a 16-bit binary comparison.
3256 * This is called from the b-tree code.
3260 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3262 u_int32_t searchParentID
, trialParentID
;
3265 searchParentID
= searchKey
->parentID
;
3266 trialParentID
= trialKey
->parentID
;
3269 if (searchParentID
> trialParentID
) {
3271 } else if (searchParentID
< trialParentID
) {
3274 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3275 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3276 int length1
= searchKey
->nodeName
.length
;
3277 int length2
= trialKey
->nodeName
.length
;
3281 if (length1
< length2
) {
3284 } else if (length1
> length2
) {
3311 * Compare two standard HFS catalog keys
3313 * Result: +n search key > trial key
3314 * 0 search key = trial key
3315 * -n search key < trial key
3318 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3320 cnid_t searchParentID
, trialParentID
;
3323 searchParentID
= searchKey
->parentID
;
3324 trialParentID
= trialKey
->parentID
;
3326 if (searchParentID
> trialParentID
)
3328 else if (searchParentID
< trialParentID
)
3330 else /* parent dirID's are equal, compare names */
3331 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3338 * Compare two HFS+ catalog keys
3340 * Result: +n search key > trial key
3341 * 0 search key = trial key
3342 * -n search key < trial key
3345 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3347 cnid_t searchParentID
, trialParentID
;
3350 searchParentID
= searchKey
->parentID
;
3351 trialParentID
= trialKey
->parentID
;
3353 if (searchParentID
> trialParentID
) {
3356 else if (searchParentID
< trialParentID
) {
3359 /* parent node ID's are equal, compare names */
3360 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3361 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3363 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3364 searchKey
->nodeName
.length
,
3365 &trialKey
->nodeName
.unicode
[0],
3366 trialKey
->nodeName
.length
);
3374 * buildkey - build a Catalog b-tree key from a cnode descriptor
3377 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3378 HFSPlusCatalogKey
*key
, int retry
)
3380 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3382 size_t unicodeBytes
= 0;
3384 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3385 return (EINVAL
); /* invalid name */
3387 key
->parentID
= descp
->cd_parentcnid
;
3388 key
->nodeName
.length
= 0;
3390 * Convert filename from UTF-8 into Unicode
3393 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3394 utf8_flags
|= UTF_DECOMPOSED
;
3395 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3396 key
->nodeName
.unicode
, &unicodeBytes
,
3397 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3398 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3399 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3401 if (result
!= ENAMETOOLONG
)
3402 result
= EINVAL
; /* name has invalid characters */
3407 * For HFS volumes convert to an HFS compatible key
3409 * XXX need to save the encoding that succeeded
3411 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
3412 HFSCatalogKey hfskey
;
3414 bzero(&hfskey
, sizeof(hfskey
));
3415 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3416 hfskey
.parentID
= key
->parentID
;
3417 hfskey
.nodeName
[0] = 0;
3418 if (key
->nodeName
.length
> 0) {
3419 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
3420 key
->nodeName
.length
* 2,
3421 key
->nodeName
.unicode
,
3422 &hfskey
.nodeName
[0], retry
) != 0) {
3425 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3427 bcopy(&hfskey
, key
, sizeof(hfskey
));
3434 * Resolve hard link reference to obtain the inode record.
3438 cat_resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3440 FSBufferDescriptor btdata
;
3441 struct BTreeIterator
*iterator
;
3442 struct cat_desc idesc
;
3447 BDINIT(btdata
, recp
);
3450 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3451 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
3453 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3454 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
3457 /* Get space for iterator */
3458 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3459 bzero(iterator
, sizeof(*iterator
));
3461 /* Build a descriptor for private dir. */
3462 idesc
.cd_parentcnid
= parentcnid
;
3463 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
3464 idesc
.cd_namelen
= strlen(inodename
);
3467 idesc
.cd_encoding
= 0;
3468 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
3470 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3471 &btdata
, NULL
, NULL
);
3474 /* Make sure there's a reference */
3475 if (recp
->hl_linkCount
== 0)
3476 recp
->hl_linkCount
= 2;
3478 printf("HFS resolvelink: can't find %s\n", inodename
);
3481 FREE(iterator
, M_TEMP
);
3483 return (result
? ENOENT
: 0);
3487 * Resolve hard link reference to obtain the inode number.
3490 resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
)
3492 struct HFSPlusCatalogFile record
;
3496 * Since we know resolvelinkid is only called from
3497 * cat_getdirentries, we can assume that only file
3498 * hardlinks need to be resolved (cat_getdirentries
3499 * can resolve directory hardlinks in place).
3501 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
3503 if (record
.fileID
== 0)
3506 *ino
= record
.fileID
;
3512 * getkey - get a key from id by doing a thread lookup
3515 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
3517 struct BTreeIterator
* iterator
;
3518 FSBufferDescriptor btdata
;
3521 CatalogRecord
* recp
;
3525 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
3527 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3528 bzero(iterator
, sizeof(*iterator
));
3529 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
3531 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
3532 BDINIT(btdata
, recp
);
3534 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3535 &btdata
, &datasize
, iterator
);
3539 /* Turn thread record into a cnode key (in place) */
3540 switch (recp
->recordType
) {
3541 case kHFSFileThreadRecord
:
3542 case kHFSFolderThreadRecord
:
3543 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
3544 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
3545 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
3548 case kHFSPlusFileThreadRecord
:
3549 case kHFSPlusFolderThreadRecord
:
3550 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
3551 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
3552 (keyp
->hfsPlus
.nodeName
.length
* 2);
3553 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
3562 FREE(iterator
, M_TEMP
);
3565 return MacToVFSError(result
);
3569 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
3570 * null arguments to cat_idlookup instead, but we save around 10% by not building the
3571 * cat_desc here). Both key and attrp must point to real structures.
3573 * The key's parent id is the only part of the key expected to be used by the caller.
3574 * The name portion of the key may not always be valid (ie in the case of a hard link).
3578 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
3582 result
= getkey(hfsmp
, cnid
, key
);
3585 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
3588 * Check for a raw file hardlink inode.
3589 * Fix up the parent id in the key if necessary.
3590 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
3592 if ((result
== 0) &&
3593 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3594 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
3595 cnid_t nextlinkid
= 0;
3596 cnid_t prevlinkid
= 0;
3597 struct cat_desc linkdesc
;
3600 * Pick up the first link in the chain and get a descriptor for it.
3601 * This allows blind bulk access checks to work for hardlinks.
3603 if ((cat_lookuplinkbyid(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
3604 (nextlinkid
!= 0)) {
3605 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
3606 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
3607 cat_releasedesc(&linkdesc
);
3611 return MacToVFSError(result
);
3616 * buildrecord - build a default catalog directory or file record
3619 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
3620 CatalogRecord
*crp
, u_int32_t
*recordSize
)
3622 int type
= attrp
->ca_mode
& S_IFMT
;
3623 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
3626 createtime
= UTCToLocal(createtime
);
3627 if (type
== S_IFDIR
) {
3628 bzero(crp
, sizeof(HFSCatalogFolder
));
3629 crp
->recordType
= kHFSFolderRecord
;
3630 crp
->hfsFolder
.folderID
= cnid
;
3631 crp
->hfsFolder
.createDate
= createtime
;
3632 crp
->hfsFolder
.modifyDate
= createtime
;
3633 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
3634 *recordSize
= sizeof(HFSCatalogFolder
);
3636 bzero(crp
, sizeof(HFSCatalogFile
));
3637 crp
->recordType
= kHFSFileRecord
;
3638 crp
->hfsFile
.fileID
= cnid
;
3639 crp
->hfsFile
.createDate
= createtime
;
3640 crp
->hfsFile
.modifyDate
= createtime
;
3641 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
3642 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
3643 *recordSize
= sizeof(HFSCatalogFile
);
3646 struct HFSPlusBSDInfo
* bsdp
= NULL
;
3648 if (type
== S_IFDIR
) {
3649 crp
->recordType
= kHFSPlusFolderRecord
;
3650 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
3651 crp
->hfsPlusFolder
.valence
= 0;
3652 crp
->hfsPlusFolder
.folderID
= cnid
;
3653 crp
->hfsPlusFolder
.createDate
= createtime
;
3654 crp
->hfsPlusFolder
.contentModDate
= createtime
;
3655 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
3656 crp
->hfsPlusFolder
.accessDate
= createtime
;
3657 crp
->hfsPlusFolder
.backupDate
= 0;
3658 crp
->hfsPlusFolder
.textEncoding
= encoding
;
3659 crp
->hfsPlusFolder
.folderCount
= 0;
3660 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
3661 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
3662 bsdp
->special
.linkCount
= 1;
3663 *recordSize
= sizeof(HFSPlusCatalogFolder
);
3665 crp
->recordType
= kHFSPlusFileRecord
;
3666 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
3667 crp
->hfsPlusFile
.reserved1
= 0;
3668 crp
->hfsPlusFile
.fileID
= cnid
;
3669 crp
->hfsPlusFile
.createDate
= createtime
;
3670 crp
->hfsPlusFile
.contentModDate
= createtime
;
3671 crp
->hfsPlusFile
.accessDate
= createtime
;
3672 crp
->hfsPlusFile
.attributeModDate
= createtime
;
3673 crp
->hfsPlusFile
.backupDate
= 0;
3674 crp
->hfsPlusFile
.textEncoding
= encoding
;
3675 crp
->hfsPlusFile
.reserved2
= 0;
3676 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
3677 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
3678 /* BLK/CHR need to save the device info */
3679 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
3680 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
3682 bsdp
->special
.linkCount
= 1;
3684 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
3685 *recordSize
= sizeof(HFSPlusCatalogFile
);
3687 bsdp
->ownerID
= attrp
->ca_uid
;
3688 bsdp
->groupID
= attrp
->ca_gid
;
3689 bsdp
->fileMode
= attrp
->ca_mode
;
3690 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
3691 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
3697 * builddesc - build a cnode descriptor from an HFS+ key
3700 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
3701 int isdir
, struct cat_desc
*descp
)
3704 unsigned char * nameptr
;
3707 unsigned char tmpbuff
[128];
3709 /* guess a size... */
3710 bufsize
= (3 * key
->nodeName
.length
) + 1;
3711 if (bufsize
>= sizeof(tmpbuff
) - 1) {
3712 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3714 nameptr
= &tmpbuff
[0];
3717 result
= utf8_encodestr(key
->nodeName
.unicode
,
3718 key
->nodeName
.length
* sizeof(UniChar
),
3719 nameptr
, (size_t *)&utf8len
,
3722 if (result
== ENAMETOOLONG
) {
3723 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
3724 key
->nodeName
.length
* sizeof(UniChar
),
3726 FREE(nameptr
, M_TEMP
);
3727 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3729 result
= utf8_encodestr(key
->nodeName
.unicode
,
3730 key
->nodeName
.length
* sizeof(UniChar
),
3731 nameptr
, (size_t *)&utf8len
,
3734 descp
->cd_parentcnid
= key
->parentID
;
3735 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
3736 descp
->cd_namelen
= utf8len
;
3737 descp
->cd_cnid
= cnid
;
3738 descp
->cd_hint
= hint
;
3739 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
3741 descp
->cd_flags
|= CD_ISDIR
;
3742 descp
->cd_encoding
= encoding
;
3743 if (nameptr
!= &tmpbuff
[0]) {
3744 FREE(nameptr
, M_TEMP
);
3751 * getbsdattr - get attributes in bsd format
3755 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
3757 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
3758 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
3760 attrp
->ca_recflags
= crp
->flags
;
3761 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
3762 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
3763 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
3764 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
3765 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
3766 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
3768 if ((bsd
->fileMode
& S_IFMT
) == 0) {
3769 attrp
->ca_flags
= 0;
3770 attrp
->ca_uid
= hfsmp
->hfs_uid
;
3771 attrp
->ca_gid
= hfsmp
->hfs_gid
;
3773 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
3775 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
3777 attrp
->ca_linkcount
= 1;
3780 attrp
->ca_linkcount
= 1; /* may be overridden below */
3782 attrp
->ca_uid
= bsd
->ownerID
;
3783 attrp
->ca_gid
= bsd
->groupID
;
3784 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
3785 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
3786 switch (attrp
->ca_mode
& S_IFMT
) {
3787 case S_IFCHR
: /* fall through */
3789 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
3792 case S_IFDIR
: /* fall through */
3794 /* Pick up the hard link count */
3795 if (bsd
->special
.linkCount
> 0)
3796 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
3801 * Override the permissions as determined by the mount auguments
3802 * in ALMOST the same way unset permissions are treated but keep
3803 * track of whether or not the file or folder is hfs locked
3804 * by leaving the h_pflags field unchanged from what was unpacked
3805 * out of the catalog.
3808 * This code was used to do UID translation with MNT_IGNORE_OWNERS
3809 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
3810 * at the VFS layer, so there is no need to do it here now; this also
3811 * allows VFS to let root see the real UIDs.
3813 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
3814 * attrp->ca_uid = hfsmp->hfs_uid;
3815 * attrp->ca_gid = hfsmp->hfs_gid;
3821 if (!S_ISDIR(attrp
->ca_mode
)) {
3822 attrp
->ca_mode
&= ~S_IFMT
;
3823 attrp
->ca_mode
|= S_IFDIR
;
3825 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
3826 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
3827 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
3829 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3830 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
3831 attrp
->ca_flags
|= UF_HIDDEN
;
3833 /* Keep IMMUTABLE bits in sync with HFS locked flag */
3834 if (crp
->flags
& kHFSFileLockedMask
) {
3835 /* The file's supposed to be locked:
3836 Make sure at least one of the IMMUTABLE bits is set: */
3837 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
3838 attrp
->ca_flags
|= UF_IMMUTABLE
;
3840 /* The file's supposed to be unlocked: */
3841 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
3843 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3844 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
3845 attrp
->ca_flags
|= UF_HIDDEN
;
3846 /* get total blocks (both forks) */
3847 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
3849 /* On HFS+ the ThreadExists flag must always be set. */
3850 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
3851 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
3853 /* Pick up the hardlink first link, if any. */
3854 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
3857 attrp
->ca_fileid
= crp
->fileID
;
3859 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
3863 * promotekey - promote hfs key to hfs plus key
3867 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
3868 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
3870 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
3874 *encoding
= hfsmp
->hfs_encoding
;
3876 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
3877 kHFSPlusMaxFileNameChars
, &uniCount
);
3879 * When an HFS name cannot be encoded with the current
3880 * encoding use MacRoman as a fallback.
3882 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
3884 (void) mac_roman_to_unicode(hfskey
->nodeName
,
3885 keyp
->nodeName
.unicode
,
3886 kHFSPlusMaxFileNameChars
,
3890 keyp
->nodeName
.length
= uniCount
;
3891 keyp
->parentID
= hfskey
->parentID
;
3895 * promotefork - promote hfs fork info to hfs plus
3899 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
3900 int resource
, struct cat_fork
* forkp
)
3902 struct HFSPlusExtentDescriptor
*xp
;
3903 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
3905 bzero(forkp
, sizeof(*forkp
));
3906 xp
= &forkp
->cf_extents
[0];
3908 forkp
->cf_size
= filep
->rsrcLogicalSize
;
3909 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
3910 forkp
->cf_bytesread
= 0;
3911 forkp
->cf_vblocks
= 0;
3912 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
3913 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
3914 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
3915 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
3916 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
3917 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
3919 forkp
->cf_size
= filep
->dataLogicalSize
;
3920 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
3921 forkp
->cf_bytesread
= 0;
3922 forkp
->cf_vblocks
= 0;
3923 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
3924 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
3925 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
3926 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
3927 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
3928 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
3933 * promoteattr - promote standard hfs catalog attributes to hfs plus
3937 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
3939 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
3941 if (dataPtr
->recordType
== kHFSFolderRecord
) {
3942 const struct HFSCatalogFolder
* folder
;
3944 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
3945 crp
->recordType
= kHFSPlusFolderRecord
;
3946 crp
->flags
= folder
->flags
;
3947 crp
->fileID
= folder
->folderID
;
3948 crp
->createDate
= LocalToUTC(folder
->createDate
);
3949 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
3950 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
3951 crp
->reserved1
= folder
->valence
;
3953 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
3955 const struct HFSCatalogFile
* file
;
3957 file
= (const struct HFSCatalogFile
*) dataPtr
;
3958 crp
->recordType
= kHFSPlusFileRecord
;
3959 crp
->flags
= file
->flags
;
3960 crp
->fileID
= file
->fileID
;
3961 crp
->createDate
= LocalToUTC(file
->createDate
);
3962 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
3963 crp
->backupDate
= LocalToUTC(file
->backupDate
);
3966 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
3967 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
3968 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
3969 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
3971 crp
->textEncoding
= 0;
3972 crp
->attributeModDate
= crp
->contentModDate
;
3973 crp
->accessDate
= crp
->contentModDate
;
3974 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
3978 * Build a catalog node thread record from a catalog key
3979 * and return the size of the record.
3982 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
3987 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
3988 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
3990 size
= sizeof(HFSCatalogThread
);
3993 rec
->recordType
= kHFSFolderThreadRecord
;
3995 rec
->recordType
= kHFSFileThreadRecord
;
3996 rec
->parentID
= key
->parentID
;
3997 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4000 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4001 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4003 size
= sizeof(HFSPlusCatalogThread
);
4005 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4007 rec
->recordType
= kHFSPlusFileThreadRecord
;
4009 rec
->parentID
= key
->parentID
;
4010 bcopy(&key
->nodeName
, &rec
->nodeName
,
4011 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4013 /* HFS Plus has varaible sized thread records */
4014 size
-= (sizeof(rec
->nodeName
.unicode
) -
4015 (rec
->nodeName
.length
* sizeof(UniChar
)));
4022 * Build a catalog node thread key.
4025 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4028 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4029 key
->hfs
.reserved
= 0;
4030 key
->hfs
.parentID
= parentID
;
4031 key
->hfs
.nodeName
[0] = 0;
4033 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4034 key
->hfsPlus
.parentID
= parentID
;
4035 key
->hfsPlus
.nodeName
.length
= 0;
4040 * Extract the text encoding from a catalog node record.
4043 getencoding(const CatalogRecord
*crp
)
4047 if (crp
->recordType
== kHFSPlusFolderRecord
)
4048 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4049 else if (crp
->recordType
== kHFSPlusFileRecord
)
4050 encoding
= crp
->hfsPlusFile
.textEncoding
;
4058 * Extract the CNID from a catalog node record.
4061 getcnid(const CatalogRecord
*crp
)
4065 switch (crp
->recordType
) {
4066 case kHFSFolderRecord
:
4067 cnid
= crp
->hfsFolder
.folderID
;
4069 case kHFSFileRecord
:
4070 cnid
= crp
->hfsFile
.fileID
;
4072 case kHFSPlusFolderRecord
:
4073 cnid
= crp
->hfsPlusFolder
.folderID
;
4075 case kHFSPlusFileRecord
:
4076 cnid
= crp
->hfsPlusFile
.fileID
;
4079 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp
);
4087 * Extract the parent ID from a catalog node record.
4090 getparentcnid(const CatalogRecord
*recp
)
4094 switch (recp
->recordType
) {
4095 case kHFSFileThreadRecord
:
4096 case kHFSFolderThreadRecord
:
4097 cnid
= recp
->hfsThread
.parentID
;
4100 case kHFSPlusFileThreadRecord
:
4101 case kHFSPlusFolderThreadRecord
:
4102 cnid
= recp
->hfsPlusThread
.parentID
;
4105 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4113 * Determine if a catalog node record is a directory.
4116 isadir(const CatalogRecord
*crp
)
4118 return (crp
->recordType
== kHFSFolderRecord
||
4119 crp
->recordType
== kHFSPlusFolderRecord
);