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)
3043 * Callback to pack directory entries.
3044 * Called with packdirentry_state for each item in a directory.
3047 /* Hard link information collected during cat_getdirentries. */
3050 user_addr_t dirent_addr
;
3052 typedef struct linkinfo linkinfo_t
;
3054 /* State information for the getdirentries_callback function. */
3055 struct packdirentry_state
{
3056 int cbs_flags
; /* VNODE_READDIR_* flags */
3057 u_int32_t cbs_parentID
;
3058 u_int32_t cbs_index
;
3060 ExtendedVCB
* cbs_hfsmp
;
3063 int32_t cbs_maxlinks
;
3064 linkinfo_t
* cbs_linkinfo
;
3065 struct cat_desc
* cbs_desc
;
3066 u_int8_t
* cbs_namebuf
;
3068 * The following fields are only used for NFS readdir, which
3069 * uses the next file id as the seek offset of each entry.
3071 struct direntry
* cbs_direntry
;
3072 struct direntry
* cbs_prevdirentry
;
3073 u_int32_t cbs_previlinkref
;
3074 Boolean cbs_hasprevdirentry
;
3079 * getdirentries callback for HFS Plus directories.
3082 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3083 struct packdirentry_state
*state
)
3085 struct hfsmount
*hfsmp
;
3086 const CatalogName
*cnp
;
3089 struct dirent catent
;
3090 struct direntry
* entry
= NULL
;
3092 u_int32_t ilinkref
= 0;
3093 u_int32_t curlinkref
= 0;
3096 u_int8_t type
= DT_UNKNOWN
;
3097 u_int8_t is_mangled
= 0;
3098 u_int8_t is_link
= 0;
3100 user_addr_t uiobase
= USER_ADDR_NULL
;
3105 Boolean stop_after_pack
= false;
3107 hfsmp
= state
->cbs_hfsmp
;
3108 curID
= ckp
->hfsPlus
.parentID
;
3110 /* We're done when parent directory changes */
3111 if (state
->cbs_parentID
!= curID
) {
3113 * If the parent ID is different from curID this means we've hit
3114 * the EOF for the directory. To help future callers, we mark
3115 * the cbs_eof boolean. However, we should only mark the EOF
3116 * boolean if we're about to return from this function.
3118 * This is because this callback function does its own uiomove
3119 * to get the data to userspace. If we set the boolean before determining
3120 * whether or not the current entry has enough room to write its
3121 * data to userland, we could fool the callers of this catalog function
3122 * into thinking they've hit EOF earlier than they really would have.
3123 * In that case, we'd know that we have more entries to process and
3124 * send to userland, but we didn't have enough room.
3126 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3127 * about to return and won't write any new data back
3128 * to userland. In the stop_after_pack case, we'll set this boolean
3129 * regardless, so it's slightly safer to let that logic mark the boolean,
3130 * especially since it's closer to the return of this function.
3133 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3134 /* The last record has not been returned yet, so we
3135 * want to stop after packing the last item
3137 if (state
->cbs_hasprevdirentry
) {
3138 stop_after_pack
= true;
3140 state
->cbs_eof
= true;
3141 state
->cbs_result
= ENOENT
;
3142 return (0); /* stop */
3145 state
->cbs_eof
= true;
3146 state
->cbs_result
= ENOENT
;
3147 return (0); /* stop */
3151 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3152 entry
= state
->cbs_direntry
;
3153 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3154 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3156 * The NFS server sometimes needs to make filenames fit in
3157 * NAME_MAX bytes (since its client may not be able to
3158 * handle a longer name). In that case, NFS will ask us
3159 * to mangle the name to keep it short enough.
3161 maxnamelen
= NAME_MAX
+ 1;
3163 maxnamelen
= sizeof(entry
->d_name
);
3166 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3167 maxnamelen
= sizeof(catent
.d_name
);
3170 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3171 /* The last item returns a non-zero invalid cookie */
3174 switch(crp
->recordType
) {
3175 case kHFSPlusFolderRecord
:
3177 cnid
= crp
->hfsPlusFolder
.folderID
;
3178 /* Hide our private system directories. */
3179 if (curID
== kHFSRootFolderID
) {
3180 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3181 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3186 case kHFSPlusFileRecord
:
3187 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3188 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3189 cnid
= crp
->hfsPlusFile
.fileID
;
3191 * When a hardlink link is encountered save its link ref.
3193 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3194 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3195 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3196 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3197 /* If link ref is inode's file id then use it directly. */
3198 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3199 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3201 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3204 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3205 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3206 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3207 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3208 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3209 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3210 /* A directory's link resolves to a directory. */
3212 /* A directory's link ref is always inode's file id. */
3213 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3216 /* Hide the journal files */
3217 if ((curID
== kHFSRootFolderID
) &&
3218 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3219 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3220 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3225 return (0); /* stop */
3228 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3230 namelen
= cnp
->ustr
.length
;
3232 * For MacRoman encoded names (textEncoding == 0), assume that it's ascii
3233 * and convert it directly in an attempt to avoid the more
3234 * expensive utf8_encodestr conversion.
3236 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3239 const u_int16_t
*chp
;
3241 chp
= &cnp
->ustr
.unicode
[0];
3242 for (i
= 0; i
< (int)namelen
; ++i
) {
3244 if (ch
> 0x007f || ch
== 0x0000) {
3245 /* Perform expensive utf8_encodestr conversion */
3248 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3250 nameptr
[namelen
] = '\0';
3254 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3255 nameptr
, &namelen
, maxnamelen
, ':', 0);
3258 /* Check result returned from encoding the filename to utf8 */
3259 if (result
== ENAMETOOLONG
) {
3261 * If we were looking at a catalog record for a hardlink (not the inode),
3262 * then we want to use its link ID as opposed to the inode ID for
3263 * a mangled name. For all other cases, they are the same. Note that
3264 * due to the way directory hardlinks are implemented, the actual link
3265 * is going to be counted as a file record, so we can catch both
3268 cnid_t linkid
= cnid
;
3270 linkid
= crp
->hfsPlusFile
.fileID
;
3273 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3274 cnp
->ustr
.unicode
, maxnamelen
,
3275 (ByteCount
*)&namelen
, nameptr
, linkid
);
3280 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3282 * The index is 1 relative and includes "." and ".."
3284 * Also stuff the cnid in the upper 32 bits of the cookie.
3285 * The cookie is stored to the previous entry, which will
3286 * be packed and copied this time
3288 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3289 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3290 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3292 catent
.d_type
= type
;
3293 catent
.d_namlen
= namelen
;
3294 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3296 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3298 catent
.d_fileno
= cnid
;
3299 uioaddr
= (caddr_t
) &catent
;
3302 /* Save current base address for post processing of hard-links. */
3303 if (ilinkref
|| state
->cbs_previlinkref
) {
3304 uiobase
= uio_curriovbase(state
->cbs_uio
);
3306 /* If this entry won't fit then we're done */
3307 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3308 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3309 return (0); /* stop */
3312 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3313 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3314 if (state
->cbs_result
== 0) {
3317 /* Remember previous entry */
3318 state
->cbs_desc
->cd_cnid
= cnid
;
3319 if (type
== DT_DIR
) {
3320 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3322 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3324 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3325 state
->cbs_desc
->cd_namelen
= 0;
3328 state
->cbs_desc
->cd_encoding
= xxxx
;
3331 state
->cbs_desc
->cd_namelen
= namelen
;
3332 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3334 /* Store unmangled name for the directory hint else it will
3335 * restart readdir at the last location again
3337 u_int8_t
*new_nameptr
;
3339 size_t tmp_namelen
= 0;
3341 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3342 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3343 cnp
->ustr
.length
* sizeof(UniChar
),
3345 new_nameptr
= hfs_malloc(bufsize
);
3346 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3347 cnp
->ustr
.length
* sizeof(UniChar
),
3348 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3350 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3351 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3353 hfs_free(new_nameptr
, bufsize
);
3356 if (state
->cbs_hasprevdirentry
) {
3357 curlinkref
= ilinkref
; /* save current */
3358 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3361 * Record any hard links for post processing.
3363 if ((ilinkref
!= 0) &&
3364 (state
->cbs_result
== 0) &&
3365 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3366 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3367 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3368 state
->cbs_nlinks
++;
3370 if (state
->cbs_hasprevdirentry
) {
3371 ilinkref
= curlinkref
; /* restore current */
3375 /* Fill the direntry to be used the next time */
3376 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3377 if (stop_after_pack
) {
3378 state
->cbs_eof
= true;
3379 return (0); /* stop */
3381 entry
->d_type
= type
;
3382 entry
->d_namlen
= namelen
;
3383 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3385 /* File number = 0 means skip entry */
3386 entry
->d_fileno
= 0;
3388 entry
->d_fileno
= cnid
;
3390 /* swap the current and previous entry */
3391 struct direntry
* tmp
;
3392 tmp
= state
->cbs_direntry
;
3393 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3394 state
->cbs_prevdirentry
= tmp
;
3395 state
->cbs_hasprevdirentry
= true;
3396 state
->cbs_previlinkref
= ilinkref
;
3399 /* Continue iteration if there's room */
3400 return (state
->cbs_result
== 0 &&
3401 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3406 * getdirentries callback for standard HFS (non HFS+) directories.
3409 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3410 struct packdirentry_state
*state
)
3412 struct hfsmount
*hfsmp
;
3413 const CatalogName
*cnp
;
3416 struct dirent catent
;
3418 u_int8_t type
= DT_UNKNOWN
;
3425 hfsmp
= state
->cbs_hfsmp
;
3427 curID
= ckp
->hfs
.parentID
;
3429 /* We're done when parent directory changes */
3430 if (state
->cbs_parentID
!= curID
) {
3431 state
->cbs_result
= ENOENT
;
3432 return (0); /* stop */
3435 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3436 maxnamelen
= sizeof(catent
.d_name
);
3438 switch(crp
->recordType
) {
3439 case kHFSFolderRecord
:
3441 cnid
= crp
->hfsFolder
.folderID
;
3443 case kHFSFileRecord
:
3445 cnid
= crp
->hfsFile
.fileID
;
3448 return (0); /* stop */
3451 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3452 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3454 * When an HFS name cannot be encoded with the current
3455 * volume encoding we use MacRoman as a fallback.
3458 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3460 catent
.d_type
= type
;
3461 catent
.d_namlen
= namelen
;
3462 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3463 catent
.d_fileno
= cnid
;
3464 uioaddr
= (caddr_t
) &catent
;
3466 /* If this entry won't fit then we're done */
3467 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3468 return (0); /* stop */
3471 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3472 if (state
->cbs_result
== 0) {
3475 /* Remember previous entry */
3476 state
->cbs_desc
->cd_cnid
= cnid
;
3477 if (type
== DT_DIR
) {
3478 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3480 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3482 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3483 state
->cbs_desc
->cd_namelen
= 0;
3485 state
->cbs_desc
->cd_namelen
= namelen
;
3486 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3489 /* Continue iteration if there's room */
3490 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3495 * Pack a uio buffer with directory entries from the catalog
3498 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3499 uio_t uio
, int flags
, int * items
, int * eofflag
)
3502 BTreeIterator
* iterator
;
3504 struct packdirentry_state state
;
3513 extended
= flags
& VNODE_READDIR_EXTENDED
;
3515 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3518 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3520 #define MAX_LINKINFO_ENTRIES 275
3522 * Get a buffer for link info array, btree iterator and a direntry.
3524 * We impose an cap of 275 link entries when trying to compute
3525 * the total number of hardlink entries that we'll allow in the
3526 * linkinfo array, as this has been shown to noticeably impact performance.
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 // This value cannot underflow: both entrycnt and the rhs are unsigned 32-bit
3536 // ints, so the worst-case MIN of them is 0.
3537 maxlinks
= MIN (entrycnt
, (u_int32_t
)(uio_resid(uio
) / SMALL_DIRENTRY_SIZE
));
3538 // Prevent overflow.
3539 maxlinks
= MIN (maxlinks
, MAX_LINKINFO_ENTRIES
);
3540 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3543 bufsize
+= 2*sizeof(struct direntry
);
3545 buffer
= hfs_mallocz(bufsize
);
3547 state
.cbs_flags
= flags
;
3548 state
.cbs_hasprevdirentry
= false;
3549 state
.cbs_previlinkref
= 0;
3550 state
.cbs_nlinks
= 0;
3551 state
.cbs_maxlinks
= maxlinks
;
3552 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3554 * We need to set cbs_eof to false regardless of whether or not the
3555 * control flow is actually in the extended case, since we use this
3556 * field to track whether or not we've returned EOF from the iterator function.
3558 state
.cbs_eof
= false;
3560 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3561 key
= (CatalogKey
*)&iterator
->key
;
3563 index
= dirhint
->dh_index
+ 1;
3565 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3566 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3569 * Attempt to build a key from cached filename
3571 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3572 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3573 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3578 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3580 * Position the iterator at the directory's thread record.
3581 * (i.e. just before the first entry)
3583 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3584 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3585 iterator
->hint
.index
= 0;
3590 * If the last entry wasn't cached then position the btree iterator
3594 * Position the iterator at the directory's thread record.
3595 * (i.e. just before the first entry)
3597 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3598 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3600 result
= MacToVFSError(result
);
3604 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3607 * Iterate until we reach the entry just
3608 * before the one we want to start with.
3611 struct position_state ps
;
3616 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3619 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3620 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3624 result
= MacToVFSError(result
);
3626 result
= MacToVFSError(result
);
3627 if (result
== ENOENT
) {
3629 * ENOENT means we've hit the EOF.
3630 * suppress the error, and set the eof flag.
3633 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3641 state
.cbs_index
= index
;
3642 state
.cbs_hfsmp
= hfsmp
;
3643 state
.cbs_uio
= uio
;
3644 state
.cbs_desc
= &dirhint
->dh_desc
;
3645 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3646 state
.cbs_result
= 0;
3647 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3649 /* Use a temporary buffer to hold intermediate descriptor names. */
3650 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3651 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3652 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3653 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3654 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3657 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3659 enum BTreeIterationOperations op
;
3660 if (extended
&& index
!= 0 && have_key
)
3661 op
= kBTreeCurrentRecord
;
3663 op
= kBTreeNextRecord
;
3666 * Process as many entries as possible starting at iterator->key.
3668 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3670 result
= BTIterateRecords(fcb
, op
, iterator
,
3671 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3673 /* For extended calls, every call to getdirentries_callback()
3674 * transfers the previous directory entry found to the user
3675 * buffer. Therefore when BTIterateRecords reaches the end of
3676 * Catalog BTree, call getdirentries_callback() again with
3677 * dummy values to copy the last directory entry stored in
3678 * packdirentry_state
3680 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3684 bzero(&ckp
, sizeof(ckp
));
3685 bzero(&crp
, sizeof(crp
));
3687 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3692 /* HFS (standard) */
3693 result
= BTIterateRecords(fcb
, op
, iterator
,
3694 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3698 /* Note that state.cbs_index is still valid on errors */
3699 *items
= state
.cbs_index
- index
;
3700 index
= state
.cbs_index
;
3703 * Also note that cbs_eof is set in all cases if we ever hit EOF
3704 * during the enumeration by the catalog callback. Mark the directory's hint
3705 * descriptor as having hit EOF.
3708 if (state
.cbs_eof
) {
3709 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3713 /* Finish updating the catalog iterator. */
3714 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3715 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3716 dirhint
->dh_index
= index
- 1;
3718 /* Fix up the name. */
3719 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3720 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3721 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3723 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3724 dirhint
->dh_desc
.cd_namelen
= 0;
3728 * Post process any hard links to get the real file id.
3730 if (state
.cbs_nlinks
> 0) {
3732 user_addr_t address
;
3735 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3736 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3738 /* This assumes that d_ino is always first field. */
3739 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3740 if (address
== (user_addr_t
)0)
3742 if (uio_isuserspace(uio
)) {
3744 ino64_t fileid_64
= (ino64_t
)fileid
;
3745 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3747 (void) copyout(&fileid
, address
, sizeof(fileid
));
3749 } else /* system space */ {
3751 ino64_t fileid_64
= (ino64_t
)fileid
;
3752 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3754 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3760 if (state
.cbs_result
)
3761 result
= state
.cbs_result
;
3763 result
= MacToVFSError(result
);
3765 if (result
== ENOENT
) {
3770 hfs_free(buffer
, bufsize
);
3777 * Callback to establish directory position.
3778 * Called with position_state for each item in a directory.
3781 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3782 struct position_state
*state
)
3786 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3787 curID
= ckp
->hfsPlus
.parentID
;
3791 curID
= ckp
->hfs
.parentID
;
3795 /* Make sure parent directory didn't change */
3796 if (state
->parentID
!= curID
) {
3798 * The parent ID is different from curID this means we've hit
3799 * the EOF for the directory.
3801 state
->error
= ENOENT
;
3802 return (0); /* stop */
3805 /* Count this entry */
3806 switch(crp
->recordType
) {
3807 case kHFSPlusFolderRecord
:
3808 case kHFSPlusFileRecord
:
3810 case kHFSFolderRecord
:
3811 case kHFSFileRecord
:
3816 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3817 crp
->recordType
, curID
);
3818 state
->error
= EINVAL
;
3819 return (0); /* stop */
3822 return (state
->count
< state
->index
);
3827 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3829 * The name portion of the key is compared using a 16-bit binary comparison.
3830 * This is called from the b-tree code.
3833 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3835 u_int32_t searchParentID
, trialParentID
;
3838 searchParentID
= searchKey
->parentID
;
3839 trialParentID
= trialKey
->parentID
;
3842 if (searchParentID
> trialParentID
) {
3844 } else if (searchParentID
< trialParentID
) {
3847 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3848 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3849 int length1
= searchKey
->nodeName
.length
;
3850 int length2
= trialKey
->nodeName
.length
;
3852 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3861 * Compare two standard HFS catalog keys
3863 * Result: +n search key > trial key
3864 * 0 search key = trial key
3865 * -n search key < trial key
3868 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3870 cnid_t searchParentID
, trialParentID
;
3873 searchParentID
= searchKey
->parentID
;
3874 trialParentID
= trialKey
->parentID
;
3876 if (searchParentID
> trialParentID
)
3878 else if (searchParentID
< trialParentID
)
3880 else /* parent dirID's are equal, compare names */
3881 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3889 * Compare two HFS+ catalog keys
3891 * Result: +n search key > trial key
3892 * 0 search key = trial key
3893 * -n search key < trial key
3896 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3898 cnid_t searchParentID
, trialParentID
;
3901 searchParentID
= searchKey
->parentID
;
3902 trialParentID
= trialKey
->parentID
;
3904 if (searchParentID
> trialParentID
) {
3907 else if (searchParentID
< trialParentID
) {
3910 /* parent node ID's are equal, compare names */
3911 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3912 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3914 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3915 searchKey
->nodeName
.length
,
3916 &trialKey
->nodeName
.unicode
[0],
3917 trialKey
->nodeName
.length
);
3925 * buildkey - build a Catalog b-tree key from a cnode descriptor
3928 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3929 HFSPlusCatalogKey
*key
, int retry
)
3931 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3932 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3934 size_t unicodeBytes
= 0;
3940 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3941 return (EINVAL
); /* invalid name */
3943 key
->parentID
= descp
->cd_parentcnid
;
3944 key
->nodeName
.length
= 0;
3946 * Convert filename from UTF-8 into Unicode
3949 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3950 utf8_flags
|= UTF_DECOMPOSED
;
3951 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3952 key
->nodeName
.unicode
, &unicodeBytes
,
3953 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3954 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3955 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3957 if (result
!= ENAMETOOLONG
)
3958 result
= EINVAL
; /* name has invalid characters */
3964 * For HFS volumes convert to an HFS compatible key
3966 * XXX need to save the encoding that succeeded
3969 HFSCatalogKey hfskey
;
3971 bzero(&hfskey
, sizeof(hfskey
));
3972 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3973 hfskey
.parentID
= key
->parentID
;
3974 hfskey
.nodeName
[0] = 0;
3975 if (key
->nodeName
.length
> 0) {
3977 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3978 key
->nodeName
.length
* 2,
3979 key
->nodeName
.unicode
,
3980 &hfskey
.nodeName
[0], retry
)) != 0) {
3981 if (res
!= ENAMETOOLONG
)
3986 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3988 bcopy(&hfskey
, key
, sizeof(hfskey
));
3997 * Resolve hard link reference to obtain the inode record.
4000 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
4002 FSBufferDescriptor btdata
;
4003 struct BTreeIterator
*iterator
;
4004 struct cat_desc idesc
;
4009 BDINIT(btdata
, recp
);
4012 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4013 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4015 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4016 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4019 /* Get space for iterator */
4020 iterator
= hfs_mallocz(sizeof(*iterator
));
4022 /* Build a descriptor for private dir. */
4023 idesc
.cd_parentcnid
= parentcnid
;
4024 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4025 idesc
.cd_namelen
= strlen(inodename
);
4028 idesc
.cd_encoding
= 0;
4029 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4031 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4032 &btdata
, NULL
, NULL
);
4035 /* Make sure there's a reference */
4036 if (recp
->hl_linkCount
== 0)
4037 recp
->hl_linkCount
= 2;
4039 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4042 hfs_free(iterator
, sizeof(*iterator
));
4044 return (result
? ENOENT
: 0);
4048 * Resolve hard link reference to obtain the inode number.
4051 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4053 struct HFSPlusCatalogFile record
;
4057 * Since we know resolvelinkid is only called from
4058 * cat_getdirentries, we can assume that only file
4059 * hardlinks need to be resolved (cat_getdirentries
4060 * can resolve directory hardlinks in place).
4062 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4064 if (record
.fileID
== 0)
4067 *ino
= record
.fileID
;
4073 * getkey - get a key from id by doing a thread lookup
4076 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4078 struct BTreeIterator
* iterator
;
4079 FSBufferDescriptor btdata
;
4082 CatalogRecord
* recp
;
4086 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4088 iterator
= hfs_mallocz(sizeof(*iterator
));
4089 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4091 recp
= hfs_malloc(sizeof(CatalogRecord
));
4092 BDINIT(btdata
, recp
);
4094 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4095 &btdata
, &datasize
, iterator
);
4099 /* Turn thread record into a cnode key (in place) */
4100 switch (recp
->recordType
) {
4103 case kHFSFileThreadRecord
:
4104 case kHFSFolderThreadRecord
:
4105 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4106 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4107 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4111 case kHFSPlusFileThreadRecord
:
4112 case kHFSPlusFolderThreadRecord
:
4113 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4114 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4115 (keyp
->hfsPlus
.nodeName
.length
* 2);
4116 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4125 hfs_free(iterator
, sizeof(*iterator
));
4126 hfs_free(recp
, sizeof(*recp
));
4128 return MacToVFSError(result
);
4132 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4133 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4134 * cat_desc here). Both key and attrp must point to real structures.
4136 * The key's parent id is the only part of the key expected to be used by the caller.
4137 * The name portion of the key may not always be valid (ie in the case of a hard link).
4140 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4144 result
= getkey(hfsmp
, cnid
, key
);
4147 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4150 * Check for a raw file hardlink inode.
4151 * Fix up the parent id in the key if necessary.
4152 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4154 if ((result
== 0) &&
4155 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4156 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4157 cnid_t nextlinkid
= 0;
4158 cnid_t prevlinkid
= 0;
4159 struct cat_desc linkdesc
;
4162 * Pick up the first link in the chain and get a descriptor for it.
4163 * This allows blind bulk access checks to work for hardlinks.
4165 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4166 (nextlinkid
!= 0)) {
4167 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4168 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4169 cat_releasedesc(&linkdesc
);
4173 return MacToVFSError(result
);
4178 * buildrecord - build a default catalog directory or file record
4181 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4182 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4184 int type
= attrp
->ca_mode
& S_IFMT
;
4185 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4188 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4190 if (type
== S_IFDIR
) {
4191 crp
->recordType
= kHFSPlusFolderRecord
;
4192 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4193 crp
->hfsPlusFolder
.valence
= 0;
4194 crp
->hfsPlusFolder
.folderID
= cnid
;
4195 crp
->hfsPlusFolder
.createDate
= createtime
;
4196 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4197 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4198 crp
->hfsPlusFolder
.accessDate
= createtime
;
4199 crp
->hfsPlusFolder
.backupDate
= 0;
4200 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4201 crp
->hfsPlusFolder
.folderCount
= 0;
4202 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4203 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4204 bsdp
->special
.linkCount
= 1;
4205 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4207 crp
->recordType
= kHFSPlusFileRecord
;
4208 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4209 crp
->hfsPlusFile
.reserved1
= 0;
4210 crp
->hfsPlusFile
.fileID
= cnid
;
4211 crp
->hfsPlusFile
.createDate
= createtime
;
4212 crp
->hfsPlusFile
.contentModDate
= createtime
;
4213 crp
->hfsPlusFile
.accessDate
= createtime
;
4214 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4215 crp
->hfsPlusFile
.backupDate
= 0;
4216 crp
->hfsPlusFile
.textEncoding
= encoding
;
4217 crp
->hfsPlusFile
.reserved2
= 0;
4218 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4219 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4220 /* BLK/CHR need to save the device info */
4221 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4222 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4224 bsdp
->special
.linkCount
= 1;
4226 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4227 *recordSize
= sizeof(HFSPlusCatalogFile
);
4229 bsdp
->ownerID
= attrp
->ca_uid
;
4230 bsdp
->groupID
= attrp
->ca_gid
;
4231 bsdp
->fileMode
= attrp
->ca_mode
;
4232 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4233 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4237 createtime
= UTCToLocal(createtime
);
4238 if (type
== S_IFDIR
) {
4239 bzero(crp
, sizeof(HFSCatalogFolder
));
4240 crp
->recordType
= kHFSFolderRecord
;
4241 crp
->hfsFolder
.folderID
= cnid
;
4242 crp
->hfsFolder
.createDate
= createtime
;
4243 crp
->hfsFolder
.modifyDate
= createtime
;
4244 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4245 *recordSize
= sizeof(HFSCatalogFolder
);
4247 bzero(crp
, sizeof(HFSCatalogFile
));
4248 crp
->recordType
= kHFSFileRecord
;
4249 crp
->hfsFile
.fileID
= cnid
;
4250 crp
->hfsFile
.createDate
= createtime
;
4251 crp
->hfsFile
.modifyDate
= createtime
;
4252 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4253 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4254 *recordSize
= sizeof(HFSCatalogFile
);
4263 * builddesc - build a cnode descriptor from an HFS+ key
4266 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4267 int isdir
, struct cat_desc
*descp
)
4270 unsigned char * nameptr
;
4273 unsigned char tmpbuff
[128];
4275 /* guess a size... */
4276 bufsize
= (3 * key
->nodeName
.length
) + 1;
4277 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4278 nameptr
= hfs_malloc(bufsize
);
4280 nameptr
= &tmpbuff
[0];
4283 result
= utf8_encodestr(key
->nodeName
.unicode
,
4284 key
->nodeName
.length
* sizeof(UniChar
),
4285 nameptr
, (size_t *)&utf8len
,
4288 if (result
== ENAMETOOLONG
) {
4289 if (nameptr
!= &tmpbuff
[0])
4290 hfs_free(nameptr
, bufsize
);
4291 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4292 key
->nodeName
.length
* sizeof(UniChar
),
4294 nameptr
= hfs_malloc(bufsize
);
4296 result
= utf8_encodestr(key
->nodeName
.unicode
,
4297 key
->nodeName
.length
* sizeof(UniChar
),
4298 nameptr
, (size_t *)&utf8len
,
4301 descp
->cd_parentcnid
= key
->parentID
;
4302 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4303 descp
->cd_namelen
= utf8len
;
4304 descp
->cd_cnid
= cnid
;
4305 descp
->cd_hint
= hint
;
4306 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4308 descp
->cd_flags
|= CD_ISDIR
;
4309 descp
->cd_encoding
= encoding
;
4310 if (nameptr
!= &tmpbuff
[0]) {
4311 hfs_free(nameptr
, bufsize
);
4318 * getbsdattr - get attributes in bsd format
4322 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4324 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4325 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4327 attrp
->ca_recflags
= crp
->flags
;
4328 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4329 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4330 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4331 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4332 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4333 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4335 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4336 attrp
->ca_flags
= 0;
4337 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4338 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4340 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4342 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4344 attrp
->ca_linkcount
= 1;
4347 attrp
->ca_linkcount
= 1; /* may be overridden below */
4349 attrp
->ca_uid
= bsd
->ownerID
;
4350 attrp
->ca_gid
= bsd
->groupID
;
4351 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4352 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4353 switch (attrp
->ca_mode
& S_IFMT
) {
4354 case S_IFCHR
: /* fall through */
4356 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4362 /* Pick up the hard link count */
4363 if (bsd
->special
.linkCount
> 0)
4364 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4369 * Override the permissions as determined by the mount auguments
4370 * in ALMOST the same way unset permissions are treated but keep
4371 * track of whether or not the file or folder is hfs locked
4372 * by leaving the h_pflags field unchanged from what was unpacked
4373 * out of the catalog.
4376 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4377 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4378 * at the VFS layer, so there is no need to do it here now; this also
4379 * allows VFS to let root see the real UIDs.
4381 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4382 * attrp->ca_uid = hfsmp->hfs_uid;
4383 * attrp->ca_gid = hfsmp->hfs_gid;
4389 if (!S_ISDIR(attrp
->ca_mode
)) {
4390 attrp
->ca_mode
&= ~S_IFMT
;
4391 attrp
->ca_mode
|= S_IFDIR
;
4393 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4394 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4395 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4397 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4398 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4399 attrp
->ca_flags
|= UF_HIDDEN
;
4401 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4402 if (crp
->flags
& kHFSFileLockedMask
) {
4403 /* The file's supposed to be locked:
4404 Make sure at least one of the IMMUTABLE bits is set: */
4405 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4406 attrp
->ca_flags
|= UF_IMMUTABLE
;
4408 /* The file's supposed to be unlocked: */
4409 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4411 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4412 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4413 attrp
->ca_flags
|= UF_HIDDEN
;
4414 /* get total blocks (both forks) */
4415 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4417 /* On HFS+ the ThreadExists flag must always be set. */
4418 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4419 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4421 /* Pick up the hardlink first link, if any. */
4422 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4425 attrp
->ca_fileid
= crp
->fileID
;
4427 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4432 * promotekey - promote hfs key to hfs plus key
4436 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4437 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4439 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4443 *encoding
= hfsmp
->hfs_encoding
;
4445 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4446 kHFSPlusMaxFileNameChars
, &uniCount
);
4448 * When an HFS name cannot be encoded with the current
4449 * encoding use MacRoman as a fallback.
4451 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4453 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4454 keyp
->nodeName
.unicode
,
4455 kHFSPlusMaxFileNameChars
,
4459 keyp
->nodeName
.length
= uniCount
;
4460 keyp
->parentID
= hfskey
->parentID
;
4464 * promotefork - promote hfs fork info to hfs plus
4468 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4469 int resource
, struct cat_fork
* forkp
)
4471 struct HFSPlusExtentDescriptor
*xp
;
4472 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4474 bzero(forkp
, sizeof(*forkp
));
4475 xp
= &forkp
->cf_extents
[0];
4477 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4478 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4479 forkp
->cf_bytesread
= 0;
4480 forkp
->cf_vblocks
= 0;
4481 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4482 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4483 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4484 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4485 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4486 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4488 forkp
->cf_size
= filep
->dataLogicalSize
;
4489 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4490 forkp
->cf_bytesread
= 0;
4491 forkp
->cf_vblocks
= 0;
4492 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4493 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4494 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4495 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4496 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4497 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4502 * promoteattr - promote standard hfs catalog attributes to hfs plus
4506 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4508 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4510 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4511 const struct HFSCatalogFolder
* folder
;
4513 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4514 crp
->recordType
= kHFSPlusFolderRecord
;
4515 crp
->flags
= folder
->flags
;
4516 crp
->fileID
= folder
->folderID
;
4517 crp
->createDate
= LocalToUTC(folder
->createDate
);
4518 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4519 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4520 crp
->reserved1
= folder
->valence
;
4522 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4524 const struct HFSCatalogFile
* file
;
4526 file
= (const struct HFSCatalogFile
*) dataPtr
;
4527 crp
->recordType
= kHFSPlusFileRecord
;
4528 crp
->flags
= file
->flags
;
4529 crp
->fileID
= file
->fileID
;
4530 crp
->createDate
= LocalToUTC(file
->createDate
);
4531 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4532 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4535 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4536 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4537 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4538 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4540 crp
->textEncoding
= 0;
4541 crp
->attributeModDate
= crp
->contentModDate
;
4542 crp
->accessDate
= crp
->contentModDate
;
4543 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4548 * Build a catalog node thread record from a catalog key
4549 * and return the size of the record.
4552 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4557 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4558 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4560 size
= sizeof(HFSPlusCatalogThread
);
4562 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4564 rec
->recordType
= kHFSPlusFileThreadRecord
;
4566 rec
->parentID
= key
->parentID
;
4567 bcopy(&key
->nodeName
, &rec
->nodeName
,
4568 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4570 /* HFS Plus has variable sized thread records */
4571 size
-= (sizeof(rec
->nodeName
.unicode
) -
4572 (rec
->nodeName
.length
* sizeof(UniChar
)));
4577 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4578 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4580 size
= sizeof(HFSCatalogThread
);
4583 rec
->recordType
= kHFSFolderThreadRecord
;
4585 rec
->recordType
= kHFSFileThreadRecord
;
4586 rec
->parentID
= key
->parentID
;
4587 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4596 * Build a catalog node thread key.
4599 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4602 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4603 key
->hfsPlus
.parentID
= parentID
;
4604 key
->hfsPlus
.nodeName
.length
= 0;
4608 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4609 key
->hfs
.reserved
= 0;
4610 key
->hfs
.parentID
= parentID
;
4611 key
->hfs
.nodeName
[0] = 0;
4618 * Extract the text encoding from a catalog node record.
4621 getencoding(const CatalogRecord
*crp
)
4625 if (crp
->recordType
== kHFSPlusFolderRecord
)
4626 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4627 else if (crp
->recordType
== kHFSPlusFileRecord
)
4628 encoding
= crp
->hfsPlusFile
.textEncoding
;
4636 * Extract the CNID from a catalog node record.
4639 getcnid(const CatalogRecord
*crp
)
4643 switch (crp
->recordType
) {
4646 case kHFSFolderRecord
:
4647 cnid
= crp
->hfsFolder
.folderID
;
4649 case kHFSFileRecord
:
4650 cnid
= crp
->hfsFile
.fileID
;
4654 case kHFSPlusFolderRecord
:
4655 cnid
= crp
->hfsPlusFolder
.folderID
;
4657 case kHFSPlusFileRecord
:
4658 cnid
= crp
->hfsPlusFile
.fileID
;
4661 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4669 * Extract the parent ID from a catalog node record.
4672 getparentcnid(const CatalogRecord
*recp
)
4676 switch (recp
->recordType
) {
4679 case kHFSFileThreadRecord
:
4680 case kHFSFolderThreadRecord
:
4681 cnid
= recp
->hfsThread
.parentID
;
4685 case kHFSPlusFileThreadRecord
:
4686 case kHFSPlusFolderThreadRecord
:
4687 cnid
= recp
->hfsPlusThread
.parentID
;
4690 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4698 * Determine if a catalog node record is a directory.
4701 isadir(const CatalogRecord
*crp
)
4703 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4707 if (crp
->recordType
== kHFSFolderRecord
) {
4716 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4717 * (not inode) using catalog record id. Note that this function does
4718 * NOT resolve directory hard link to its directory inode and return
4721 * Note: The caller is responsible for releasing the output catalog
4722 * descriptor (when supplied outdescp is non-null).
4725 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4726 u_int8_t forktype
, struct cat_desc
*outdescp
,
4727 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4729 struct BTreeIterator
*iterator
= NULL
;
4730 FSBufferDescriptor btdata
;
4733 CatalogRecord
*recp
= NULL
;
4736 /* No directory hard links on standard HFS */
4737 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4741 iterator
= hfs_mallocz(sizeof(*iterator
));
4742 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4744 recp
= hfs_malloc(sizeof(CatalogRecord
));
4745 BDINIT(btdata
, recp
);
4747 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4748 &btdata
, &datasize
, iterator
);
4752 /* Directory hard links are catalog file record */
4753 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4758 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4759 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4760 (keyp
->hfsPlus
.nodeName
.length
* 2);
4761 if (forktype
== kHFSResourceForkType
) {
4762 /* Lookup resource fork for directory hard link */
4763 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4765 /* Lookup data fork, if any, for directory hard link */
4766 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4769 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4770 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4773 /* Just for sanity, make sure that id in catalog record and thread record match */
4774 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4775 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4776 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4782 hfs_free(recp
, sizeof(*recp
));
4784 hfs_free(iterator
, sizeof(*iterator
));
4786 return MacToVFSError(error
);
4790 * cnode_update_dirlink - update the catalog node for directory hard link
4791 * described by descp using the data from attrp and forkp.
4794 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4795 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4797 if (forktype
== kHFSResourceForkType
) {
4798 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4800 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);
4804 void hfs_fork_copy(struct cat_fork
*dst
, const struct cat_fork
*src
,
4805 HFSPlusExtentDescriptor
*extents
)
4807 /* Copy everything but the extents into the dest fork */
4808 memcpy(dst
, src
, offsetof(struct cat_fork
, cf_extents
));
4809 /* Then copy the supplied extents into the fork */
4810 memcpy(dst
->cf_extents
, extents
, sizeof(HFSPlusExtentRecord
));