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_new_size
= 0;
200 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
201 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
202 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
203 datafp
->cf_bytesread
=
204 recp
->hfsPlusFile
.dataFork
.clumpSize
*
205 HFSTOVCB(hfsmp
)->blockSize
;
207 datafp
->cf_bytesread
= 0;
209 datafp
->cf_vblocks
= 0;
210 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
211 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
213 /* Convert the resource fork. */
214 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
215 rsrcfp
->cf_new_size
= 0;
216 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
217 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
218 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
219 datafp
->cf_bytesread
=
220 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
221 HFSTOVCB(hfsmp
)->blockSize
;
223 datafp
->cf_bytesread
= 0;
225 rsrcfp
->cf_vblocks
= 0;
226 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
227 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
232 * Convert a raw catalog key and record into an in-core catalog descriptor.
234 * Note: The caller is responsible for releasing the catalog descriptor.
239 struct hfsmount
*hfsmp
,
241 CatalogRecord
* recp
,
242 struct cat_desc
*descp
)
244 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
245 HFSPlusCatalogKey
* pluskey
= NULL
;
249 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
250 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
253 pluskey
= (HFSPlusCatalogKey
*)key
;
254 encoding
= getencoding(recp
);
257 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
259 FREE(pluskey
, M_TEMP
);
270 cat_releasedesc(struct cat_desc
*descp
)
272 const u_int8_t
* name
;
277 if ((descp
->cd_flags
& CD_HASBUF
) &&
278 (descp
->cd_nameptr
!= NULL
)) {
279 name
= descp
->cd_nameptr
;
280 descp
->cd_nameptr
= NULL
;
281 descp
->cd_namelen
= 0;
282 vfs_removename((const char *)name
);
284 descp
->cd_nameptr
= NULL
;
285 descp
->cd_namelen
= 0;
286 descp
->cd_flags
&= ~CD_HASBUF
;
290 * These Catalog functions allow access to the HFS Catalog (database).
291 * The catalog b-tree lock must be acquired before calling any of these routines.
295 * cat_lookup - lookup a catalog node using a cnode descriptor
297 * Note: The caller is responsible for releasing the output
298 * catalog descriptor (when supplied outdescp is non-null).
302 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
303 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
304 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
310 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
312 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
314 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
318 result
= cat_lookupbykey(hfsmp
, keyp
, 0, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
320 if (result
== ENOENT
) {
322 struct cat_desc temp_desc
;
323 if (outdescp
== NULL
) {
324 bzero(&temp_desc
, sizeof(temp_desc
));
325 outdescp
= &temp_desc
;
327 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
329 *desc_cnid
= outdescp
->cd_cnid
;
331 if (outdescp
== &temp_desc
) {
332 /* Release the local copy of desc */
333 cat_releasedesc(outdescp
);
335 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
336 // make MacRoman key from utf-8
337 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
338 // update desc text encoding so that other catalog ops succeed
349 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
351 struct BTreeIterator
*iterator
;
352 struct FSBufferDescriptor file_data
;
353 struct HFSCatalogFile file_rec
;
358 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
361 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
363 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
364 bzero(&iterator
[0], 2* sizeof(*iterator
));
365 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
369 BDINIT(file_data
, &file_rec
);
370 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
374 if (file_rec
.recordType
!= kHFSFileRecord
) {
379 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
380 struct FSBufferDescriptor thread_data
;
381 struct HFSCatalogThread thread_rec
;
383 file_rec
.flags
|= kHFSThreadExistsMask
;
384 BDINIT(thread_data
, &thread_rec
);
385 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
386 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
388 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
392 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
393 (void) BTFlushPath(fcb
);
396 (void) BTFlushPath(fcb
);
397 FREE(iterator
, M_TEMP
);
399 return MacToVFSError(result
);
404 * cat_findname - obtain a descriptor from cnid
406 * Only a thread lookup is performed.
408 * Note: The caller is responsible for releasing the output
409 * catalog descriptor (when supplied outdescp is non-null).
414 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
416 struct BTreeIterator
* iterator
;
417 FSBufferDescriptor btdata
;
419 CatalogRecord
* recp
;
425 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
427 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
428 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
429 iterator
->hint
.nodeNum
= 0;
431 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
432 BDINIT(btdata
, recp
);
434 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
438 /* Turn thread record into a cnode key (in place). */
439 switch (recp
->recordType
) {
440 case kHFSFolderThreadRecord
:
443 case kHFSFileThreadRecord
:
444 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
445 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
448 case kHFSPlusFolderThreadRecord
:
451 case kHFSPlusFileThreadRecord
:
452 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
453 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
454 (keyp
->hfsPlus
.nodeName
.length
* 2);
461 HFSPlusCatalogKey
* pluskey
= NULL
;
464 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
465 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
466 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
467 FREE(pluskey
, M_TEMP
);
470 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
474 FREE(iterator
, M_TEMP
);
476 return MacToVFSError(result
);
480 * cat_idlookup - lookup a catalog node using a cnode id
482 * Note: The caller is responsible for releasing the output
483 * catalog descriptor (when supplied outdescp is non-null).
487 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
,
488 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
490 struct BTreeIterator
* iterator
;
491 FSBufferDescriptor btdata
;
494 CatalogRecord
* recp
;
498 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
500 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
501 bzero(iterator
, sizeof(*iterator
));
502 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
504 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
505 BDINIT(btdata
, recp
);
507 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
508 &btdata
, &datasize
, iterator
);
512 /* Turn thread record into a cnode key (in place) */
513 switch (recp
->recordType
) {
514 case kHFSFileThreadRecord
:
515 case kHFSFolderThreadRecord
:
516 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
517 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
520 case kHFSPlusFileThreadRecord
:
521 case kHFSPlusFolderThreadRecord
:
522 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
523 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
524 (keyp
->hfsPlus
.nodeName
.length
* 2);
532 result
= cat_lookupbykey(hfsmp
, keyp
, allow_system_files
, 0, 0, outdescp
, attrp
, forkp
, NULL
);
533 /* No corresponding file/folder record found for a thread record,
534 * mark the volume inconsistent.
536 if (result
== 0 && outdescp
) {
537 cnid_t dcnid
= outdescp
->cd_cnid
;
539 * Just for sanity's case, let's make sure that
540 * the key in the thread matches the key in the record.
543 printf("Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
549 FREE(iterator
, M_TEMP
);
551 return MacToVFSError(result
);
556 * cat_lookupmangled - lookup a catalog node using a mangled name
559 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
560 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
569 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
570 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
573 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
574 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
575 fileID
== hfsmp
->hfs_jnlfileid
||
576 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
580 result
= cat_idlookup(hfsmp
, fileID
, 0, outdescp
, attrp
, forkp
);
583 /* It must be in the correct directory */
584 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
587 if (((u_int16_t
)outdescp
->cd_namelen
< prefixlen
) ||
588 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
594 cat_releasedesc(outdescp
);
600 * cat_lookupbykey - lookup a catalog node using a cnode key
603 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int allow_system_files
, u_long hint
, int wantrsrc
,
604 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
606 struct BTreeIterator
* iterator
;
607 FSBufferDescriptor btdata
;
608 CatalogRecord
* recp
;
616 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
618 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
619 BDINIT(btdata
, recp
);
620 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
621 bzero(iterator
, sizeof(*iterator
));
622 iterator
->hint
.nodeNum
= hint
;
623 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
625 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
626 &btdata
, &datasize
, iterator
);
630 /* Save the cnid and encoding now in case there's a hard link */
631 cnid
= getcnid(recp
);
632 encoding
= getencoding(recp
);
633 hint
= iterator
->hint
.nodeNum
;
635 /* Hide the journal files (if any) */
636 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
637 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
638 !allow_system_files
) {
645 * When a hardlink link is encountered, auto resolve it.
647 * The catalog record will change, and possibly its type.
651 && (recp
->recordType
== kHFSPlusFileRecord
)
652 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->vcbCrDate
) ||
653 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
657 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
658 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
660 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
661 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
662 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
665 if (isfilelink
|| isdirlink
) {
666 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
667 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
673 struct HFSPlusCatalogFile cnoderec
;
675 promoteattr(hfsmp
, recp
, &cnoderec
);
676 getbsdattr(hfsmp
, &cnoderec
, attrp
);
678 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
680 attrp
->ca_linkref
= ilink
;
685 bzero(forkp
, sizeof(*forkp
));
686 } else if (std_hfs
) {
687 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
688 } else if (wantrsrc
) {
689 /* Convert the resource fork. */
690 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
691 forkp
->cf_new_size
= 0;
692 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
693 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
694 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
695 forkp
->cf_bytesread
=
696 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
697 HFSTOVCB(hfsmp
)->blockSize
;
699 forkp
->cf_bytesread
= 0;
701 forkp
->cf_vblocks
= 0;
702 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
703 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
708 /* Convert the data fork. */
709 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
710 forkp
->cf_new_size
= 0;
711 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
712 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
713 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
714 forkp
->cf_bytesread
=
715 recp
->hfsPlusFile
.dataFork
.clumpSize
*
716 HFSTOVCB(hfsmp
)->blockSize
;
718 forkp
->cf_bytesread
= 0;
720 forkp
->cf_vblocks
= 0;
721 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
722 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
724 /* Validate the fork's resident extents. */
726 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
727 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
728 /* Suppress any bad extents so a remove can succeed. */
729 forkp
->cf_extents
[i
].startBlock
= 0;
730 forkp
->cf_extents
[i
].blockCount
= 0;
733 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
736 validblks
+= forkp
->cf_extents
[i
].blockCount
;
739 /* Adjust for any missing blocks. */
740 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
743 forkp
->cf_blocks
= validblks
;
745 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
747 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
748 if (psize
< forkp
->cf_size
) {
749 forkp
->cf_size
= psize
;
756 HFSPlusCatalogKey
* pluskey
= NULL
;
759 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
760 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
763 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
765 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
767 FREE(pluskey
, M_TEMP
);
771 if (desc_cnid
!= NULL
) {
775 FREE(iterator
, M_TEMP
);
778 return MacToVFSError(result
);
783 * cat_create - create a node in the catalog
785 * NOTE: both the catalog file and attribute file locks must
786 * be held before calling this function.
788 * The caller is responsible for releasing the output
789 * catalog descriptor (when supplied outdescp is non-null).
793 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
794 struct cat_desc
*out_descp
)
798 FSBufferDescriptor btdata
;
803 u_long encoding
= kTextEncodingMacRoman
;
806 modeformat
= attrp
->ca_mode
& S_IFMT
;
808 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
809 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
812 * Get the next CNID. We can change it since we hold the catalog lock.
814 nextCNID
= hfsmp
->vcbNxtCNID
;
815 if (nextCNID
== 0xFFFFFFFF) {
819 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
820 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
821 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
822 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
829 /* Get space for iterator, key and data */
830 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
831 bto
->iterator
.hint
.nodeNum
= 0;
833 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
838 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
839 bto
->key
.nodeName
.length
);
840 hfs_setencodingbits(hfsmp
, encoding
);
844 * Insert the thread record first
846 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
847 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
848 S_ISDIR(attrp
->ca_mode
));
849 btdata
.bufferAddress
= &bto
->data
;
850 btdata
.itemSize
= datalen
;
851 btdata
.itemCount
= 1;
854 // this call requires the attribute file lock to be held
855 result
= file_attribute_exist(hfsmp
, nextCNID
);
856 if (result
== EEXIST
) {
857 // that cnid has orphaned attributes so just skip it.
858 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
859 nextCNID
= kHFSFirstUserCatalogNodeID
;
863 if (result
) goto exit
;
865 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
867 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
868 if ((result
== btExists
) && !std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
870 * Allow CNIDs on HFS Plus volumes to wrap around
872 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
873 nextCNID
= kHFSFirstUserCatalogNodeID
;
879 if (result
) goto exit
;
883 * CNID is now established. If we have wrapped then
884 * update the vcbNxtCNID.
886 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
887 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
888 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
889 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
894 * Now insert the file/directory record
896 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
897 btdata
.bufferAddress
= &bto
->data
;
898 btdata
.itemSize
= datalen
;
899 btdata
.itemCount
= 1;
901 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
903 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
905 if (result
== btExists
)
908 /* Back out the thread record */
909 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
910 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
911 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
912 /* Error on deleting extra thread record, mark
913 * volume inconsistent
915 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
916 hfs_mark_volume_inconsistent(hfsmp
);
923 * Insert was successful, update name, parent and volume
925 if (out_descp
!= NULL
) {
926 HFSPlusCatalogKey
* pluskey
= NULL
;
929 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
930 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
933 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
935 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
936 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
938 FREE(pluskey
, M_TEMP
);
941 attrp
->ca_fileid
= nextCNID
;
944 (void) BTFlushPath(fcb
);
947 return MacToVFSError(result
);
952 * cnode_rename - rename a catalog node
954 * Assumes that the target's directory exists.
956 * Order of B-tree operations:
957 * 1. BTSearchRecord(from_cnode, &data);
958 * 2. BTInsertRecord(to_cnode, &data);
959 * 3. BTDeleteRecord(from_cnode);
960 * 4. BTDeleteRecord(from_thread);
961 * 5. BTInsertRecord(to_thread);
963 * Note: The caller is responsible for releasing the output
964 * catalog descriptor (when supplied out_cdp is non-null).
969 struct hfsmount
* hfsmp
,
970 struct cat_desc
* from_cdp
,
971 struct cat_desc
* todir_cdp
,
972 struct cat_desc
* to_cdp
,
973 struct cat_desc
* out_cdp
)
975 struct BTreeIterator
* to_iterator
= NULL
;
976 struct BTreeIterator
* from_iterator
= NULL
;
977 FSBufferDescriptor btdata
;
978 CatalogRecord
* recp
= NULL
;
979 HFSPlusCatalogKey
* to_key
;
986 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
991 vcb
= HFSTOVCB(hfsmp
);
992 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
993 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
995 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
998 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
999 bzero(from_iterator
, sizeof(*from_iterator
));
1000 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1003 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1004 bzero(to_iterator
, sizeof(*to_iterator
));
1005 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1008 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1009 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1010 BDINIT(btdata
, recp
);
1013 * When moving a directory, make sure its a valid move.
1015 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1016 struct BTreeIterator iterator
;
1017 cnid_t cnid
= from_cdp
->cd_cnid
;
1018 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1020 /* First check the obvious ones */
1021 if (cnid
== fsRtDirID
||
1022 cnid
== to_cdp
->cd_parentcnid
||
1027 bzero(&iterator
, sizeof(iterator
));
1029 * Traverse destination path all the way back to the root
1030 * making sure that source directory is not encountered.
1033 while (pathcnid
> fsRtDirID
) {
1034 buildthreadkey(pathcnid
, std_hfs
,
1035 (CatalogKey
*)&iterator
.key
);
1036 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
1038 if (result
) goto exit
;
1040 pathcnid
= getparentcnid(recp
);
1041 if (pathcnid
== cnid
|| pathcnid
== 0) {
1049 * Step 1: Find cnode data at old location
1051 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1052 &datasize
, from_iterator
);
1054 if (std_hfs
|| (result
!= btNotFound
))
1057 struct cat_desc temp_desc
;
1059 /* Probably the node has mangled name */
1060 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1064 /* The file has mangled name. Search the cnode data using full name */
1065 bzero(from_iterator
, sizeof(*from_iterator
));
1066 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1068 cat_releasedesc(&temp_desc
);
1072 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1074 cat_releasedesc(&temp_desc
);
1078 cat_releasedesc(&temp_desc
);
1081 /* Check if the source is directory hard link. We do not change
1082 * directory flag because it is later used to initialize result descp
1086 (recp
->recordType
== kHFSPlusFileRecord
) &&
1087 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1092 * Update the text encoding (on disk and in descriptor).
1094 * Note that hardlink inodes don't require a text encoding hint.
1097 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1098 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1099 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1100 hfs_setencodingbits(hfsmp
, encoding
);
1101 recp
->hfsPlusFile
.textEncoding
= encoding
;
1103 out_cdp
->cd_encoding
= encoding
;
1106 if (std_hfs
&& !directory
&&
1107 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1111 * If the keys are identical then there's nothing left to do!
1113 * update the hint and exit
1116 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1118 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1122 /* Step 2: Insert cnode at new location */
1123 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1124 if (result
== btExists
) {
1125 int fromtype
= recp
->recordType
;
1127 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1128 goto exit
; /* EEXIST */
1130 /* Find cnode data at new location */
1131 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1135 if ((fromtype
!= recp
->recordType
) ||
1136 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1138 goto exit
; /* EEXIST */
1140 /* The old name is a case variant and must be removed */
1141 result
= BTDeleteRecord(fcb
, from_iterator
);
1145 /* Insert cnode (now that case duplicate is gone) */
1146 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1148 /* Try and restore original before leaving */
1153 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1155 printf("cat_create: could not undo (BTInsert = %d)", err
);
1156 hfs_mark_volume_inconsistent(hfsmp
);
1162 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1171 /* Step 3: Remove cnode from old location */
1173 result
= BTDeleteRecord(fcb
, from_iterator
);
1175 /* Try and delete new record before leaving */
1180 err
= BTDeleteRecord(fcb
, to_iterator
);
1182 printf("cat_create: could not undo (BTDelete = %d)", err
);
1183 hfs_mark_volume_inconsistent(hfsmp
);
1189 (void) BTDeleteRecord(fcb
, to_iterator
);
1195 /* #### POINT OF NO RETURN #### */
1198 * Step 4: Remove cnode's old thread record
1200 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1201 (void) BTDeleteRecord(fcb
, from_iterator
);
1204 * Step 5: Insert cnode's new thread record
1205 * (optional for HFS files)
1208 /* For directory hard links, always create a file thread
1209 * record. For everything else, use the directory flag.
1212 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1214 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1216 btdata
.itemSize
= datasize
;
1217 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1218 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1222 HFSPlusCatalogKey
* pluskey
= NULL
;
1225 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1226 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1228 /* Save the real encoding hint in the Finder Info (field 4). */
1229 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1232 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1233 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1237 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1239 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1240 encoding
, directory
, out_cdp
);
1242 FREE(pluskey
, M_TEMP
);
1246 (void) BTFlushPath(fcb
);
1248 FREE(from_iterator
, M_TEMP
);
1250 FREE(to_iterator
, M_TEMP
);
1253 return MacToVFSError(result
);
1258 * cat_delete - delete a node from the catalog
1260 * Order of B-tree operations:
1261 * 1. BTDeleteRecord(cnode);
1262 * 2. BTDeleteRecord(thread);
1263 * 3. BTUpdateRecord(parent);
1267 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1270 BTreeIterator
*iterator
;
1275 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1276 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1280 * The root directory cannot be deleted
1281 * A directory must be empty
1282 * A file must be zero length (no blocks)
1284 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1285 descp
->cd_parentcnid
== kHFSRootParentID
)
1288 /* XXX Preflight Missing */
1290 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1291 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1292 iterator
->hint
.nodeNum
= 0;
1295 * Derive a key from either the file ID (for a virtual inode)
1296 * or the descriptor.
1298 if (descp
->cd_namelen
== 0) {
1299 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1300 cnid
= attrp
->ca_fileid
;
1302 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1303 cnid
= descp
->cd_cnid
;
1309 result
= BTDeleteRecord(fcb
, iterator
);
1311 if (std_hfs
|| (result
!= btNotFound
))
1314 struct cat_desc temp_desc
;
1316 /* Probably the node has mangled name */
1317 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1321 /* The file has mangled name. Delete the file using full name */
1322 bzero(iterator
, sizeof(*iterator
));
1323 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1324 cnid
= temp_desc
.cd_cnid
;
1326 cat_releasedesc(&temp_desc
);
1330 result
= BTDeleteRecord(fcb
, iterator
);
1332 cat_releasedesc(&temp_desc
);
1336 cat_releasedesc(&temp_desc
);
1339 /* Delete thread record. On error, mark volume inconsistent */
1340 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1341 if (BTDeleteRecord(fcb
, iterator
)) {
1343 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1344 hfs_mark_volume_inconsistent(hfsmp
);
1349 (void) BTFlushPath(fcb
);
1351 return MacToVFSError(result
);
1356 * cnode_update - update the catalog node described by descp
1357 * using the data from attrp and forkp.
1361 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1362 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1365 BTreeIterator
* iterator
;
1366 struct update_state state
;
1370 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1371 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1373 state
.s_desc
= descp
;
1374 state
.s_attr
= attrp
;
1375 state
.s_datafork
= dataforkp
;
1376 state
.s_rsrcfork
= rsrcforkp
;
1377 state
.s_hfsmp
= hfsmp
;
1379 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1380 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1383 * For open-deleted files we need to do a lookup by cnid
1384 * (using thread rec).
1386 * For hard links, the target of the update is the inode
1387 * itself (not the link record) so a lookup by fileid
1388 * (i.e. thread rec) is needed.
1390 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1391 (descp
->cd_namelen
== 0) ||
1392 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
1393 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1395 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1400 /* Pass a node hint */
1401 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1403 result
= BTUpdateRecord(fcb
, iterator
,
1404 (IterateCallBackProcPtr
)catrec_update
, &state
);
1408 /* Update the node hint. */
1409 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1412 (void) BTFlushPath(fcb
);
1414 return MacToVFSError(result
);
1418 * catrec_update - Update the fields of a catalog record
1419 * This is called from within BTUpdateRecord.
1422 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1424 struct cat_desc
*descp
;
1425 struct cat_attr
*attrp
;
1426 struct cat_fork
*forkp
;
1427 struct hfsmount
*hfsmp
;
1431 descp
= state
->s_desc
;
1432 attrp
= state
->s_attr
;
1433 hfsmp
= state
->s_hfsmp
;
1434 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1436 switch (crp
->recordType
) {
1437 case kHFSFolderRecord
: {
1438 HFSCatalogFolder
*dir
;
1440 dir
= (struct HFSCatalogFolder
*)crp
;
1441 /* Do a quick sanity check */
1442 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1443 (dir
->folderID
!= descp
->cd_cnid
))
1444 return (btNotFound
);
1445 dir
->valence
= attrp
->ca_entries
;
1446 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1447 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1448 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1449 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1450 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1453 case kHFSFileRecord
: {
1454 HFSCatalogFile
*file
;
1456 file
= (struct HFSCatalogFile
*)crp
;
1457 /* Do a quick sanity check */
1458 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1459 (file
->fileID
!= attrp
->ca_fileid
))
1460 return (btNotFound
);
1461 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1462 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1463 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1464 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1465 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1466 if (state
->s_rsrcfork
) {
1467 forkp
= state
->s_rsrcfork
;
1468 file
->rsrcLogicalSize
= forkp
->cf_size
;
1469 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1470 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1471 file
->rsrcExtents
[i
].startBlock
=
1472 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1473 file
->rsrcExtents
[i
].blockCount
=
1474 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1477 if (state
->s_datafork
) {
1478 forkp
= state
->s_datafork
;
1479 file
->dataLogicalSize
= forkp
->cf_size
;
1480 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1481 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1482 file
->dataExtents
[i
].startBlock
=
1483 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1484 file
->dataExtents
[i
].blockCount
=
1485 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1489 /* Synchronize the lock state */
1490 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1491 file
->flags
|= kHFSFileLockedMask
;
1493 file
->flags
&= ~kHFSFileLockedMask
;
1496 case kHFSPlusFolderRecord
: {
1497 HFSPlusCatalogFolder
*dir
;
1499 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1500 /* Do a quick sanity check */
1501 if (dir
->folderID
!= attrp
->ca_fileid
) {
1502 printf("catrec_update: id %d != %d\n", dir
->folderID
, attrp
->ca_fileid
);
1503 return (btNotFound
);
1505 dir
->flags
= attrp
->ca_recflags
;
1506 dir
->valence
= attrp
->ca_entries
;
1507 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1508 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1509 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1510 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1511 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1512 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1513 /* Note: directory hardlink inodes don't require a text encoding hint. */
1514 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1515 dir
->textEncoding
= descp
->cd_encoding
;
1517 dir
->folderCount
= attrp
->ca_dircount
;
1518 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1520 * Update the BSD Info if it was already initialized on
1521 * disk or if the runtime values have been modified.
1523 * If the BSD info was already initialized, but
1524 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1525 * probably different than what was on disk. We don't want
1526 * to overwrite the on-disk values (so if we turn off
1527 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1528 * This way, we can still change fields like the mode or
1529 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1531 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1532 * won't change the uid or gid from their defaults. So, if
1533 * the BSD info wasn't set, and the runtime values are not
1534 * default, then what changed was the mode or flags. We
1535 * have to set the uid and gid to something, so use the
1536 * supplied values (which will be default), which has the
1537 * same effect as creating a new file while
1538 * MNT_UNKNOWNPERMISSIONS is set.
1540 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1541 (attrp
->ca_flags
!= 0) ||
1542 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1543 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1544 ((attrp
->ca_mode
& ALLPERMS
) !=
1545 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1546 if ((dir
->bsdInfo
.fileMode
== 0) ||
1547 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1548 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1549 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1551 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1552 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1553 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1554 /* A directory hardlink has a link count. */
1555 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1556 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1561 case kHFSPlusFileRecord
: {
1562 HFSPlusCatalogFile
*file
;
1564 file
= (struct HFSPlusCatalogFile
*)crp
;
1565 /* Do a quick sanity check */
1566 if (file
->fileID
!= attrp
->ca_fileid
)
1567 return (btNotFound
);
1568 file
->flags
= attrp
->ca_recflags
;
1569 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1570 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1571 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1572 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1573 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1574 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1576 * Note: file hardlink inodes don't require a text encoding
1577 * hint, but they do have a first link value.
1579 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1580 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1582 file
->textEncoding
= descp
->cd_encoding
;
1584 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1586 * Update the BSD Info if it was already initialized on
1587 * disk or if the runtime values have been modified.
1589 * If the BSD info was already initialized, but
1590 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1591 * probably different than what was on disk. We don't want
1592 * to overwrite the on-disk values (so if we turn off
1593 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1594 * This way, we can still change fields like the mode or
1595 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1597 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1598 * won't change the uid or gid from their defaults. So, if
1599 * the BSD info wasn't set, and the runtime values are not
1600 * default, then what changed was the mode or flags. We
1601 * have to set the uid and gid to something, so use the
1602 * supplied values (which will be default), which has the
1603 * same effect as creating a new file while
1604 * MNT_UNKNOWNPERMISSIONS is set.
1606 if ((file
->bsdInfo
.fileMode
!= 0) ||
1607 (attrp
->ca_flags
!= 0) ||
1608 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1609 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1610 ((attrp
->ca_mode
& ALLPERMS
) !=
1611 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1612 if ((file
->bsdInfo
.fileMode
== 0) ||
1613 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1614 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1615 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1617 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1618 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1619 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1621 if (state
->s_rsrcfork
) {
1622 forkp
= state
->s_rsrcfork
;
1623 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1624 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1625 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1626 sizeof(HFSPlusExtentRecord
));
1627 /* Push blocks read to disk */
1628 file
->resourceFork
.clumpSize
=
1629 howmany(forkp
->cf_bytesread
, blksize
);
1631 if (state
->s_datafork
) {
1632 forkp
= state
->s_datafork
;
1633 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1634 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1635 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1636 sizeof(HFSPlusExtentRecord
));
1637 /* Push blocks read to disk */
1638 file
->dataFork
.clumpSize
=
1639 howmany(forkp
->cf_bytesread
, blksize
);
1642 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1643 (file
->resourceFork
.extents
[0].startBlock
==
1644 file
->dataFork
.extents
[0].startBlock
)) {
1645 panic("catrec_update: rsrc fork == data fork");
1648 /* Synchronize the lock state */
1649 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1650 file
->flags
|= kHFSFileLockedMask
;
1652 file
->flags
&= ~kHFSFileLockedMask
;
1654 /* Push out special field if necessary */
1655 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
1656 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1657 } else if (descp
->cd_cnid
!= attrp
->ca_fileid
|| attrp
->ca_linkcount
== 2) {
1658 file
->hl_linkCount
= attrp
->ca_linkcount
;
1663 return (btNotFound
);
1668 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
1669 * catalog btree of given cnid by walking up the parent chain till it reaches
1670 * either the root folder, or the private metadata directory for storing
1671 * directory hard links. This function updates the corresponding in-core
1672 * cnode, if any, and the directory record in the catalog btree.
1673 * On success, returns zero. On failure, returns non-zero value.
1677 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
1681 struct cat_desc desc
;
1682 struct cat_attr attr
;
1684 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
1685 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
1686 /* Update the bit in corresponding cnode, if any, in the hash.
1687 * If the cnode has the bit already set, stop the traversal.
1689 retval
= hfs_chash_set_childlinkbit(hfsmp
->hfs_raw_dev
, cnid
);
1694 /* Update the catalog record on disk if either cnode was not
1695 * found in the hash, or if a cnode was found and the cnode
1696 * did not have the bit set previously.
1698 retval
= hfs_start_transaction(hfsmp
);
1702 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1704 /* Look up our catalog folder record */
1705 retval
= cat_idlookup(hfsmp
, cnid
, 0, &desc
, &attr
, NULL
);
1707 hfs_systemfile_unlock(hfsmp
, lockflags
);
1708 hfs_end_transaction(hfsmp
);
1712 /* Update the bit in the catalog record */
1713 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
1714 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
1716 hfs_systemfile_unlock(hfsmp
, lockflags
);
1717 hfs_end_transaction(hfsmp
);
1718 cat_releasedesc(&desc
);
1722 hfs_systemfile_unlock(hfsmp
, lockflags
);
1723 hfs_end_transaction(hfsmp
);
1725 cnid
= desc
.cd_parentcnid
;
1726 cat_releasedesc(&desc
);
1732 /* This function traverses the parent directory hierarchy from the given
1733 * directory to one level below root directory and checks if any of its
1735 * 1. A directory hard link.
1736 * 2. The 'pointed at' directory.
1737 * If any of these conditions fail or an internal error is encountered
1738 * during look up of the catalog record, this function returns non-zero value.
1742 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
1744 HFSPlusCatalogKey
*keyp
;
1746 FSBufferDescriptor btdata
;
1747 HFSPlusCatalogFolder folder
;
1753 BDINIT(btdata
, &folder
);
1754 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
1755 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
1756 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1758 while (cnid
!= kHFSRootParentID
) {
1759 /* Check if the 'pointed at' directory is an ancestor */
1760 if (pointed_at_cnid
== cnid
) {
1764 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
1765 printf("cat_check_link_ancestry: getkey for %u failed\n", cnid
);
1766 invalid
= 1; /* On errors, assume an invalid parent */
1769 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
1770 printf("cat_check_link_ancestry: cannot find %u\n", cnid
);
1771 invalid
= 1; /* On errors, assume an invalid parent */
1774 /* Check if this ancestor is a directory hard link */
1775 if (folder
.flags
& kHFSHasLinkChainMask
) {
1779 cnid
= keyp
->parentID
;
1787 * updatelink_callback - update a link's chain
1790 struct linkupdate_state
{
1797 updatelink_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
1799 HFSPlusCatalogFile
*file
;
1801 if (crp
->recordType
!= kHFSPlusFileRecord
) {
1802 printf("updatelink_callback: unexpected rec type %d\n", crp
->recordType
);
1803 return (btNotFound
);
1806 file
= (struct HFSPlusCatalogFile
*)crp
;
1807 if (file
->flags
& kHFSHasLinkChainMask
) {
1808 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
1809 file
->hl_prevLinkID
= state
->prevlinkid
;
1811 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
1812 file
->hl_nextLinkID
= state
->nextlinkid
;
1815 printf("updatelink_callback: file %d isn't a chain\n", file
->fileID
);
1821 * cat_updatelink - update a link's chain
1825 cat_updatelink(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
1828 BTreeIterator
* iterator
;
1829 struct linkupdate_state state
;
1832 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1833 state
.filelinkid
= linkfileid
;
1834 state
.prevlinkid
= prevlinkid
;
1835 state
.nextlinkid
= nextlinkid
;
1837 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1838 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1839 iterator
->hint
.nodeNum
= 0;
1841 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
1843 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)updatelink_callback
, &state
);
1844 (void) BTFlushPath(fcb
);
1846 printf("cat_updatelink: couldn't resolve cnid %d\n", linkfileid
);
1848 return MacToVFSError(result
);
1852 * cat_lookuplink - lookup a link by it's name
1856 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1859 BTreeIterator
* iterator
;
1860 struct FSBufferDescriptor btdata
;
1861 struct HFSPlusCatalogFile file
;
1864 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1866 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1867 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1868 iterator
->hint
.nodeNum
= 0;
1870 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
1873 BDINIT(btdata
, &file
);
1875 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1878 if (file
.recordType
!= kHFSPlusFileRecord
) {
1882 *linkfileid
= file
.fileID
;
1884 if (file
.flags
& kHFSHasLinkChainMask
) {
1885 *prevlinkid
= file
.hl_prevLinkID
;
1886 *nextlinkid
= file
.hl_nextLinkID
;
1892 return MacToVFSError(result
);
1897 * cat_lookuplink - lookup a link by its cnid
1901 cat_lookuplinkbyid(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1904 BTreeIterator
* iterator
;
1905 struct FSBufferDescriptor btdata
;
1906 struct HFSPlusCatalogFile file
;
1909 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1911 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1912 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1913 iterator
->hint
.nodeNum
= 0;
1915 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
1916 printf("cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid
, result
);
1919 BDINIT(btdata
, &file
);
1921 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1922 printf("cat_lookuplinkbyid: cannot find %d\n", linkfileid
);
1925 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
1926 if (file
.flags
& kHFSHasLinkChainMask
) {
1929 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
1931 /* ADL inodes don't have a chain (its in an EA) */
1932 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1933 result
= ENOLINK
; /* signal to caller to get head of list */
1934 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1936 *nextlinkid
= file
.hl_firstLinkID
;
1938 *prevlinkid
= file
.hl_prevLinkID
;
1939 *nextlinkid
= file
.hl_nextLinkID
;
1946 return MacToVFSError(result
);
1951 * cat_createlink - create a link in the catalog
1953 * The following cat_attr fields are expected to be set:
1959 * ca_finderinfo (type and creator)
1963 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1964 cnid_t nextlinkid
, cnid_t
*linkfileid
)
1968 FSBufferDescriptor btdata
;
1969 HFSPlusForkData
*rsrcforkp
;
1973 int thread_inserted
= 0;
1974 int alias_allocated
= 0;
1977 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1980 * Get the next CNID. We can change it since we hold the catalog lock.
1982 nextCNID
= hfsmp
->vcbNxtCNID
;
1983 if (nextCNID
== 0xFFFFFFFF) {
1984 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
1985 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
1986 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
1987 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
1989 hfsmp
->vcbNxtCNID
++;
1991 MarkVCBDirty(hfsmp
);
1993 /* Get space for iterator, key and data */
1994 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
1995 bto
->iterator
.hint
.nodeNum
= 0;
1996 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
1998 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2000 printf("cat_createlink: err %d from buildkey\n", result
);
2004 /* This is our only chance to set the encoding (other than a rename). */
2005 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2007 /* Insert the thread record first. */
2008 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2009 btdata
.bufferAddress
= &bto
->data
;
2010 btdata
.itemSize
= datalen
;
2011 btdata
.itemCount
= 1;
2014 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2016 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2017 if ((result
== btExists
) && (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2019 * Allow CNIDs on HFS Plus volumes to wrap around
2021 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
2022 nextCNID
= kHFSFirstUserCatalogNodeID
;
2027 thread_inserted
= 1;
2035 * CNID is now established. If we have wrapped then
2036 * update the vcbNxtCNID.
2038 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2039 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
2040 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
2041 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2046 * Now insert the link record.
2048 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2050 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2051 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2052 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2054 /* For directory hard links, create alias in resource fork */
2055 if (descp
->cd_flags
& CD_ISDIR
) {
2056 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2059 alias_allocated
= 1;
2061 btdata
.bufferAddress
= &bto
->data
;
2062 btdata
.itemSize
= datalen
;
2063 btdata
.itemCount
= 1;
2065 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2067 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2069 if (result
== btExists
)
2073 if (linkfileid
!= NULL
) {
2074 *linkfileid
= nextCNID
;
2078 if (thread_inserted
) {
2079 printf("cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result
));
2081 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2082 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2083 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2084 hfs_mark_volume_inconsistent(hfsmp
);
2087 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2088 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2089 rsrcforkp
->extents
[0].blockCount
);
2090 rsrcforkp
->extents
[0].startBlock
= 0;
2091 rsrcforkp
->extents
[0].blockCount
= 0;
2094 (void) BTFlushPath(fcb
);
2097 return MacToVFSError(result
);
2100 /* Directory hard links are visible as aliases on pre-Leopard systems and
2101 * as normal directories on Leopard or later. All directory hard link aliases
2102 * have the same resource fork content except for the three uniquely
2103 * identifying values that are updated in the resource fork data when the alias
2104 * is created. The following array is the constant resource fork data used
2105 * only for creating directory hard link aliases.
2107 static const char hfs_dirlink_alias_rsrc
[] = {
2108 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00,
2123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2124 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 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, 0x48, 0x2b,
2127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2134 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2136 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2139 /* Constants for directory hard link alias */
2141 /* Size of resource fork data array for directory hard link alias */
2142 kHFSAliasSize
= 0x1d0,
2144 /* Volume type for ejectable devices like disk image */
2145 kHFSAliasVolTypeEjectable
= 0x5,
2147 /* Offset for volume create date, in Mac OS local time */
2148 kHFSAliasVolCreateDateOffset
= 0x12a,
2150 /* Offset for the type of volume */
2151 kHFSAliasVolTypeOffset
= 0x130,
2153 /* Offset for folder ID of the parent directory of the directory inode */
2154 kHFSAliasParentIDOffset
= 0x132,
2156 /* Offset for folder ID of the directory inode */
2157 kHFSAliasTargetIDOffset
= 0x176,
2160 /* Create and write an alias that points at the directory represented by given
2161 * inode number on the same volume. Directory hard links are visible as
2162 * aliases in pre-Leopard systems and this function creates these aliases.
2164 * Note: This code is very specific to creating alias for the purpose
2165 * of directory hard links only, and should not be generalized.
2168 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2176 HFSPlusForkData
*rsrcforkp
;
2180 rsrcforkp
= &(crp
->resourceFork
);
2182 blksize
= hfsmp
->blockSize
;
2183 blkcount
= howmany(kHFSAliasSize
, blksize
);
2184 sectorsize
= hfsmp
->hfs_logical_block_size
;
2185 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2187 /* Allocate some disk space for the alias content. */
2188 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
, 1, 1,
2189 &rsrcforkp
->extents
[0].startBlock
,
2190 &rsrcforkp
->extents
[0].blockCount
);
2192 rsrcforkp
->extents
[0].startBlock
= 0;
2196 /* Acquire a buffer cache block for our block. */
2197 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2198 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2200 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2202 journal_modify_block_start(hfsmp
->jnl
, bp
);
2205 /* Generate alias content */
2206 alias
= (char *)buf_dataptr(bp
);
2207 bzero(alias
, buf_size(bp
));
2208 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2210 /* Set the volume create date, local time in Mac OS format */
2211 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2212 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2214 /* If the file system is on a virtual device like disk image,
2215 * update the volume type to be ejectable device.
2217 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2218 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2219 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2222 /* Set id of the parent of the target directory */
2223 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2224 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2226 /* Set id of the target directory */
2227 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2228 *valptr
= OSSwapHostToBigInt32(inode_num
);
2230 /* Write alias content to disk. */
2232 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2233 } else if ((result
= buf_bwrite(bp
))) {
2237 /* Finish initializing the fork data. */
2238 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2239 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2242 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2243 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
);
2244 rsrcforkp
->extents
[0].startBlock
= 0;
2245 rsrcforkp
->extents
[0].blockCount
= 0;
2246 rsrcforkp
->logicalSize
= 0;
2247 rsrcforkp
->totalBlocks
= 0;
2253 * cat_deletelink - delete a link from the catalog
2257 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2259 struct HFSPlusCatalogFile file
;
2260 struct cat_attr cattr
;
2261 uint32_t totalBlocks
;
2265 bzero(&file
, sizeof (file
));
2266 bzero(&cattr
, sizeof (cattr
));
2267 cattr
.ca_fileid
= descp
->cd_cnid
;
2269 /* Directory links have alias content to remove. */
2270 if (descp
->cd_flags
& CD_ISDIR
) {
2272 BTreeIterator
* iterator
;
2273 struct FSBufferDescriptor btdata
;
2275 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2277 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2278 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2279 iterator
->hint
.nodeNum
= 0;
2281 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2284 BDINIT(btdata
, &file
);
2286 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2291 result
= cat_delete(hfsmp
, descp
, &cattr
);
2293 if ((result
== 0) &&
2294 (descp
->cd_flags
& CD_ISDIR
) &&
2295 (file
.recordType
== kHFSPlusFileRecord
)) {
2297 totalBlocks
= file
.resourceFork
.totalBlocks
;
2299 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2300 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2301 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2305 (void) BlockDeallocate(hfsmp
,
2306 file
.resourceFork
.extents
[i
].startBlock
,
2307 file
.resourceFork
.extents
[i
].blockCount
);
2309 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2310 file
.resourceFork
.extents
[i
].startBlock
= 0;
2311 file
.resourceFork
.extents
[i
].blockCount
= 0;
2320 * Callback to collect directory entries.
2321 * Called with readattr_state for each item in a directory.
2323 struct readattr_state
{
2324 struct hfsmount
*hfsmp
;
2325 struct cat_entrylist
*list
;
2332 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2333 struct readattr_state
*state
)
2335 struct cat_entrylist
*list
= state
->list
;
2336 struct hfsmount
*hfsmp
= state
->hfsmp
;
2337 struct cat_entry
*cep
;
2340 if (list
->realentries
>= list
->maxentries
)
2341 return (0); /* stop */
2343 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2345 switch(rec
->recordType
) {
2346 case kHFSPlusFolderRecord
:
2347 case kHFSPlusFileRecord
:
2348 case kHFSFolderRecord
:
2349 case kHFSFileRecord
:
2350 if (parentcnid
!= state
->dir_cnid
) {
2351 state
->error
= ENOENT
;
2352 return (0); /* stop */
2356 state
->error
= ENOENT
;
2357 return (0); /* stop */
2360 /* Hide the private system directories and journal files */
2361 if (parentcnid
== kHFSRootFolderID
) {
2362 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2363 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2364 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2365 list
->skipentries
++;
2366 return (1); /* continue */
2369 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2370 (rec
->recordType
== kHFSPlusFileRecord
) &&
2371 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2372 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2373 list
->skipentries
++;
2374 return (1); /* continue */
2378 cep
= &list
->entry
[list
->realentries
++];
2380 if (state
->stdhfs
) {
2381 struct HFSPlusCatalogFile cnoderec
;
2382 HFSPlusCatalogKey
* pluskey
;
2385 promoteattr(hfsmp
, rec
, &cnoderec
);
2386 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2388 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2389 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2390 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2391 FREE(pluskey
, M_TEMP
);
2393 if (rec
->recordType
== kHFSFileRecord
) {
2394 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2396 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2397 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2398 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2399 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2402 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2403 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2404 isadir(rec
), &cep
->ce_desc
);
2406 if (rec
->recordType
== kHFSPlusFileRecord
) {
2407 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2408 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2409 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2410 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2412 /* Save link reference for later processing. */
2413 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2414 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2415 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2416 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2417 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2418 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2419 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2424 return (list
->realentries
< list
->maxentries
);
2428 * Pack a cat_entrylist buffer with attributes from the catalog
2430 * Note: index is zero relative
2434 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
2438 BTreeIterator
* iterator
;
2439 struct readattr_state state
;
2447 ce_list
->realentries
= 0;
2449 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2450 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2451 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2453 state
.hfsmp
= hfsmp
;
2454 state
.list
= ce_list
;
2455 state
.dir_cnid
= parentcnid
;
2456 state
.stdhfs
= std_hfs
;
2459 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2460 bzero(iterator
, sizeof(*iterator
));
2461 key
= (CatalogKey
*)&iterator
->key
;
2463 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2464 index
= dirhint
->dh_index
+ 1;
2467 * Attempt to build a key from cached filename
2469 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2470 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2476 * If the last entry wasn't cached then position the btree iterator
2478 if ((index
== 0) || !have_key
) {
2480 * Position the iterator at the directory's thread record.
2481 * (i.e. just before the first entry)
2483 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2484 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2486 result
= MacToVFSError(result
);
2491 * Iterate until we reach the entry just
2492 * before the one we want to start with.
2495 struct position_state ps
;
2500 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2503 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2504 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2508 result
= MacToVFSError(result
);
2510 result
= MacToVFSError(result
);
2516 /* Fill list with entries starting at iterator->key. */
2517 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2518 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2521 result
= state
.error
;
2522 else if (ce_list
->realentries
== 0)
2525 result
= MacToVFSError(result
);
2531 * Resolve any hard links.
2533 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2534 struct FndrFileInfo
*fip
;
2535 struct cat_entry
*cep
;
2536 struct HFSPlusCatalogFile filerec
;
2540 cep
= &ce_list
->entry
[i
];
2541 if (cep
->ce_attr
.ca_linkref
== 0)
2544 /* Note: Finder info is still in Big Endian */
2545 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
2547 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2548 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2549 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2552 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2553 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
2554 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
2555 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
2558 if (isfilelink
|| isdirlink
) {
2559 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
2561 /* Repack entry from inode record. */
2562 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
2563 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
2564 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
2565 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
2566 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
2570 FREE(iterator
, M_TEMP
);
2572 return MacToVFSError(result
);
2575 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
2578 * Callback to pack directory entries.
2579 * Called with packdirentry_state for each item in a directory.
2582 /* Hard link information collected during cat_getdirentries. */
2585 user_addr_t dirent_addr
;
2587 typedef struct linkinfo linkinfo_t
;
2589 /* State information for the getdirentries_callback function. */
2590 struct packdirentry_state
{
2592 u_int32_t cbs_parentID
;
2593 u_int32_t cbs_index
;
2595 ExtendedVCB
* cbs_hfsmp
;
2598 int32_t cbs_maxlinks
;
2599 linkinfo_t
* cbs_linkinfo
;
2600 struct cat_desc
* cbs_desc
;
2601 u_int8_t
* cbs_namebuf
;
2603 * The following fields are only used for NFS readdir, which
2604 * uses the next file id as the seek offset of each entry.
2606 struct direntry
* cbs_direntry
;
2607 struct direntry
* cbs_prevdirentry
;
2608 u_int32_t cbs_previlinkref
;
2609 Boolean cbs_hasprevdirentry
;
2614 * getdirentries callback for HFS Plus directories.
2617 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2618 struct packdirentry_state
*state
)
2620 struct hfsmount
*hfsmp
;
2621 const CatalogName
*cnp
;
2624 struct dirent catent
;
2625 struct direntry
* entry
= NULL
;
2627 u_int32_t ilinkref
= 0;
2628 u_int32_t curlinkref
= 0;
2631 u_int8_t type
= DT_UNKNOWN
;
2632 u_int8_t is_mangled
= 0;
2634 user_addr_t uiobase
= USER_ADDR_NULL
;
2639 Boolean stop_after_pack
= false;
2641 hfsmp
= state
->cbs_hfsmp
;
2642 curID
= ckp
->hfsPlus
.parentID
;
2644 /* We're done when parent directory changes */
2645 if (state
->cbs_parentID
!= curID
) {
2646 if (state
->cbs_extended
) {
2647 /* The last record has not been returned yet, so we
2648 * want to stop after packing the last item
2650 if (state
->cbs_hasprevdirentry
) {
2651 stop_after_pack
= true;
2653 state
->cbs_result
= ENOENT
;
2654 return (0); /* stop */
2657 state
->cbs_result
= ENOENT
;
2658 return (0); /* stop */
2662 if (state
->cbs_extended
) {
2663 entry
= state
->cbs_direntry
;
2664 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
2665 maxnamelen
= NAME_MAX
;
2667 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2668 maxnamelen
= NAME_MAX
;
2671 if (state
->cbs_extended
&& stop_after_pack
) {
2672 /* The last item returns a non-zero invalid cookie */
2675 switch(crp
->recordType
) {
2676 case kHFSPlusFolderRecord
:
2678 cnid
= crp
->hfsPlusFolder
.folderID
;
2679 /* Hide our private system directories. */
2680 if (curID
== kHFSRootFolderID
) {
2681 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2682 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2687 case kHFSPlusFileRecord
:
2688 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
2689 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
2690 cnid
= crp
->hfsPlusFile
.fileID
;
2692 * When a hardlink link is encountered save its link ref.
2694 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2695 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
2696 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2697 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2698 /* If link ref is inode's file id then use it directly. */
2699 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
2700 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2702 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
2704 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2705 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
2706 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2707 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
2708 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2709 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2710 /* A directory's link resolves to a directory. */
2712 /* A directory's link ref is always inode's file id. */
2713 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2715 /* Hide the journal files */
2716 if ((curID
== kHFSRootFolderID
) &&
2717 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
2718 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
2719 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
2724 return (0); /* stop */
2727 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
2729 namelen
= cnp
->ustr
.length
;
2731 * For MacRoman encoded names, assume that its ascii and
2732 * convert it directly in an attempt to avoid the more
2733 * expensive utf8_encodestr conversion.
2735 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
2738 const u_int16_t
*chp
;
2740 chp
= &cnp
->ustr
.unicode
[0];
2741 for (i
= 0; i
< (int)namelen
; ++i
) {
2743 if (ch
> 0x007f || ch
== 0x0000) {
2744 /* Perform expensive utf8_encodestr conversion */
2747 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
2749 nameptr
[namelen
] = '\0';
2753 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
2754 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
2757 /* Check result returned from encoding the filename to utf8 */
2758 if (result
== ENAMETOOLONG
) {
2759 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
2760 cnp
->ustr
.unicode
, maxnamelen
+ 1,
2761 (ByteCount
*)&namelen
, nameptr
, cnid
);
2766 if (state
->cbs_extended
) {
2768 * The index is 1 relative and includes "." and ".."
2770 * Also stuff the cnid in the upper 32 bits of the cookie.
2771 * The cookie is stored to the previous entry, which will
2772 * be packed and copied this time
2774 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
2775 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
2776 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
2778 catent
.d_type
= type
;
2779 catent
.d_namlen
= namelen
;
2780 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2782 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
2784 catent
.d_fileno
= cnid
;
2785 uioaddr
= (caddr_t
) &catent
;
2788 /* Save current base address for post processing of hard-links. */
2789 if (ilinkref
|| state
->cbs_previlinkref
) {
2790 uiobase
= uio_curriovbase(state
->cbs_uio
);
2792 /* If this entry won't fit then we're done */
2793 if ((uiosize
> uio_resid(state
->cbs_uio
)) ||
2794 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
2795 return (0); /* stop */
2798 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
2799 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2800 if (state
->cbs_result
== 0) {
2803 /* Remember previous entry */
2804 state
->cbs_desc
->cd_cnid
= cnid
;
2805 if (type
== DT_DIR
) {
2806 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2808 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2810 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2811 state
->cbs_desc
->cd_namelen
= 0;
2814 state
->cbs_desc
->cd_encoding
= xxxx
;
2817 state
->cbs_desc
->cd_namelen
= namelen
;
2818 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
2820 /* Store unmangled name for the directory hint else it will
2821 * restart readdir at the last location again
2823 u_int8_t
*new_nameptr
;
2825 size_t tmp_namelen
= 0;
2827 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
2828 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
2829 cnp
->ustr
.length
* sizeof(UniChar
),
2831 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
2832 result
= utf8_encodestr(cnp
->ustr
.unicode
,
2833 cnp
->ustr
.length
* sizeof(UniChar
),
2834 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
2836 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2837 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
2839 FREE(new_nameptr
, M_TEMP
);
2842 if (state
->cbs_hasprevdirentry
) {
2843 curlinkref
= ilinkref
; /* save current */
2844 ilinkref
= state
->cbs_previlinkref
; /* use previous */
2847 * Record any hard links for post processing.
2849 if ((ilinkref
!= 0) &&
2850 (state
->cbs_result
== 0) &&
2851 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
2852 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
2853 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
2854 state
->cbs_nlinks
++;
2856 if (state
->cbs_hasprevdirentry
) {
2857 ilinkref
= curlinkref
; /* restore current */
2861 /* Fill the direntry to be used the next time */
2862 if (state
->cbs_extended
) {
2863 if (stop_after_pack
) {
2864 state
->cbs_eof
= true;
2865 return (0); /* stop */
2867 entry
->d_type
= type
;
2868 entry
->d_namlen
= namelen
;
2869 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
2871 /* File number = 0 means skip entry */
2872 entry
->d_fileno
= 0;
2874 entry
->d_fileno
= cnid
;
2876 /* swap the current and previous entry */
2877 struct direntry
* tmp
;
2878 tmp
= state
->cbs_direntry
;
2879 state
->cbs_direntry
= state
->cbs_prevdirentry
;
2880 state
->cbs_prevdirentry
= tmp
;
2881 state
->cbs_hasprevdirentry
= true;
2882 state
->cbs_previlinkref
= ilinkref
;
2885 /* Continue iteration if there's room */
2886 return (state
->cbs_result
== 0 &&
2887 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2891 * getdirentries callback for standard HFS (non HFS+) directories.
2894 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2895 struct packdirentry_state
*state
)
2897 struct hfsmount
*hfsmp
;
2898 const CatalogName
*cnp
;
2901 struct dirent catent
;
2903 u_int8_t type
= DT_UNKNOWN
;
2910 hfsmp
= state
->cbs_hfsmp
;
2912 curID
= ckp
->hfs
.parentID
;
2914 /* We're done when parent directory changes */
2915 if (state
->cbs_parentID
!= curID
) {
2916 state
->cbs_result
= ENOENT
;
2917 return (0); /* stop */
2920 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2921 maxnamelen
= NAME_MAX
;
2923 switch(crp
->recordType
) {
2924 case kHFSFolderRecord
:
2926 cnid
= crp
->hfsFolder
.folderID
;
2928 case kHFSFileRecord
:
2930 cnid
= crp
->hfsFile
.fileID
;
2933 return (0); /* stop */
2936 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
2937 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
2939 * When an HFS name cannot be encoded with the current
2940 * volume encoding we use MacRoman as a fallback.
2943 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
2945 catent
.d_type
= type
;
2946 catent
.d_namlen
= namelen
;
2947 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2948 catent
.d_fileno
= cnid
;
2949 uioaddr
= (caddr_t
) &catent
;
2951 /* If this entry won't fit then we're done */
2952 if (uiosize
> uio_resid(state
->cbs_uio
)) {
2953 return (0); /* stop */
2956 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2957 if (state
->cbs_result
== 0) {
2960 /* Remember previous entry */
2961 state
->cbs_desc
->cd_cnid
= cnid
;
2962 if (type
== DT_DIR
) {
2963 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2965 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2967 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2968 state
->cbs_desc
->cd_namelen
= 0;
2970 state
->cbs_desc
->cd_namelen
= namelen
;
2971 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
2974 /* Continue iteration if there's room */
2975 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2979 * Pack a uio buffer with directory entries from the catalog
2983 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
2984 uio_t uio
, int extended
, int * items
, int * eofflag
)
2987 BTreeIterator
* iterator
;
2989 struct packdirentry_state state
;
2997 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3000 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3003 * Get a buffer for link info array, btree iterator and a direntry:
3005 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
3006 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3008 bufsize
+= 2*sizeof(struct direntry
);
3010 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3011 bzero(buffer
, bufsize
);
3013 state
.cbs_extended
= extended
;
3014 state
.cbs_hasprevdirentry
= false;
3015 state
.cbs_previlinkref
= 0;
3016 state
.cbs_nlinks
= 0;
3017 state
.cbs_maxlinks
= maxlinks
;
3018 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3020 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3021 key
= (CatalogKey
*)&iterator
->key
;
3023 index
= dirhint
->dh_index
+ 1;
3025 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3026 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3027 state
.cbs_eof
= false;
3030 * Attempt to build a key from cached filename
3032 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3033 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3034 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3039 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3041 * Position the iterator at the directory's thread record.
3042 * (i.e. just before the first entry)
3044 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3045 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3046 iterator
->hint
.index
= 0;
3051 * If the last entry wasn't cached then position the btree iterator
3055 * Position the iterator at the directory's thread record.
3056 * (i.e. just before the first entry)
3058 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3059 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3061 result
= MacToVFSError(result
);
3065 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3068 * Iterate until we reach the entry just
3069 * before the one we want to start with.
3072 struct position_state ps
;
3077 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3080 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3081 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3085 result
= MacToVFSError(result
);
3087 result
= MacToVFSError(result
);
3093 state
.cbs_index
= index
;
3094 state
.cbs_hfsmp
= hfsmp
;
3095 state
.cbs_uio
= uio
;
3096 state
.cbs_desc
= &dirhint
->dh_desc
;
3097 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3098 state
.cbs_result
= 0;
3099 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3101 /* Use a temporary buffer to hold intermediate descriptor names. */
3102 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3103 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3104 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3105 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3106 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3109 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3111 enum BTreeIterationOperations op
;
3112 if (extended
&& index
!= 0 && have_key
)
3113 op
= kBTreeCurrentRecord
;
3115 op
= kBTreeNextRecord
;
3118 * Process as many entries as possible starting at iterator->key.
3120 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
3121 result
= BTIterateRecords(fcb
, op
, iterator
,
3122 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3124 result
= BTIterateRecords(fcb
, op
, iterator
,
3125 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3127 /* For extended calls, every call to getdirentries_callback()
3128 * transfers the previous directory entry found to the user
3129 * buffer. Therefore when BTIterateRecords reaches the end of
3130 * Catalog BTree, call getdirentries_callback() again with
3131 * dummy values to copy the last directory entry stored in
3132 * packdirentry_state
3134 if (state
.cbs_extended
&& (result
== fsBTRecordNotFoundErr
)) {
3138 bzero(&ckp
, sizeof(ckp
));
3139 bzero(&crp
, sizeof(crp
));
3141 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3145 /* Note that state.cbs_index is still valid on errors */
3146 *items
= state
.cbs_index
- index
;
3147 index
= state
.cbs_index
;
3149 if (state
.cbs_eof
) {
3153 /* Finish updating the catalog iterator. */
3154 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3155 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3156 dirhint
->dh_index
= index
- 1;
3158 /* Fix up the name. */
3159 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3160 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3161 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3163 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3164 dirhint
->dh_desc
.cd_namelen
= 0;
3168 * Post process any hard links to get the real file id.
3170 if (state
.cbs_nlinks
> 0) {
3171 u_int32_t fileid
= 0;
3172 user_addr_t address
;
3175 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3176 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3178 /* This assumes that d_ino is always first field. */
3179 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3180 if (address
== (user_addr_t
)0)
3182 if (uio_isuserspace(uio
)) {
3184 ino64_t fileid_64
= (ino64_t
)fileid
;
3185 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3187 (void) copyout(&fileid
, address
, sizeof(fileid
));
3189 } else /* system space */ {
3191 ino64_t fileid_64
= (ino64_t
)fileid
;
3192 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3194 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3200 if (state
.cbs_result
)
3201 result
= state
.cbs_result
;
3203 result
= MacToVFSError(result
);
3205 if (result
== ENOENT
) {
3210 FREE(buffer
, M_TEMP
);
3217 * Callback to establish directory position.
3218 * Called with position_state for each item in a directory.
3221 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3222 struct position_state
*state
)
3226 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
3227 curID
= ckp
->hfs
.parentID
;
3229 curID
= ckp
->hfsPlus
.parentID
;
3231 /* Make sure parent directory didn't change */
3232 if (state
->parentID
!= curID
) {
3233 state
->error
= EINVAL
;
3234 return (0); /* stop */
3237 /* Count this entry */
3238 switch(crp
->recordType
) {
3239 case kHFSPlusFolderRecord
:
3240 case kHFSPlusFileRecord
:
3241 case kHFSFolderRecord
:
3242 case kHFSFileRecord
:
3246 printf("cat_findposition: invalid record type %d in dir %d\n",
3247 crp
->recordType
, curID
);
3248 state
->error
= EINVAL
;
3249 return (0); /* stop */
3252 return (state
->count
< state
->index
);
3257 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3259 * The name portion of the key is compared using a 16-bit binary comparison.
3260 * This is called from the b-tree code.
3264 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3266 u_int32_t searchParentID
, trialParentID
;
3269 searchParentID
= searchKey
->parentID
;
3270 trialParentID
= trialKey
->parentID
;
3273 if (searchParentID
> trialParentID
) {
3275 } else if (searchParentID
< trialParentID
) {
3278 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3279 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3280 int length1
= searchKey
->nodeName
.length
;
3281 int length2
= trialKey
->nodeName
.length
;
3285 if (length1
< length2
) {
3288 } else if (length1
> length2
) {
3315 * Compare two standard HFS catalog keys
3317 * Result: +n search key > trial key
3318 * 0 search key = trial key
3319 * -n search key < trial key
3322 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3324 cnid_t searchParentID
, trialParentID
;
3327 searchParentID
= searchKey
->parentID
;
3328 trialParentID
= trialKey
->parentID
;
3330 if (searchParentID
> trialParentID
)
3332 else if (searchParentID
< trialParentID
)
3334 else /* parent dirID's are equal, compare names */
3335 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3342 * Compare two HFS+ catalog keys
3344 * Result: +n search key > trial key
3345 * 0 search key = trial key
3346 * -n search key < trial key
3349 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3351 cnid_t searchParentID
, trialParentID
;
3354 searchParentID
= searchKey
->parentID
;
3355 trialParentID
= trialKey
->parentID
;
3357 if (searchParentID
> trialParentID
) {
3360 else if (searchParentID
< trialParentID
) {
3363 /* parent node ID's are equal, compare names */
3364 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3365 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3367 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3368 searchKey
->nodeName
.length
,
3369 &trialKey
->nodeName
.unicode
[0],
3370 trialKey
->nodeName
.length
);
3378 * buildkey - build a Catalog b-tree key from a cnode descriptor
3381 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3382 HFSPlusCatalogKey
*key
, int retry
)
3384 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3386 size_t unicodeBytes
= 0;
3388 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3389 return (EINVAL
); /* invalid name */
3391 key
->parentID
= descp
->cd_parentcnid
;
3392 key
->nodeName
.length
= 0;
3394 * Convert filename from UTF-8 into Unicode
3397 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3398 utf8_flags
|= UTF_DECOMPOSED
;
3399 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3400 key
->nodeName
.unicode
, &unicodeBytes
,
3401 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3402 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3403 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3405 if (result
!= ENAMETOOLONG
)
3406 result
= EINVAL
; /* name has invalid characters */
3411 * For HFS volumes convert to an HFS compatible key
3413 * XXX need to save the encoding that succeeded
3415 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
3416 HFSCatalogKey hfskey
;
3418 bzero(&hfskey
, sizeof(hfskey
));
3419 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3420 hfskey
.parentID
= key
->parentID
;
3421 hfskey
.nodeName
[0] = 0;
3422 if (key
->nodeName
.length
> 0) {
3423 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
3424 key
->nodeName
.length
* 2,
3425 key
->nodeName
.unicode
,
3426 &hfskey
.nodeName
[0], retry
) != 0) {
3429 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3431 bcopy(&hfskey
, key
, sizeof(hfskey
));
3438 * Resolve hard link reference to obtain the inode record.
3442 cat_resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3444 FSBufferDescriptor btdata
;
3445 struct BTreeIterator
*iterator
;
3446 struct cat_desc idesc
;
3451 BDINIT(btdata
, recp
);
3454 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3455 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
3457 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3458 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
3461 /* Get space for iterator */
3462 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3463 bzero(iterator
, sizeof(*iterator
));
3465 /* Build a descriptor for private dir. */
3466 idesc
.cd_parentcnid
= parentcnid
;
3467 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
3468 idesc
.cd_namelen
= strlen(inodename
);
3471 idesc
.cd_encoding
= 0;
3472 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
3474 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3475 &btdata
, NULL
, NULL
);
3478 /* Make sure there's a reference */
3479 if (recp
->hl_linkCount
== 0)
3480 recp
->hl_linkCount
= 2;
3482 printf("HFS resolvelink: can't find %s\n", inodename
);
3485 FREE(iterator
, M_TEMP
);
3487 return (result
? ENOENT
: 0);
3491 * Resolve hard link reference to obtain the inode number.
3494 resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
)
3496 struct HFSPlusCatalogFile record
;
3500 * Since we know resolvelinkid is only called from
3501 * cat_getdirentries, we can assume that only file
3502 * hardlinks need to be resolved (cat_getdirentries
3503 * can resolve directory hardlinks in place).
3505 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
3507 if (record
.fileID
== 0)
3510 *ino
= record
.fileID
;
3516 * getkey - get a key from id by doing a thread lookup
3519 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
3521 struct BTreeIterator
* iterator
;
3522 FSBufferDescriptor btdata
;
3525 CatalogRecord
* recp
;
3529 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
3531 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3532 bzero(iterator
, sizeof(*iterator
));
3533 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
3535 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
3536 BDINIT(btdata
, recp
);
3538 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3539 &btdata
, &datasize
, iterator
);
3543 /* Turn thread record into a cnode key (in place) */
3544 switch (recp
->recordType
) {
3545 case kHFSFileThreadRecord
:
3546 case kHFSFolderThreadRecord
:
3547 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
3548 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
3549 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
3552 case kHFSPlusFileThreadRecord
:
3553 case kHFSPlusFolderThreadRecord
:
3554 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
3555 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
3556 (keyp
->hfsPlus
.nodeName
.length
* 2);
3557 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
3566 FREE(iterator
, M_TEMP
);
3569 return MacToVFSError(result
);
3573 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
3574 * null arguments to cat_idlookup instead, but we save around 10% by not building the
3575 * cat_desc here). Both key and attrp must point to real structures.
3577 * The key's parent id is the only part of the key expected to be used by the caller.
3578 * The name portion of the key may not always be valid (ie in the case of a hard link).
3582 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
3586 result
= getkey(hfsmp
, cnid
, key
);
3589 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
3592 * Check for a raw file hardlink inode.
3593 * Fix up the parent id in the key if necessary.
3594 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
3596 if ((result
== 0) &&
3597 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3598 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
3599 cnid_t nextlinkid
= 0;
3600 cnid_t prevlinkid
= 0;
3601 struct cat_desc linkdesc
;
3604 * Pick up the first link in the chain and get a descriptor for it.
3605 * This allows blind bulk access checks to work for hardlinks.
3607 if ((cat_lookuplinkbyid(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
3608 (nextlinkid
!= 0)) {
3609 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
3610 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
3611 cat_releasedesc(&linkdesc
);
3615 return MacToVFSError(result
);
3620 * buildrecord - build a default catalog directory or file record
3623 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
3624 CatalogRecord
*crp
, u_int32_t
*recordSize
)
3626 int type
= attrp
->ca_mode
& S_IFMT
;
3627 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
3630 createtime
= UTCToLocal(createtime
);
3631 if (type
== S_IFDIR
) {
3632 bzero(crp
, sizeof(HFSCatalogFolder
));
3633 crp
->recordType
= kHFSFolderRecord
;
3634 crp
->hfsFolder
.folderID
= cnid
;
3635 crp
->hfsFolder
.createDate
= createtime
;
3636 crp
->hfsFolder
.modifyDate
= createtime
;
3637 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
3638 *recordSize
= sizeof(HFSCatalogFolder
);
3640 bzero(crp
, sizeof(HFSCatalogFile
));
3641 crp
->recordType
= kHFSFileRecord
;
3642 crp
->hfsFile
.fileID
= cnid
;
3643 crp
->hfsFile
.createDate
= createtime
;
3644 crp
->hfsFile
.modifyDate
= createtime
;
3645 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
3646 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
3647 *recordSize
= sizeof(HFSCatalogFile
);
3650 struct HFSPlusBSDInfo
* bsdp
= NULL
;
3652 if (type
== S_IFDIR
) {
3653 crp
->recordType
= kHFSPlusFolderRecord
;
3654 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
3655 crp
->hfsPlusFolder
.valence
= 0;
3656 crp
->hfsPlusFolder
.folderID
= cnid
;
3657 crp
->hfsPlusFolder
.createDate
= createtime
;
3658 crp
->hfsPlusFolder
.contentModDate
= createtime
;
3659 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
3660 crp
->hfsPlusFolder
.accessDate
= createtime
;
3661 crp
->hfsPlusFolder
.backupDate
= 0;
3662 crp
->hfsPlusFolder
.textEncoding
= encoding
;
3663 crp
->hfsPlusFolder
.folderCount
= 0;
3664 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
3665 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
3666 bsdp
->special
.linkCount
= 1;
3667 *recordSize
= sizeof(HFSPlusCatalogFolder
);
3669 crp
->recordType
= kHFSPlusFileRecord
;
3670 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
3671 crp
->hfsPlusFile
.reserved1
= 0;
3672 crp
->hfsPlusFile
.fileID
= cnid
;
3673 crp
->hfsPlusFile
.createDate
= createtime
;
3674 crp
->hfsPlusFile
.contentModDate
= createtime
;
3675 crp
->hfsPlusFile
.accessDate
= createtime
;
3676 crp
->hfsPlusFile
.attributeModDate
= createtime
;
3677 crp
->hfsPlusFile
.backupDate
= 0;
3678 crp
->hfsPlusFile
.textEncoding
= encoding
;
3679 crp
->hfsPlusFile
.reserved2
= 0;
3680 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
3681 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
3682 /* BLK/CHR need to save the device info */
3683 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
3684 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
3686 bsdp
->special
.linkCount
= 1;
3688 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
3689 *recordSize
= sizeof(HFSPlusCatalogFile
);
3691 bsdp
->ownerID
= attrp
->ca_uid
;
3692 bsdp
->groupID
= attrp
->ca_gid
;
3693 bsdp
->fileMode
= attrp
->ca_mode
;
3694 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
3695 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
3701 * builddesc - build a cnode descriptor from an HFS+ key
3704 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
3705 int isdir
, struct cat_desc
*descp
)
3708 unsigned char * nameptr
;
3711 unsigned char tmpbuff
[128];
3713 /* guess a size... */
3714 bufsize
= (3 * key
->nodeName
.length
) + 1;
3715 if (bufsize
>= sizeof(tmpbuff
) - 1) {
3716 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3718 nameptr
= &tmpbuff
[0];
3721 result
= utf8_encodestr(key
->nodeName
.unicode
,
3722 key
->nodeName
.length
* sizeof(UniChar
),
3723 nameptr
, (size_t *)&utf8len
,
3726 if (result
== ENAMETOOLONG
) {
3727 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
3728 key
->nodeName
.length
* sizeof(UniChar
),
3730 FREE(nameptr
, M_TEMP
);
3731 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3733 result
= utf8_encodestr(key
->nodeName
.unicode
,
3734 key
->nodeName
.length
* sizeof(UniChar
),
3735 nameptr
, (size_t *)&utf8len
,
3738 descp
->cd_parentcnid
= key
->parentID
;
3739 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
3740 descp
->cd_namelen
= utf8len
;
3741 descp
->cd_cnid
= cnid
;
3742 descp
->cd_hint
= hint
;
3743 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
3745 descp
->cd_flags
|= CD_ISDIR
;
3746 descp
->cd_encoding
= encoding
;
3747 if (nameptr
!= &tmpbuff
[0]) {
3748 FREE(nameptr
, M_TEMP
);
3755 * getbsdattr - get attributes in bsd format
3759 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
3761 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
3762 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
3764 attrp
->ca_recflags
= crp
->flags
;
3765 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
3766 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
3767 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
3768 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
3769 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
3770 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
3772 if ((bsd
->fileMode
& S_IFMT
) == 0) {
3773 attrp
->ca_flags
= 0;
3774 attrp
->ca_uid
= hfsmp
->hfs_uid
;
3775 attrp
->ca_gid
= hfsmp
->hfs_gid
;
3777 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
3779 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
3781 attrp
->ca_linkcount
= 1;
3784 attrp
->ca_linkcount
= 1; /* may be overridden below */
3786 attrp
->ca_uid
= bsd
->ownerID
;
3787 attrp
->ca_gid
= bsd
->groupID
;
3788 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
3789 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
3790 switch (attrp
->ca_mode
& S_IFMT
) {
3791 case S_IFCHR
: /* fall through */
3793 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
3796 case S_IFDIR
: /* fall through */
3798 /* Pick up the hard link count */
3799 if (bsd
->special
.linkCount
> 0)
3800 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
3805 * Override the permissions as determined by the mount auguments
3806 * in ALMOST the same way unset permissions are treated but keep
3807 * track of whether or not the file or folder is hfs locked
3808 * by leaving the h_pflags field unchanged from what was unpacked
3809 * out of the catalog.
3812 * This code was used to do UID translation with MNT_IGNORE_OWNERS
3813 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
3814 * at the VFS layer, so there is no need to do it here now; this also
3815 * allows VFS to let root see the real UIDs.
3817 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
3818 * attrp->ca_uid = hfsmp->hfs_uid;
3819 * attrp->ca_gid = hfsmp->hfs_gid;
3825 if (!S_ISDIR(attrp
->ca_mode
)) {
3826 attrp
->ca_mode
&= ~S_IFMT
;
3827 attrp
->ca_mode
|= S_IFDIR
;
3829 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
3830 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
3831 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
3833 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3834 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
3835 attrp
->ca_flags
|= UF_HIDDEN
;
3837 /* Keep IMMUTABLE bits in sync with HFS locked flag */
3838 if (crp
->flags
& kHFSFileLockedMask
) {
3839 /* The file's supposed to be locked:
3840 Make sure at least one of the IMMUTABLE bits is set: */
3841 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
3842 attrp
->ca_flags
|= UF_IMMUTABLE
;
3844 /* The file's supposed to be unlocked: */
3845 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
3847 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3848 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
3849 attrp
->ca_flags
|= UF_HIDDEN
;
3850 /* get total blocks (both forks) */
3851 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
3853 /* On HFS+ the ThreadExists flag must always be set. */
3854 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
3855 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
3857 /* Pick up the hardlink first link, if any. */
3858 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
3861 attrp
->ca_fileid
= crp
->fileID
;
3863 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
3867 * promotekey - promote hfs key to hfs plus key
3871 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
3872 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
3874 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
3878 *encoding
= hfsmp
->hfs_encoding
;
3880 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
3881 kHFSPlusMaxFileNameChars
, &uniCount
);
3883 * When an HFS name cannot be encoded with the current
3884 * encoding use MacRoman as a fallback.
3886 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
3888 (void) mac_roman_to_unicode(hfskey
->nodeName
,
3889 keyp
->nodeName
.unicode
,
3890 kHFSPlusMaxFileNameChars
,
3894 keyp
->nodeName
.length
= uniCount
;
3895 keyp
->parentID
= hfskey
->parentID
;
3899 * promotefork - promote hfs fork info to hfs plus
3903 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
3904 int resource
, struct cat_fork
* forkp
)
3906 struct HFSPlusExtentDescriptor
*xp
;
3907 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
3909 bzero(forkp
, sizeof(*forkp
));
3910 xp
= &forkp
->cf_extents
[0];
3912 forkp
->cf_size
= filep
->rsrcLogicalSize
;
3913 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
3914 forkp
->cf_bytesread
= 0;
3915 forkp
->cf_vblocks
= 0;
3916 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
3917 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
3918 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
3919 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
3920 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
3921 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
3923 forkp
->cf_size
= filep
->dataLogicalSize
;
3924 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
3925 forkp
->cf_bytesread
= 0;
3926 forkp
->cf_vblocks
= 0;
3927 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
3928 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
3929 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
3930 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
3931 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
3932 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
3937 * promoteattr - promote standard hfs catalog attributes to hfs plus
3941 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
3943 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
3945 if (dataPtr
->recordType
== kHFSFolderRecord
) {
3946 const struct HFSCatalogFolder
* folder
;
3948 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
3949 crp
->recordType
= kHFSPlusFolderRecord
;
3950 crp
->flags
= folder
->flags
;
3951 crp
->fileID
= folder
->folderID
;
3952 crp
->createDate
= LocalToUTC(folder
->createDate
);
3953 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
3954 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
3955 crp
->reserved1
= folder
->valence
;
3957 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
3959 const struct HFSCatalogFile
* file
;
3961 file
= (const struct HFSCatalogFile
*) dataPtr
;
3962 crp
->recordType
= kHFSPlusFileRecord
;
3963 crp
->flags
= file
->flags
;
3964 crp
->fileID
= file
->fileID
;
3965 crp
->createDate
= LocalToUTC(file
->createDate
);
3966 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
3967 crp
->backupDate
= LocalToUTC(file
->backupDate
);
3970 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
3971 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
3972 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
3973 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
3975 crp
->textEncoding
= 0;
3976 crp
->attributeModDate
= crp
->contentModDate
;
3977 crp
->accessDate
= crp
->contentModDate
;
3978 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
3982 * Build a catalog node thread record from a catalog key
3983 * and return the size of the record.
3986 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
3991 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
3992 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
3994 size
= sizeof(HFSCatalogThread
);
3997 rec
->recordType
= kHFSFolderThreadRecord
;
3999 rec
->recordType
= kHFSFileThreadRecord
;
4000 rec
->parentID
= key
->parentID
;
4001 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4004 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4005 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4007 size
= sizeof(HFSPlusCatalogThread
);
4009 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4011 rec
->recordType
= kHFSPlusFileThreadRecord
;
4013 rec
->parentID
= key
->parentID
;
4014 bcopy(&key
->nodeName
, &rec
->nodeName
,
4015 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4017 /* HFS Plus has varaible sized thread records */
4018 size
-= (sizeof(rec
->nodeName
.unicode
) -
4019 (rec
->nodeName
.length
* sizeof(UniChar
)));
4026 * Build a catalog node thread key.
4029 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4032 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4033 key
->hfs
.reserved
= 0;
4034 key
->hfs
.parentID
= parentID
;
4035 key
->hfs
.nodeName
[0] = 0;
4037 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4038 key
->hfsPlus
.parentID
= parentID
;
4039 key
->hfsPlus
.nodeName
.length
= 0;
4044 * Extract the text encoding from a catalog node record.
4047 getencoding(const CatalogRecord
*crp
)
4051 if (crp
->recordType
== kHFSPlusFolderRecord
)
4052 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4053 else if (crp
->recordType
== kHFSPlusFileRecord
)
4054 encoding
= crp
->hfsPlusFile
.textEncoding
;
4062 * Extract the CNID from a catalog node record.
4065 getcnid(const CatalogRecord
*crp
)
4069 switch (crp
->recordType
) {
4070 case kHFSFolderRecord
:
4071 cnid
= crp
->hfsFolder
.folderID
;
4073 case kHFSFileRecord
:
4074 cnid
= crp
->hfsFile
.fileID
;
4076 case kHFSPlusFolderRecord
:
4077 cnid
= crp
->hfsPlusFolder
.folderID
;
4079 case kHFSPlusFileRecord
:
4080 cnid
= crp
->hfsPlusFile
.fileID
;
4083 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp
);
4091 * Extract the parent ID from a catalog node record.
4094 getparentcnid(const CatalogRecord
*recp
)
4098 switch (recp
->recordType
) {
4099 case kHFSFileThreadRecord
:
4100 case kHFSFolderThreadRecord
:
4101 cnid
= recp
->hfsThread
.parentID
;
4104 case kHFSPlusFileThreadRecord
:
4105 case kHFSPlusFolderThreadRecord
:
4106 cnid
= recp
->hfsPlusThread
.parentID
;
4109 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4117 * Determine if a catalog node record is a directory.
4120 isadir(const CatalogRecord
*crp
)
4122 return (crp
->recordType
== kHFSFolderRecord
||
4123 crp
->recordType
== kHFSPlusFolderRecord
);