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_int32_t 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_int32_t 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_int32_t hint
, u_int32_t 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_int32_t
*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_int32_t
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);
518 /* check for NULL name */
519 if (keyp
->hfs
.nodeName
[0] == 0) {
524 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
527 case kHFSPlusFileThreadRecord
:
528 case kHFSPlusFolderThreadRecord
:
529 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
531 /* check for NULL name */
532 if (keyp
->hfsPlus
.nodeName
.length
== 0) {
537 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
538 (keyp
->hfsPlus
.nodeName
.length
* 2);
546 result
= cat_lookupbykey(hfsmp
, keyp
, allow_system_files
, 0, 0, outdescp
, attrp
, forkp
, NULL
);
547 /* No corresponding file/folder record found for a thread record,
548 * mark the volume inconsistent.
550 if (result
== 0 && outdescp
) {
551 cnid_t dcnid
= outdescp
->cd_cnid
;
553 * Just for sanity's case, let's make sure that
554 * the key in the thread matches the key in the record.
557 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
563 FREE(iterator
, M_TEMP
);
565 return MacToVFSError(result
);
570 * cat_lookupmangled - lookup a catalog node using a mangled name
573 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
574 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
579 int extlen1
, extlen2
;
584 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
585 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
588 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
589 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
590 fileID
== hfsmp
->hfs_jnlfileid
||
591 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
595 result
= cat_idlookup(hfsmp
, fileID
, 0, outdescp
, attrp
, forkp
);
598 /* It must be in the correct directory */
599 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
602 if (((u_int16_t
)outdescp
->cd_namelen
< prefixlen
) ||
603 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
606 extlen1
= CountFilenameExtensionChars(descp
->cd_nameptr
, descp
->cd_namelen
);
607 extlen2
= CountFilenameExtensionChars(outdescp
->cd_nameptr
, outdescp
->cd_namelen
);
608 if (extlen1
!= extlen2
)
611 if (bcmp(outdescp
->cd_nameptr
+ (outdescp
->cd_namelen
- extlen2
),
612 descp
->cd_nameptr
+ (descp
->cd_namelen
- extlen1
),
619 cat_releasedesc(outdescp
);
625 * cat_lookupbykey - lookup a catalog node using a cnode key
628 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int allow_system_files
, u_int32_t hint
, int wantrsrc
,
629 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
631 struct BTreeIterator
* iterator
;
632 FSBufferDescriptor btdata
;
633 CatalogRecord
* recp
;
639 u_int32_t encoding
= 0;
641 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
643 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
644 BDINIT(btdata
, recp
);
645 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
646 bzero(iterator
, sizeof(*iterator
));
647 iterator
->hint
.nodeNum
= hint
;
648 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
650 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
651 &btdata
, &datasize
, iterator
);
655 /* Save the cnid and encoding now in case there's a hard link */
656 cnid
= getcnid(recp
);
657 encoding
= getencoding(recp
);
658 hint
= iterator
->hint
.nodeNum
;
660 /* Hide the journal files (if any) */
661 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
662 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
663 !allow_system_files
) {
670 * When a hardlink link is encountered, auto resolve it.
672 * The catalog record will change, and possibly its type.
676 && (recp
->recordType
== kHFSPlusFileRecord
)
677 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->vcbCrDate
) ||
678 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
682 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
683 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
685 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
686 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
687 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
690 if (isfilelink
|| isdirlink
) {
691 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
692 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
698 struct HFSPlusCatalogFile cnoderec
;
700 promoteattr(hfsmp
, recp
, &cnoderec
);
701 getbsdattr(hfsmp
, &cnoderec
, attrp
);
703 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
705 attrp
->ca_linkref
= ilink
;
710 bzero(forkp
, sizeof(*forkp
));
711 } else if (std_hfs
) {
712 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
713 } else if (wantrsrc
) {
714 /* Convert the resource fork. */
715 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
716 forkp
->cf_new_size
= 0;
717 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
718 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
719 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
720 forkp
->cf_bytesread
=
721 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
722 HFSTOVCB(hfsmp
)->blockSize
;
724 forkp
->cf_bytesread
= 0;
726 forkp
->cf_vblocks
= 0;
727 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
728 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
733 /* Convert the data fork. */
734 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
735 forkp
->cf_new_size
= 0;
736 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
737 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
738 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
739 forkp
->cf_bytesread
=
740 recp
->hfsPlusFile
.dataFork
.clumpSize
*
741 HFSTOVCB(hfsmp
)->blockSize
;
743 forkp
->cf_bytesread
= 0;
745 forkp
->cf_vblocks
= 0;
746 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
747 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
749 /* Validate the fork's resident extents. */
751 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
752 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
753 /* Suppress any bad extents so a remove can succeed. */
754 forkp
->cf_extents
[i
].startBlock
= 0;
755 forkp
->cf_extents
[i
].blockCount
= 0;
758 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
761 validblks
+= forkp
->cf_extents
[i
].blockCount
;
764 /* Adjust for any missing blocks. */
765 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
768 forkp
->cf_blocks
= validblks
;
770 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
772 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
773 if (psize
< forkp
->cf_size
) {
774 forkp
->cf_size
= psize
;
781 HFSPlusCatalogKey
* pluskey
= NULL
;
784 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
785 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
788 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
790 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
792 FREE(pluskey
, M_TEMP
);
796 if (desc_cnid
!= NULL
) {
800 FREE(iterator
, M_TEMP
);
803 return MacToVFSError(result
);
808 * cat_create - create a node in the catalog
810 * NOTE: both the catalog file and attribute file locks must
811 * be held before calling this function.
813 * The caller is responsible for releasing the output
814 * catalog descriptor (when supplied outdescp is non-null).
818 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
819 struct cat_desc
*out_descp
)
823 FSBufferDescriptor btdata
;
828 u_int32_t encoding
= kTextEncodingMacRoman
;
831 modeformat
= attrp
->ca_mode
& S_IFMT
;
833 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
834 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
837 * Get the next CNID. We can change it since we hold the catalog lock.
839 nextCNID
= hfsmp
->vcbNxtCNID
;
840 if (nextCNID
== 0xFFFFFFFF) {
844 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
845 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
846 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
847 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
854 /* Get space for iterator, key and data */
855 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
856 bto
->iterator
.hint
.nodeNum
= 0;
858 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
863 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
864 bto
->key
.nodeName
.length
);
865 hfs_setencodingbits(hfsmp
, encoding
);
869 * Insert the thread record first
871 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
872 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
873 S_ISDIR(attrp
->ca_mode
));
874 btdata
.bufferAddress
= &bto
->data
;
875 btdata
.itemSize
= datalen
;
876 btdata
.itemCount
= 1;
879 // this call requires the attribute file lock to be held
880 result
= file_attribute_exist(hfsmp
, nextCNID
);
881 if (result
== EEXIST
) {
882 // that cnid has orphaned attributes so just skip it.
883 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
884 nextCNID
= kHFSFirstUserCatalogNodeID
;
888 if (result
) goto exit
;
890 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
892 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
893 if ((result
== btExists
) && !std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
895 * Allow CNIDs on HFS Plus volumes to wrap around
897 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
898 nextCNID
= kHFSFirstUserCatalogNodeID
;
904 if (result
) goto exit
;
908 * CNID is now established. If we have wrapped then
909 * update the vcbNxtCNID.
911 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
912 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
913 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
914 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
919 * Now insert the file/directory record
921 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
922 btdata
.bufferAddress
= &bto
->data
;
923 btdata
.itemSize
= datalen
;
924 btdata
.itemCount
= 1;
926 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
928 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
930 if (result
== btExists
)
933 /* Back out the thread record */
934 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
935 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
936 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
937 /* Error on deleting extra thread record, mark
938 * volume inconsistent
940 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
941 hfs_mark_volume_inconsistent(hfsmp
);
948 * Insert was successful, update name, parent and volume
950 if (out_descp
!= NULL
) {
951 HFSPlusCatalogKey
* pluskey
= NULL
;
954 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
955 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
958 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
960 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
961 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
963 FREE(pluskey
, M_TEMP
);
966 attrp
->ca_fileid
= nextCNID
;
969 (void) BTFlushPath(fcb
);
972 return MacToVFSError(result
);
977 * cnode_rename - rename a catalog node
979 * Assumes that the target's directory exists.
981 * Order of B-tree operations:
982 * 1. BTSearchRecord(from_cnode, &data);
983 * 2. BTInsertRecord(to_cnode, &data);
984 * 3. BTDeleteRecord(from_cnode);
985 * 4. BTDeleteRecord(from_thread);
986 * 5. BTInsertRecord(to_thread);
988 * Note: The caller is responsible for releasing the output
989 * catalog descriptor (when supplied out_cdp is non-null).
994 struct hfsmount
* hfsmp
,
995 struct cat_desc
* from_cdp
,
996 struct cat_desc
* todir_cdp
,
997 struct cat_desc
* to_cdp
,
998 struct cat_desc
* out_cdp
)
1000 struct BTreeIterator
* to_iterator
= NULL
;
1001 struct BTreeIterator
* from_iterator
= NULL
;
1002 FSBufferDescriptor btdata
;
1003 CatalogRecord
* recp
= NULL
;
1004 HFSPlusCatalogKey
* to_key
;
1011 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1014 u_int32_t encoding
= 0;
1016 vcb
= HFSTOVCB(hfsmp
);
1017 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1018 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1020 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1023 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
1024 bzero(from_iterator
, sizeof(*from_iterator
));
1025 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1028 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1029 bzero(to_iterator
, sizeof(*to_iterator
));
1030 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1033 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1034 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1035 BDINIT(btdata
, recp
);
1038 * When moving a directory, make sure its a valid move.
1040 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1041 struct BTreeIterator iterator
;
1042 cnid_t cnid
= from_cdp
->cd_cnid
;
1043 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1045 /* First check the obvious ones */
1046 if (cnid
== fsRtDirID
||
1047 cnid
== to_cdp
->cd_parentcnid
||
1052 bzero(&iterator
, sizeof(iterator
));
1054 * Traverse destination path all the way back to the root
1055 * making sure that source directory is not encountered.
1058 while (pathcnid
> fsRtDirID
) {
1059 buildthreadkey(pathcnid
, std_hfs
,
1060 (CatalogKey
*)&iterator
.key
);
1061 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
1063 if (result
) goto exit
;
1065 pathcnid
= getparentcnid(recp
);
1066 if (pathcnid
== cnid
|| pathcnid
== 0) {
1074 * Step 1: Find cnode data at old location
1076 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1077 &datasize
, from_iterator
);
1079 if (std_hfs
|| (result
!= btNotFound
))
1082 struct cat_desc temp_desc
;
1084 /* Probably the node has mangled name */
1085 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1089 /* The file has mangled name. Search the cnode data using full name */
1090 bzero(from_iterator
, sizeof(*from_iterator
));
1091 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1093 cat_releasedesc(&temp_desc
);
1097 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1099 cat_releasedesc(&temp_desc
);
1103 cat_releasedesc(&temp_desc
);
1106 /* Check if the source is directory hard link. We do not change
1107 * directory flag because it is later used to initialize result descp
1111 (recp
->recordType
== kHFSPlusFileRecord
) &&
1112 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1117 * Update the text encoding (on disk and in descriptor).
1119 * Note that hardlink inodes don't require a text encoding hint.
1122 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1123 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1124 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1125 hfs_setencodingbits(hfsmp
, encoding
);
1126 recp
->hfsPlusFile
.textEncoding
= encoding
;
1128 out_cdp
->cd_encoding
= encoding
;
1131 if (std_hfs
&& !directory
&&
1132 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1136 * If the keys are identical then there's nothing left to do!
1138 * update the hint and exit
1141 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1143 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1147 /* Step 2: Insert cnode at new location */
1148 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1149 if (result
== btExists
) {
1150 int fromtype
= recp
->recordType
;
1152 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1153 goto exit
; /* EEXIST */
1155 /* Find cnode data at new location */
1156 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1160 if ((fromtype
!= recp
->recordType
) ||
1161 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1163 goto exit
; /* EEXIST */
1165 /* The old name is a case variant and must be removed */
1166 result
= BTDeleteRecord(fcb
, from_iterator
);
1170 /* Insert cnode (now that case duplicate is gone) */
1171 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1173 /* Try and restore original before leaving */
1178 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1180 printf("hfs: cat_create: could not undo (BTInsert = %d)", err
);
1181 hfs_mark_volume_inconsistent(hfsmp
);
1187 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1196 /* Step 3: Remove cnode from old location */
1198 result
= BTDeleteRecord(fcb
, from_iterator
);
1200 /* Try and delete new record before leaving */
1205 err
= BTDeleteRecord(fcb
, to_iterator
);
1207 printf("hfs: cat_create: could not undo (BTDelete = %d)", err
);
1208 hfs_mark_volume_inconsistent(hfsmp
);
1214 (void) BTDeleteRecord(fcb
, to_iterator
);
1220 /* #### POINT OF NO RETURN #### */
1223 * Step 4: Remove cnode's old thread record
1225 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1226 (void) BTDeleteRecord(fcb
, from_iterator
);
1229 * Step 5: Insert cnode's new thread record
1230 * (optional for HFS files)
1233 /* For directory hard links, always create a file thread
1234 * record. For everything else, use the directory flag.
1237 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1239 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1241 btdata
.itemSize
= datasize
;
1242 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1243 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1247 HFSPlusCatalogKey
* pluskey
= NULL
;
1250 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1251 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1253 /* Save the real encoding hint in the Finder Info (field 4). */
1254 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1257 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1258 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1262 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1264 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1265 encoding
, directory
, out_cdp
);
1267 FREE(pluskey
, M_TEMP
);
1271 (void) BTFlushPath(fcb
);
1273 FREE(from_iterator
, M_TEMP
);
1275 FREE(to_iterator
, M_TEMP
);
1278 return MacToVFSError(result
);
1283 * cat_delete - delete a node from the catalog
1285 * Order of B-tree operations:
1286 * 1. BTDeleteRecord(cnode);
1287 * 2. BTDeleteRecord(thread);
1288 * 3. BTUpdateRecord(parent);
1292 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1295 BTreeIterator
*iterator
;
1300 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1301 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1305 * The root directory cannot be deleted
1306 * A directory must be empty
1307 * A file must be zero length (no blocks)
1309 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1310 descp
->cd_parentcnid
== kHFSRootParentID
)
1313 /* XXX Preflight Missing */
1315 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1316 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1317 iterator
->hint
.nodeNum
= 0;
1320 * Derive a key from either the file ID (for a virtual inode)
1321 * or the descriptor.
1323 if (descp
->cd_namelen
== 0) {
1324 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1325 cnid
= attrp
->ca_fileid
;
1327 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1328 cnid
= descp
->cd_cnid
;
1334 result
= BTDeleteRecord(fcb
, iterator
);
1336 if (std_hfs
|| (result
!= btNotFound
))
1339 struct cat_desc temp_desc
;
1341 /* Probably the node has mangled name */
1342 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1346 /* The file has mangled name. Delete the file using full name */
1347 bzero(iterator
, sizeof(*iterator
));
1348 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1349 cnid
= temp_desc
.cd_cnid
;
1351 cat_releasedesc(&temp_desc
);
1355 result
= BTDeleteRecord(fcb
, iterator
);
1357 cat_releasedesc(&temp_desc
);
1361 cat_releasedesc(&temp_desc
);
1364 /* Delete thread record. On error, mark volume inconsistent */
1365 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1366 if (BTDeleteRecord(fcb
, iterator
)) {
1368 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1369 hfs_mark_volume_inconsistent(hfsmp
);
1374 (void) BTFlushPath(fcb
);
1376 return MacToVFSError(result
);
1381 * cnode_update - update the catalog node described by descp
1382 * using the data from attrp and forkp.
1386 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1387 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1390 BTreeIterator
* iterator
;
1391 struct update_state state
;
1395 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1396 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1398 state
.s_desc
= descp
;
1399 state
.s_attr
= attrp
;
1400 state
.s_datafork
= dataforkp
;
1401 state
.s_rsrcfork
= rsrcforkp
;
1402 state
.s_hfsmp
= hfsmp
;
1404 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1405 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1408 * For open-deleted files we need to do a lookup by cnid
1409 * (using thread rec).
1411 * For hard links, the target of the update is the inode
1412 * itself (not the link record) so a lookup by fileid
1413 * (i.e. thread rec) is needed.
1415 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1416 (descp
->cd_namelen
== 0) ||
1417 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
1418 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1420 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1425 /* Pass a node hint */
1426 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1428 result
= BTUpdateRecord(fcb
, iterator
,
1429 (IterateCallBackProcPtr
)catrec_update
, &state
);
1433 /* Update the node hint. */
1434 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1437 (void) BTFlushPath(fcb
);
1439 return MacToVFSError(result
);
1443 * catrec_update - Update the fields of a catalog record
1444 * This is called from within BTUpdateRecord.
1447 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1449 struct cat_desc
*descp
;
1450 struct cat_attr
*attrp
;
1451 struct cat_fork
*forkp
;
1452 struct hfsmount
*hfsmp
;
1456 descp
= state
->s_desc
;
1457 attrp
= state
->s_attr
;
1458 hfsmp
= state
->s_hfsmp
;
1459 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1461 switch (crp
->recordType
) {
1462 case kHFSFolderRecord
: {
1463 HFSCatalogFolder
*dir
;
1465 dir
= (struct HFSCatalogFolder
*)crp
;
1466 /* Do a quick sanity check */
1467 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1468 (dir
->folderID
!= descp
->cd_cnid
))
1469 return (btNotFound
);
1470 dir
->valence
= attrp
->ca_entries
;
1471 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1472 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1473 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1474 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1475 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1478 case kHFSFileRecord
: {
1479 HFSCatalogFile
*file
;
1481 file
= (struct HFSCatalogFile
*)crp
;
1482 /* Do a quick sanity check */
1483 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1484 (file
->fileID
!= attrp
->ca_fileid
))
1485 return (btNotFound
);
1486 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1487 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1488 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1489 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1490 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1491 if (state
->s_rsrcfork
) {
1492 forkp
= state
->s_rsrcfork
;
1493 file
->rsrcLogicalSize
= forkp
->cf_size
;
1494 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1495 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1496 file
->rsrcExtents
[i
].startBlock
=
1497 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1498 file
->rsrcExtents
[i
].blockCount
=
1499 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1502 if (state
->s_datafork
) {
1503 forkp
= state
->s_datafork
;
1504 file
->dataLogicalSize
= forkp
->cf_size
;
1505 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1506 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1507 file
->dataExtents
[i
].startBlock
=
1508 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1509 file
->dataExtents
[i
].blockCount
=
1510 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1514 /* Synchronize the lock state */
1515 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1516 file
->flags
|= kHFSFileLockedMask
;
1518 file
->flags
&= ~kHFSFileLockedMask
;
1521 case kHFSPlusFolderRecord
: {
1522 HFSPlusCatalogFolder
*dir
;
1524 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1525 /* Do a quick sanity check */
1526 if (dir
->folderID
!= attrp
->ca_fileid
) {
1527 printf("hfs: catrec_update: id %d != %d\n", dir
->folderID
, attrp
->ca_fileid
);
1528 return (btNotFound
);
1530 dir
->flags
= attrp
->ca_recflags
;
1531 dir
->valence
= attrp
->ca_entries
;
1532 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1533 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1534 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1535 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1536 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1537 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1538 /* Note: directory hardlink inodes don't require a text encoding hint. */
1539 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1540 dir
->textEncoding
= descp
->cd_encoding
;
1542 dir
->folderCount
= attrp
->ca_dircount
;
1543 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1545 * Update the BSD Info if it was already initialized on
1546 * disk or if the runtime values have been modified.
1548 * If the BSD info was already initialized, but
1549 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1550 * probably different than what was on disk. We don't want
1551 * to overwrite the on-disk values (so if we turn off
1552 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1553 * This way, we can still change fields like the mode or
1554 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1556 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1557 * won't change the uid or gid from their defaults. So, if
1558 * the BSD info wasn't set, and the runtime values are not
1559 * default, then what changed was the mode or flags. We
1560 * have to set the uid and gid to something, so use the
1561 * supplied values (which will be default), which has the
1562 * same effect as creating a new file while
1563 * MNT_UNKNOWNPERMISSIONS is set.
1565 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1566 (attrp
->ca_flags
!= 0) ||
1567 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1568 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1569 ((attrp
->ca_mode
& ALLPERMS
) !=
1570 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1571 if ((dir
->bsdInfo
.fileMode
== 0) ||
1572 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1573 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1574 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1576 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1577 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1578 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1579 /* A directory hardlink has a link count. */
1580 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1581 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1586 case kHFSPlusFileRecord
: {
1587 HFSPlusCatalogFile
*file
;
1589 file
= (struct HFSPlusCatalogFile
*)crp
;
1590 /* Do a quick sanity check */
1591 if (file
->fileID
!= attrp
->ca_fileid
)
1592 return (btNotFound
);
1593 file
->flags
= attrp
->ca_recflags
;
1594 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1595 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1596 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1597 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1598 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1599 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1601 * Note: file hardlink inodes don't require a text encoding
1602 * hint, but they do have a first link value.
1604 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1605 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1607 file
->textEncoding
= descp
->cd_encoding
;
1609 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1611 * Update the BSD Info if it was already initialized on
1612 * disk or if the runtime values have been modified.
1614 * If the BSD info was already initialized, but
1615 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1616 * probably different than what was on disk. We don't want
1617 * to overwrite the on-disk values (so if we turn off
1618 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1619 * This way, we can still change fields like the mode or
1620 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1622 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1623 * won't change the uid or gid from their defaults. So, if
1624 * the BSD info wasn't set, and the runtime values are not
1625 * default, then what changed was the mode or flags. We
1626 * have to set the uid and gid to something, so use the
1627 * supplied values (which will be default), which has the
1628 * same effect as creating a new file while
1629 * MNT_UNKNOWNPERMISSIONS is set.
1631 if ((file
->bsdInfo
.fileMode
!= 0) ||
1632 (attrp
->ca_flags
!= 0) ||
1633 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1634 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1635 ((attrp
->ca_mode
& ALLPERMS
) !=
1636 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1637 if ((file
->bsdInfo
.fileMode
== 0) ||
1638 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1639 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1640 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1642 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1643 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1644 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1646 if (state
->s_rsrcfork
) {
1647 forkp
= state
->s_rsrcfork
;
1648 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1649 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1650 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1651 sizeof(HFSPlusExtentRecord
));
1652 /* Push blocks read to disk */
1653 file
->resourceFork
.clumpSize
=
1654 howmany(forkp
->cf_bytesread
, blksize
);
1656 if (state
->s_datafork
) {
1657 forkp
= state
->s_datafork
;
1658 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1659 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1660 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1661 sizeof(HFSPlusExtentRecord
));
1662 /* Push blocks read to disk */
1663 file
->dataFork
.clumpSize
=
1664 howmany(forkp
->cf_bytesread
, blksize
);
1667 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1668 (file
->resourceFork
.extents
[0].startBlock
==
1669 file
->dataFork
.extents
[0].startBlock
)) {
1670 panic("hfs: catrec_update: rsrc fork == data fork");
1673 /* Synchronize the lock state */
1674 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1675 file
->flags
|= kHFSFileLockedMask
;
1677 file
->flags
&= ~kHFSFileLockedMask
;
1679 /* Push out special field if necessary */
1680 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
1681 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1682 } else if (descp
->cd_cnid
!= attrp
->ca_fileid
|| attrp
->ca_linkcount
== 2) {
1683 file
->hl_linkCount
= attrp
->ca_linkcount
;
1688 return (btNotFound
);
1693 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
1694 * catalog btree of given cnid by walking up the parent chain till it reaches
1695 * either the root folder, or the private metadata directory for storing
1696 * directory hard links. This function updates the corresponding in-core
1697 * cnode, if any, and the directory record in the catalog btree.
1698 * On success, returns zero. On failure, returns non-zero value.
1702 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
1706 struct cat_desc desc
;
1707 struct cat_attr attr
;
1709 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
1710 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
1711 /* Update the bit in corresponding cnode, if any, in the hash.
1712 * If the cnode has the bit already set, stop the traversal.
1714 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
1719 /* Update the catalog record on disk if either cnode was not
1720 * found in the hash, or if a cnode was found and the cnode
1721 * did not have the bit set previously.
1723 retval
= hfs_start_transaction(hfsmp
);
1727 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1729 /* Look up our catalog folder record */
1730 retval
= cat_idlookup(hfsmp
, cnid
, 0, &desc
, &attr
, NULL
);
1732 hfs_systemfile_unlock(hfsmp
, lockflags
);
1733 hfs_end_transaction(hfsmp
);
1737 /* Update the bit in the catalog record */
1738 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
1739 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
1741 hfs_systemfile_unlock(hfsmp
, lockflags
);
1742 hfs_end_transaction(hfsmp
);
1743 cat_releasedesc(&desc
);
1747 hfs_systemfile_unlock(hfsmp
, lockflags
);
1748 hfs_end_transaction(hfsmp
);
1750 cnid
= desc
.cd_parentcnid
;
1751 cat_releasedesc(&desc
);
1757 /* This function traverses the parent directory hierarchy from the given
1758 * directory to one level below root directory and checks if any of its
1760 * 1. A directory hard link.
1761 * 2. The 'pointed at' directory.
1762 * If any of these conditions fail or an internal error is encountered
1763 * during look up of the catalog record, this function returns non-zero value.
1767 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
1769 HFSPlusCatalogKey
*keyp
;
1771 FSBufferDescriptor btdata
;
1772 HFSPlusCatalogFolder folder
;
1778 BDINIT(btdata
, &folder
);
1779 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
1780 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
1781 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1783 while (cnid
!= kHFSRootParentID
) {
1784 /* Check if the 'pointed at' directory is an ancestor */
1785 if (pointed_at_cnid
== cnid
) {
1789 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
1790 printf("hfs: cat_check_link_ancestry: getkey for %u failed\n", cnid
);
1791 invalid
= 1; /* On errors, assume an invalid parent */
1794 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
1795 printf("hfs: cat_check_link_ancestry: cannot find %u\n", cnid
);
1796 invalid
= 1; /* On errors, assume an invalid parent */
1799 /* Check if this ancestor is a directory hard link */
1800 if (folder
.flags
& kHFSHasLinkChainMask
) {
1804 cnid
= keyp
->parentID
;
1812 * updatelink_callback - update a link's chain
1815 struct linkupdate_state
{
1822 updatelink_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
1824 HFSPlusCatalogFile
*file
;
1826 if (crp
->recordType
!= kHFSPlusFileRecord
) {
1827 printf("hfs: updatelink_callback: unexpected rec type %d\n", crp
->recordType
);
1828 return (btNotFound
);
1831 file
= (struct HFSPlusCatalogFile
*)crp
;
1832 if (file
->flags
& kHFSHasLinkChainMask
) {
1833 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
1834 file
->hl_prevLinkID
= state
->prevlinkid
;
1836 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
1837 file
->hl_nextLinkID
= state
->nextlinkid
;
1840 printf("hfs: updatelink_callback: file %d isn't a chain\n", file
->fileID
);
1846 * cat_updatelink - update a link's chain
1850 cat_updatelink(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
1853 BTreeIterator
* iterator
;
1854 struct linkupdate_state state
;
1857 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1858 state
.filelinkid
= linkfileid
;
1859 state
.prevlinkid
= prevlinkid
;
1860 state
.nextlinkid
= nextlinkid
;
1862 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1863 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1864 iterator
->hint
.nodeNum
= 0;
1866 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
1868 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)updatelink_callback
, &state
);
1869 (void) BTFlushPath(fcb
);
1871 printf("hfs: cat_updatelink: couldn't resolve cnid %d\n", linkfileid
);
1873 return MacToVFSError(result
);
1877 * cat_lookuplink - lookup a link by it's name
1881 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1884 BTreeIterator
* iterator
;
1885 struct FSBufferDescriptor btdata
;
1886 struct HFSPlusCatalogFile file
;
1889 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1891 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1892 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1893 iterator
->hint
.nodeNum
= 0;
1895 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
1898 BDINIT(btdata
, &file
);
1900 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1903 if (file
.recordType
!= kHFSPlusFileRecord
) {
1907 *linkfileid
= file
.fileID
;
1909 if (file
.flags
& kHFSHasLinkChainMask
) {
1910 *prevlinkid
= file
.hl_prevLinkID
;
1911 *nextlinkid
= file
.hl_nextLinkID
;
1917 return MacToVFSError(result
);
1922 * cat_lookuplink - lookup a link by its cnid
1926 cat_lookuplinkbyid(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1929 BTreeIterator
* iterator
;
1930 struct FSBufferDescriptor btdata
;
1931 struct HFSPlusCatalogFile file
;
1934 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1936 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1937 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1938 iterator
->hint
.nodeNum
= 0;
1940 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
1941 printf("hfs: cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid
, result
);
1944 BDINIT(btdata
, &file
);
1946 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1947 printf("hfs: cat_lookuplinkbyid: cannot find %d\n", linkfileid
);
1950 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
1951 if (file
.flags
& kHFSHasLinkChainMask
) {
1954 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
1956 /* ADL inodes don't have a chain (its in an EA) */
1957 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1958 result
= ENOLINK
; /* signal to caller to get head of list */
1959 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1961 *nextlinkid
= file
.hl_firstLinkID
;
1963 *prevlinkid
= file
.hl_prevLinkID
;
1964 *nextlinkid
= file
.hl_nextLinkID
;
1971 return MacToVFSError(result
);
1976 * cat_createlink - create a link in the catalog
1978 * The following cat_attr fields are expected to be set:
1984 * ca_finderinfo (type and creator)
1988 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1989 cnid_t nextlinkid
, cnid_t
*linkfileid
)
1993 FSBufferDescriptor btdata
;
1994 HFSPlusForkData
*rsrcforkp
;
1998 int thread_inserted
= 0;
1999 int alias_allocated
= 0;
2002 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2005 * Get the next CNID. We can change it since we hold the catalog lock.
2007 nextCNID
= hfsmp
->vcbNxtCNID
;
2008 if (nextCNID
== 0xFFFFFFFF) {
2009 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
2010 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2011 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
2012 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2014 hfsmp
->vcbNxtCNID
++;
2016 MarkVCBDirty(hfsmp
);
2018 /* Get space for iterator, key and data */
2019 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
2020 bto
->iterator
.hint
.nodeNum
= 0;
2021 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2023 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2025 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2029 /* This is our only chance to set the encoding (other than a rename). */
2030 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2032 /* Insert the thread record first. */
2033 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2034 btdata
.bufferAddress
= &bto
->data
;
2035 btdata
.itemSize
= datalen
;
2036 btdata
.itemCount
= 1;
2039 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2041 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2042 if ((result
== btExists
) && (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2044 * Allow CNIDs on HFS Plus volumes to wrap around
2046 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
2047 nextCNID
= kHFSFirstUserCatalogNodeID
;
2052 thread_inserted
= 1;
2060 * CNID is now established. If we have wrapped then
2061 * update the vcbNxtCNID.
2063 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2064 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
2065 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
2066 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2071 * Now insert the link record.
2073 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2075 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2076 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2077 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2079 /* For directory hard links, create alias in resource fork */
2080 if (descp
->cd_flags
& CD_ISDIR
) {
2081 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2084 alias_allocated
= 1;
2086 btdata
.bufferAddress
= &bto
->data
;
2087 btdata
.itemSize
= datalen
;
2088 btdata
.itemCount
= 1;
2090 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2092 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2094 if (result
== btExists
)
2098 if (linkfileid
!= NULL
) {
2099 *linkfileid
= nextCNID
;
2103 if (thread_inserted
) {
2104 printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result
));
2106 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2107 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2108 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2109 hfs_mark_volume_inconsistent(hfsmp
);
2112 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2113 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2114 rsrcforkp
->extents
[0].blockCount
);
2115 rsrcforkp
->extents
[0].startBlock
= 0;
2116 rsrcforkp
->extents
[0].blockCount
= 0;
2119 (void) BTFlushPath(fcb
);
2122 return MacToVFSError(result
);
2125 /* Directory hard links are visible as aliases on pre-Leopard systems and
2126 * as normal directories on Leopard or later. All directory hard link aliases
2127 * have the same resource fork content except for the three uniquely
2128 * identifying values that are updated in the resource fork data when the alias
2129 * is created. The following array is the constant resource fork data used
2130 * only for creating directory hard link aliases.
2132 static const char hfs_dirlink_alias_rsrc
[] = {
2133 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2149 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2159 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2161 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2164 /* Constants for directory hard link alias */
2166 /* Size of resource fork data array for directory hard link alias */
2167 kHFSAliasSize
= 0x1d0,
2169 /* Volume type for ejectable devices like disk image */
2170 kHFSAliasVolTypeEjectable
= 0x5,
2172 /* Offset for volume create date, in Mac OS local time */
2173 kHFSAliasVolCreateDateOffset
= 0x12a,
2175 /* Offset for the type of volume */
2176 kHFSAliasVolTypeOffset
= 0x130,
2178 /* Offset for folder ID of the parent directory of the directory inode */
2179 kHFSAliasParentIDOffset
= 0x132,
2181 /* Offset for folder ID of the directory inode */
2182 kHFSAliasTargetIDOffset
= 0x176,
2185 /* Create and write an alias that points at the directory represented by given
2186 * inode number on the same volume. Directory hard links are visible as
2187 * aliases in pre-Leopard systems and this function creates these aliases.
2189 * Note: This code is very specific to creating alias for the purpose
2190 * of directory hard links only, and should not be generalized.
2193 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2201 HFSPlusForkData
*rsrcforkp
;
2205 rsrcforkp
= &(crp
->resourceFork
);
2207 blksize
= hfsmp
->blockSize
;
2208 blkcount
= howmany(kHFSAliasSize
, blksize
);
2209 sectorsize
= hfsmp
->hfs_logical_block_size
;
2210 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2212 /* Allocate some disk space for the alias content. */
2213 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
, 1, 1,
2214 &rsrcforkp
->extents
[0].startBlock
,
2215 &rsrcforkp
->extents
[0].blockCount
);
2217 rsrcforkp
->extents
[0].startBlock
= 0;
2221 /* Acquire a buffer cache block for our block. */
2222 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2223 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2225 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2227 journal_modify_block_start(hfsmp
->jnl
, bp
);
2230 /* Generate alias content */
2231 alias
= (char *)buf_dataptr(bp
);
2232 bzero(alias
, buf_size(bp
));
2233 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2235 /* Set the volume create date, local time in Mac OS format */
2236 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2237 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2239 /* If the file system is on a virtual device like disk image,
2240 * update the volume type to be ejectable device.
2242 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2243 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2244 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2247 /* Set id of the parent of the target directory */
2248 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2249 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2251 /* Set id of the target directory */
2252 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2253 *valptr
= OSSwapHostToBigInt32(inode_num
);
2255 /* Write alias content to disk. */
2257 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2258 } else if ((result
= buf_bwrite(bp
))) {
2262 /* Finish initializing the fork data. */
2263 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2264 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2267 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2268 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
);
2269 rsrcforkp
->extents
[0].startBlock
= 0;
2270 rsrcforkp
->extents
[0].blockCount
= 0;
2271 rsrcforkp
->logicalSize
= 0;
2272 rsrcforkp
->totalBlocks
= 0;
2278 * cat_deletelink - delete a link from the catalog
2282 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2284 struct HFSPlusCatalogFile file
;
2285 struct cat_attr cattr
;
2286 uint32_t totalBlocks
;
2290 bzero(&file
, sizeof (file
));
2291 bzero(&cattr
, sizeof (cattr
));
2292 cattr
.ca_fileid
= descp
->cd_cnid
;
2294 /* Directory links have alias content to remove. */
2295 if (descp
->cd_flags
& CD_ISDIR
) {
2297 BTreeIterator
* iterator
;
2298 struct FSBufferDescriptor btdata
;
2300 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2302 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2303 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2304 iterator
->hint
.nodeNum
= 0;
2306 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2309 BDINIT(btdata
, &file
);
2311 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2316 result
= cat_delete(hfsmp
, descp
, &cattr
);
2318 if ((result
== 0) &&
2319 (descp
->cd_flags
& CD_ISDIR
) &&
2320 (file
.recordType
== kHFSPlusFileRecord
)) {
2322 totalBlocks
= file
.resourceFork
.totalBlocks
;
2324 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2325 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2326 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2330 (void) BlockDeallocate(hfsmp
,
2331 file
.resourceFork
.extents
[i
].startBlock
,
2332 file
.resourceFork
.extents
[i
].blockCount
);
2334 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2335 file
.resourceFork
.extents
[i
].startBlock
= 0;
2336 file
.resourceFork
.extents
[i
].blockCount
= 0;
2345 * Callback to collect directory entries.
2346 * Called with readattr_state for each item in a directory.
2348 struct readattr_state
{
2349 struct hfsmount
*hfsmp
;
2350 struct cat_entrylist
*list
;
2357 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2358 struct readattr_state
*state
)
2360 struct cat_entrylist
*list
= state
->list
;
2361 struct hfsmount
*hfsmp
= state
->hfsmp
;
2362 struct cat_entry
*cep
;
2365 if (list
->realentries
>= list
->maxentries
)
2366 return (0); /* stop */
2368 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2370 switch(rec
->recordType
) {
2371 case kHFSPlusFolderRecord
:
2372 case kHFSPlusFileRecord
:
2373 case kHFSFolderRecord
:
2374 case kHFSFileRecord
:
2375 if (parentcnid
!= state
->dir_cnid
) {
2376 state
->error
= ENOENT
;
2377 return (0); /* stop */
2381 state
->error
= ENOENT
;
2382 return (0); /* stop */
2385 /* Hide the private system directories and journal files */
2386 if (parentcnid
== kHFSRootFolderID
) {
2387 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2388 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2389 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2390 list
->skipentries
++;
2391 return (1); /* continue */
2394 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2395 (rec
->recordType
== kHFSPlusFileRecord
) &&
2396 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2397 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2398 list
->skipentries
++;
2399 return (1); /* continue */
2403 cep
= &list
->entry
[list
->realentries
++];
2405 if (state
->stdhfs
) {
2406 struct HFSPlusCatalogFile cnoderec
;
2407 HFSPlusCatalogKey
* pluskey
;
2410 promoteattr(hfsmp
, rec
, &cnoderec
);
2411 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2413 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2414 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2415 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2416 FREE(pluskey
, M_TEMP
);
2418 if (rec
->recordType
== kHFSFileRecord
) {
2419 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2421 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2422 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2423 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2424 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2427 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2428 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2429 isadir(rec
), &cep
->ce_desc
);
2431 if (rec
->recordType
== kHFSPlusFileRecord
) {
2432 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2433 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2434 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2435 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2437 /* Save link reference for later processing. */
2438 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2439 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2440 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2441 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2442 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2443 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2444 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2449 return (list
->realentries
< list
->maxentries
);
2453 * Pack a cat_entrylist buffer with attributes from the catalog
2455 * Note: index is zero relative
2459 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
2463 BTreeIterator
* iterator
;
2464 struct readattr_state state
;
2472 ce_list
->realentries
= 0;
2474 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2475 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2476 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2478 state
.hfsmp
= hfsmp
;
2479 state
.list
= ce_list
;
2480 state
.dir_cnid
= parentcnid
;
2481 state
.stdhfs
= std_hfs
;
2484 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2485 bzero(iterator
, sizeof(*iterator
));
2486 key
= (CatalogKey
*)&iterator
->key
;
2488 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2489 index
= dirhint
->dh_index
+ 1;
2492 * Attempt to build a key from cached filename
2494 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2495 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2501 * If the last entry wasn't cached then position the btree iterator
2503 if ((index
== 0) || !have_key
) {
2505 * Position the iterator at the directory's thread record.
2506 * (i.e. just before the first entry)
2508 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2509 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2511 result
= MacToVFSError(result
);
2516 * Iterate until we reach the entry just
2517 * before the one we want to start with.
2520 struct position_state ps
;
2525 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2528 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2529 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2533 result
= MacToVFSError(result
);
2535 result
= MacToVFSError(result
);
2541 /* Fill list with entries starting at iterator->key. */
2542 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2543 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2546 result
= state
.error
;
2547 else if (ce_list
->realentries
== 0)
2550 result
= MacToVFSError(result
);
2556 * Resolve any hard links.
2558 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2559 struct FndrFileInfo
*fip
;
2560 struct cat_entry
*cep
;
2561 struct HFSPlusCatalogFile filerec
;
2565 cep
= &ce_list
->entry
[i
];
2566 if (cep
->ce_attr
.ca_linkref
== 0)
2569 /* Note: Finder info is still in Big Endian */
2570 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
2572 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2573 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2574 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2577 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2578 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
2579 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
2580 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
2583 if (isfilelink
|| isdirlink
) {
2584 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
2586 /* Repack entry from inode record. */
2587 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
2588 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
2589 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
2590 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
2591 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
2595 FREE(iterator
, M_TEMP
);
2597 return MacToVFSError(result
);
2600 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
2603 * Callback to pack directory entries.
2604 * Called with packdirentry_state for each item in a directory.
2607 /* Hard link information collected during cat_getdirentries. */
2610 user_addr_t dirent_addr
;
2612 typedef struct linkinfo linkinfo_t
;
2614 /* State information for the getdirentries_callback function. */
2615 struct packdirentry_state
{
2617 u_int32_t cbs_parentID
;
2618 u_int32_t cbs_index
;
2620 ExtendedVCB
* cbs_hfsmp
;
2623 int32_t cbs_maxlinks
;
2624 linkinfo_t
* cbs_linkinfo
;
2625 struct cat_desc
* cbs_desc
;
2626 u_int8_t
* cbs_namebuf
;
2628 * The following fields are only used for NFS readdir, which
2629 * uses the next file id as the seek offset of each entry.
2631 struct direntry
* cbs_direntry
;
2632 struct direntry
* cbs_prevdirentry
;
2633 u_int32_t cbs_previlinkref
;
2634 Boolean cbs_hasprevdirentry
;
2639 * getdirentries callback for HFS Plus directories.
2642 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2643 struct packdirentry_state
*state
)
2645 struct hfsmount
*hfsmp
;
2646 const CatalogName
*cnp
;
2649 struct dirent catent
;
2650 struct direntry
* entry
= NULL
;
2652 u_int32_t ilinkref
= 0;
2653 u_int32_t curlinkref
= 0;
2656 u_int8_t type
= DT_UNKNOWN
;
2657 u_int8_t is_mangled
= 0;
2658 u_int8_t is_link
= 0;
2660 user_addr_t uiobase
= USER_ADDR_NULL
;
2665 Boolean stop_after_pack
= false;
2667 hfsmp
= state
->cbs_hfsmp
;
2668 curID
= ckp
->hfsPlus
.parentID
;
2670 /* We're done when parent directory changes */
2671 if (state
->cbs_parentID
!= curID
) {
2673 * If the parent ID is different from curID this means we've hit
2674 * the EOF for the directory. To help future callers, we mark
2675 * the cbs_eof boolean. However, we should only mark the EOF
2676 * boolean if we're about to return from this function.
2678 * This is because this callback function does its own uiomove
2679 * to get the data to userspace. If we set the boolean before determining
2680 * whether or not the current entry has enough room to write its
2681 * data to userland, we could fool the callers of this catalog function
2682 * into thinking they've hit EOF earlier than they really would have.
2683 * In that case, we'd know that we have more entries to process and
2684 * send to userland, but we didn't have enough room.
2686 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
2687 * about to return and won't write any new data back
2688 * to userland. In the stop_after_pack case, we'll set this boolean
2689 * regardless, so it's slightly safer to let that logic mark the boolean,
2690 * especially since it's closer to the return of this function.
2693 if (state
->cbs_extended
) {
2694 /* The last record has not been returned yet, so we
2695 * want to stop after packing the last item
2697 if (state
->cbs_hasprevdirentry
) {
2698 stop_after_pack
= true;
2700 state
->cbs_eof
= true;
2701 state
->cbs_result
= ENOENT
;
2702 return (0); /* stop */
2705 state
->cbs_eof
= true;
2706 state
->cbs_result
= ENOENT
;
2707 return (0); /* stop */
2711 if (state
->cbs_extended
) {
2712 entry
= state
->cbs_direntry
;
2713 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
2714 maxnamelen
= NAME_MAX
;
2716 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2717 maxnamelen
= NAME_MAX
;
2720 if (state
->cbs_extended
&& stop_after_pack
) {
2721 /* The last item returns a non-zero invalid cookie */
2724 switch(crp
->recordType
) {
2725 case kHFSPlusFolderRecord
:
2727 cnid
= crp
->hfsPlusFolder
.folderID
;
2728 /* Hide our private system directories. */
2729 if (curID
== kHFSRootFolderID
) {
2730 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2731 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2736 case kHFSPlusFileRecord
:
2737 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
2738 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
2739 cnid
= crp
->hfsPlusFile
.fileID
;
2741 * When a hardlink link is encountered save its link ref.
2743 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2744 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
2745 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2746 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2747 /* If link ref is inode's file id then use it directly. */
2748 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
2749 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2751 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
2754 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2755 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
2756 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2757 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
2758 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2759 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2760 /* A directory's link resolves to a directory. */
2762 /* A directory's link ref is always inode's file id. */
2763 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2766 /* Hide the journal files */
2767 if ((curID
== kHFSRootFolderID
) &&
2768 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
2769 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
2770 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
2775 return (0); /* stop */
2778 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
2780 namelen
= cnp
->ustr
.length
;
2782 * For MacRoman encoded names, assume that its ascii and
2783 * convert it directly in an attempt to avoid the more
2784 * expensive utf8_encodestr conversion.
2786 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
2789 const u_int16_t
*chp
;
2791 chp
= &cnp
->ustr
.unicode
[0];
2792 for (i
= 0; i
< (int)namelen
; ++i
) {
2794 if (ch
> 0x007f || ch
== 0x0000) {
2795 /* Perform expensive utf8_encodestr conversion */
2798 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
2800 nameptr
[namelen
] = '\0';
2804 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
2805 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
2808 /* Check result returned from encoding the filename to utf8 */
2809 if (result
== ENAMETOOLONG
) {
2811 * If we were looking at a catalog record for a hardlink (not the inode),
2812 * then we want to use its link ID as opposed to the inode ID for
2813 * a mangled name. For all other cases, they are the same. Note that
2814 * due to the way directory hardlinks are implemented, the actual link
2815 * is going to be counted as a file record, so we can catch both
2818 cnid_t linkid
= cnid
;
2820 linkid
= crp
->hfsPlusFile
.fileID
;
2823 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
2824 cnp
->ustr
.unicode
, maxnamelen
+ 1,
2825 (ByteCount
*)&namelen
, nameptr
, linkid
);
2830 if (state
->cbs_extended
) {
2832 * The index is 1 relative and includes "." and ".."
2834 * Also stuff the cnid in the upper 32 bits of the cookie.
2835 * The cookie is stored to the previous entry, which will
2836 * be packed and copied this time
2838 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
2839 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
2840 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
2842 catent
.d_type
= type
;
2843 catent
.d_namlen
= namelen
;
2844 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2846 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
2848 catent
.d_fileno
= cnid
;
2849 uioaddr
= (caddr_t
) &catent
;
2852 /* Save current base address for post processing of hard-links. */
2853 if (ilinkref
|| state
->cbs_previlinkref
) {
2854 uiobase
= uio_curriovbase(state
->cbs_uio
);
2856 /* If this entry won't fit then we're done */
2857 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
2858 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
2859 return (0); /* stop */
2862 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
2863 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2864 if (state
->cbs_result
== 0) {
2867 /* Remember previous entry */
2868 state
->cbs_desc
->cd_cnid
= cnid
;
2869 if (type
== DT_DIR
) {
2870 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2872 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2874 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2875 state
->cbs_desc
->cd_namelen
= 0;
2878 state
->cbs_desc
->cd_encoding
= xxxx
;
2881 state
->cbs_desc
->cd_namelen
= namelen
;
2882 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
2884 /* Store unmangled name for the directory hint else it will
2885 * restart readdir at the last location again
2887 u_int8_t
*new_nameptr
;
2889 size_t tmp_namelen
= 0;
2891 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
2892 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
2893 cnp
->ustr
.length
* sizeof(UniChar
),
2895 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
2896 result
= utf8_encodestr(cnp
->ustr
.unicode
,
2897 cnp
->ustr
.length
* sizeof(UniChar
),
2898 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
2900 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2901 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
2903 FREE(new_nameptr
, M_TEMP
);
2906 if (state
->cbs_hasprevdirentry
) {
2907 curlinkref
= ilinkref
; /* save current */
2908 ilinkref
= state
->cbs_previlinkref
; /* use previous */
2911 * Record any hard links for post processing.
2913 if ((ilinkref
!= 0) &&
2914 (state
->cbs_result
== 0) &&
2915 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
2916 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
2917 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
2918 state
->cbs_nlinks
++;
2920 if (state
->cbs_hasprevdirentry
) {
2921 ilinkref
= curlinkref
; /* restore current */
2925 /* Fill the direntry to be used the next time */
2926 if (state
->cbs_extended
) {
2927 if (stop_after_pack
) {
2928 state
->cbs_eof
= true;
2929 return (0); /* stop */
2931 entry
->d_type
= type
;
2932 entry
->d_namlen
= namelen
;
2933 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
2935 /* File number = 0 means skip entry */
2936 entry
->d_fileno
= 0;
2938 entry
->d_fileno
= cnid
;
2940 /* swap the current and previous entry */
2941 struct direntry
* tmp
;
2942 tmp
= state
->cbs_direntry
;
2943 state
->cbs_direntry
= state
->cbs_prevdirentry
;
2944 state
->cbs_prevdirentry
= tmp
;
2945 state
->cbs_hasprevdirentry
= true;
2946 state
->cbs_previlinkref
= ilinkref
;
2949 /* Continue iteration if there's room */
2950 return (state
->cbs_result
== 0 &&
2951 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2955 * getdirentries callback for standard HFS (non HFS+) directories.
2958 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2959 struct packdirentry_state
*state
)
2961 struct hfsmount
*hfsmp
;
2962 const CatalogName
*cnp
;
2965 struct dirent catent
;
2967 u_int8_t type
= DT_UNKNOWN
;
2974 hfsmp
= state
->cbs_hfsmp
;
2976 curID
= ckp
->hfs
.parentID
;
2978 /* We're done when parent directory changes */
2979 if (state
->cbs_parentID
!= curID
) {
2980 state
->cbs_result
= ENOENT
;
2981 return (0); /* stop */
2984 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2985 maxnamelen
= NAME_MAX
;
2987 switch(crp
->recordType
) {
2988 case kHFSFolderRecord
:
2990 cnid
= crp
->hfsFolder
.folderID
;
2992 case kHFSFileRecord
:
2994 cnid
= crp
->hfsFile
.fileID
;
2997 return (0); /* stop */
3000 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3001 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
3003 * When an HFS name cannot be encoded with the current
3004 * volume encoding we use MacRoman as a fallback.
3007 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
3009 catent
.d_type
= type
;
3010 catent
.d_namlen
= namelen
;
3011 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3012 catent
.d_fileno
= cnid
;
3013 uioaddr
= (caddr_t
) &catent
;
3015 /* If this entry won't fit then we're done */
3016 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3017 return (0); /* stop */
3020 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3021 if (state
->cbs_result
== 0) {
3024 /* Remember previous entry */
3025 state
->cbs_desc
->cd_cnid
= cnid
;
3026 if (type
== DT_DIR
) {
3027 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3029 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3031 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3032 state
->cbs_desc
->cd_namelen
= 0;
3034 state
->cbs_desc
->cd_namelen
= namelen
;
3035 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3038 /* Continue iteration if there's room */
3039 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3043 * Pack a uio buffer with directory entries from the catalog
3047 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
3048 uio_t uio
, int extended
, int * items
, int * eofflag
)
3051 BTreeIterator
* iterator
;
3053 struct packdirentry_state state
;
3061 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3064 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3067 * Get a buffer for link info array, btree iterator and a direntry:
3069 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
3070 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3072 bufsize
+= 2*sizeof(struct direntry
);
3074 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3075 bzero(buffer
, bufsize
);
3077 state
.cbs_extended
= extended
;
3078 state
.cbs_hasprevdirentry
= false;
3079 state
.cbs_previlinkref
= 0;
3080 state
.cbs_nlinks
= 0;
3081 state
.cbs_maxlinks
= maxlinks
;
3082 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3084 * We need to set cbs_eof to false regardless of whether or not the
3085 * control flow is actually in the extended case, since we use this
3086 * field to track whether or not we've returned EOF from the iterator function.
3088 state
.cbs_eof
= false;
3090 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3091 key
= (CatalogKey
*)&iterator
->key
;
3093 index
= dirhint
->dh_index
+ 1;
3095 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3096 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3099 * Attempt to build a key from cached filename
3101 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3102 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3103 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3108 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3110 * Position the iterator at the directory's thread record.
3111 * (i.e. just before the first entry)
3113 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3114 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3115 iterator
->hint
.index
= 0;
3120 * If the last entry wasn't cached then position the btree iterator
3124 * Position the iterator at the directory's thread record.
3125 * (i.e. just before the first entry)
3127 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3128 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3130 result
= MacToVFSError(result
);
3134 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3137 * Iterate until we reach the entry just
3138 * before the one we want to start with.
3141 struct position_state ps
;
3146 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3149 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3150 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3154 result
= MacToVFSError(result
);
3156 result
= MacToVFSError(result
);
3162 state
.cbs_index
= index
;
3163 state
.cbs_hfsmp
= hfsmp
;
3164 state
.cbs_uio
= uio
;
3165 state
.cbs_desc
= &dirhint
->dh_desc
;
3166 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3167 state
.cbs_result
= 0;
3168 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3170 /* Use a temporary buffer to hold intermediate descriptor names. */
3171 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3172 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3173 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3174 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3175 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3178 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3180 enum BTreeIterationOperations op
;
3181 if (extended
&& index
!= 0 && have_key
)
3182 op
= kBTreeCurrentRecord
;
3184 op
= kBTreeNextRecord
;
3187 * Process as many entries as possible starting at iterator->key.
3189 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
3190 result
= BTIterateRecords(fcb
, op
, iterator
,
3191 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3193 result
= BTIterateRecords(fcb
, op
, iterator
,
3194 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3196 /* For extended calls, every call to getdirentries_callback()
3197 * transfers the previous directory entry found to the user
3198 * buffer. Therefore when BTIterateRecords reaches the end of
3199 * Catalog BTree, call getdirentries_callback() again with
3200 * dummy values to copy the last directory entry stored in
3201 * packdirentry_state
3203 if (state
.cbs_extended
&& (result
== fsBTRecordNotFoundErr
)) {
3207 bzero(&ckp
, sizeof(ckp
));
3208 bzero(&crp
, sizeof(crp
));
3210 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3214 /* Note that state.cbs_index is still valid on errors */
3215 *items
= state
.cbs_index
- index
;
3216 index
= state
.cbs_index
;
3219 * Also note that cbs_eof is set in all cases if we ever hit EOF
3220 * during the enumeration by the catalog callback. Mark the directory's hint
3221 * descriptor as having hit EOF.
3223 if (state
.cbs_eof
) {
3224 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3228 /* Finish updating the catalog iterator. */
3229 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3230 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3231 dirhint
->dh_index
= index
- 1;
3233 /* Fix up the name. */
3234 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3235 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3236 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3238 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3239 dirhint
->dh_desc
.cd_namelen
= 0;
3243 * Post process any hard links to get the real file id.
3245 if (state
.cbs_nlinks
> 0) {
3247 user_addr_t address
;
3250 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3251 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3253 /* This assumes that d_ino is always first field. */
3254 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3255 if (address
== (user_addr_t
)0)
3257 if (uio_isuserspace(uio
)) {
3259 ino64_t fileid_64
= (ino64_t
)fileid
;
3260 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3262 (void) copyout(&fileid
, address
, sizeof(fileid
));
3264 } else /* system space */ {
3266 ino64_t fileid_64
= (ino64_t
)fileid
;
3267 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3269 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3275 if (state
.cbs_result
)
3276 result
= state
.cbs_result
;
3278 result
= MacToVFSError(result
);
3280 if (result
== ENOENT
) {
3285 FREE(buffer
, M_TEMP
);
3292 * Callback to establish directory position.
3293 * Called with position_state for each item in a directory.
3296 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3297 struct position_state
*state
)
3301 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
3302 curID
= ckp
->hfs
.parentID
;
3304 curID
= ckp
->hfsPlus
.parentID
;
3306 /* Make sure parent directory didn't change */
3307 if (state
->parentID
!= curID
) {
3308 state
->error
= EINVAL
;
3309 return (0); /* stop */
3312 /* Count this entry */
3313 switch(crp
->recordType
) {
3314 case kHFSPlusFolderRecord
:
3315 case kHFSPlusFileRecord
:
3316 case kHFSFolderRecord
:
3317 case kHFSFileRecord
:
3321 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3322 crp
->recordType
, curID
);
3323 state
->error
= EINVAL
;
3324 return (0); /* stop */
3327 return (state
->count
< state
->index
);
3332 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3334 * The name portion of the key is compared using a 16-bit binary comparison.
3335 * This is called from the b-tree code.
3339 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3341 u_int32_t searchParentID
, trialParentID
;
3344 searchParentID
= searchKey
->parentID
;
3345 trialParentID
= trialKey
->parentID
;
3348 if (searchParentID
> trialParentID
) {
3350 } else if (searchParentID
< trialParentID
) {
3353 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3354 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3355 int length1
= searchKey
->nodeName
.length
;
3356 int length2
= trialKey
->nodeName
.length
;
3360 if (length1
< length2
) {
3363 } else if (length1
> length2
) {
3390 * Compare two standard HFS catalog keys
3392 * Result: +n search key > trial key
3393 * 0 search key = trial key
3394 * -n search key < trial key
3397 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3399 cnid_t searchParentID
, trialParentID
;
3402 searchParentID
= searchKey
->parentID
;
3403 trialParentID
= trialKey
->parentID
;
3405 if (searchParentID
> trialParentID
)
3407 else if (searchParentID
< trialParentID
)
3409 else /* parent dirID's are equal, compare names */
3410 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3417 * Compare two HFS+ catalog keys
3419 * Result: +n search key > trial key
3420 * 0 search key = trial key
3421 * -n search key < trial key
3424 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3426 cnid_t searchParentID
, trialParentID
;
3429 searchParentID
= searchKey
->parentID
;
3430 trialParentID
= trialKey
->parentID
;
3432 if (searchParentID
> trialParentID
) {
3435 else if (searchParentID
< trialParentID
) {
3438 /* parent node ID's are equal, compare names */
3439 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3440 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3442 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3443 searchKey
->nodeName
.length
,
3444 &trialKey
->nodeName
.unicode
[0],
3445 trialKey
->nodeName
.length
);
3453 * buildkey - build a Catalog b-tree key from a cnode descriptor
3456 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3457 HFSPlusCatalogKey
*key
, int retry
)
3459 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3461 size_t unicodeBytes
= 0;
3463 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3464 return (EINVAL
); /* invalid name */
3466 key
->parentID
= descp
->cd_parentcnid
;
3467 key
->nodeName
.length
= 0;
3469 * Convert filename from UTF-8 into Unicode
3472 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3473 utf8_flags
|= UTF_DECOMPOSED
;
3474 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3475 key
->nodeName
.unicode
, &unicodeBytes
,
3476 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3477 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3478 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3480 if (result
!= ENAMETOOLONG
)
3481 result
= EINVAL
; /* name has invalid characters */
3486 * For HFS volumes convert to an HFS compatible key
3488 * XXX need to save the encoding that succeeded
3490 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
3491 HFSCatalogKey hfskey
;
3493 bzero(&hfskey
, sizeof(hfskey
));
3494 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3495 hfskey
.parentID
= key
->parentID
;
3496 hfskey
.nodeName
[0] = 0;
3497 if (key
->nodeName
.length
> 0) {
3499 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3500 key
->nodeName
.length
* 2,
3501 key
->nodeName
.unicode
,
3502 &hfskey
.nodeName
[0], retry
)) != 0) {
3503 if (res
!= ENAMETOOLONG
)
3508 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3510 bcopy(&hfskey
, key
, sizeof(hfskey
));
3517 * Resolve hard link reference to obtain the inode record.
3521 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3523 FSBufferDescriptor btdata
;
3524 struct BTreeIterator
*iterator
;
3525 struct cat_desc idesc
;
3530 BDINIT(btdata
, recp
);
3533 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3534 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
3536 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3537 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
3540 /* Get space for iterator */
3541 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3542 bzero(iterator
, sizeof(*iterator
));
3544 /* Build a descriptor for private dir. */
3545 idesc
.cd_parentcnid
= parentcnid
;
3546 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
3547 idesc
.cd_namelen
= strlen(inodename
);
3550 idesc
.cd_encoding
= 0;
3551 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
3553 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3554 &btdata
, NULL
, NULL
);
3557 /* Make sure there's a reference */
3558 if (recp
->hl_linkCount
== 0)
3559 recp
->hl_linkCount
= 2;
3561 printf("hfs: cat_resolvelink: can't find %s\n", inodename
);
3564 FREE(iterator
, M_TEMP
);
3566 return (result
? ENOENT
: 0);
3570 * Resolve hard link reference to obtain the inode number.
3573 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
3575 struct HFSPlusCatalogFile record
;
3579 * Since we know resolvelinkid is only called from
3580 * cat_getdirentries, we can assume that only file
3581 * hardlinks need to be resolved (cat_getdirentries
3582 * can resolve directory hardlinks in place).
3584 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
3586 if (record
.fileID
== 0)
3589 *ino
= record
.fileID
;
3595 * getkey - get a key from id by doing a thread lookup
3598 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
3600 struct BTreeIterator
* iterator
;
3601 FSBufferDescriptor btdata
;
3604 CatalogRecord
* recp
;
3608 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
3610 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3611 bzero(iterator
, sizeof(*iterator
));
3612 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
3614 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
3615 BDINIT(btdata
, recp
);
3617 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3618 &btdata
, &datasize
, iterator
);
3622 /* Turn thread record into a cnode key (in place) */
3623 switch (recp
->recordType
) {
3624 case kHFSFileThreadRecord
:
3625 case kHFSFolderThreadRecord
:
3626 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
3627 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
3628 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
3631 case kHFSPlusFileThreadRecord
:
3632 case kHFSPlusFolderThreadRecord
:
3633 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
3634 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
3635 (keyp
->hfsPlus
.nodeName
.length
* 2);
3636 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
3645 FREE(iterator
, M_TEMP
);
3648 return MacToVFSError(result
);
3652 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
3653 * null arguments to cat_idlookup instead, but we save around 10% by not building the
3654 * cat_desc here). Both key and attrp must point to real structures.
3656 * The key's parent id is the only part of the key expected to be used by the caller.
3657 * The name portion of the key may not always be valid (ie in the case of a hard link).
3661 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
3665 result
= getkey(hfsmp
, cnid
, key
);
3668 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
3671 * Check for a raw file hardlink inode.
3672 * Fix up the parent id in the key if necessary.
3673 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
3675 if ((result
== 0) &&
3676 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3677 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
3678 cnid_t nextlinkid
= 0;
3679 cnid_t prevlinkid
= 0;
3680 struct cat_desc linkdesc
;
3683 * Pick up the first link in the chain and get a descriptor for it.
3684 * This allows blind bulk access checks to work for hardlinks.
3686 if ((cat_lookuplinkbyid(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
3687 (nextlinkid
!= 0)) {
3688 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
3689 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
3690 cat_releasedesc(&linkdesc
);
3694 return MacToVFSError(result
);
3699 * buildrecord - build a default catalog directory or file record
3702 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
3703 CatalogRecord
*crp
, u_int32_t
*recordSize
)
3705 int type
= attrp
->ca_mode
& S_IFMT
;
3706 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
3709 createtime
= UTCToLocal(createtime
);
3710 if (type
== S_IFDIR
) {
3711 bzero(crp
, sizeof(HFSCatalogFolder
));
3712 crp
->recordType
= kHFSFolderRecord
;
3713 crp
->hfsFolder
.folderID
= cnid
;
3714 crp
->hfsFolder
.createDate
= createtime
;
3715 crp
->hfsFolder
.modifyDate
= createtime
;
3716 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
3717 *recordSize
= sizeof(HFSCatalogFolder
);
3719 bzero(crp
, sizeof(HFSCatalogFile
));
3720 crp
->recordType
= kHFSFileRecord
;
3721 crp
->hfsFile
.fileID
= cnid
;
3722 crp
->hfsFile
.createDate
= createtime
;
3723 crp
->hfsFile
.modifyDate
= createtime
;
3724 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
3725 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
3726 *recordSize
= sizeof(HFSCatalogFile
);
3729 struct HFSPlusBSDInfo
* bsdp
= NULL
;
3731 if (type
== S_IFDIR
) {
3732 crp
->recordType
= kHFSPlusFolderRecord
;
3733 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
3734 crp
->hfsPlusFolder
.valence
= 0;
3735 crp
->hfsPlusFolder
.folderID
= cnid
;
3736 crp
->hfsPlusFolder
.createDate
= createtime
;
3737 crp
->hfsPlusFolder
.contentModDate
= createtime
;
3738 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
3739 crp
->hfsPlusFolder
.accessDate
= createtime
;
3740 crp
->hfsPlusFolder
.backupDate
= 0;
3741 crp
->hfsPlusFolder
.textEncoding
= encoding
;
3742 crp
->hfsPlusFolder
.folderCount
= 0;
3743 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
3744 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
3745 bsdp
->special
.linkCount
= 1;
3746 *recordSize
= sizeof(HFSPlusCatalogFolder
);
3748 crp
->recordType
= kHFSPlusFileRecord
;
3749 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
3750 crp
->hfsPlusFile
.reserved1
= 0;
3751 crp
->hfsPlusFile
.fileID
= cnid
;
3752 crp
->hfsPlusFile
.createDate
= createtime
;
3753 crp
->hfsPlusFile
.contentModDate
= createtime
;
3754 crp
->hfsPlusFile
.accessDate
= createtime
;
3755 crp
->hfsPlusFile
.attributeModDate
= createtime
;
3756 crp
->hfsPlusFile
.backupDate
= 0;
3757 crp
->hfsPlusFile
.textEncoding
= encoding
;
3758 crp
->hfsPlusFile
.reserved2
= 0;
3759 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
3760 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
3761 /* BLK/CHR need to save the device info */
3762 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
3763 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
3765 bsdp
->special
.linkCount
= 1;
3767 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
3768 *recordSize
= sizeof(HFSPlusCatalogFile
);
3770 bsdp
->ownerID
= attrp
->ca_uid
;
3771 bsdp
->groupID
= attrp
->ca_gid
;
3772 bsdp
->fileMode
= attrp
->ca_mode
;
3773 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
3774 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
3780 * builddesc - build a cnode descriptor from an HFS+ key
3783 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
3784 int isdir
, struct cat_desc
*descp
)
3787 unsigned char * nameptr
;
3790 unsigned char tmpbuff
[128];
3792 /* guess a size... */
3793 bufsize
= (3 * key
->nodeName
.length
) + 1;
3794 if (bufsize
>= sizeof(tmpbuff
) - 1) {
3795 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3797 nameptr
= &tmpbuff
[0];
3800 result
= utf8_encodestr(key
->nodeName
.unicode
,
3801 key
->nodeName
.length
* sizeof(UniChar
),
3802 nameptr
, (size_t *)&utf8len
,
3805 if (result
== ENAMETOOLONG
) {
3806 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
3807 key
->nodeName
.length
* sizeof(UniChar
),
3809 FREE(nameptr
, M_TEMP
);
3810 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3812 result
= utf8_encodestr(key
->nodeName
.unicode
,
3813 key
->nodeName
.length
* sizeof(UniChar
),
3814 nameptr
, (size_t *)&utf8len
,
3817 descp
->cd_parentcnid
= key
->parentID
;
3818 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
3819 descp
->cd_namelen
= utf8len
;
3820 descp
->cd_cnid
= cnid
;
3821 descp
->cd_hint
= hint
;
3822 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
3824 descp
->cd_flags
|= CD_ISDIR
;
3825 descp
->cd_encoding
= encoding
;
3826 if (nameptr
!= &tmpbuff
[0]) {
3827 FREE(nameptr
, M_TEMP
);
3834 * getbsdattr - get attributes in bsd format
3838 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
3840 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
3841 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
3843 attrp
->ca_recflags
= crp
->flags
;
3844 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
3845 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
3846 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
3847 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
3848 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
3849 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
3851 if ((bsd
->fileMode
& S_IFMT
) == 0) {
3852 attrp
->ca_flags
= 0;
3853 attrp
->ca_uid
= hfsmp
->hfs_uid
;
3854 attrp
->ca_gid
= hfsmp
->hfs_gid
;
3856 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
3858 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
3860 attrp
->ca_linkcount
= 1;
3863 attrp
->ca_linkcount
= 1; /* may be overridden below */
3865 attrp
->ca_uid
= bsd
->ownerID
;
3866 attrp
->ca_gid
= bsd
->groupID
;
3867 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
3868 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
3869 switch (attrp
->ca_mode
& S_IFMT
) {
3870 case S_IFCHR
: /* fall through */
3872 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
3875 case S_IFDIR
: /* fall through */
3877 /* Pick up the hard link count */
3878 if (bsd
->special
.linkCount
> 0)
3879 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
3884 * Override the permissions as determined by the mount auguments
3885 * in ALMOST the same way unset permissions are treated but keep
3886 * track of whether or not the file or folder is hfs locked
3887 * by leaving the h_pflags field unchanged from what was unpacked
3888 * out of the catalog.
3891 * This code was used to do UID translation with MNT_IGNORE_OWNERS
3892 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
3893 * at the VFS layer, so there is no need to do it here now; this also
3894 * allows VFS to let root see the real UIDs.
3896 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
3897 * attrp->ca_uid = hfsmp->hfs_uid;
3898 * attrp->ca_gid = hfsmp->hfs_gid;
3904 if (!S_ISDIR(attrp
->ca_mode
)) {
3905 attrp
->ca_mode
&= ~S_IFMT
;
3906 attrp
->ca_mode
|= S_IFDIR
;
3908 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
3909 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
3910 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
3912 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3913 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
3914 attrp
->ca_flags
|= UF_HIDDEN
;
3916 /* Keep IMMUTABLE bits in sync with HFS locked flag */
3917 if (crp
->flags
& kHFSFileLockedMask
) {
3918 /* The file's supposed to be locked:
3919 Make sure at least one of the IMMUTABLE bits is set: */
3920 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
3921 attrp
->ca_flags
|= UF_IMMUTABLE
;
3923 /* The file's supposed to be unlocked: */
3924 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
3926 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3927 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
3928 attrp
->ca_flags
|= UF_HIDDEN
;
3929 /* get total blocks (both forks) */
3930 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
3932 /* On HFS+ the ThreadExists flag must always be set. */
3933 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
3934 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
3936 /* Pick up the hardlink first link, if any. */
3937 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
3940 attrp
->ca_fileid
= crp
->fileID
;
3942 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
3946 * promotekey - promote hfs key to hfs plus key
3950 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
3951 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
3953 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
3957 *encoding
= hfsmp
->hfs_encoding
;
3959 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
3960 kHFSPlusMaxFileNameChars
, &uniCount
);
3962 * When an HFS name cannot be encoded with the current
3963 * encoding use MacRoman as a fallback.
3965 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
3967 (void) mac_roman_to_unicode(hfskey
->nodeName
,
3968 keyp
->nodeName
.unicode
,
3969 kHFSPlusMaxFileNameChars
,
3973 keyp
->nodeName
.length
= uniCount
;
3974 keyp
->parentID
= hfskey
->parentID
;
3978 * promotefork - promote hfs fork info to hfs plus
3982 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
3983 int resource
, struct cat_fork
* forkp
)
3985 struct HFSPlusExtentDescriptor
*xp
;
3986 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
3988 bzero(forkp
, sizeof(*forkp
));
3989 xp
= &forkp
->cf_extents
[0];
3991 forkp
->cf_size
= filep
->rsrcLogicalSize
;
3992 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
3993 forkp
->cf_bytesread
= 0;
3994 forkp
->cf_vblocks
= 0;
3995 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
3996 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
3997 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
3998 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
3999 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4000 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4002 forkp
->cf_size
= filep
->dataLogicalSize
;
4003 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4004 forkp
->cf_bytesread
= 0;
4005 forkp
->cf_vblocks
= 0;
4006 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4007 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4008 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4009 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4010 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4011 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4016 * promoteattr - promote standard hfs catalog attributes to hfs plus
4020 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4022 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4024 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4025 const struct HFSCatalogFolder
* folder
;
4027 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4028 crp
->recordType
= kHFSPlusFolderRecord
;
4029 crp
->flags
= folder
->flags
;
4030 crp
->fileID
= folder
->folderID
;
4031 crp
->createDate
= LocalToUTC(folder
->createDate
);
4032 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4033 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4034 crp
->reserved1
= folder
->valence
;
4036 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4038 const struct HFSCatalogFile
* file
;
4040 file
= (const struct HFSCatalogFile
*) dataPtr
;
4041 crp
->recordType
= kHFSPlusFileRecord
;
4042 crp
->flags
= file
->flags
;
4043 crp
->fileID
= file
->fileID
;
4044 crp
->createDate
= LocalToUTC(file
->createDate
);
4045 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4046 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4049 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4050 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4051 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4052 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4054 crp
->textEncoding
= 0;
4055 crp
->attributeModDate
= crp
->contentModDate
;
4056 crp
->accessDate
= crp
->contentModDate
;
4057 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4061 * Build a catalog node thread record from a catalog key
4062 * and return the size of the record.
4065 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4070 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4071 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4073 size
= sizeof(HFSCatalogThread
);
4076 rec
->recordType
= kHFSFolderThreadRecord
;
4078 rec
->recordType
= kHFSFileThreadRecord
;
4079 rec
->parentID
= key
->parentID
;
4080 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4083 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4084 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4086 size
= sizeof(HFSPlusCatalogThread
);
4088 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4090 rec
->recordType
= kHFSPlusFileThreadRecord
;
4092 rec
->parentID
= key
->parentID
;
4093 bcopy(&key
->nodeName
, &rec
->nodeName
,
4094 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4096 /* HFS Plus has varaible sized thread records */
4097 size
-= (sizeof(rec
->nodeName
.unicode
) -
4098 (rec
->nodeName
.length
* sizeof(UniChar
)));
4105 * Build a catalog node thread key.
4108 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4111 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4112 key
->hfs
.reserved
= 0;
4113 key
->hfs
.parentID
= parentID
;
4114 key
->hfs
.nodeName
[0] = 0;
4116 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4117 key
->hfsPlus
.parentID
= parentID
;
4118 key
->hfsPlus
.nodeName
.length
= 0;
4123 * Extract the text encoding from a catalog node record.
4126 getencoding(const CatalogRecord
*crp
)
4130 if (crp
->recordType
== kHFSPlusFolderRecord
)
4131 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4132 else if (crp
->recordType
== kHFSPlusFileRecord
)
4133 encoding
= crp
->hfsPlusFile
.textEncoding
;
4141 * Extract the CNID from a catalog node record.
4144 getcnid(const CatalogRecord
*crp
)
4148 switch (crp
->recordType
) {
4149 case kHFSFolderRecord
:
4150 cnid
= crp
->hfsFolder
.folderID
;
4152 case kHFSFileRecord
:
4153 cnid
= crp
->hfsFile
.fileID
;
4155 case kHFSPlusFolderRecord
:
4156 cnid
= crp
->hfsPlusFolder
.folderID
;
4158 case kHFSPlusFileRecord
:
4159 cnid
= crp
->hfsPlusFile
.fileID
;
4162 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp
);
4170 * Extract the parent ID from a catalog node record.
4173 getparentcnid(const CatalogRecord
*recp
)
4177 switch (recp
->recordType
) {
4178 case kHFSFileThreadRecord
:
4179 case kHFSFolderThreadRecord
:
4180 cnid
= recp
->hfsThread
.parentID
;
4183 case kHFSPlusFileThreadRecord
:
4184 case kHFSPlusFolderThreadRecord
:
4185 cnid
= recp
->hfsPlusThread
.parentID
;
4188 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4196 * Determine if a catalog node record is a directory.
4199 isadir(const CatalogRecord
*crp
)
4201 return (crp
->recordType
== kHFSFolderRecord
||
4202 crp
->recordType
== kHFSPlusFolderRecord
);