2 * Copyright (c) 2000-2015 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 "BTreesInternal.h"
47 #include "BTreesPrivate.h"
48 #include "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 const struct cat_fork
* s_datafork
;
71 const 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 #define HFS_LOOKUP_SYSFILE 0x1 /* If set, allow lookup of system files */
94 #define HFS_LOOKUP_HARDLINK 0x2 /* If set, allow lookup of hard link records and not resolve the hard links */
95 #define HFS_LOOKUP_CASESENSITIVE 0x4 /* If set, verify results of a file/directory record match input case */
96 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
97 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
99 int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
100 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
102 /* Internal catalog support routines */
104 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
105 struct position_state
*state
);
107 static int resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
);
109 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
111 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
112 HFSPlusCatalogKey
*key
, int retry
);
114 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
116 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, u_int32_t
*recordSize
);
118 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
120 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
121 int isdir
, struct cat_desc
*descp
);
123 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
126 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
);
127 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
128 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
131 static cnid_t
getcnid(const CatalogRecord
*crp
);
132 static u_int32_t
getencoding(const CatalogRecord
*crp
);
133 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
135 static int isadir(const CatalogRecord
*crp
);
137 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
139 static int cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
);
141 static int cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
142 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
);
146 /* HFS ID Hashtable Functions */
147 #define IDHASH(hfsmp, inum) (&hfsmp->hfs_idhashtbl[(inum) & hfsmp->hfs_idhash])
149 /* Initialize the HFS ID hash table */
151 hfs_idhash_init (struct hfsmount
*hfsmp
) {
152 /* secured by catalog lock so no lock init needed */
153 hfsmp
->hfs_idhashtbl
= hashinit(HFS_IDHASH_DEFAULT
, M_TEMP
, &hfsmp
->hfs_idhash
);
156 /* Free the HFS ID hash table */
158 hfs_idhash_destroy (struct hfsmount
*hfsmp
) {
159 /* during failed mounts & unmounts */
160 FREE(hfsmp
->hfs_idhashtbl
, M_TEMP
);
165 typedef struct cat_preflightid {
167 LIST_ENTRY(cat_preflightid) id_hash;
171 u_long hfs_idhash; / size of cnid/fileid hash table -1 /
172 LIST_HEAD(idhashhead, cat_preflightid) *hfs_idhashtbl; / base of ID hash /
176 * Check the run-time ID hashtable.
178 * The catalog lock must be held (like other functions in this file).
181 * 1 if the ID is in the hash table.
182 * 0 if the ID is not in the hash table
184 int cat_check_idhash (struct hfsmount
*hfsmp
, cnid_t test_fileid
) {
186 cat_preflightid_t
*preflight
;
189 for (preflight
= IDHASH(hfsmp
, test_fileid
)->lh_first
; preflight
; preflight
= preflight
->id_hash
.le_next
) {
190 if (preflight
->fileid
== test_fileid
) {
199 /* Insert the supplied preflight into the ID hash table */
200 int cat_insert_idhash (struct hfsmount
*hfsmp
, cat_preflightid_t
*preflight
) {
203 LIST_INSERT_HEAD(IDHASH(hfsmp
, (preflight
->fileid
)), preflight
, id_hash
);
210 /* Remove the data structure with the specified ID from the hashtable */
211 int cat_remove_idhash (cat_preflightid_t
*preflight
) {
213 if ((preflight
) && ((preflight
->id_hash
.le_next
|| preflight
->id_hash
.le_prev
))) {
214 LIST_REMOVE (preflight
, id_hash
);
215 preflight
->id_hash
.le_next
= NULL
;
216 preflight
->id_hash
.le_prev
= NULL
;
225 * Acquire a new CNID for use.
227 * This is slightly more complicated than just pulling the value from the
228 * hfsmount data structure. We need to validate that the ID is not in-use
229 * even if we've not wrapped around and that there are not any lingering
230 * or orphaned fileIDs for this ID.
232 * Also validate that there are not any pending insertions into the
233 * catalog by checking the ID hash table.
236 cat_acquire_cnid (struct hfsmount
*hfsmp
, cnid_t
*new_cnid
)
239 struct BTreeIterator
*iterator
;
240 FSBufferDescriptor btdata
;
247 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
249 * Get the next CNID. We can change it since we hold the catalog lock.
252 nextCNID
= hfsmp
->vcbNxtCNID
;
253 if (nextCNID
== 0xFFFFFFFF) {
259 /* don't allow more than one wrap-around */
262 hfs_lock_mount (hfsmp
);
263 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
264 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
265 hfs_unlock_mount (hfsmp
);
270 hfs_note_header_minor_change(hfsmp
);
272 /* First check that there are not any entries pending in the hash table with this ID */
273 if (cat_check_idhash (hfsmp
, nextCNID
)) {
274 /* Someone wants to insert this into the catalog but hasn't done so yet. Skip it */
278 /* Check to see if a thread record exists for the target ID we just got */
279 iterator
= hfs_mallocz(sizeof(*iterator
));
280 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&iterator
->key
);
282 recp
= hfs_malloc(sizeof(CatalogRecord
));
283 BDINIT(btdata
, recp
);
285 result
= BTSearchRecord(hfsmp
->hfs_catalog_cp
->c_datafork
, iterator
, &btdata
, &datasize
, iterator
);
286 hfs_free(recp
, sizeof(CatalogRecord
));
287 hfs_free(iterator
, sizeof(*iterator
));
289 if (result
== btNotFound
) {
290 /* Good. File ID was not in use. Move on to checking EA B-Tree */
291 result
= file_attribute_exist (hfsmp
, nextCNID
);
292 if (result
== EEXIST
) {
293 /* This CNID has orphaned EAs. Skip it and move on to the next one */
298 /* For any other error, return the result */
303 * Now validate that there are no lingering cnodes with this ID. If a cnode
304 * has been removed on-disk (marked C_NOEXISTS), but has not yet been reclaimed,
305 * then it will still have an entry in the cnode hash table. This means that
306 * a subsequent lookup will find THAT entry and believe this one has been deleted
307 * prematurely. If there is a lingering cnode, then just skip this entry and move on.
309 * Note that we pass (existence_only == 1) argument to hfs_chash_snoop.
311 if (!std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
312 if (hfs_chash_snoop (hfsmp
, nextCNID
, 1, NULL
, NULL
) == 0) {
318 * If we get here, then we didn't see any thread records, orphaned EAs,
319 * or stale cnodes. This ID is safe to vend out.
321 *new_cnid
= nextCNID
;
323 else if (result
== noErr
) {
324 /* move on to the next ID */
328 /* For any other situation, just bail out */
337 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, __unused proc_t p
)
342 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
343 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
345 result
= BTReserveSpace(hfsmp
->hfs_catalog_cp
->c_datafork
, ops
, (void*)cookie
);
348 hfs_systemfile_unlock(hfsmp
, lockflags
);
350 return MacToVFSError(result
);
354 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, __unused proc_t p
)
358 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
359 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
361 (void) BTReleaseReserve(hfsmp
->hfs_catalog_cp
->c_datafork
, (void*)cookie
);
364 hfs_systemfile_unlock(hfsmp
, lockflags
);
369 struct hfsmount
*hfsmp
,
370 CatalogRecord
* recp
,
371 struct cat_attr
*attrp
,
372 struct cat_fork
*datafp
,
373 struct cat_fork
*rsrcfp
)
375 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
378 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
382 struct HFSPlusCatalogFile cnoderec
;
384 promoteattr(hfsmp
, recp
, &cnoderec
);
385 getbsdattr(hfsmp
, &cnoderec
, attrp
);
390 bzero(datafp
, sizeof(*datafp
));
394 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
395 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
399 /* Convert the data fork. */
400 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
401 datafp
->cf_new_size
= 0;
402 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
403 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
404 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
405 datafp
->cf_bytesread
=
406 recp
->hfsPlusFile
.dataFork
.clumpSize
*
407 HFSTOVCB(hfsmp
)->blockSize
;
409 datafp
->cf_bytesread
= 0;
411 datafp
->cf_vblocks
= 0;
412 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
413 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
415 /* Convert the resource fork. */
416 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
417 rsrcfp
->cf_new_size
= 0;
418 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
419 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
420 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
421 datafp
->cf_bytesread
=
422 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
423 HFSTOVCB(hfsmp
)->blockSize
;
425 datafp
->cf_bytesread
= 0;
427 rsrcfp
->cf_vblocks
= 0;
428 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
429 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
434 * Convert a raw catalog key and record into an in-core catalog descriptor.
436 * Note: The caller is responsible for releasing the catalog descriptor.
440 struct hfsmount
*hfsmp
,
442 CatalogRecord
* recp
,
443 struct cat_desc
*descp
)
445 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
446 HFSPlusCatalogKey
* pluskey
= NULL
;
452 pluskey
= (HFSPlusCatalogKey
*)key
;
453 encoding
= getencoding(recp
);
457 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
458 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
462 /* Get the CNID before calling builddesc. Need to error check it. */
463 cnid
= getcnid(recp
);
465 /* If ths CNID == 0, it's invalid. Mark as corrupt */
466 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
470 builddesc(pluskey
, cnid
, 0, encoding
, isadir(recp
), descp
);
475 hfs_free(pluskey
, sizeof(*pluskey
));
487 cat_releasedesc(struct cat_desc
*descp
)
489 const u_int8_t
* name
;
494 if ((descp
->cd_flags
& CD_HASBUF
) &&
495 (descp
->cd_nameptr
!= NULL
)) {
496 name
= descp
->cd_nameptr
;
497 descp
->cd_nameptr
= NULL
;
498 descp
->cd_namelen
= 0;
499 vfs_removename((const char *)name
);
501 descp
->cd_nameptr
= NULL
;
502 descp
->cd_namelen
= 0;
503 descp
->cd_flags
&= ~CD_HASBUF
;
507 * These Catalog functions allow access to the HFS Catalog (database).
508 * The catalog b-tree lock must be acquired before calling any of these routines.
512 * cat_lookup - lookup a catalog node using a cnode descriptor
514 * Note: The caller is responsible for releasing the output
515 * catalog descriptor (when supplied outdescp is non-null).
518 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
, int force_casesensitive_lookup
,
519 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
520 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
527 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
528 flags
= force_casesensitive_lookup
? HFS_LOOKUP_CASESENSITIVE
: 0;
530 keyp
= hfs_malloc(sizeof(CatalogKey
));
532 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
536 result
= cat_lookupbykey(hfsmp
, keyp
, flags
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
538 if (result
== ENOENT
) {
540 struct cat_desc temp_desc
;
541 if (outdescp
== NULL
) {
542 bzero(&temp_desc
, sizeof(temp_desc
));
543 outdescp
= &temp_desc
;
545 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
547 *desc_cnid
= outdescp
->cd_cnid
;
549 if (outdescp
== &temp_desc
) {
550 /* Release the local copy of desc */
551 cat_releasedesc(outdescp
);
553 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
554 // make MacRoman key from utf-8
555 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
556 // update desc text encoding so that other catalog ops succeed
560 hfs_free(keyp
, sizeof(*keyp
));
566 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
568 struct BTreeIterator
*iterator
;
569 struct FSBufferDescriptor file_data
;
570 struct HFSCatalogFile file_rec
;
575 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
578 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
580 iterator
= hfs_mallocz(2 * sizeof(*iterator
));
581 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
585 BDINIT(file_data
, &file_rec
);
586 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
590 if (file_rec
.recordType
!= kHFSFileRecord
) {
595 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
596 struct FSBufferDescriptor thread_data
;
597 struct HFSCatalogThread thread_rec
;
599 file_rec
.flags
|= kHFSThreadExistsMask
;
600 BDINIT(thread_data
, &thread_rec
);
601 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
602 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
604 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
608 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
609 (void) BTFlushPath(fcb
);
612 (void) BTFlushPath(fcb
);
613 hfs_free(iterator
, 2 * sizeof(*iterator
));
615 return MacToVFSError(result
);
620 * cat_findname - obtain a descriptor from cnid
622 * Only a thread lookup is performed.
624 * Note: The caller is responsible for releasing the output
625 * catalog descriptor (when supplied outdescp is non-null).
629 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
631 struct BTreeIterator
* iterator
;
632 FSBufferDescriptor btdata
;
634 CatalogRecord
* recp
;
641 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
646 iterator
= hfs_malloc(sizeof(*iterator
));
647 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
648 iterator
->hint
.nodeNum
= 0;
650 recp
= hfs_malloc(sizeof(CatalogRecord
));
651 BDINIT(btdata
, recp
);
653 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
657 /* Turn thread record into a cnode key (in place). */
658 switch (recp
->recordType
) {
661 case kHFSFolderThreadRecord
:
664 case kHFSFileThreadRecord
:
665 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
666 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
670 case kHFSPlusFolderThreadRecord
:
673 case kHFSPlusFileThreadRecord
:
674 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
675 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
676 (keyp
->hfsPlus
.nodeName
.length
* 2);
685 HFSPlusCatalogKey
* pluskey
= NULL
;
688 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
689 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
690 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
691 hfs_free(pluskey
, sizeof(*pluskey
));
695 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
699 hfs_free(recp
, sizeof(*recp
));
700 hfs_free(iterator
, sizeof(*iterator
));
702 return MacToVFSError(result
);
706 * cat_idlookup - lookup a catalog node using a cnode id
708 * Note: The caller is responsible for releasing the output
709 * catalog descriptor (when supplied outdescp is non-null).
712 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
, int wantrsrc
,
713 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
715 struct BTreeIterator
* iterator
;
716 FSBufferDescriptor btdata
;
719 CatalogRecord
* recp
;
723 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
725 iterator
= hfs_mallocz(sizeof(*iterator
));
726 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
728 recp
= hfs_malloc(sizeof(CatalogRecord
));
729 BDINIT(btdata
, recp
);
731 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
732 &btdata
, &datasize
, iterator
);
736 /* Turn thread record into a cnode key (in place) */
737 switch (recp
->recordType
) {
740 case kHFSFileThreadRecord
:
741 case kHFSFolderThreadRecord
:
742 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
744 /* check for NULL name */
745 if (keyp
->hfs
.nodeName
[0] == 0) {
750 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
754 case kHFSPlusFileThreadRecord
:
755 case kHFSPlusFolderThreadRecord
:
756 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
758 /* check for NULL name */
759 if (keyp
->hfsPlus
.nodeName
.length
== 0) {
764 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
765 (keyp
->hfsPlus
.nodeName
.length
* 2);
773 result
= cat_lookupbykey(hfsmp
, keyp
,
774 ((allow_system_files
!= 0) ? HFS_LOOKUP_SYSFILE
: 0),
775 0, wantrsrc
, outdescp
, attrp
, forkp
, NULL
);
776 /* No corresponding file/folder record found for a thread record,
777 * mark the volume inconsistent.
779 if (result
== 0 && outdescp
) {
780 cnid_t dcnid
= outdescp
->cd_cnid
;
782 * Just for sanity's case, let's make sure that
783 * the key in the thread matches the key in the record.
786 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
791 hfs_free(recp
, sizeof(*recp
));
792 hfs_free(iterator
, sizeof(*iterator
));
794 return MacToVFSError(result
);
799 * cat_lookupmangled - lookup a catalog node using a mangled name
802 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
803 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
808 u_int8_t utf8
[NAME_MAX
+ 1];
810 u_int16_t unicode
[kHFSPlusMaxFileNameChars
+ 1];
816 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
817 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
820 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
821 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
822 fileID
== hfsmp
->hfs_jnlfileid
||
823 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
827 result
= cat_idlookup(hfsmp
, fileID
, 0, 0, outdescp
, attrp
, forkp
);
830 /* It must be in the correct directory */
831 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
835 * Compare the mangled version of file name looked up from the
836 * disk with the mangled name provided by the user. Note that
837 * this comparison is case-sensitive, which should be fine
838 * since we're trying to prevent user space from constructing
839 * a mangled name that differs from the one they'd get from the
842 result
= utf8_decodestr(outdescp
->cd_nameptr
, outdescp
->cd_namelen
,
843 unicode
, &unicodelen
, sizeof(unicode
), ':', 0);
847 result
= ConvertUnicodeToUTF8Mangled(unicodelen
, unicode
,
848 sizeof(utf8
), &utf8len
, utf8
, fileID
);
850 ((u_int16_t
)descp
->cd_namelen
!= utf8len
) ||
851 (bcmp(descp
->cd_nameptr
, utf8
, utf8len
) != 0)) {
858 cat_releasedesc(outdescp
);
864 * cat_lookupbykey - lookup a catalog node using a cnode key
867 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
868 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
870 struct BTreeIterator
* iterator
;
871 FSBufferDescriptor btdata
;
872 CatalogRecord
* recp
;
878 u_int32_t encoding
= 0;
881 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
883 recp
= hfs_malloc(sizeof(CatalogRecord
));
884 BDINIT(btdata
, recp
);
885 iterator
= hfs_mallocz(sizeof(*iterator
));
886 iterator
->hint
.nodeNum
= hint
;
887 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
889 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
890 &btdata
, &datasize
, iterator
);
894 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
895 cnid
= getcnid(recp
);
897 /* CNID of 0 is invalid. Mark as corrupt */
898 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
904 parentid
= keyp
->hfsPlus
.parentID
;
907 encoding
= getencoding(recp
);
908 hint
= iterator
->hint
.nodeNum
;
910 /* Hide the journal files (if any) */
911 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
912 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
913 !(flags
& HFS_LOOKUP_SYSFILE
)) {
914 result
= HFS_ERESERVEDNAME
;
918 if (!std_hfs
&& !(hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)) {
919 /* Make sure the case of the file was correct if requested */
920 if (flags
& HFS_LOOKUP_CASESENSITIVE
) {
921 if (0 != cat_binarykeycompare(&keyp
->hfsPlus
, (HFSPlusCatalogKey
*)&iterator
->key
)) {
922 result
= HFS_ERESERVEDNAME
;
929 * When a hardlink link is encountered, auto resolve it.
931 * The catalog record will change, and possibly its type.
935 && (recp
->recordType
== kHFSPlusFileRecord
)
936 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_itime
) ||
937 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
941 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
942 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
944 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
945 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
946 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
949 if ((isfilelink
|| isdirlink
) && !(flags
& HFS_LOOKUP_HARDLINK
)) {
950 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
951 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
957 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
959 /* Update the inode number for this hard link */
960 attrp
->ca_linkref
= ilink
;
964 * Set kHFSHasLinkChainBit for hard links, and reset it for all
965 * other items. Also set linkCount to 1 for regular files.
967 * Due to some bug (rdar://8505977), some regular files can have
968 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
969 * are not really hard links. The runtime code should not consider
970 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
971 * and linkCount for regular file before we vend it out. This might
972 * also result in repairing the bad files on disk, if the corresponding
973 * file is modified and updated on disk.
976 /* This is a hard link and the link count bit was not set */
977 if (!(attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
978 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp
->vcbVN
, cnid
, ilink
);
979 attrp
->ca_recflags
|= kHFSHasLinkChainMask
;
982 /* Make sure that this non-hard link (regular) record is not
983 * an inode record that was looked up and we do not end up
984 * reseting the hard link bit on it.
986 if ((parentid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
987 (parentid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
988 /* This is not a hard link or inode and the link count bit was set */
989 if (attrp
->ca_recflags
& kHFSHasLinkChainMask
) {
990 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp
->vcbVN
, cnid
);
991 attrp
->ca_recflags
&= ~kHFSHasLinkChainMask
;
993 /* This is a regular file and the link count was more than 1 */
994 if (S_ISREG(attrp
->ca_mode
) && (attrp
->ca_linkcount
> 1)) {
995 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp
->vcbVN
, cnid
, attrp
->ca_linkcount
);
996 attrp
->ca_linkcount
= 1;
1003 struct HFSPlusCatalogFile cnoderec
;
1005 promoteattr(hfsmp
, recp
, &cnoderec
);
1006 getbsdattr(hfsmp
, &cnoderec
, attrp
);
1010 if (forkp
!= NULL
) {
1012 bzero(forkp
, sizeof(*forkp
));
1016 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
1019 else if (wantrsrc
) {
1020 /* Convert the resource fork. */
1021 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
1022 forkp
->cf_new_size
= 0;
1023 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1024 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1025 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1026 forkp
->cf_bytesread
=
1027 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
1028 HFSTOVCB(hfsmp
)->blockSize
;
1030 forkp
->cf_bytesread
= 0;
1032 forkp
->cf_vblocks
= 0;
1033 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
1034 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1037 u_int32_t validblks
;
1039 /* Convert the data fork. */
1040 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
1041 forkp
->cf_new_size
= 0;
1042 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
1043 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1044 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1045 forkp
->cf_bytesread
=
1046 recp
->hfsPlusFile
.dataFork
.clumpSize
*
1047 HFSTOVCB(hfsmp
)->blockSize
;
1049 forkp
->cf_bytesread
= 0;
1051 forkp
->cf_vblocks
= 0;
1052 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
1053 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1055 /* Validate the fork's resident extents. */
1057 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1058 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
1059 /* Suppress any bad extents so a remove can succeed. */
1060 forkp
->cf_extents
[i
].startBlock
= 0;
1061 forkp
->cf_extents
[i
].blockCount
= 0;
1062 /* Disable writes */
1063 if (attrp
!= NULL
) {
1064 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
1067 validblks
+= forkp
->cf_extents
[i
].blockCount
;
1070 /* Adjust for any missing blocks. */
1071 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
1075 * This is technically a volume corruption.
1076 * If the total number of blocks calculated by iterating + summing
1077 * the extents in the resident extent records, is less than that
1078 * which is reported in the catalog entry, we should force a fsck.
1079 * Only modifying ca_blocks here is not guaranteed to make it out
1080 * to disk; it is a runtime-only field.
1082 * Note that we could have gotten into this state if we had invalid ranges
1083 * that existed in borrowed blocks that somehow made it out to disk.
1084 * The cnode's on disk block count should never be greater
1085 * than that which is in its extent records.
1088 (void) hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1090 forkp
->cf_blocks
= validblks
;
1091 if (attrp
!= NULL
) {
1092 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1094 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
1095 if (psize
< forkp
->cf_size
) {
1096 forkp
->cf_size
= psize
;
1102 if (descp
!= NULL
) {
1103 HFSPlusCatalogKey
* pluskey
= NULL
;
1106 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
1110 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1111 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
1116 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
1120 hfs_free(pluskey
, sizeof(*pluskey
));
1126 if (desc_cnid
!= NULL
) {
1130 hfs_free(iterator
, sizeof(*iterator
));
1131 hfs_free(recp
, sizeof(*recp
));
1133 return MacToVFSError(result
);
1138 * cat_create - create a node in the catalog
1139 * using MacRoman encoding
1141 * NOTE: both the catalog file and attribute file locks must
1142 * be held before calling this function.
1144 * The caller is responsible for releasing the output
1145 * catalog descriptor (when supplied outdescp is non-null).
1148 cat_create(struct hfsmount
*hfsmp
, cnid_t new_fileid
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1149 struct cat_desc
*out_descp
)
1153 FSBufferDescriptor btdata
;
1157 u_int32_t encoding
= kTextEncodingMacRoman
;
1160 modeformat
= attrp
->ca_mode
& S_IFMT
;
1162 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1163 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1165 /* The caller is expected to reserve a CNID before calling this function! */
1167 /* Get space for iterator, key and data */
1168 bto
= hfs_malloc(sizeof(struct btobj
));
1169 bto
->iterator
.hint
.nodeNum
= 0;
1171 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
1176 * Insert the thread record first
1178 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
1179 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
1180 S_ISDIR(attrp
->ca_mode
));
1181 btdata
.bufferAddress
= &bto
->data
;
1182 btdata
.itemSize
= datalen
;
1183 btdata
.itemCount
= 1;
1185 /* Caller asserts the following:
1186 * 1) this CNID is not in use by any orphaned EAs
1187 * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
1188 * 3) There are no thread or catalog records for this ID
1190 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
1191 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1198 * Now insert the file/directory record
1200 buildrecord(attrp
, new_fileid
, std_hfs
, encoding
, &bto
->data
, &datalen
);
1201 btdata
.bufferAddress
= &bto
->data
;
1202 btdata
.itemSize
= datalen
;
1203 btdata
.itemCount
= 1;
1205 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
1207 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1209 if (result
== btExists
)
1212 /* Back out the thread record */
1213 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
1214 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
1215 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
1216 /* Error on deleting extra thread record, mark
1217 * volume inconsistent
1219 printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid
, hfsmp
->vcbVN
);
1220 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1227 * Insert was successful, update name, parent and volume
1229 if (out_descp
!= NULL
) {
1230 HFSPlusCatalogKey
* pluskey
= NULL
;
1233 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1237 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1238 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1242 builddesc(pluskey
, new_fileid
, bto
->iterator
.hint
.nodeNum
,
1243 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1246 hfs_free(pluskey
, sizeof(*pluskey
));
1251 attrp
->ca_fileid
= new_fileid
;
1254 (void) BTFlushPath(fcb
);
1255 hfs_free(bto
, sizeof(*bto
));
1257 return MacToVFSError(result
);
1262 * cnode_rename - rename a catalog node
1264 * Assumes that the target's directory exists.
1266 * Order of B-tree operations:
1267 * 1. BTSearchRecord(from_cnode, &data);
1268 * 2. BTInsertRecord(to_cnode, &data);
1269 * 3. BTDeleteRecord(from_cnode);
1270 * 4. BTDeleteRecord(from_thread);
1271 * 5. BTInsertRecord(to_thread);
1273 * Note: The caller is responsible for releasing the output
1274 * catalog descriptor (when supplied out_cdp is non-null).
1278 struct hfsmount
* hfsmp
,
1279 struct cat_desc
* from_cdp
,
1280 struct cat_desc
* todir_cdp
,
1281 struct cat_desc
* to_cdp
,
1282 struct cat_desc
* out_cdp
)
1284 struct BTreeIterator
* to_iterator
= NULL
;
1285 struct BTreeIterator
* from_iterator
= NULL
;
1286 FSBufferDescriptor btdata
;
1287 CatalogRecord
* recp
= NULL
;
1288 HFSPlusCatalogKey
* to_key
;
1295 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1298 u_int32_t encoding
= 0;
1300 vcb
= HFSTOVCB(hfsmp
);
1301 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1302 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1304 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1307 from_iterator
= hfs_mallocz(sizeof(*from_iterator
));
1308 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1311 to_iterator
= hfs_mallocz(sizeof(*to_iterator
));
1312 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1315 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1316 recp
= hfs_malloc(sizeof(CatalogRecord
));
1317 BDINIT(btdata
, recp
);
1320 * When moving a directory, make sure its a valid move.
1322 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1323 struct BTreeIterator
*dir_iterator
= NULL
;
1325 cnid_t cnid
= from_cdp
->cd_cnid
;
1326 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1328 /* First check the obvious ones */
1329 if (cnid
== fsRtDirID
||
1330 cnid
== to_cdp
->cd_parentcnid
||
1335 /* now allocate the dir_iterator */
1336 dir_iterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
1339 * Traverse destination path all the way back to the root
1340 * making sure that source directory is not encountered.
1343 while (pathcnid
> fsRtDirID
) {
1344 buildthreadkey(pathcnid
, std_hfs
, (CatalogKey
*)&dir_iterator
->key
);
1345 result
= BTSearchRecord(fcb
, dir_iterator
, &btdata
, &datasize
, NULL
);
1347 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1350 pathcnid
= getparentcnid(recp
);
1351 if (pathcnid
== cnid
|| pathcnid
== 0) {
1353 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1357 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1361 * Step 1: Find cnode data at old location
1363 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1364 &datasize
, from_iterator
);
1366 if (std_hfs
|| (result
!= btNotFound
))
1369 struct cat_desc temp_desc
;
1371 /* Probably the node has mangled name */
1372 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1376 /* The file has mangled name. Search the cnode data using full name */
1377 bzero(from_iterator
, sizeof(*from_iterator
));
1378 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1380 cat_releasedesc(&temp_desc
);
1384 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1386 cat_releasedesc(&temp_desc
);
1390 cat_releasedesc(&temp_desc
);
1393 /* Check if the source is directory hard link. We do not change
1394 * directory flag because it is later used to initialize result descp
1398 (recp
->recordType
== kHFSPlusFileRecord
) &&
1399 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1404 * Update the text encoding (on disk and in descriptor),
1405 * using hfs_pickencoding to get the new encoding when available.
1407 * Note that hardlink inodes don't require a text encoding hint.
1410 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1411 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1412 #if !TARGET_OS_EMBEDDED
1413 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1415 encoding
= kTextEncodingMacRoman
;
1417 hfs_setencodingbits(hfsmp
, encoding
);
1418 recp
->hfsPlusFile
.textEncoding
= encoding
;
1420 out_cdp
->cd_encoding
= encoding
;
1424 if (std_hfs
&& !directory
&&
1425 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
)) {
1432 * If the keys are identical then there's nothing left to do!
1434 * update the hint and exit
1437 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1439 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1443 /* Step 2: Insert cnode at new location */
1444 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1445 if (result
== btExists
) {
1446 int fromtype
= recp
->recordType
;
1449 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1450 goto exit
; /* EEXIST */
1452 /* Find cnode data at new location */
1453 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1457 /* Get the CNID after calling searchrecord */
1458 cnid
= getcnid (recp
);
1460 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1465 if ((fromtype
!= recp
->recordType
) ||
1466 (from_cdp
->cd_cnid
!= cnid
)) {
1468 goto exit
; /* EEXIST */
1470 /* The old name is a case variant and must be removed */
1471 result
= BTDeleteRecord(fcb
, from_iterator
);
1475 /* Insert cnode (now that case duplicate is gone) */
1476 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1478 /* Try and restore original before leaving */
1483 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1485 printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err
);
1486 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1492 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1501 /* Step 3: Remove cnode from old location */
1503 result
= BTDeleteRecord(fcb
, from_iterator
);
1505 /* Try and delete new record before leaving */
1510 err
= BTDeleteRecord(fcb
, to_iterator
);
1512 printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err
);
1513 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1519 (void) BTDeleteRecord(fcb
, to_iterator
);
1525 /* #### POINT OF NO RETURN #### */
1528 * Step 4: Remove cnode's old thread record
1530 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1531 (void) BTDeleteRecord(fcb
, from_iterator
);
1534 * Step 5: Insert cnode's new thread record
1535 * (optional for HFS files)
1538 /* For directory hard links, always create a file thread
1539 * record. For everything else, use the directory flag.
1542 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1544 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1546 btdata
.itemSize
= datasize
;
1547 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1548 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1552 HFSPlusCatalogKey
* pluskey
= NULL
;
1555 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1559 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1560 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1562 /* Save the real encoding hint in the Finder Info (field 4). */
1563 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1566 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1567 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1572 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1573 encoding
, directory
, out_cdp
);
1576 hfs_free(pluskey
, sizeof(*pluskey
));
1582 (void) BTFlushPath(fcb
);
1584 hfs_free(from_iterator
, sizeof(*from_iterator
));
1586 hfs_free(to_iterator
, sizeof(*to_iterator
));
1588 hfs_free(recp
, sizeof(*recp
));
1589 return MacToVFSError(result
);
1594 * cat_delete - delete a node from the catalog
1596 * Order of B-tree operations:
1597 * 1. BTDeleteRecord(cnode);
1598 * 2. BTDeleteRecord(thread);
1599 * 3. BTUpdateRecord(parent);
1602 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1605 BTreeIterator
*iterator
;
1610 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1611 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1615 * The root directory cannot be deleted
1616 * A directory must be empty
1617 * A file must be zero length (no blocks)
1619 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1620 descp
->cd_parentcnid
== kHFSRootParentID
)
1623 /* XXX Preflight Missing */
1625 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1626 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1627 iterator
->hint
.nodeNum
= 0;
1630 * Derive a key from either the file ID (for a virtual inode)
1631 * or the descriptor.
1633 if (descp
->cd_namelen
== 0) {
1634 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1635 cnid
= attrp
->ca_fileid
;
1637 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1638 cnid
= descp
->cd_cnid
;
1644 result
= BTDeleteRecord(fcb
, iterator
);
1646 if (std_hfs
|| (result
!= btNotFound
))
1649 struct cat_desc temp_desc
;
1651 /* Probably the node has mangled name */
1652 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1656 /* The file has mangled name. Delete the file using full name */
1657 bzero(iterator
, sizeof(*iterator
));
1658 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1659 cnid
= temp_desc
.cd_cnid
;
1661 cat_releasedesc(&temp_desc
);
1665 result
= BTDeleteRecord(fcb
, iterator
);
1667 cat_releasedesc(&temp_desc
);
1671 cat_releasedesc(&temp_desc
);
1674 /* Delete thread record. On error, mark volume inconsistent */
1675 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1676 if (BTDeleteRecord(fcb
, iterator
)) {
1678 printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid
, hfsmp
->vcbVN
);
1679 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
1684 (void) BTFlushPath(fcb
);
1686 return MacToVFSError(result
);
1691 * cat_update_internal - update the catalog node described by descp
1692 * using the data from attrp and forkp.
1693 * If update_hardlink is true, the hard link catalog record is updated
1694 * and not the inode catalog record.
1697 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1698 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1701 BTreeIterator
* iterator
;
1702 struct update_state state
;
1705 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1707 state
.s_desc
= descp
;
1708 state
.s_attr
= attrp
;
1709 state
.s_datafork
= dataforkp
;
1710 state
.s_rsrcfork
= rsrcforkp
;
1711 state
.s_hfsmp
= hfsmp
;
1713 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1714 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1717 * For open-deleted files we need to do a lookup by cnid
1718 * (using thread rec).
1720 * For hard links and if not requested by caller, the target
1721 * of the update is the inode itself (not the link record)
1722 * so a lookup by fileid (i.e. thread rec) is needed.
1724 if ((update_hardlink
== false) &&
1725 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1726 (descp
->cd_namelen
== 0) ||
1727 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1728 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1730 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1735 /* Pass a node hint */
1736 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1738 result
= BTUpdateRecord(fcb
, iterator
,
1739 (IterateCallBackProcPtr
)catrec_update
, &state
);
1743 /* Update the node hint. */
1744 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1747 (void) BTFlushPath(fcb
);
1749 return MacToVFSError(result
);
1753 * cat_update - update the catalog node described by descp
1754 * using the data from attrp and forkp.
1757 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1758 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1760 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1764 * catrec_update - Update the fields of a catalog record
1765 * This is called from within BTUpdateRecord.
1768 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1770 struct cat_desc
*descp
;
1771 struct cat_attr
*attrp
;
1772 const struct cat_fork
*forkp
;
1773 struct hfsmount
*hfsmp
;
1776 descp
= state
->s_desc
;
1777 attrp
= state
->s_attr
;
1778 hfsmp
= state
->s_hfsmp
;
1779 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1781 switch (crp
->recordType
) {
1784 case kHFSFolderRecord
: {
1785 HFSCatalogFolder
*dir
;
1787 dir
= (struct HFSCatalogFolder
*)crp
;
1788 /* Do a quick sanity check */
1789 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1790 (dir
->folderID
!= descp
->cd_cnid
))
1791 return (btNotFound
);
1792 dir
->valence
= attrp
->ca_entries
;
1793 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1794 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1795 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1796 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1797 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1800 case kHFSFileRecord
: {
1801 HFSCatalogFile
*file
;
1804 file
= (struct HFSCatalogFile
*)crp
;
1805 /* Do a quick sanity check */
1806 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1807 (file
->fileID
!= attrp
->ca_fileid
))
1808 return (btNotFound
);
1809 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1810 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1811 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1812 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1813 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1814 if (state
->s_rsrcfork
) {
1815 forkp
= state
->s_rsrcfork
;
1816 file
->rsrcLogicalSize
= forkp
->cf_size
;
1817 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1818 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1819 file
->rsrcExtents
[i
].startBlock
=
1820 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1821 file
->rsrcExtents
[i
].blockCount
=
1822 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1825 if (state
->s_datafork
) {
1826 forkp
= state
->s_datafork
;
1827 file
->dataLogicalSize
= forkp
->cf_size
;
1828 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1829 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1830 file
->dataExtents
[i
].startBlock
=
1831 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1832 file
->dataExtents
[i
].blockCount
=
1833 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1837 /* Synchronize the lock state */
1838 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1839 file
->flags
|= kHFSFileLockedMask
;
1841 file
->flags
&= ~kHFSFileLockedMask
;
1846 case kHFSPlusFolderRecord
: {
1847 HFSPlusCatalogFolder
*dir
;
1849 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1850 /* Do a quick sanity check */
1851 if (dir
->folderID
!= attrp
->ca_fileid
) {
1852 printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir
->folderID
, attrp
->ca_fileid
, hfsmp
->vcbVN
);
1853 return (btNotFound
);
1855 dir
->flags
= attrp
->ca_recflags
;
1856 dir
->valence
= attrp
->ca_entries
;
1857 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1858 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1859 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1860 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1861 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1862 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1863 /* Note: directory hardlink inodes don't require a text encoding hint. */
1864 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1865 dir
->textEncoding
= descp
->cd_encoding
;
1867 dir
->folderCount
= attrp
->ca_dircount
;
1868 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1870 * Update the BSD Info if it was already initialized on
1871 * disk or if the runtime values have been modified.
1873 * If the BSD info was already initialized, but
1874 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1875 * probably different than what was on disk. We don't want
1876 * to overwrite the on-disk values (so if we turn off
1877 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1878 * This way, we can still change fields like the mode or
1879 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1881 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1882 * won't change the uid or gid from their defaults. So, if
1883 * the BSD info wasn't set, and the runtime values are not
1884 * default, then what changed was the mode or flags. We
1885 * have to set the uid and gid to something, so use the
1886 * supplied values (which will be default), which has the
1887 * same effect as creating a new file while
1888 * MNT_UNKNOWNPERMISSIONS is set.
1890 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1891 (attrp
->ca_flags
!= 0) ||
1892 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1893 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1894 ((attrp
->ca_mode
& ALLPERMS
) !=
1895 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1896 if ((dir
->bsdInfo
.fileMode
== 0) ||
1897 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1898 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1899 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1901 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1902 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1903 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1904 /* A directory hardlink has a link count. */
1905 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1906 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1911 case kHFSPlusFileRecord
: {
1912 HFSPlusCatalogFile
*file
;
1915 file
= (struct HFSPlusCatalogFile
*)crp
;
1916 /* Do a quick sanity check */
1917 if (file
->fileID
!= attrp
->ca_fileid
)
1918 return (btNotFound
);
1919 file
->flags
= attrp
->ca_recflags
;
1920 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1921 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1922 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1923 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1924 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1925 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1927 * Note: file hardlink inodes don't require a text encoding
1928 * hint, but they do have a first link value.
1930 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1931 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1933 file
->textEncoding
= descp
->cd_encoding
;
1935 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1937 * Update the BSD Info if it was already initialized on
1938 * disk or if the runtime values have been modified.
1940 * If the BSD info was already initialized, but
1941 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1942 * probably different than what was on disk. We don't want
1943 * to overwrite the on-disk values (so if we turn off
1944 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1945 * This way, we can still change fields like the mode or
1946 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1948 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1949 * won't change the uid or gid from their defaults. So, if
1950 * the BSD info wasn't set, and the runtime values are not
1951 * default, then what changed was the mode or flags. We
1952 * have to set the uid and gid to something, so use the
1953 * supplied values (which will be default), which has the
1954 * same effect as creating a new file while
1955 * MNT_UNKNOWNPERMISSIONS is set.
1957 * Do not modify bsdInfo for directory hard link records.
1958 * They are set during creation and are not modifiable, so just
1961 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1962 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1963 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1966 ((file
->bsdInfo
.fileMode
!= 0) ||
1967 (attrp
->ca_flags
!= 0) ||
1968 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1969 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1970 ((attrp
->ca_mode
& ALLPERMS
) !=
1971 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1972 if ((file
->bsdInfo
.fileMode
== 0) ||
1973 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1974 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1975 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1977 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1978 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1979 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1981 if (state
->s_rsrcfork
) {
1982 forkp
= state
->s_rsrcfork
;
1983 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1984 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1985 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1986 sizeof(HFSPlusExtentRecord
));
1987 /* Push blocks read to disk */
1988 file
->resourceFork
.clumpSize
=
1989 howmany(forkp
->cf_bytesread
, blksize
);
1991 if (state
->s_datafork
) {
1992 forkp
= state
->s_datafork
;
1993 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1994 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1995 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1996 sizeof(HFSPlusExtentRecord
));
1997 /* Push blocks read to disk */
1998 file
->dataFork
.clumpSize
=
1999 howmany(forkp
->cf_bytesread
, blksize
);
2002 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
2003 (file
->resourceFork
.extents
[0].startBlock
==
2004 file
->dataFork
.extents
[0].startBlock
)) {
2005 panic("hfs: catrec_update: rsrc fork == data fork");
2008 /* Synchronize the lock state */
2009 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
2010 file
->flags
|= kHFSFileLockedMask
;
2012 file
->flags
&= ~kHFSFileLockedMask
;
2014 /* Push out special field if necessary */
2015 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
2016 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
2020 * Protect against the degenerate case where the descriptor contains the
2021 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
2022 * the linkcount was greater than 1 (the default value), then it must have become
2023 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
2025 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
2026 (file
->hl_linkCount
> 1)) {
2027 file
->hl_linkCount
= attrp
->ca_linkcount
;
2033 return (btNotFound
);
2038 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
2039 * catalog btree of given cnid by walking up the parent chain till it reaches
2040 * either the root folder, or the private metadata directory for storing
2041 * directory hard links. This function updates the corresponding in-core
2042 * cnode, if any, and the directory record in the catalog btree.
2043 * On success, returns zero. On failure, returns non-zero value.
2046 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
2050 struct cat_desc desc
;
2051 struct cat_attr attr
;
2053 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
2054 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
2055 /* Update the bit in corresponding cnode, if any, in the hash.
2056 * If the cnode has the bit already set, stop the traversal.
2058 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
2063 /* Update the catalog record on disk if either cnode was not
2064 * found in the hash, or if a cnode was found and the cnode
2065 * did not have the bit set previously.
2067 retval
= hfs_start_transaction(hfsmp
);
2071 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2073 /* Look up our catalog folder record */
2074 retval
= cat_idlookup(hfsmp
, cnid
, 0, 0, &desc
, &attr
, NULL
);
2076 hfs_systemfile_unlock(hfsmp
, lockflags
);
2077 hfs_end_transaction(hfsmp
);
2081 /* Update the bit in the catalog record */
2082 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
2083 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
2085 hfs_systemfile_unlock(hfsmp
, lockflags
);
2086 hfs_end_transaction(hfsmp
);
2087 cat_releasedesc(&desc
);
2091 hfs_systemfile_unlock(hfsmp
, lockflags
);
2092 hfs_end_transaction(hfsmp
);
2094 cnid
= desc
.cd_parentcnid
;
2095 cat_releasedesc(&desc
);
2101 /* This function traverses the parent directory hierarchy from the given
2102 * directory to one level below root directory and checks if any of its
2104 * 1. A directory hard link.
2105 * 2. The 'pointed at' directory.
2106 * If any of these conditions fail or an internal error is encountered
2107 * during look up of the catalog record, this function returns non-zero value.
2110 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
2112 HFSPlusCatalogKey
*keyp
;
2114 FSBufferDescriptor btdata
;
2115 HFSPlusCatalogFolder folder
;
2121 BDINIT(btdata
, &folder
);
2122 ip
= hfs_malloc(sizeof(*ip
));
2123 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
2124 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2126 while (cnid
!= kHFSRootParentID
) {
2127 /* Check if the 'pointed at' directory is an ancestor */
2128 if (pointed_at_cnid
== cnid
) {
2132 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
2133 printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2134 invalid
= 1; /* On errors, assume an invalid parent */
2137 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
2138 printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2139 invalid
= 1; /* On errors, assume an invalid parent */
2142 /* Check if this ancestor is a directory hard link */
2143 if (folder
.flags
& kHFSHasLinkChainMask
) {
2147 cnid
= keyp
->parentID
;
2149 hfs_free(ip
, sizeof(*ip
));
2155 * update_siblinglinks_callback - update a link's chain
2158 struct linkupdate_state
{
2165 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
2167 HFSPlusCatalogFile
*file
;
2169 if (crp
->recordType
!= kHFSPlusFileRecord
) {
2170 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
2171 return (btNotFound
);
2174 file
= (struct HFSPlusCatalogFile
*)crp
;
2175 if (file
->flags
& kHFSHasLinkChainMask
) {
2176 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
2177 file
->hl_prevLinkID
= state
->prevlinkid
;
2179 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
2180 file
->hl_nextLinkID
= state
->nextlinkid
;
2183 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
2189 * cat_update_siblinglinks - update a link's chain
2192 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
2195 BTreeIterator
* iterator
;
2196 struct linkupdate_state state
;
2199 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2200 state
.filelinkid
= linkfileid
;
2201 state
.prevlinkid
= prevlinkid
;
2202 state
.nextlinkid
= nextlinkid
;
2204 /* Create an iterator for use by us temporarily */
2205 iterator
= hfs_mallocz(sizeof(*iterator
));
2207 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
2209 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
2210 (void) BTFlushPath(fcb
);
2212 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid
, hfsmp
->vcbVN
);
2215 hfs_free(iterator
, sizeof(*iterator
));
2216 return MacToVFSError(result
);
2220 * cat_lookuplink - lookup a link by it's name
2223 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2226 BTreeIterator
* iterator
;
2227 struct FSBufferDescriptor btdata
;
2228 struct HFSPlusCatalogFile file
;
2231 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2233 /* Create an iterator for use by us temporarily */
2234 iterator
= hfs_mallocz(sizeof(*iterator
));
2236 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2239 BDINIT(btdata
, &file
);
2241 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2244 if (file
.recordType
!= kHFSPlusFileRecord
) {
2248 *linkfileid
= file
.fileID
;
2250 if (file
.flags
& kHFSHasLinkChainMask
) {
2251 *prevlinkid
= file
.hl_prevLinkID
;
2252 *nextlinkid
= file
.hl_nextLinkID
;
2258 hfs_free(iterator
, sizeof(*iterator
));
2259 return MacToVFSError(result
);
2264 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2267 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2270 BTreeIterator
* iterator
;
2271 struct FSBufferDescriptor btdata
;
2272 struct HFSPlusCatalogFile file
;
2275 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2277 /* Create an iterator for use by us temporarily */
2278 iterator
= hfs_mallocz(sizeof(*iterator
));
2280 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2283 BDINIT(btdata
, &file
);
2285 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2288 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2289 if (file
.flags
& kHFSHasLinkChainMask
) {
2292 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2294 /* directory inodes don't have a chain (its in an EA) */
2295 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2296 result
= ENOLINK
; /* signal to caller to get head of list */
2297 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2299 *nextlinkid
= file
.hl_firstLinkID
;
2301 *prevlinkid
= file
.hl_prevLinkID
;
2302 *nextlinkid
= file
.hl_nextLinkID
;
2309 hfs_free(iterator
, sizeof(*iterator
));
2310 return MacToVFSError(result
);
2315 * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
2318 cat_lookup_lastlink(struct hfsmount
*hfsmp
, cnid_t linkfileid
,
2319 cnid_t
*lastlink
, struct cat_desc
*cdesc
)
2322 BTreeIterator
* iterator
;
2323 struct FSBufferDescriptor btdata
;
2324 struct HFSPlusCatalogFile file
;
2328 cnid_t currentlink
= linkfileid
;
2330 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2332 /* Create an iterator for use by us temporarily */
2333 iterator
= hfs_malloc(sizeof(*iterator
));
2335 while ((foundlast
== 0) && (itercount
< HFS_LINK_MAX
)) {
2337 bzero(iterator
, sizeof(*iterator
));
2339 if ((result
= getkey(hfsmp
, currentlink
, (CatalogKey
*)&iterator
->key
))) {
2342 BDINIT(btdata
, &file
);
2344 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2348 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2349 if (file
.flags
& kHFSHasLinkChainMask
) {
2352 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2354 * The raw inode for a directory hardlink doesn't have a chain.
2355 * Its link information lives in an EA.
2357 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2358 /* We don't iterate to find the oldest directory hardlink. */
2362 else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2363 /* Raw inode for file hardlink (the base inode) */
2364 currentlink
= file
.hl_firstLinkID
;
2367 * One minor special-casing here is necessary.
2368 * If our ID brought us to the raw hardlink inode, and it does
2369 * not have any siblings, then it's an open-unlinked file, and we
2370 * should not proceed any further.
2372 if (currentlink
== 0) {
2378 /* Otherwise, this item's parent is a legitimate directory in the namespace */
2379 if (file
.hl_nextLinkID
== 0) {
2380 /* If nextLinkID is 0, then we found the end; no more hardlinks */
2382 *lastlink
= currentlink
;
2384 * Since we had to construct a catalog key to do this lookup
2385 * we still hold it in-hand. We might as well use it to build
2386 * the descriptor that the caller asked for.
2388 builddesc ((HFSPlusCatalogKey
*)&iterator
->key
, currentlink
, 0, 0, 0, cdesc
);
2392 currentlink
= file
.hl_nextLinkID
;
2396 /* Sorry, can't help you without a link chain */
2402 /* If we didn't find what we were looking for, zero out the args */
2403 if (foundlast
== 0) {
2405 bzero (cdesc
, sizeof(struct cat_desc
));
2412 hfs_free(iterator
, sizeof(*iterator
));
2413 return MacToVFSError(result
);
2418 * cat_createlink - create a link in the catalog
2420 * The following cat_attr fields are expected to be set:
2426 * ca_finderinfo (type and creator)
2429 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2430 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2434 FSBufferDescriptor btdata
;
2435 HFSPlusForkData
*rsrcforkp
;
2438 int thread_inserted
= 0;
2439 int alias_allocated
= 0;
2443 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
2445 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2448 * Get the next CNID. Note that we are currently holding catalog lock.
2450 result
= cat_acquire_cnid(hfsmp
, &nextCNID
);
2455 /* Get space for iterator, key and data */
2456 bto
= hfs_malloc(sizeof(struct btobj
));
2457 bto
->iterator
.hint
.nodeNum
= 0;
2458 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2460 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2462 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2467 * Insert the thread record first.
2469 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2470 btdata
.bufferAddress
= &bto
->data
;
2471 btdata
.itemSize
= datalen
;
2472 btdata
.itemCount
= 1;
2474 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2475 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2479 thread_inserted
= 1;
2482 * Now insert the link record.
2484 buildrecord(attrp
, nextCNID
, 0, kTextEncodingMacUnicode
, &bto
->data
, &datalen
);
2486 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2487 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2488 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2490 /* For directory hard links, create alias in resource fork */
2491 if (descp
->cd_flags
& CD_ISDIR
) {
2492 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2495 alias_allocated
= 1;
2497 btdata
.bufferAddress
= &bto
->data
;
2498 btdata
.itemSize
= datalen
;
2499 btdata
.itemCount
= 1;
2501 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2503 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2505 if (result
== btExists
)
2509 if (linkfileid
!= NULL
) {
2510 *linkfileid
= nextCNID
;
2514 if (thread_inserted
) {
2515 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result
), hfsmp
->vcbVN
);
2517 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2518 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2519 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2520 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
2523 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2524 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2525 rsrcforkp
->extents
[0].blockCount
, 0);
2526 rsrcforkp
->extents
[0].startBlock
= 0;
2527 rsrcforkp
->extents
[0].blockCount
= 0;
2530 (void) BTFlushPath(fcb
);
2531 hfs_free(bto
, sizeof(*bto
));
2533 return MacToVFSError(result
);
2536 /* Directory hard links are visible as aliases on pre-Leopard systems and
2537 * as normal directories on Leopard or later. All directory hard link aliases
2538 * have the same resource fork content except for the three uniquely
2539 * identifying values that are updated in the resource fork data when the alias
2540 * is created. The following array is the constant resource fork data used
2541 * only for creating directory hard link aliases.
2543 static const char hfs_dirlink_alias_rsrc
[] = {
2544 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2545 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2546 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2547 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2548 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2549 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2550 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2551 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2552 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2553 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2554 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2555 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2557 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2558 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2560 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2562 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2563 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2564 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2570 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2572 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2575 /* Constants for directory hard link alias */
2577 /* Size of resource fork data array for directory hard link alias */
2578 kHFSAliasSize
= 0x1d0,
2580 /* Volume type for ejectable devices like disk image */
2581 kHFSAliasVolTypeEjectable
= 0x5,
2583 /* Offset for volume create date, in Mac OS local time */
2584 kHFSAliasVolCreateDateOffset
= 0x12a,
2586 /* Offset for the type of volume */
2587 kHFSAliasVolTypeOffset
= 0x130,
2589 /* Offset for folder ID of the parent directory of the directory inode */
2590 kHFSAliasParentIDOffset
= 0x132,
2592 /* Offset for folder ID of the directory inode */
2593 kHFSAliasTargetIDOffset
= 0x176,
2596 /* Create and write an alias that points at the directory represented by given
2597 * inode number on the same volume. Directory hard links are visible as
2598 * aliases in pre-Leopard systems and this function creates these aliases.
2600 * Note: This code is very specific to creating alias for the purpose
2601 * of directory hard links only, and should not be generalized.
2604 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2612 HFSPlusForkData
*rsrcforkp
;
2616 rsrcforkp
= &(crp
->resourceFork
);
2618 blksize
= hfsmp
->blockSize
;
2619 blkcount
= howmany(kHFSAliasSize
, blksize
);
2620 sectorsize
= hfsmp
->hfs_logical_block_size
;
2621 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2623 /* Allocate some disk space for the alias content. */
2624 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2625 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2626 &rsrcforkp
->extents
[0].startBlock
,
2627 &rsrcforkp
->extents
[0].blockCount
);
2628 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2629 if (result
== dskFulErr
) {
2630 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2631 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
| HFS_ALLOC_FLUSHTXN
,
2632 &rsrcforkp
->extents
[0].startBlock
,
2633 &rsrcforkp
->extents
[0].blockCount
);
2636 rsrcforkp
->extents
[0].startBlock
= 0;
2640 /* Acquire a buffer cache block for our block. */
2641 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2642 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2644 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2646 journal_modify_block_start(hfsmp
->jnl
, bp
);
2649 /* Generate alias content */
2650 alias
= (char *)buf_dataptr(bp
);
2651 bzero(alias
, buf_size(bp
));
2652 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2654 /* Set the volume create date, local time in Mac OS format */
2655 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2656 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2658 /* If the file system is on a virtual device like disk image,
2659 * update the volume type to be ejectable device.
2661 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2662 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2663 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2666 /* Set id of the parent of the target directory */
2667 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2668 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2670 /* Set id of the target directory */
2671 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2672 *valptr
= OSSwapHostToBigInt32(inode_num
);
2674 /* Write alias content to disk. */
2676 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2677 } else if ((result
= buf_bwrite(bp
))) {
2681 /* Finish initializing the fork data. */
2682 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2683 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2686 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2687 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2688 rsrcforkp
->extents
[0].startBlock
= 0;
2689 rsrcforkp
->extents
[0].blockCount
= 0;
2690 rsrcforkp
->logicalSize
= 0;
2691 rsrcforkp
->totalBlocks
= 0;
2697 * cat_deletelink - delete a link from the catalog
2700 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2702 struct HFSPlusCatalogFile file
;
2703 struct cat_attr cattr
;
2704 uint32_t totalBlocks
;
2708 bzero(&file
, sizeof (file
));
2709 bzero(&cattr
, sizeof (cattr
));
2710 cattr
.ca_fileid
= descp
->cd_cnid
;
2712 /* Directory links have alias content to remove. */
2713 if (descp
->cd_flags
& CD_ISDIR
) {
2715 BTreeIterator
* iterator
;
2716 struct FSBufferDescriptor btdata
;
2718 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2720 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2721 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2722 iterator
->hint
.nodeNum
= 0;
2724 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2727 BDINIT(btdata
, &file
);
2729 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2734 result
= cat_delete(hfsmp
, descp
, &cattr
);
2736 if ((result
== 0) &&
2737 (descp
->cd_flags
& CD_ISDIR
) &&
2738 (file
.recordType
== kHFSPlusFileRecord
)) {
2740 totalBlocks
= file
.resourceFork
.totalBlocks
;
2742 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2743 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2744 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2748 (void) BlockDeallocate(hfsmp
,
2749 file
.resourceFork
.extents
[i
].startBlock
,
2750 file
.resourceFork
.extents
[i
].blockCount
, 0);
2752 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2753 file
.resourceFork
.extents
[i
].startBlock
= 0;
2754 file
.resourceFork
.extents
[i
].blockCount
= 0;
2763 * Callback to collect directory entries.
2764 * Called with readattr_state for each item in a directory.
2766 struct readattr_state
{
2767 struct hfsmount
*hfsmp
;
2768 struct cat_entrylist
*list
;
2776 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2777 struct readattr_state
*state
)
2779 struct cat_entrylist
*list
= state
->list
;
2780 struct hfsmount
*hfsmp
= state
->hfsmp
;
2781 struct cat_entry
*cep
;
2784 if (list
->realentries
>= list
->maxentries
)
2785 return (0); /* stop */
2787 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2789 switch(rec
->recordType
) {
2790 case kHFSPlusFolderRecord
:
2791 case kHFSPlusFileRecord
:
2793 case kHFSFolderRecord
:
2794 case kHFSFileRecord
:
2796 if (parentcnid
!= state
->dir_cnid
) {
2797 state
->error
= ENOENT
;
2798 state
->reached_eof
= 1;
2799 return (0); /* stop */
2803 state
->error
= ENOENT
;
2804 return (0); /* stop */
2807 /* Hide the private system directories and journal files */
2808 if (parentcnid
== kHFSRootFolderID
) {
2809 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2810 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2811 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2812 list
->skipentries
++;
2813 return (1); /* continue */
2816 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2817 (rec
->recordType
== kHFSPlusFileRecord
) &&
2818 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2819 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2820 list
->skipentries
++;
2821 return (1); /* continue */
2825 cep
= &list
->entry
[list
->realentries
++];
2827 if (state
->stdhfs
== 0) {
2828 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2829 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2830 isadir(rec
), &cep
->ce_desc
);
2832 if (rec
->recordType
== kHFSPlusFileRecord
) {
2833 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2834 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2835 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2836 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2838 /* Save link reference for later processing. */
2839 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2840 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2841 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2842 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2843 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2844 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2845 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2851 struct HFSPlusCatalogFile cnoderec
;
2852 HFSPlusCatalogKey
* pluskey
;
2855 promoteattr(hfsmp
, rec
, &cnoderec
);
2856 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2858 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
2859 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2860 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2861 hfs_free(pluskey
, sizeof(*pluskey
));
2863 if (rec
->recordType
== kHFSFileRecord
) {
2864 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2866 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2867 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2868 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2869 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2874 return (list
->realentries
< list
->maxentries
);
2878 * Pack a cat_entrylist buffer with attributes from the catalog
2880 * Note: index is zero relative
2883 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
, int *reachedeof
)
2887 BTreeIterator
* iterator
;
2888 struct readattr_state state
;
2895 int reached_eof
= 0;
2897 ce_list
->realentries
= 0;
2899 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2900 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2901 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2903 bzero (&state
, sizeof(struct readattr_state
));
2905 state
.hfsmp
= hfsmp
;
2906 state
.list
= ce_list
;
2907 state
.dir_cnid
= parentcnid
;
2908 state
.stdhfs
= std_hfs
;
2911 iterator
= hfs_mallocz(sizeof(*iterator
));
2912 key
= (CatalogKey
*)&iterator
->key
;
2914 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2915 index
= dirhint
->dh_index
+ 1;
2918 * Attempt to build a key from cached filename
2920 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2921 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2927 * If the last entry wasn't cached then position the btree iterator
2929 if ((index
== 0) || !have_key
) {
2931 * Position the iterator at the directory's thread record.
2932 * (i.e. just before the first entry)
2934 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2935 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2937 result
= MacToVFSError(result
);
2942 * Iterate until we reach the entry just
2943 * before the one we want to start with.
2946 struct position_state ps
;
2951 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2954 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2955 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2959 result
= MacToVFSError(result
);
2963 * Note: the index may now point to EOF if the directory
2964 * was modified in between system calls. We will return
2965 * ENOENT from cat_findposition if this is the case, and
2966 * when we bail out with an error, our caller (hfs_readdirattr_internal)
2967 * will suppress the error and indicate EOF to its caller.
2969 result
= MacToVFSError(result
);
2975 /* Fill list with entries starting at iterator->key. */
2976 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2977 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2980 result
= state
.error
;
2981 reached_eof
= state
.reached_eof
;
2983 else if (ce_list
->realentries
== 0) {
2988 result
= MacToVFSError(result
);
2995 * Resolve any hard links.
2997 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2998 struct FndrFileInfo
*fip
;
2999 struct cat_entry
*cep
;
3000 struct HFSPlusCatalogFile filerec
;
3004 cep
= &ce_list
->entry
[i
];
3005 if (cep
->ce_attr
.ca_linkref
== 0)
3008 /* Note: Finder info is still in Big Endian */
3009 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
3011 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3012 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
3013 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
3016 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3017 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
3018 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
3019 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
3022 if (isfilelink
|| isdirlink
) {
3023 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
3025 /* Repack entry from inode record. */
3026 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
3027 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
3028 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
3029 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
3030 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
3035 hfs_free(iterator
, sizeof(*iterator
));
3036 *reachedeof
= reached_eof
;
3037 return MacToVFSError(result
);
3040 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3041 #define MAX_LINKINFO_ENTRIES 3000
3044 * Callback to pack directory entries.
3045 * Called with packdirentry_state for each item in a directory.
3048 /* Hard link information collected during cat_getdirentries. */
3051 user_addr_t dirent_addr
;
3053 typedef struct linkinfo linkinfo_t
;
3055 /* State information for the getdirentries_callback function. */
3056 struct packdirentry_state
{
3057 int cbs_flags
; /* VNODE_READDIR_* flags */
3058 u_int32_t cbs_parentID
;
3059 u_int32_t cbs_index
;
3061 ExtendedVCB
* cbs_hfsmp
;
3064 int32_t cbs_maxlinks
;
3065 linkinfo_t
* cbs_linkinfo
;
3066 struct cat_desc
* cbs_desc
;
3067 u_int8_t
* cbs_namebuf
;
3069 * The following fields are only used for NFS readdir, which
3070 * uses the next file id as the seek offset of each entry.
3072 struct direntry
* cbs_direntry
;
3073 struct direntry
* cbs_prevdirentry
;
3074 u_int32_t cbs_previlinkref
;
3075 Boolean cbs_hasprevdirentry
;
3080 * getdirentries callback for HFS Plus directories.
3083 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3084 struct packdirentry_state
*state
)
3086 struct hfsmount
*hfsmp
;
3087 const CatalogName
*cnp
;
3090 struct dirent catent
;
3091 struct direntry
* entry
= NULL
;
3093 u_int32_t ilinkref
= 0;
3094 u_int32_t curlinkref
= 0;
3097 u_int8_t type
= DT_UNKNOWN
;
3098 u_int8_t is_mangled
= 0;
3099 u_int8_t is_link
= 0;
3101 user_addr_t uiobase
= USER_ADDR_NULL
;
3106 Boolean stop_after_pack
= false;
3108 hfsmp
= state
->cbs_hfsmp
;
3109 curID
= ckp
->hfsPlus
.parentID
;
3111 /* We're done when parent directory changes */
3112 if (state
->cbs_parentID
!= curID
) {
3114 * If the parent ID is different from curID this means we've hit
3115 * the EOF for the directory. To help future callers, we mark
3116 * the cbs_eof boolean. However, we should only mark the EOF
3117 * boolean if we're about to return from this function.
3119 * This is because this callback function does its own uiomove
3120 * to get the data to userspace. If we set the boolean before determining
3121 * whether or not the current entry has enough room to write its
3122 * data to userland, we could fool the callers of this catalog function
3123 * into thinking they've hit EOF earlier than they really would have.
3124 * In that case, we'd know that we have more entries to process and
3125 * send to userland, but we didn't have enough room.
3127 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3128 * about to return and won't write any new data back
3129 * to userland. In the stop_after_pack case, we'll set this boolean
3130 * regardless, so it's slightly safer to let that logic mark the boolean,
3131 * especially since it's closer to the return of this function.
3134 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3135 /* The last record has not been returned yet, so we
3136 * want to stop after packing the last item
3138 if (state
->cbs_hasprevdirentry
) {
3139 stop_after_pack
= true;
3141 state
->cbs_eof
= true;
3142 state
->cbs_result
= ENOENT
;
3143 return (0); /* stop */
3146 state
->cbs_eof
= true;
3147 state
->cbs_result
= ENOENT
;
3148 return (0); /* stop */
3152 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3153 entry
= state
->cbs_direntry
;
3154 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3155 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3157 * The NFS server sometimes needs to make filenames fit in
3158 * NAME_MAX bytes (since its client may not be able to
3159 * handle a longer name). In that case, NFS will ask us
3160 * to mangle the name to keep it short enough.
3162 maxnamelen
= NAME_MAX
+ 1;
3164 maxnamelen
= sizeof(entry
->d_name
);
3167 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3168 maxnamelen
= sizeof(catent
.d_name
);
3171 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3172 /* The last item returns a non-zero invalid cookie */
3175 switch(crp
->recordType
) {
3176 case kHFSPlusFolderRecord
:
3178 cnid
= crp
->hfsPlusFolder
.folderID
;
3179 /* Hide our private system directories. */
3180 if (curID
== kHFSRootFolderID
) {
3181 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3182 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3187 case kHFSPlusFileRecord
:
3188 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3189 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3190 cnid
= crp
->hfsPlusFile
.fileID
;
3192 * When a hardlink link is encountered save its link ref.
3194 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3195 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3196 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3197 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3198 /* If link ref is inode's file id then use it directly. */
3199 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3200 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3202 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3205 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3206 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3207 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3208 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3209 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3210 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3211 /* A directory's link resolves to a directory. */
3213 /* A directory's link ref is always inode's file id. */
3214 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3217 /* Hide the journal files */
3218 if ((curID
== kHFSRootFolderID
) &&
3219 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3220 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3221 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3226 return (0); /* stop */
3229 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3231 namelen
= cnp
->ustr
.length
;
3233 * For MacRoman encoded names (textEncoding == 0), assume that it's ascii
3234 * and convert it directly in an attempt to avoid the more
3235 * expensive utf8_encodestr conversion.
3237 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3240 const u_int16_t
*chp
;
3242 chp
= &cnp
->ustr
.unicode
[0];
3243 for (i
= 0; i
< (int)namelen
; ++i
) {
3245 if (ch
> 0x007f || ch
== 0x0000) {
3246 /* Perform expensive utf8_encodestr conversion */
3249 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3251 nameptr
[namelen
] = '\0';
3255 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3256 nameptr
, &namelen
, maxnamelen
, ':', 0);
3259 /* Check result returned from encoding the filename to utf8 */
3260 if (result
== ENAMETOOLONG
) {
3262 * If we were looking at a catalog record for a hardlink (not the inode),
3263 * then we want to use its link ID as opposed to the inode ID for
3264 * a mangled name. For all other cases, they are the same. Note that
3265 * due to the way directory hardlinks are implemented, the actual link
3266 * is going to be counted as a file record, so we can catch both
3269 cnid_t linkid
= cnid
;
3271 linkid
= crp
->hfsPlusFile
.fileID
;
3274 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3275 cnp
->ustr
.unicode
, maxnamelen
,
3276 (ByteCount
*)&namelen
, nameptr
, linkid
);
3281 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3283 * The index is 1 relative and includes "." and ".."
3285 * Also stuff the cnid in the upper 32 bits of the cookie.
3286 * The cookie is stored to the previous entry, which will
3287 * be packed and copied this time
3289 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3290 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3291 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3293 catent
.d_type
= type
;
3294 catent
.d_namlen
= namelen
;
3295 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3297 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3299 catent
.d_fileno
= cnid
;
3300 uioaddr
= (caddr_t
) &catent
;
3303 /* Save current base address for post processing of hard-links. */
3304 if (ilinkref
|| state
->cbs_previlinkref
) {
3305 uiobase
= uio_curriovbase(state
->cbs_uio
);
3307 /* If this entry won't fit then we're done */
3308 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3309 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3310 return (0); /* stop */
3313 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3314 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3315 if (state
->cbs_result
== 0) {
3318 /* Remember previous entry */
3319 state
->cbs_desc
->cd_cnid
= cnid
;
3320 if (type
== DT_DIR
) {
3321 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3323 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3325 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3326 state
->cbs_desc
->cd_namelen
= 0;
3329 state
->cbs_desc
->cd_encoding
= xxxx
;
3332 state
->cbs_desc
->cd_namelen
= namelen
;
3333 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3335 /* Store unmangled name for the directory hint else it will
3336 * restart readdir at the last location again
3338 u_int8_t
*new_nameptr
;
3340 size_t tmp_namelen
= 0;
3342 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3343 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3344 cnp
->ustr
.length
* sizeof(UniChar
),
3346 new_nameptr
= hfs_malloc(bufsize
);
3347 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3348 cnp
->ustr
.length
* sizeof(UniChar
),
3349 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3351 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3352 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3354 hfs_free(new_nameptr
, bufsize
);
3357 if (state
->cbs_hasprevdirentry
) {
3358 curlinkref
= ilinkref
; /* save current */
3359 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3362 * Record any hard links for post processing.
3364 if ((ilinkref
!= 0) &&
3365 (state
->cbs_result
== 0) &&
3366 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3367 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3368 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3369 state
->cbs_nlinks
++;
3371 if (state
->cbs_hasprevdirentry
) {
3372 ilinkref
= curlinkref
; /* restore current */
3376 /* Fill the direntry to be used the next time */
3377 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3378 if (stop_after_pack
) {
3379 state
->cbs_eof
= true;
3380 return (0); /* stop */
3382 entry
->d_type
= type
;
3383 entry
->d_namlen
= namelen
;
3384 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3386 /* File number = 0 means skip entry */
3387 entry
->d_fileno
= 0;
3389 entry
->d_fileno
= cnid
;
3391 /* swap the current and previous entry */
3392 struct direntry
* tmp
;
3393 tmp
= state
->cbs_direntry
;
3394 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3395 state
->cbs_prevdirentry
= tmp
;
3396 state
->cbs_hasprevdirentry
= true;
3397 state
->cbs_previlinkref
= ilinkref
;
3400 /* Continue iteration if there's room */
3401 return (state
->cbs_result
== 0 &&
3402 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3407 * getdirentries callback for standard HFS (non HFS+) directories.
3410 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3411 struct packdirentry_state
*state
)
3413 struct hfsmount
*hfsmp
;
3414 const CatalogName
*cnp
;
3417 struct dirent catent
;
3419 u_int8_t type
= DT_UNKNOWN
;
3426 hfsmp
= state
->cbs_hfsmp
;
3428 curID
= ckp
->hfs
.parentID
;
3430 /* We're done when parent directory changes */
3431 if (state
->cbs_parentID
!= curID
) {
3432 state
->cbs_result
= ENOENT
;
3433 return (0); /* stop */
3436 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3437 maxnamelen
= sizeof(catent
.d_name
);
3439 switch(crp
->recordType
) {
3440 case kHFSFolderRecord
:
3442 cnid
= crp
->hfsFolder
.folderID
;
3444 case kHFSFileRecord
:
3446 cnid
= crp
->hfsFile
.fileID
;
3449 return (0); /* stop */
3452 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3453 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3455 * When an HFS name cannot be encoded with the current
3456 * volume encoding we use MacRoman as a fallback.
3459 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3461 catent
.d_type
= type
;
3462 catent
.d_namlen
= namelen
;
3463 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3464 catent
.d_fileno
= cnid
;
3465 uioaddr
= (caddr_t
) &catent
;
3467 /* If this entry won't fit then we're done */
3468 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3469 return (0); /* stop */
3472 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3473 if (state
->cbs_result
== 0) {
3476 /* Remember previous entry */
3477 state
->cbs_desc
->cd_cnid
= cnid
;
3478 if (type
== DT_DIR
) {
3479 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3481 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3483 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3484 state
->cbs_desc
->cd_namelen
= 0;
3486 state
->cbs_desc
->cd_namelen
= namelen
;
3487 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3490 /* Continue iteration if there's room */
3491 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3496 * Pack a uio buffer with directory entries from the catalog
3499 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3500 uio_t uio
, int flags
, int * items
, int * eofflag
)
3503 BTreeIterator
* iterator
;
3505 struct packdirentry_state state
;
3514 extended
= flags
& VNODE_READDIR_EXTENDED
;
3516 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3519 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3522 * Get a buffer for link info array, btree iterator and a direntry.
3524 * We impose an cap of 3000 link entries when trying to compute
3525 * the total number of hardlink entries that we'll allow in the
3528 * Note that in the case where there are very few hardlinks,
3529 * this does not restrict or prevent us from vending out as many entries
3530 * as we can to the uio_resid, because the getdirentries callback
3531 * uiomoves the directory entries to the uio itself and does not use
3532 * this MALLOC'd array. It also limits itself to maxlinks of hardlinks.
3535 /* Now compute the maximum link array size */
3536 maxlinks
= MIN (entrycnt
, MAX_LINKINFO_ENTRIES
);
3537 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3540 bufsize
+= 2*sizeof(struct direntry
);
3542 buffer
= hfs_mallocz(bufsize
);
3544 state
.cbs_flags
= flags
;
3545 state
.cbs_hasprevdirentry
= false;
3546 state
.cbs_previlinkref
= 0;
3547 state
.cbs_nlinks
= 0;
3548 state
.cbs_maxlinks
= maxlinks
;
3549 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3551 * We need to set cbs_eof to false regardless of whether or not the
3552 * control flow is actually in the extended case, since we use this
3553 * field to track whether or not we've returned EOF from the iterator function.
3555 state
.cbs_eof
= false;
3557 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3558 key
= (CatalogKey
*)&iterator
->key
;
3560 index
= dirhint
->dh_index
+ 1;
3562 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3563 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3566 * Attempt to build a key from cached filename
3568 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3569 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3570 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3575 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3577 * Position the iterator at the directory's thread record.
3578 * (i.e. just before the first entry)
3580 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3581 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3582 iterator
->hint
.index
= 0;
3587 * If the last entry wasn't cached then position the btree iterator
3591 * Position the iterator at the directory's thread record.
3592 * (i.e. just before the first entry)
3594 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3595 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3597 result
= MacToVFSError(result
);
3601 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3604 * Iterate until we reach the entry just
3605 * before the one we want to start with.
3608 struct position_state ps
;
3613 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3616 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3617 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3621 result
= MacToVFSError(result
);
3623 result
= MacToVFSError(result
);
3624 if (result
== ENOENT
) {
3626 * ENOENT means we've hit the EOF.
3627 * suppress the error, and set the eof flag.
3630 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3638 state
.cbs_index
= index
;
3639 state
.cbs_hfsmp
= hfsmp
;
3640 state
.cbs_uio
= uio
;
3641 state
.cbs_desc
= &dirhint
->dh_desc
;
3642 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3643 state
.cbs_result
= 0;
3644 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3646 /* Use a temporary buffer to hold intermediate descriptor names. */
3647 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3648 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3649 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3650 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3651 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3654 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3656 enum BTreeIterationOperations op
;
3657 if (extended
&& index
!= 0 && have_key
)
3658 op
= kBTreeCurrentRecord
;
3660 op
= kBTreeNextRecord
;
3663 * Process as many entries as possible starting at iterator->key.
3665 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3667 result
= BTIterateRecords(fcb
, op
, iterator
,
3668 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3670 /* For extended calls, every call to getdirentries_callback()
3671 * transfers the previous directory entry found to the user
3672 * buffer. Therefore when BTIterateRecords reaches the end of
3673 * Catalog BTree, call getdirentries_callback() again with
3674 * dummy values to copy the last directory entry stored in
3675 * packdirentry_state
3677 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3681 bzero(&ckp
, sizeof(ckp
));
3682 bzero(&crp
, sizeof(crp
));
3684 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3689 /* HFS (standard) */
3690 result
= BTIterateRecords(fcb
, op
, iterator
,
3691 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3695 /* Note that state.cbs_index is still valid on errors */
3696 *items
= state
.cbs_index
- index
;
3697 index
= state
.cbs_index
;
3700 * Also note that cbs_eof is set in all cases if we ever hit EOF
3701 * during the enumeration by the catalog callback. Mark the directory's hint
3702 * descriptor as having hit EOF.
3705 if (state
.cbs_eof
) {
3706 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3710 /* Finish updating the catalog iterator. */
3711 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3712 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3713 dirhint
->dh_index
= index
- 1;
3715 /* Fix up the name. */
3716 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3717 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3718 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3720 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3721 dirhint
->dh_desc
.cd_namelen
= 0;
3725 * Post process any hard links to get the real file id.
3727 if (state
.cbs_nlinks
> 0) {
3729 user_addr_t address
;
3732 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3733 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3735 /* This assumes that d_ino is always first field. */
3736 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3737 if (address
== (user_addr_t
)0)
3739 if (uio_isuserspace(uio
)) {
3741 ino64_t fileid_64
= (ino64_t
)fileid
;
3742 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3744 (void) copyout(&fileid
, address
, sizeof(fileid
));
3746 } else /* system space */ {
3748 ino64_t fileid_64
= (ino64_t
)fileid
;
3749 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3751 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3757 if (state
.cbs_result
)
3758 result
= state
.cbs_result
;
3760 result
= MacToVFSError(result
);
3762 if (result
== ENOENT
) {
3767 hfs_free(buffer
, bufsize
);
3774 * Callback to establish directory position.
3775 * Called with position_state for each item in a directory.
3778 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3779 struct position_state
*state
)
3783 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3784 curID
= ckp
->hfsPlus
.parentID
;
3788 curID
= ckp
->hfs
.parentID
;
3792 /* Make sure parent directory didn't change */
3793 if (state
->parentID
!= curID
) {
3795 * The parent ID is different from curID this means we've hit
3796 * the EOF for the directory.
3798 state
->error
= ENOENT
;
3799 return (0); /* stop */
3802 /* Count this entry */
3803 switch(crp
->recordType
) {
3804 case kHFSPlusFolderRecord
:
3805 case kHFSPlusFileRecord
:
3807 case kHFSFolderRecord
:
3808 case kHFSFileRecord
:
3813 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3814 crp
->recordType
, curID
);
3815 state
->error
= EINVAL
;
3816 return (0); /* stop */
3819 return (state
->count
< state
->index
);
3824 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3826 * The name portion of the key is compared using a 16-bit binary comparison.
3827 * This is called from the b-tree code.
3830 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3832 u_int32_t searchParentID
, trialParentID
;
3835 searchParentID
= searchKey
->parentID
;
3836 trialParentID
= trialKey
->parentID
;
3839 if (searchParentID
> trialParentID
) {
3841 } else if (searchParentID
< trialParentID
) {
3844 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3845 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3846 int length1
= searchKey
->nodeName
.length
;
3847 int length2
= trialKey
->nodeName
.length
;
3849 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3858 * Compare two standard HFS catalog keys
3860 * Result: +n search key > trial key
3861 * 0 search key = trial key
3862 * -n search key < trial key
3865 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3867 cnid_t searchParentID
, trialParentID
;
3870 searchParentID
= searchKey
->parentID
;
3871 trialParentID
= trialKey
->parentID
;
3873 if (searchParentID
> trialParentID
)
3875 else if (searchParentID
< trialParentID
)
3877 else /* parent dirID's are equal, compare names */
3878 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3886 * Compare two HFS+ catalog keys
3888 * Result: +n search key > trial key
3889 * 0 search key = trial key
3890 * -n search key < trial key
3893 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3895 cnid_t searchParentID
, trialParentID
;
3898 searchParentID
= searchKey
->parentID
;
3899 trialParentID
= trialKey
->parentID
;
3901 if (searchParentID
> trialParentID
) {
3904 else if (searchParentID
< trialParentID
) {
3907 /* parent node ID's are equal, compare names */
3908 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3909 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3911 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3912 searchKey
->nodeName
.length
,
3913 &trialKey
->nodeName
.unicode
[0],
3914 trialKey
->nodeName
.length
);
3922 * buildkey - build a Catalog b-tree key from a cnode descriptor
3925 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3926 HFSPlusCatalogKey
*key
, int retry
)
3928 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3929 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3931 size_t unicodeBytes
= 0;
3937 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3938 return (EINVAL
); /* invalid name */
3940 key
->parentID
= descp
->cd_parentcnid
;
3941 key
->nodeName
.length
= 0;
3943 * Convert filename from UTF-8 into Unicode
3946 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3947 utf8_flags
|= UTF_DECOMPOSED
;
3948 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3949 key
->nodeName
.unicode
, &unicodeBytes
,
3950 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3951 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3952 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3954 if (result
!= ENAMETOOLONG
)
3955 result
= EINVAL
; /* name has invalid characters */
3961 * For HFS volumes convert to an HFS compatible key
3963 * XXX need to save the encoding that succeeded
3966 HFSCatalogKey hfskey
;
3968 bzero(&hfskey
, sizeof(hfskey
));
3969 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3970 hfskey
.parentID
= key
->parentID
;
3971 hfskey
.nodeName
[0] = 0;
3972 if (key
->nodeName
.length
> 0) {
3974 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3975 key
->nodeName
.length
* 2,
3976 key
->nodeName
.unicode
,
3977 &hfskey
.nodeName
[0], retry
)) != 0) {
3978 if (res
!= ENAMETOOLONG
)
3983 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3985 bcopy(&hfskey
, key
, sizeof(hfskey
));
3994 * Resolve hard link reference to obtain the inode record.
3997 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3999 FSBufferDescriptor btdata
;
4000 struct BTreeIterator
*iterator
;
4001 struct cat_desc idesc
;
4006 BDINIT(btdata
, recp
);
4009 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4010 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4012 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4013 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4016 /* Get space for iterator */
4017 iterator
= hfs_mallocz(sizeof(*iterator
));
4019 /* Build a descriptor for private dir. */
4020 idesc
.cd_parentcnid
= parentcnid
;
4021 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4022 idesc
.cd_namelen
= strlen(inodename
);
4025 idesc
.cd_encoding
= 0;
4026 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4028 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4029 &btdata
, NULL
, NULL
);
4032 /* Make sure there's a reference */
4033 if (recp
->hl_linkCount
== 0)
4034 recp
->hl_linkCount
= 2;
4036 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4039 hfs_free(iterator
, sizeof(*iterator
));
4041 return (result
? ENOENT
: 0);
4045 * Resolve hard link reference to obtain the inode number.
4048 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4050 struct HFSPlusCatalogFile record
;
4054 * Since we know resolvelinkid is only called from
4055 * cat_getdirentries, we can assume that only file
4056 * hardlinks need to be resolved (cat_getdirentries
4057 * can resolve directory hardlinks in place).
4059 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4061 if (record
.fileID
== 0)
4064 *ino
= record
.fileID
;
4070 * getkey - get a key from id by doing a thread lookup
4073 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4075 struct BTreeIterator
* iterator
;
4076 FSBufferDescriptor btdata
;
4079 CatalogRecord
* recp
;
4083 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4085 iterator
= hfs_mallocz(sizeof(*iterator
));
4086 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4088 recp
= hfs_malloc(sizeof(CatalogRecord
));
4089 BDINIT(btdata
, recp
);
4091 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4092 &btdata
, &datasize
, iterator
);
4096 /* Turn thread record into a cnode key (in place) */
4097 switch (recp
->recordType
) {
4100 case kHFSFileThreadRecord
:
4101 case kHFSFolderThreadRecord
:
4102 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4103 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4104 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4108 case kHFSPlusFileThreadRecord
:
4109 case kHFSPlusFolderThreadRecord
:
4110 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4111 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4112 (keyp
->hfsPlus
.nodeName
.length
* 2);
4113 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4122 hfs_free(iterator
, sizeof(*iterator
));
4123 hfs_free(recp
, sizeof(*recp
));
4125 return MacToVFSError(result
);
4129 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4130 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4131 * cat_desc here). Both key and attrp must point to real structures.
4133 * The key's parent id is the only part of the key expected to be used by the caller.
4134 * The name portion of the key may not always be valid (ie in the case of a hard link).
4137 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4141 result
= getkey(hfsmp
, cnid
, key
);
4144 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4147 * Check for a raw file hardlink inode.
4148 * Fix up the parent id in the key if necessary.
4149 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4151 if ((result
== 0) &&
4152 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4153 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4154 cnid_t nextlinkid
= 0;
4155 cnid_t prevlinkid
= 0;
4156 struct cat_desc linkdesc
;
4159 * Pick up the first link in the chain and get a descriptor for it.
4160 * This allows blind bulk access checks to work for hardlinks.
4162 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4163 (nextlinkid
!= 0)) {
4164 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4165 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4166 cat_releasedesc(&linkdesc
);
4170 return MacToVFSError(result
);
4175 * buildrecord - build a default catalog directory or file record
4178 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4179 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4181 int type
= attrp
->ca_mode
& S_IFMT
;
4182 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4185 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4187 if (type
== S_IFDIR
) {
4188 crp
->recordType
= kHFSPlusFolderRecord
;
4189 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4190 crp
->hfsPlusFolder
.valence
= 0;
4191 crp
->hfsPlusFolder
.folderID
= cnid
;
4192 crp
->hfsPlusFolder
.createDate
= createtime
;
4193 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4194 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4195 crp
->hfsPlusFolder
.accessDate
= createtime
;
4196 crp
->hfsPlusFolder
.backupDate
= 0;
4197 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4198 crp
->hfsPlusFolder
.folderCount
= 0;
4199 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4200 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4201 bsdp
->special
.linkCount
= 1;
4202 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4204 crp
->recordType
= kHFSPlusFileRecord
;
4205 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4206 crp
->hfsPlusFile
.reserved1
= 0;
4207 crp
->hfsPlusFile
.fileID
= cnid
;
4208 crp
->hfsPlusFile
.createDate
= createtime
;
4209 crp
->hfsPlusFile
.contentModDate
= createtime
;
4210 crp
->hfsPlusFile
.accessDate
= createtime
;
4211 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4212 crp
->hfsPlusFile
.backupDate
= 0;
4213 crp
->hfsPlusFile
.textEncoding
= encoding
;
4214 crp
->hfsPlusFile
.reserved2
= 0;
4215 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4216 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4217 /* BLK/CHR need to save the device info */
4218 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4219 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4221 bsdp
->special
.linkCount
= 1;
4223 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4224 *recordSize
= sizeof(HFSPlusCatalogFile
);
4226 bsdp
->ownerID
= attrp
->ca_uid
;
4227 bsdp
->groupID
= attrp
->ca_gid
;
4228 bsdp
->fileMode
= attrp
->ca_mode
;
4229 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4230 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4234 createtime
= UTCToLocal(createtime
);
4235 if (type
== S_IFDIR
) {
4236 bzero(crp
, sizeof(HFSCatalogFolder
));
4237 crp
->recordType
= kHFSFolderRecord
;
4238 crp
->hfsFolder
.folderID
= cnid
;
4239 crp
->hfsFolder
.createDate
= createtime
;
4240 crp
->hfsFolder
.modifyDate
= createtime
;
4241 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4242 *recordSize
= sizeof(HFSCatalogFolder
);
4244 bzero(crp
, sizeof(HFSCatalogFile
));
4245 crp
->recordType
= kHFSFileRecord
;
4246 crp
->hfsFile
.fileID
= cnid
;
4247 crp
->hfsFile
.createDate
= createtime
;
4248 crp
->hfsFile
.modifyDate
= createtime
;
4249 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4250 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4251 *recordSize
= sizeof(HFSCatalogFile
);
4260 * builddesc - build a cnode descriptor from an HFS+ key
4263 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4264 int isdir
, struct cat_desc
*descp
)
4267 unsigned char * nameptr
;
4270 unsigned char tmpbuff
[128];
4272 /* guess a size... */
4273 bufsize
= (3 * key
->nodeName
.length
) + 1;
4274 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4275 nameptr
= hfs_malloc(bufsize
);
4277 nameptr
= &tmpbuff
[0];
4280 result
= utf8_encodestr(key
->nodeName
.unicode
,
4281 key
->nodeName
.length
* sizeof(UniChar
),
4282 nameptr
, (size_t *)&utf8len
,
4285 if (result
== ENAMETOOLONG
) {
4286 if (nameptr
!= &tmpbuff
[0])
4287 hfs_free(nameptr
, bufsize
);
4288 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4289 key
->nodeName
.length
* sizeof(UniChar
),
4291 nameptr
= hfs_malloc(bufsize
);
4293 result
= utf8_encodestr(key
->nodeName
.unicode
,
4294 key
->nodeName
.length
* sizeof(UniChar
),
4295 nameptr
, (size_t *)&utf8len
,
4298 descp
->cd_parentcnid
= key
->parentID
;
4299 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4300 descp
->cd_namelen
= utf8len
;
4301 descp
->cd_cnid
= cnid
;
4302 descp
->cd_hint
= hint
;
4303 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4305 descp
->cd_flags
|= CD_ISDIR
;
4306 descp
->cd_encoding
= encoding
;
4307 if (nameptr
!= &tmpbuff
[0]) {
4308 hfs_free(nameptr
, bufsize
);
4315 * getbsdattr - get attributes in bsd format
4319 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4321 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4322 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4324 attrp
->ca_recflags
= crp
->flags
;
4325 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4326 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4327 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4328 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4329 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4330 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4332 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4333 attrp
->ca_flags
= 0;
4334 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4335 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4337 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4339 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4341 attrp
->ca_linkcount
= 1;
4344 attrp
->ca_linkcount
= 1; /* may be overridden below */
4346 attrp
->ca_uid
= bsd
->ownerID
;
4347 attrp
->ca_gid
= bsd
->groupID
;
4348 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4349 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4350 switch (attrp
->ca_mode
& S_IFMT
) {
4351 case S_IFCHR
: /* fall through */
4353 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4359 /* Pick up the hard link count */
4360 if (bsd
->special
.linkCount
> 0)
4361 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4366 * Override the permissions as determined by the mount auguments
4367 * in ALMOST the same way unset permissions are treated but keep
4368 * track of whether or not the file or folder is hfs locked
4369 * by leaving the h_pflags field unchanged from what was unpacked
4370 * out of the catalog.
4373 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4374 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4375 * at the VFS layer, so there is no need to do it here now; this also
4376 * allows VFS to let root see the real UIDs.
4378 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4379 * attrp->ca_uid = hfsmp->hfs_uid;
4380 * attrp->ca_gid = hfsmp->hfs_gid;
4386 if (!S_ISDIR(attrp
->ca_mode
)) {
4387 attrp
->ca_mode
&= ~S_IFMT
;
4388 attrp
->ca_mode
|= S_IFDIR
;
4390 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4391 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4392 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4394 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4395 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4396 attrp
->ca_flags
|= UF_HIDDEN
;
4398 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4399 if (crp
->flags
& kHFSFileLockedMask
) {
4400 /* The file's supposed to be locked:
4401 Make sure at least one of the IMMUTABLE bits is set: */
4402 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4403 attrp
->ca_flags
|= UF_IMMUTABLE
;
4405 /* The file's supposed to be unlocked: */
4406 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4408 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4409 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4410 attrp
->ca_flags
|= UF_HIDDEN
;
4411 /* get total blocks (both forks) */
4412 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4414 /* On HFS+ the ThreadExists flag must always be set. */
4415 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4416 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4418 /* Pick up the hardlink first link, if any. */
4419 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4422 attrp
->ca_fileid
= crp
->fileID
;
4424 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4429 * promotekey - promote hfs key to hfs plus key
4433 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4434 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4436 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4440 *encoding
= hfsmp
->hfs_encoding
;
4442 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4443 kHFSPlusMaxFileNameChars
, &uniCount
);
4445 * When an HFS name cannot be encoded with the current
4446 * encoding use MacRoman as a fallback.
4448 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4450 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4451 keyp
->nodeName
.unicode
,
4452 kHFSPlusMaxFileNameChars
,
4456 keyp
->nodeName
.length
= uniCount
;
4457 keyp
->parentID
= hfskey
->parentID
;
4461 * promotefork - promote hfs fork info to hfs plus
4465 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4466 int resource
, struct cat_fork
* forkp
)
4468 struct HFSPlusExtentDescriptor
*xp
;
4469 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4471 bzero(forkp
, sizeof(*forkp
));
4472 xp
= &forkp
->cf_extents
[0];
4474 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4475 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4476 forkp
->cf_bytesread
= 0;
4477 forkp
->cf_vblocks
= 0;
4478 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4479 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4480 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4481 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4482 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4483 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4485 forkp
->cf_size
= filep
->dataLogicalSize
;
4486 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4487 forkp
->cf_bytesread
= 0;
4488 forkp
->cf_vblocks
= 0;
4489 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4490 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4491 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4492 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4493 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4494 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4499 * promoteattr - promote standard hfs catalog attributes to hfs plus
4503 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4505 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4507 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4508 const struct HFSCatalogFolder
* folder
;
4510 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4511 crp
->recordType
= kHFSPlusFolderRecord
;
4512 crp
->flags
= folder
->flags
;
4513 crp
->fileID
= folder
->folderID
;
4514 crp
->createDate
= LocalToUTC(folder
->createDate
);
4515 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4516 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4517 crp
->reserved1
= folder
->valence
;
4519 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4521 const struct HFSCatalogFile
* file
;
4523 file
= (const struct HFSCatalogFile
*) dataPtr
;
4524 crp
->recordType
= kHFSPlusFileRecord
;
4525 crp
->flags
= file
->flags
;
4526 crp
->fileID
= file
->fileID
;
4527 crp
->createDate
= LocalToUTC(file
->createDate
);
4528 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4529 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4532 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4533 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4534 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4535 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4537 crp
->textEncoding
= 0;
4538 crp
->attributeModDate
= crp
->contentModDate
;
4539 crp
->accessDate
= crp
->contentModDate
;
4540 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4545 * Build a catalog node thread record from a catalog key
4546 * and return the size of the record.
4549 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4554 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4555 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4557 size
= sizeof(HFSPlusCatalogThread
);
4559 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4561 rec
->recordType
= kHFSPlusFileThreadRecord
;
4563 rec
->parentID
= key
->parentID
;
4564 bcopy(&key
->nodeName
, &rec
->nodeName
,
4565 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4567 /* HFS Plus has variable sized thread records */
4568 size
-= (sizeof(rec
->nodeName
.unicode
) -
4569 (rec
->nodeName
.length
* sizeof(UniChar
)));
4574 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4575 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4577 size
= sizeof(HFSCatalogThread
);
4580 rec
->recordType
= kHFSFolderThreadRecord
;
4582 rec
->recordType
= kHFSFileThreadRecord
;
4583 rec
->parentID
= key
->parentID
;
4584 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4593 * Build a catalog node thread key.
4596 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4599 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4600 key
->hfsPlus
.parentID
= parentID
;
4601 key
->hfsPlus
.nodeName
.length
= 0;
4605 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4606 key
->hfs
.reserved
= 0;
4607 key
->hfs
.parentID
= parentID
;
4608 key
->hfs
.nodeName
[0] = 0;
4615 * Extract the text encoding from a catalog node record.
4618 getencoding(const CatalogRecord
*crp
)
4622 if (crp
->recordType
== kHFSPlusFolderRecord
)
4623 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4624 else if (crp
->recordType
== kHFSPlusFileRecord
)
4625 encoding
= crp
->hfsPlusFile
.textEncoding
;
4633 * Extract the CNID from a catalog node record.
4636 getcnid(const CatalogRecord
*crp
)
4640 switch (crp
->recordType
) {
4643 case kHFSFolderRecord
:
4644 cnid
= crp
->hfsFolder
.folderID
;
4646 case kHFSFileRecord
:
4647 cnid
= crp
->hfsFile
.fileID
;
4651 case kHFSPlusFolderRecord
:
4652 cnid
= crp
->hfsPlusFolder
.folderID
;
4654 case kHFSPlusFileRecord
:
4655 cnid
= crp
->hfsPlusFile
.fileID
;
4658 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4666 * Extract the parent ID from a catalog node record.
4669 getparentcnid(const CatalogRecord
*recp
)
4673 switch (recp
->recordType
) {
4676 case kHFSFileThreadRecord
:
4677 case kHFSFolderThreadRecord
:
4678 cnid
= recp
->hfsThread
.parentID
;
4682 case kHFSPlusFileThreadRecord
:
4683 case kHFSPlusFolderThreadRecord
:
4684 cnid
= recp
->hfsPlusThread
.parentID
;
4687 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4695 * Determine if a catalog node record is a directory.
4698 isadir(const CatalogRecord
*crp
)
4700 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4704 if (crp
->recordType
== kHFSFolderRecord
) {
4713 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4714 * (not inode) using catalog record id. Note that this function does
4715 * NOT resolve directory hard link to its directory inode and return
4718 * Note: The caller is responsible for releasing the output catalog
4719 * descriptor (when supplied outdescp is non-null).
4722 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4723 u_int8_t forktype
, struct cat_desc
*outdescp
,
4724 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4726 struct BTreeIterator
*iterator
= NULL
;
4727 FSBufferDescriptor btdata
;
4730 CatalogRecord
*recp
= NULL
;
4733 /* No directory hard links on standard HFS */
4734 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4738 iterator
= hfs_mallocz(sizeof(*iterator
));
4739 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4741 recp
= hfs_malloc(sizeof(CatalogRecord
));
4742 BDINIT(btdata
, recp
);
4744 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4745 &btdata
, &datasize
, iterator
);
4749 /* Directory hard links are catalog file record */
4750 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4755 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4756 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4757 (keyp
->hfsPlus
.nodeName
.length
* 2);
4758 if (forktype
== kHFSResourceForkType
) {
4759 /* Lookup resource fork for directory hard link */
4760 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4762 /* Lookup data fork, if any, for directory hard link */
4763 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4766 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4767 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4770 /* Just for sanity, make sure that id in catalog record and thread record match */
4771 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4772 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4773 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4779 hfs_free(recp
, sizeof(*recp
));
4781 hfs_free(iterator
, sizeof(*iterator
));
4783 return MacToVFSError(error
);
4787 * cnode_update_dirlink - update the catalog node for directory hard link
4788 * described by descp using the data from attrp and forkp.
4791 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4792 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4794 if (forktype
== kHFSResourceForkType
) {
4795 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4797 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);
4801 void hfs_fork_copy(struct cat_fork
*dst
, const struct cat_fork
*src
,
4802 HFSPlusExtentDescriptor
*extents
)
4804 /* Copy everything but the extents into the dest fork */
4805 memcpy(dst
, src
, offsetof(struct cat_fork
, cf_extents
));
4806 /* Then copy the supplied extents into the fork */
4807 memcpy(dst
->cf_extents
, extents
, sizeof(HFSPlusExtentRecord
));