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
1140 * NOTE: both the catalog file and attribute file locks must
1141 * be held before calling this function.
1143 * The caller is responsible for releasing the output
1144 * catalog descriptor (when supplied outdescp is non-null).
1147 cat_create(struct hfsmount
*hfsmp
, cnid_t new_fileid
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1148 struct cat_desc
*out_descp
)
1152 FSBufferDescriptor btdata
;
1156 u_int32_t encoding
= kTextEncodingMacRoman
;
1159 modeformat
= attrp
->ca_mode
& S_IFMT
;
1161 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1162 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1164 /* The caller is expected to reserve a CNID before calling this function! */
1166 /* Get space for iterator, key and data */
1167 bto
= hfs_malloc(sizeof(struct btobj
));
1168 bto
->iterator
.hint
.nodeNum
= 0;
1170 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
1175 * Insert the thread record first
1177 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
1178 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
1179 S_ISDIR(attrp
->ca_mode
));
1180 btdata
.bufferAddress
= &bto
->data
;
1181 btdata
.itemSize
= datalen
;
1182 btdata
.itemCount
= 1;
1184 /* Caller asserts the following:
1185 * 1) this CNID is not in use by any orphaned EAs
1186 * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
1187 * 3) There are no thread or catalog records for this ID
1189 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
1190 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1197 * Now insert the file/directory record
1199 buildrecord(attrp
, new_fileid
, std_hfs
, encoding
, &bto
->data
, &datalen
);
1200 btdata
.bufferAddress
= &bto
->data
;
1201 btdata
.itemSize
= datalen
;
1202 btdata
.itemCount
= 1;
1204 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
1206 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1208 if (result
== btExists
)
1211 /* Back out the thread record */
1212 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
1213 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
1214 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
1215 /* Error on deleting extra thread record, mark
1216 * volume inconsistent
1218 printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid
, hfsmp
->vcbVN
);
1219 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1226 * Insert was successful, update name, parent and volume
1228 if (out_descp
!= NULL
) {
1229 HFSPlusCatalogKey
* pluskey
= NULL
;
1232 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1236 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1237 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1241 builddesc(pluskey
, new_fileid
, bto
->iterator
.hint
.nodeNum
,
1242 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1245 hfs_free(pluskey
, sizeof(*pluskey
));
1250 attrp
->ca_fileid
= new_fileid
;
1253 (void) BTFlushPath(fcb
);
1254 hfs_free(bto
, sizeof(*bto
));
1256 return MacToVFSError(result
);
1261 * cnode_rename - rename a catalog node
1263 * Assumes that the target's directory exists.
1265 * Order of B-tree operations:
1266 * 1. BTSearchRecord(from_cnode, &data);
1267 * 2. BTInsertRecord(to_cnode, &data);
1268 * 3. BTDeleteRecord(from_cnode);
1269 * 4. BTDeleteRecord(from_thread);
1270 * 5. BTInsertRecord(to_thread);
1272 * Note: The caller is responsible for releasing the output
1273 * catalog descriptor (when supplied out_cdp is non-null).
1277 struct hfsmount
* hfsmp
,
1278 struct cat_desc
* from_cdp
,
1279 struct cat_desc
* todir_cdp
,
1280 struct cat_desc
* to_cdp
,
1281 struct cat_desc
* out_cdp
)
1283 struct BTreeIterator
* to_iterator
= NULL
;
1284 struct BTreeIterator
* from_iterator
= NULL
;
1285 FSBufferDescriptor btdata
;
1286 CatalogRecord
* recp
= NULL
;
1287 HFSPlusCatalogKey
* to_key
;
1294 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1297 u_int32_t encoding
= 0;
1299 vcb
= HFSTOVCB(hfsmp
);
1300 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1301 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1303 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1306 from_iterator
= hfs_mallocz(sizeof(*from_iterator
));
1307 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1310 to_iterator
= hfs_mallocz(sizeof(*to_iterator
));
1311 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1314 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1315 recp
= hfs_malloc(sizeof(CatalogRecord
));
1316 BDINIT(btdata
, recp
);
1319 * When moving a directory, make sure its a valid move.
1321 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1322 struct BTreeIterator
*dir_iterator
= NULL
;
1324 cnid_t cnid
= from_cdp
->cd_cnid
;
1325 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1327 /* First check the obvious ones */
1328 if (cnid
== fsRtDirID
||
1329 cnid
== to_cdp
->cd_parentcnid
||
1334 /* now allocate the dir_iterator */
1335 dir_iterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
1338 * Traverse destination path all the way back to the root
1339 * making sure that source directory is not encountered.
1342 while (pathcnid
> fsRtDirID
) {
1343 buildthreadkey(pathcnid
, std_hfs
, (CatalogKey
*)&dir_iterator
->key
);
1344 result
= BTSearchRecord(fcb
, dir_iterator
, &btdata
, &datasize
, NULL
);
1346 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1349 pathcnid
= getparentcnid(recp
);
1350 if (pathcnid
== cnid
|| pathcnid
== 0) {
1352 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1356 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1360 * Step 1: Find cnode data at old location
1362 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1363 &datasize
, from_iterator
);
1365 if (std_hfs
|| (result
!= btNotFound
))
1368 struct cat_desc temp_desc
;
1370 /* Probably the node has mangled name */
1371 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1375 /* The file has mangled name. Search the cnode data using full name */
1376 bzero(from_iterator
, sizeof(*from_iterator
));
1377 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1379 cat_releasedesc(&temp_desc
);
1383 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1385 cat_releasedesc(&temp_desc
);
1389 cat_releasedesc(&temp_desc
);
1392 /* Check if the source is directory hard link. We do not change
1393 * directory flag because it is later used to initialize result descp
1397 (recp
->recordType
== kHFSPlusFileRecord
) &&
1398 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1403 * Update the text encoding (on disk and in descriptor).
1405 * Note that hardlink inodes don't require a text encoding hint.
1408 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1409 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1410 recp
->hfsPlusFile
.textEncoding
= kTextEncodingMacUnicode
;
1414 if (std_hfs
&& !directory
&&
1415 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
)) {
1422 * If the keys are identical then there's nothing left to do!
1424 * update the hint and exit
1427 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1429 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1433 /* Step 2: Insert cnode at new location */
1434 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1435 if (result
== btExists
) {
1436 int fromtype
= recp
->recordType
;
1439 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1440 goto exit
; /* EEXIST */
1442 /* Find cnode data at new location */
1443 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1447 /* Get the CNID after calling searchrecord */
1448 cnid
= getcnid (recp
);
1450 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1455 if ((fromtype
!= recp
->recordType
) ||
1456 (from_cdp
->cd_cnid
!= cnid
)) {
1458 goto exit
; /* EEXIST */
1460 /* The old name is a case variant and must be removed */
1461 result
= BTDeleteRecord(fcb
, from_iterator
);
1465 /* Insert cnode (now that case duplicate is gone) */
1466 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1468 /* Try and restore original before leaving */
1473 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1475 printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err
);
1476 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1482 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1491 /* Step 3: Remove cnode from old location */
1493 result
= BTDeleteRecord(fcb
, from_iterator
);
1495 /* Try and delete new record before leaving */
1500 err
= BTDeleteRecord(fcb
, to_iterator
);
1502 printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err
);
1503 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1509 (void) BTDeleteRecord(fcb
, to_iterator
);
1515 /* #### POINT OF NO RETURN #### */
1518 * Step 4: Remove cnode's old thread record
1520 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1521 (void) BTDeleteRecord(fcb
, from_iterator
);
1524 * Step 5: Insert cnode's new thread record
1525 * (optional for HFS files)
1528 /* For directory hard links, always create a file thread
1529 * record. For everything else, use the directory flag.
1532 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1534 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1536 btdata
.itemSize
= datasize
;
1537 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1538 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1542 HFSPlusCatalogKey
* pluskey
= NULL
;
1545 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1549 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1550 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1552 /* Save the real encoding hint in the Finder Info (field 4). */
1553 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1556 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1557 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1562 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1563 encoding
, directory
, out_cdp
);
1566 hfs_free(pluskey
, sizeof(*pluskey
));
1572 (void) BTFlushPath(fcb
);
1574 hfs_free(from_iterator
, sizeof(*from_iterator
));
1576 hfs_free(to_iterator
, sizeof(*to_iterator
));
1578 hfs_free(recp
, sizeof(*recp
));
1579 return MacToVFSError(result
);
1584 * cat_delete - delete a node from the catalog
1586 * Order of B-tree operations:
1587 * 1. BTDeleteRecord(cnode);
1588 * 2. BTDeleteRecord(thread);
1589 * 3. BTUpdateRecord(parent);
1592 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1595 BTreeIterator
*iterator
;
1600 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1601 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1605 * The root directory cannot be deleted
1606 * A directory must be empty
1607 * A file must be zero length (no blocks)
1609 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1610 descp
->cd_parentcnid
== kHFSRootParentID
)
1613 /* XXX Preflight Missing */
1615 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1616 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1617 iterator
->hint
.nodeNum
= 0;
1620 * Derive a key from either the file ID (for a virtual inode)
1621 * or the descriptor.
1623 if (descp
->cd_namelen
== 0) {
1624 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1625 cnid
= attrp
->ca_fileid
;
1627 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1628 cnid
= descp
->cd_cnid
;
1634 result
= BTDeleteRecord(fcb
, iterator
);
1636 if (std_hfs
|| (result
!= btNotFound
))
1639 struct cat_desc temp_desc
;
1641 /* Probably the node has mangled name */
1642 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1646 /* The file has mangled name. Delete the file using full name */
1647 bzero(iterator
, sizeof(*iterator
));
1648 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1649 cnid
= temp_desc
.cd_cnid
;
1651 cat_releasedesc(&temp_desc
);
1655 result
= BTDeleteRecord(fcb
, iterator
);
1657 cat_releasedesc(&temp_desc
);
1661 cat_releasedesc(&temp_desc
);
1664 /* Delete thread record. On error, mark volume inconsistent */
1665 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1666 if (BTDeleteRecord(fcb
, iterator
)) {
1668 printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid
, hfsmp
->vcbVN
);
1669 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
1674 (void) BTFlushPath(fcb
);
1676 return MacToVFSError(result
);
1681 * cat_update_internal - update the catalog node described by descp
1682 * using the data from attrp and forkp.
1683 * If update_hardlink is true, the hard link catalog record is updated
1684 * and not the inode catalog record.
1687 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1688 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1691 BTreeIterator
* iterator
;
1692 struct update_state state
;
1695 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1697 state
.s_desc
= descp
;
1698 state
.s_attr
= attrp
;
1699 state
.s_datafork
= dataforkp
;
1700 state
.s_rsrcfork
= rsrcforkp
;
1701 state
.s_hfsmp
= hfsmp
;
1703 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1704 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1707 * For open-deleted files we need to do a lookup by cnid
1708 * (using thread rec).
1710 * For hard links and if not requested by caller, the target
1711 * of the update is the inode itself (not the link record)
1712 * so a lookup by fileid (i.e. thread rec) is needed.
1714 if ((update_hardlink
== false) &&
1715 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1716 (descp
->cd_namelen
== 0) ||
1717 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1718 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1720 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1725 /* Pass a node hint */
1726 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1728 result
= BTUpdateRecord(fcb
, iterator
,
1729 (IterateCallBackProcPtr
)catrec_update
, &state
);
1733 /* Update the node hint. */
1734 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1737 (void) BTFlushPath(fcb
);
1739 return MacToVFSError(result
);
1743 * cat_update - update the catalog node described by descp
1744 * using the data from attrp and forkp.
1747 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1748 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1750 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1754 * catrec_update - Update the fields of a catalog record
1755 * This is called from within BTUpdateRecord.
1758 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1760 struct cat_desc
*descp
;
1761 struct cat_attr
*attrp
;
1762 const struct cat_fork
*forkp
;
1763 struct hfsmount
*hfsmp
;
1766 descp
= state
->s_desc
;
1767 attrp
= state
->s_attr
;
1768 hfsmp
= state
->s_hfsmp
;
1769 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1771 switch (crp
->recordType
) {
1774 case kHFSFolderRecord
: {
1775 HFSCatalogFolder
*dir
;
1777 dir
= (struct HFSCatalogFolder
*)crp
;
1778 /* Do a quick sanity check */
1779 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1780 (dir
->folderID
!= descp
->cd_cnid
))
1781 return (btNotFound
);
1782 dir
->valence
= attrp
->ca_entries
;
1783 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1784 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1785 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1786 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1787 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1790 case kHFSFileRecord
: {
1791 HFSCatalogFile
*file
;
1794 file
= (struct HFSCatalogFile
*)crp
;
1795 /* Do a quick sanity check */
1796 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1797 (file
->fileID
!= attrp
->ca_fileid
))
1798 return (btNotFound
);
1799 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1800 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1801 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1802 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1803 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1804 if (state
->s_rsrcfork
) {
1805 forkp
= state
->s_rsrcfork
;
1806 file
->rsrcLogicalSize
= forkp
->cf_size
;
1807 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1808 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1809 file
->rsrcExtents
[i
].startBlock
=
1810 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1811 file
->rsrcExtents
[i
].blockCount
=
1812 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1815 if (state
->s_datafork
) {
1816 forkp
= state
->s_datafork
;
1817 file
->dataLogicalSize
= forkp
->cf_size
;
1818 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1819 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1820 file
->dataExtents
[i
].startBlock
=
1821 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1822 file
->dataExtents
[i
].blockCount
=
1823 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1827 /* Synchronize the lock state */
1828 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1829 file
->flags
|= kHFSFileLockedMask
;
1831 file
->flags
&= ~kHFSFileLockedMask
;
1836 case kHFSPlusFolderRecord
: {
1837 HFSPlusCatalogFolder
*dir
;
1839 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1840 /* Do a quick sanity check */
1841 if (dir
->folderID
!= attrp
->ca_fileid
) {
1842 printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir
->folderID
, attrp
->ca_fileid
, hfsmp
->vcbVN
);
1843 return (btNotFound
);
1845 dir
->flags
= attrp
->ca_recflags
;
1846 dir
->valence
= attrp
->ca_entries
;
1847 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1848 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1849 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1850 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1851 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1852 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1853 /* Note: directory hardlink inodes don't require a text encoding hint. */
1854 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1855 dir
->textEncoding
= descp
->cd_encoding
;
1857 dir
->folderCount
= attrp
->ca_dircount
;
1858 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1860 * Update the BSD Info if it was already initialized on
1861 * disk or if the runtime values have been modified.
1863 * If the BSD info was already initialized, but
1864 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1865 * probably different than what was on disk. We don't want
1866 * to overwrite the on-disk values (so if we turn off
1867 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1868 * This way, we can still change fields like the mode or
1869 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1871 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1872 * won't change the uid or gid from their defaults. So, if
1873 * the BSD info wasn't set, and the runtime values are not
1874 * default, then what changed was the mode or flags. We
1875 * have to set the uid and gid to something, so use the
1876 * supplied values (which will be default), which has the
1877 * same effect as creating a new file while
1878 * MNT_UNKNOWNPERMISSIONS is set.
1880 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1881 (attrp
->ca_flags
!= 0) ||
1882 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1883 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1884 ((attrp
->ca_mode
& ALLPERMS
) !=
1885 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1886 if ((dir
->bsdInfo
.fileMode
== 0) ||
1887 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1888 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1889 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1891 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1892 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1893 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1894 /* A directory hardlink has a link count. */
1895 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1896 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1901 case kHFSPlusFileRecord
: {
1902 HFSPlusCatalogFile
*file
;
1905 file
= (struct HFSPlusCatalogFile
*)crp
;
1906 /* Do a quick sanity check */
1907 if (file
->fileID
!= attrp
->ca_fileid
)
1908 return (btNotFound
);
1909 file
->flags
= attrp
->ca_recflags
;
1910 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1911 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1912 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1913 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1914 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1915 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1917 * Note: file hardlink inodes don't require a text encoding
1918 * hint, but they do have a first link value.
1920 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1921 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1923 file
->textEncoding
= descp
->cd_encoding
;
1925 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1927 * Update the BSD Info if it was already initialized on
1928 * disk or if the runtime values have been modified.
1930 * If the BSD info was already initialized, but
1931 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1932 * probably different than what was on disk. We don't want
1933 * to overwrite the on-disk values (so if we turn off
1934 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1935 * This way, we can still change fields like the mode or
1936 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1938 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1939 * won't change the uid or gid from their defaults. So, if
1940 * the BSD info wasn't set, and the runtime values are not
1941 * default, then what changed was the mode or flags. We
1942 * have to set the uid and gid to something, so use the
1943 * supplied values (which will be default), which has the
1944 * same effect as creating a new file while
1945 * MNT_UNKNOWNPERMISSIONS is set.
1947 * Do not modify bsdInfo for directory hard link records.
1948 * They are set during creation and are not modifiable, so just
1951 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1952 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1953 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1956 ((file
->bsdInfo
.fileMode
!= 0) ||
1957 (attrp
->ca_flags
!= 0) ||
1958 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1959 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1960 ((attrp
->ca_mode
& ALLPERMS
) !=
1961 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1962 if ((file
->bsdInfo
.fileMode
== 0) ||
1963 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1964 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1965 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1967 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1968 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1969 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1971 if (state
->s_rsrcfork
) {
1972 forkp
= state
->s_rsrcfork
;
1973 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1974 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1975 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1976 sizeof(HFSPlusExtentRecord
));
1977 /* Push blocks read to disk */
1978 file
->resourceFork
.clumpSize
=
1979 howmany(forkp
->cf_bytesread
, blksize
);
1981 if (state
->s_datafork
) {
1982 forkp
= state
->s_datafork
;
1983 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1984 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1985 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1986 sizeof(HFSPlusExtentRecord
));
1987 /* Push blocks read to disk */
1988 file
->dataFork
.clumpSize
=
1989 howmany(forkp
->cf_bytesread
, blksize
);
1992 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1993 (file
->resourceFork
.extents
[0].startBlock
==
1994 file
->dataFork
.extents
[0].startBlock
)) {
1995 panic("hfs: catrec_update: rsrc fork == data fork");
1998 /* Synchronize the lock state */
1999 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
2000 file
->flags
|= kHFSFileLockedMask
;
2002 file
->flags
&= ~kHFSFileLockedMask
;
2004 /* Push out special field if necessary */
2005 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
2006 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
2010 * Protect against the degenerate case where the descriptor contains the
2011 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
2012 * the linkcount was greater than 1 (the default value), then it must have become
2013 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
2015 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
2016 (file
->hl_linkCount
> 1)) {
2017 file
->hl_linkCount
= attrp
->ca_linkcount
;
2023 return (btNotFound
);
2028 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
2029 * catalog btree of given cnid by walking up the parent chain till it reaches
2030 * either the root folder, or the private metadata directory for storing
2031 * directory hard links. This function updates the corresponding in-core
2032 * cnode, if any, and the directory record in the catalog btree.
2033 * On success, returns zero. On failure, returns non-zero value.
2036 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
2040 struct cat_desc desc
;
2041 struct cat_attr attr
;
2043 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
2044 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
2045 /* Update the bit in corresponding cnode, if any, in the hash.
2046 * If the cnode has the bit already set, stop the traversal.
2048 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
2053 /* Update the catalog record on disk if either cnode was not
2054 * found in the hash, or if a cnode was found and the cnode
2055 * did not have the bit set previously.
2057 retval
= hfs_start_transaction(hfsmp
);
2061 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2063 /* Look up our catalog folder record */
2064 retval
= cat_idlookup(hfsmp
, cnid
, 0, 0, &desc
, &attr
, NULL
);
2066 hfs_systemfile_unlock(hfsmp
, lockflags
);
2067 hfs_end_transaction(hfsmp
);
2071 /* Update the bit in the catalog record */
2072 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
2073 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
2075 hfs_systemfile_unlock(hfsmp
, lockflags
);
2076 hfs_end_transaction(hfsmp
);
2077 cat_releasedesc(&desc
);
2081 hfs_systemfile_unlock(hfsmp
, lockflags
);
2082 hfs_end_transaction(hfsmp
);
2084 cnid
= desc
.cd_parentcnid
;
2085 cat_releasedesc(&desc
);
2091 /* This function traverses the parent directory hierarchy from the given
2092 * directory to one level below root directory and checks if any of its
2094 * 1. A directory hard link.
2095 * 2. The 'pointed at' directory.
2096 * If any of these conditions fail or an internal error is encountered
2097 * during look up of the catalog record, this function returns non-zero value.
2100 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
2102 HFSPlusCatalogKey
*keyp
;
2104 FSBufferDescriptor btdata
;
2105 HFSPlusCatalogFolder folder
;
2111 BDINIT(btdata
, &folder
);
2112 ip
= hfs_malloc(sizeof(*ip
));
2113 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
2114 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2116 while (cnid
!= kHFSRootParentID
) {
2117 /* Check if the 'pointed at' directory is an ancestor */
2118 if (pointed_at_cnid
== cnid
) {
2122 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
2123 printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2124 invalid
= 1; /* On errors, assume an invalid parent */
2127 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
2128 printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2129 invalid
= 1; /* On errors, assume an invalid parent */
2132 /* Check if this ancestor is a directory hard link */
2133 if (folder
.flags
& kHFSHasLinkChainMask
) {
2137 cnid
= keyp
->parentID
;
2139 hfs_free(ip
, sizeof(*ip
));
2145 * update_siblinglinks_callback - update a link's chain
2148 struct linkupdate_state
{
2155 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
2157 HFSPlusCatalogFile
*file
;
2159 if (crp
->recordType
!= kHFSPlusFileRecord
) {
2160 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
2161 return (btNotFound
);
2164 file
= (struct HFSPlusCatalogFile
*)crp
;
2165 if (file
->flags
& kHFSHasLinkChainMask
) {
2166 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
2167 file
->hl_prevLinkID
= state
->prevlinkid
;
2169 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
2170 file
->hl_nextLinkID
= state
->nextlinkid
;
2173 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
2179 * cat_update_siblinglinks - update a link's chain
2182 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
2185 BTreeIterator
* iterator
;
2186 struct linkupdate_state state
;
2189 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2190 state
.filelinkid
= linkfileid
;
2191 state
.prevlinkid
= prevlinkid
;
2192 state
.nextlinkid
= nextlinkid
;
2194 /* Create an iterator for use by us temporarily */
2195 iterator
= hfs_mallocz(sizeof(*iterator
));
2197 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
2199 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
2200 (void) BTFlushPath(fcb
);
2202 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid
, hfsmp
->vcbVN
);
2205 hfs_free(iterator
, sizeof(*iterator
));
2206 return MacToVFSError(result
);
2210 * cat_lookuplink - lookup a link by it's name
2213 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2216 BTreeIterator
* iterator
;
2217 struct FSBufferDescriptor btdata
;
2218 struct HFSPlusCatalogFile file
;
2221 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2223 /* Create an iterator for use by us temporarily */
2224 iterator
= hfs_mallocz(sizeof(*iterator
));
2226 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2229 BDINIT(btdata
, &file
);
2231 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2234 if (file
.recordType
!= kHFSPlusFileRecord
) {
2238 *linkfileid
= file
.fileID
;
2240 if (file
.flags
& kHFSHasLinkChainMask
) {
2241 *prevlinkid
= file
.hl_prevLinkID
;
2242 *nextlinkid
= file
.hl_nextLinkID
;
2248 hfs_free(iterator
, sizeof(*iterator
));
2249 return MacToVFSError(result
);
2254 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2257 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2260 BTreeIterator
* iterator
;
2261 struct FSBufferDescriptor btdata
;
2262 struct HFSPlusCatalogFile file
;
2265 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2267 /* Create an iterator for use by us temporarily */
2268 iterator
= hfs_mallocz(sizeof(*iterator
));
2270 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2273 BDINIT(btdata
, &file
);
2275 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2278 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2279 if (file
.flags
& kHFSHasLinkChainMask
) {
2282 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2284 /* directory inodes don't have a chain (its in an EA) */
2285 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2286 result
= ENOLINK
; /* signal to caller to get head of list */
2287 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2289 *nextlinkid
= file
.hl_firstLinkID
;
2291 *prevlinkid
= file
.hl_prevLinkID
;
2292 *nextlinkid
= file
.hl_nextLinkID
;
2299 hfs_free(iterator
, sizeof(*iterator
));
2300 return MacToVFSError(result
);
2305 * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
2308 cat_lookup_lastlink(struct hfsmount
*hfsmp
, cnid_t linkfileid
,
2309 cnid_t
*lastlink
, struct cat_desc
*cdesc
)
2312 BTreeIterator
* iterator
;
2313 struct FSBufferDescriptor btdata
;
2314 struct HFSPlusCatalogFile file
;
2318 cnid_t currentlink
= linkfileid
;
2320 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2322 /* Create an iterator for use by us temporarily */
2323 iterator
= hfs_malloc(sizeof(*iterator
));
2325 while ((foundlast
== 0) && (itercount
< HFS_LINK_MAX
)) {
2327 bzero(iterator
, sizeof(*iterator
));
2329 if ((result
= getkey(hfsmp
, currentlink
, (CatalogKey
*)&iterator
->key
))) {
2332 BDINIT(btdata
, &file
);
2334 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2338 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2339 if (file
.flags
& kHFSHasLinkChainMask
) {
2342 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2344 * The raw inode for a directory hardlink doesn't have a chain.
2345 * Its link information lives in an EA.
2347 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2348 /* We don't iterate to find the oldest directory hardlink. */
2352 else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2353 /* Raw inode for file hardlink (the base inode) */
2354 currentlink
= file
.hl_firstLinkID
;
2357 * One minor special-casing here is necessary.
2358 * If our ID brought us to the raw hardlink inode, and it does
2359 * not have any siblings, then it's an open-unlinked file, and we
2360 * should not proceed any further.
2362 if (currentlink
== 0) {
2368 /* Otherwise, this item's parent is a legitimate directory in the namespace */
2369 if (file
.hl_nextLinkID
== 0) {
2370 /* If nextLinkID is 0, then we found the end; no more hardlinks */
2372 *lastlink
= currentlink
;
2374 * Since we had to construct a catalog key to do this lookup
2375 * we still hold it in-hand. We might as well use it to build
2376 * the descriptor that the caller asked for.
2378 builddesc ((HFSPlusCatalogKey
*)&iterator
->key
, currentlink
, 0, 0, 0, cdesc
);
2382 currentlink
= file
.hl_nextLinkID
;
2386 /* Sorry, can't help you without a link chain */
2392 /* If we didn't find what we were looking for, zero out the args */
2393 if (foundlast
== 0) {
2395 bzero (cdesc
, sizeof(struct cat_desc
));
2402 hfs_free(iterator
, sizeof(*iterator
));
2403 return MacToVFSError(result
);
2408 * cat_createlink - create a link in the catalog
2410 * The following cat_attr fields are expected to be set:
2416 * ca_finderinfo (type and creator)
2419 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2420 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2424 FSBufferDescriptor btdata
;
2425 HFSPlusForkData
*rsrcforkp
;
2428 int thread_inserted
= 0;
2429 int alias_allocated
= 0;
2433 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
2435 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2438 * Get the next CNID. Note that we are currently holding catalog lock.
2440 result
= cat_acquire_cnid(hfsmp
, &nextCNID
);
2445 /* Get space for iterator, key and data */
2446 bto
= hfs_malloc(sizeof(struct btobj
));
2447 bto
->iterator
.hint
.nodeNum
= 0;
2448 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2450 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2452 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2457 * Insert the thread record first.
2459 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2460 btdata
.bufferAddress
= &bto
->data
;
2461 btdata
.itemSize
= datalen
;
2462 btdata
.itemCount
= 1;
2464 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2465 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2469 thread_inserted
= 1;
2472 * Now insert the link record.
2474 buildrecord(attrp
, nextCNID
, 0, kTextEncodingMacUnicode
, &bto
->data
, &datalen
);
2476 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2477 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2478 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2480 /* For directory hard links, create alias in resource fork */
2481 if (descp
->cd_flags
& CD_ISDIR
) {
2482 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2485 alias_allocated
= 1;
2487 btdata
.bufferAddress
= &bto
->data
;
2488 btdata
.itemSize
= datalen
;
2489 btdata
.itemCount
= 1;
2491 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2493 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2495 if (result
== btExists
)
2499 if (linkfileid
!= NULL
) {
2500 *linkfileid
= nextCNID
;
2504 if (thread_inserted
) {
2505 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result
), hfsmp
->vcbVN
);
2507 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2508 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2509 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2510 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
2513 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2514 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2515 rsrcforkp
->extents
[0].blockCount
, 0);
2516 rsrcforkp
->extents
[0].startBlock
= 0;
2517 rsrcforkp
->extents
[0].blockCount
= 0;
2520 (void) BTFlushPath(fcb
);
2521 hfs_free(bto
, sizeof(*bto
));
2523 return MacToVFSError(result
);
2526 /* Directory hard links are visible as aliases on pre-Leopard systems and
2527 * as normal directories on Leopard or later. All directory hard link aliases
2528 * have the same resource fork content except for the three uniquely
2529 * identifying values that are updated in the resource fork data when the alias
2530 * is created. The following array is the constant resource fork data used
2531 * only for creating directory hard link aliases.
2533 static const char hfs_dirlink_alias_rsrc
[] = {
2534 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2535 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2536 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2537 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2538 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2539 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2540 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2541 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2542 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2543 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2544 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 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, 0x48, 0x2b,
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, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2560 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2562 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2565 /* Constants for directory hard link alias */
2567 /* Size of resource fork data array for directory hard link alias */
2568 kHFSAliasSize
= 0x1d0,
2570 /* Volume type for ejectable devices like disk image */
2571 kHFSAliasVolTypeEjectable
= 0x5,
2573 /* Offset for volume create date, in Mac OS local time */
2574 kHFSAliasVolCreateDateOffset
= 0x12a,
2576 /* Offset for the type of volume */
2577 kHFSAliasVolTypeOffset
= 0x130,
2579 /* Offset for folder ID of the parent directory of the directory inode */
2580 kHFSAliasParentIDOffset
= 0x132,
2582 /* Offset for folder ID of the directory inode */
2583 kHFSAliasTargetIDOffset
= 0x176,
2586 /* Create and write an alias that points at the directory represented by given
2587 * inode number on the same volume. Directory hard links are visible as
2588 * aliases in pre-Leopard systems and this function creates these aliases.
2590 * Note: This code is very specific to creating alias for the purpose
2591 * of directory hard links only, and should not be generalized.
2594 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2602 HFSPlusForkData
*rsrcforkp
;
2606 rsrcforkp
= &(crp
->resourceFork
);
2608 blksize
= hfsmp
->blockSize
;
2609 blkcount
= howmany(kHFSAliasSize
, blksize
);
2610 sectorsize
= hfsmp
->hfs_logical_block_size
;
2611 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2613 /* Allocate some disk space for the alias content. */
2614 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2615 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2616 &rsrcforkp
->extents
[0].startBlock
,
2617 &rsrcforkp
->extents
[0].blockCount
);
2618 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2619 if (result
== dskFulErr
) {
2620 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2621 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
| HFS_ALLOC_FLUSHTXN
,
2622 &rsrcforkp
->extents
[0].startBlock
,
2623 &rsrcforkp
->extents
[0].blockCount
);
2626 rsrcforkp
->extents
[0].startBlock
= 0;
2630 /* Acquire a buffer cache block for our block. */
2631 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2632 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2634 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2636 journal_modify_block_start(hfsmp
->jnl
, bp
);
2639 /* Generate alias content */
2640 alias
= (char *)buf_dataptr(bp
);
2641 bzero(alias
, buf_size(bp
));
2642 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2644 /* Set the volume create date, local time in Mac OS format */
2645 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2646 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2648 /* If the file system is on a virtual device like disk image,
2649 * update the volume type to be ejectable device.
2651 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2652 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2653 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2656 /* Set id of the parent of the target directory */
2657 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2658 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2660 /* Set id of the target directory */
2661 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2662 *valptr
= OSSwapHostToBigInt32(inode_num
);
2664 /* Write alias content to disk. */
2666 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2667 } else if ((result
= buf_bwrite(bp
))) {
2671 /* Finish initializing the fork data. */
2672 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2673 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2676 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2677 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2678 rsrcforkp
->extents
[0].startBlock
= 0;
2679 rsrcforkp
->extents
[0].blockCount
= 0;
2680 rsrcforkp
->logicalSize
= 0;
2681 rsrcforkp
->totalBlocks
= 0;
2687 * cat_deletelink - delete a link from the catalog
2690 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2692 struct HFSPlusCatalogFile file
;
2693 struct cat_attr cattr
;
2694 uint32_t totalBlocks
;
2698 bzero(&file
, sizeof (file
));
2699 bzero(&cattr
, sizeof (cattr
));
2700 cattr
.ca_fileid
= descp
->cd_cnid
;
2702 /* Directory links have alias content to remove. */
2703 if (descp
->cd_flags
& CD_ISDIR
) {
2705 BTreeIterator
* iterator
;
2706 struct FSBufferDescriptor btdata
;
2708 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2710 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2711 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2712 iterator
->hint
.nodeNum
= 0;
2714 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2717 BDINIT(btdata
, &file
);
2719 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2724 result
= cat_delete(hfsmp
, descp
, &cattr
);
2726 if ((result
== 0) &&
2727 (descp
->cd_flags
& CD_ISDIR
) &&
2728 (file
.recordType
== kHFSPlusFileRecord
)) {
2730 totalBlocks
= file
.resourceFork
.totalBlocks
;
2732 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2733 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2734 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2738 (void) BlockDeallocate(hfsmp
,
2739 file
.resourceFork
.extents
[i
].startBlock
,
2740 file
.resourceFork
.extents
[i
].blockCount
, 0);
2742 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2743 file
.resourceFork
.extents
[i
].startBlock
= 0;
2744 file
.resourceFork
.extents
[i
].blockCount
= 0;
2753 * Callback to collect directory entries.
2754 * Called with readattr_state for each item in a directory.
2756 struct readattr_state
{
2757 struct hfsmount
*hfsmp
;
2758 struct cat_entrylist
*list
;
2766 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2767 struct readattr_state
*state
)
2769 struct cat_entrylist
*list
= state
->list
;
2770 struct hfsmount
*hfsmp
= state
->hfsmp
;
2771 struct cat_entry
*cep
;
2774 if (list
->realentries
>= list
->maxentries
)
2775 return (0); /* stop */
2777 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2779 switch(rec
->recordType
) {
2780 case kHFSPlusFolderRecord
:
2781 case kHFSPlusFileRecord
:
2783 case kHFSFolderRecord
:
2784 case kHFSFileRecord
:
2786 if (parentcnid
!= state
->dir_cnid
) {
2787 state
->error
= ENOENT
;
2788 state
->reached_eof
= 1;
2789 return (0); /* stop */
2793 state
->error
= ENOENT
;
2794 return (0); /* stop */
2797 /* Hide the private system directories and journal files */
2798 if (parentcnid
== kHFSRootFolderID
) {
2799 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2800 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2801 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2802 list
->skipentries
++;
2803 return (1); /* continue */
2806 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2807 (rec
->recordType
== kHFSPlusFileRecord
) &&
2808 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2809 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2810 list
->skipentries
++;
2811 return (1); /* continue */
2815 cep
= &list
->entry
[list
->realentries
++];
2817 if (state
->stdhfs
== 0) {
2818 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2819 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2820 isadir(rec
), &cep
->ce_desc
);
2822 if (rec
->recordType
== kHFSPlusFileRecord
) {
2823 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2824 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2825 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2826 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2828 /* Save link reference for later processing. */
2829 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2830 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2831 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2832 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2833 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2834 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2835 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2841 struct HFSPlusCatalogFile cnoderec
;
2842 HFSPlusCatalogKey
* pluskey
;
2845 promoteattr(hfsmp
, rec
, &cnoderec
);
2846 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2848 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
2849 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2850 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2851 hfs_free(pluskey
, sizeof(*pluskey
));
2853 if (rec
->recordType
== kHFSFileRecord
) {
2854 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2856 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2857 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2858 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2859 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2864 return (list
->realentries
< list
->maxentries
);
2868 * Pack a cat_entrylist buffer with attributes from the catalog
2870 * Note: index is zero relative
2873 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
, int *reachedeof
)
2877 BTreeIterator
* iterator
;
2878 struct readattr_state state
;
2885 int reached_eof
= 0;
2887 ce_list
->realentries
= 0;
2889 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2890 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2891 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2893 bzero (&state
, sizeof(struct readattr_state
));
2895 state
.hfsmp
= hfsmp
;
2896 state
.list
= ce_list
;
2897 state
.dir_cnid
= parentcnid
;
2898 state
.stdhfs
= std_hfs
;
2901 iterator
= hfs_mallocz(sizeof(*iterator
));
2902 key
= (CatalogKey
*)&iterator
->key
;
2904 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2905 index
= dirhint
->dh_index
+ 1;
2908 * Attempt to build a key from cached filename
2910 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2911 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2917 * If the last entry wasn't cached then position the btree iterator
2919 if ((index
== 0) || !have_key
) {
2921 * Position the iterator at the directory's thread record.
2922 * (i.e. just before the first entry)
2924 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2925 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2927 result
= MacToVFSError(result
);
2932 * Iterate until we reach the entry just
2933 * before the one we want to start with.
2936 struct position_state ps
;
2941 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2944 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2945 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2949 result
= MacToVFSError(result
);
2953 * Note: the index may now point to EOF if the directory
2954 * was modified in between system calls. We will return
2955 * ENOENT from cat_findposition if this is the case, and
2956 * when we bail out with an error, our caller (hfs_readdirattr_internal)
2957 * will suppress the error and indicate EOF to its caller.
2959 result
= MacToVFSError(result
);
2965 /* Fill list with entries starting at iterator->key. */
2966 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2967 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2970 result
= state
.error
;
2971 reached_eof
= state
.reached_eof
;
2973 else if (ce_list
->realentries
== 0) {
2978 result
= MacToVFSError(result
);
2985 * Resolve any hard links.
2987 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2988 struct FndrFileInfo
*fip
;
2989 struct cat_entry
*cep
;
2990 struct HFSPlusCatalogFile filerec
;
2994 cep
= &ce_list
->entry
[i
];
2995 if (cep
->ce_attr
.ca_linkref
== 0)
2998 /* Note: Finder info is still in Big Endian */
2999 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
3001 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3002 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
3003 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
3006 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3007 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
3008 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
3009 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
3012 if (isfilelink
|| isdirlink
) {
3013 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
3015 /* Repack entry from inode record. */
3016 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
3017 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
3018 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
3019 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
3020 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
3025 hfs_free(iterator
, sizeof(*iterator
));
3026 *reachedeof
= reached_eof
;
3027 return MacToVFSError(result
);
3030 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3031 #define MAX_LINKINFO_ENTRIES 3000
3034 * Callback to pack directory entries.
3035 * Called with packdirentry_state for each item in a directory.
3038 /* Hard link information collected during cat_getdirentries. */
3041 user_addr_t dirent_addr
;
3043 typedef struct linkinfo linkinfo_t
;
3045 /* State information for the getdirentries_callback function. */
3046 struct packdirentry_state
{
3047 int cbs_flags
; /* VNODE_READDIR_* flags */
3048 u_int32_t cbs_parentID
;
3049 u_int32_t cbs_index
;
3051 ExtendedVCB
* cbs_hfsmp
;
3054 int32_t cbs_maxlinks
;
3055 linkinfo_t
* cbs_linkinfo
;
3056 struct cat_desc
* cbs_desc
;
3057 u_int8_t
* cbs_namebuf
;
3059 * The following fields are only used for NFS readdir, which
3060 * uses the next file id as the seek offset of each entry.
3062 struct direntry
* cbs_direntry
;
3063 struct direntry
* cbs_prevdirentry
;
3064 u_int32_t cbs_previlinkref
;
3065 Boolean cbs_hasprevdirentry
;
3070 * getdirentries callback for HFS Plus directories.
3073 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3074 struct packdirentry_state
*state
)
3076 struct hfsmount
*hfsmp
;
3077 const CatalogName
*cnp
;
3080 struct dirent catent
;
3081 struct direntry
* entry
= NULL
;
3083 u_int32_t ilinkref
= 0;
3084 u_int32_t curlinkref
= 0;
3087 u_int8_t type
= DT_UNKNOWN
;
3088 u_int8_t is_mangled
= 0;
3089 u_int8_t is_link
= 0;
3091 user_addr_t uiobase
= USER_ADDR_NULL
;
3096 Boolean stop_after_pack
= false;
3098 hfsmp
= state
->cbs_hfsmp
;
3099 curID
= ckp
->hfsPlus
.parentID
;
3101 /* We're done when parent directory changes */
3102 if (state
->cbs_parentID
!= curID
) {
3104 * If the parent ID is different from curID this means we've hit
3105 * the EOF for the directory. To help future callers, we mark
3106 * the cbs_eof boolean. However, we should only mark the EOF
3107 * boolean if we're about to return from this function.
3109 * This is because this callback function does its own uiomove
3110 * to get the data to userspace. If we set the boolean before determining
3111 * whether or not the current entry has enough room to write its
3112 * data to userland, we could fool the callers of this catalog function
3113 * into thinking they've hit EOF earlier than they really would have.
3114 * In that case, we'd know that we have more entries to process and
3115 * send to userland, but we didn't have enough room.
3117 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3118 * about to return and won't write any new data back
3119 * to userland. In the stop_after_pack case, we'll set this boolean
3120 * regardless, so it's slightly safer to let that logic mark the boolean,
3121 * especially since it's closer to the return of this function.
3124 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3125 /* The last record has not been returned yet, so we
3126 * want to stop after packing the last item
3128 if (state
->cbs_hasprevdirentry
) {
3129 stop_after_pack
= true;
3131 state
->cbs_eof
= true;
3132 state
->cbs_result
= ENOENT
;
3133 return (0); /* stop */
3136 state
->cbs_eof
= true;
3137 state
->cbs_result
= ENOENT
;
3138 return (0); /* stop */
3142 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3143 entry
= state
->cbs_direntry
;
3144 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3145 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3147 * The NFS server sometimes needs to make filenames fit in
3148 * NAME_MAX bytes (since its client may not be able to
3149 * handle a longer name). In that case, NFS will ask us
3150 * to mangle the name to keep it short enough.
3152 maxnamelen
= NAME_MAX
+ 1;
3154 maxnamelen
= sizeof(entry
->d_name
);
3157 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3158 maxnamelen
= sizeof(catent
.d_name
);
3161 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3162 /* The last item returns a non-zero invalid cookie */
3165 switch(crp
->recordType
) {
3166 case kHFSPlusFolderRecord
:
3168 cnid
= crp
->hfsPlusFolder
.folderID
;
3169 /* Hide our private system directories. */
3170 if (curID
== kHFSRootFolderID
) {
3171 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3172 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3177 case kHFSPlusFileRecord
:
3178 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3179 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3180 cnid
= crp
->hfsPlusFile
.fileID
;
3182 * When a hardlink link is encountered save its link ref.
3184 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3185 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3186 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3187 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3188 /* If link ref is inode's file id then use it directly. */
3189 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3190 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3192 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3195 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3196 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3197 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3198 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3199 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3200 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3201 /* A directory's link resolves to a directory. */
3203 /* A directory's link ref is always inode's file id. */
3204 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3207 /* Hide the journal files */
3208 if ((curID
== kHFSRootFolderID
) &&
3209 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3210 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3211 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3216 return (0); /* stop */
3219 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3221 namelen
= cnp
->ustr
.length
;
3223 * For MacRoman encoded names, assume that its ascii and
3224 * convert it directly in an attempt to avoid the more
3225 * expensive utf8_encodestr conversion.
3227 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3230 const u_int16_t
*chp
;
3232 chp
= &cnp
->ustr
.unicode
[0];
3233 for (i
= 0; i
< (int)namelen
; ++i
) {
3235 if (ch
> 0x007f || ch
== 0x0000) {
3236 /* Perform expensive utf8_encodestr conversion */
3239 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3241 nameptr
[namelen
] = '\0';
3245 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3246 nameptr
, &namelen
, maxnamelen
, ':', 0);
3249 /* Check result returned from encoding the filename to utf8 */
3250 if (result
== ENAMETOOLONG
) {
3252 * If we were looking at a catalog record for a hardlink (not the inode),
3253 * then we want to use its link ID as opposed to the inode ID for
3254 * a mangled name. For all other cases, they are the same. Note that
3255 * due to the way directory hardlinks are implemented, the actual link
3256 * is going to be counted as a file record, so we can catch both
3259 cnid_t linkid
= cnid
;
3261 linkid
= crp
->hfsPlusFile
.fileID
;
3264 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3265 cnp
->ustr
.unicode
, maxnamelen
,
3266 (ByteCount
*)&namelen
, nameptr
, linkid
);
3271 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3273 * The index is 1 relative and includes "." and ".."
3275 * Also stuff the cnid in the upper 32 bits of the cookie.
3276 * The cookie is stored to the previous entry, which will
3277 * be packed and copied this time
3279 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3280 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3281 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3283 catent
.d_type
= type
;
3284 catent
.d_namlen
= namelen
;
3285 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3287 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3289 catent
.d_fileno
= cnid
;
3290 uioaddr
= (caddr_t
) &catent
;
3293 /* Save current base address for post processing of hard-links. */
3294 if (ilinkref
|| state
->cbs_previlinkref
) {
3295 uiobase
= uio_curriovbase(state
->cbs_uio
);
3297 /* If this entry won't fit then we're done */
3298 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3299 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3300 return (0); /* stop */
3303 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3304 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3305 if (state
->cbs_result
== 0) {
3308 /* Remember previous entry */
3309 state
->cbs_desc
->cd_cnid
= cnid
;
3310 if (type
== DT_DIR
) {
3311 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3313 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3315 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3316 state
->cbs_desc
->cd_namelen
= 0;
3319 state
->cbs_desc
->cd_encoding
= xxxx
;
3322 state
->cbs_desc
->cd_namelen
= namelen
;
3323 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3325 /* Store unmangled name for the directory hint else it will
3326 * restart readdir at the last location again
3328 u_int8_t
*new_nameptr
;
3330 size_t tmp_namelen
= 0;
3332 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3333 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3334 cnp
->ustr
.length
* sizeof(UniChar
),
3336 new_nameptr
= hfs_malloc(bufsize
);
3337 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3338 cnp
->ustr
.length
* sizeof(UniChar
),
3339 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3341 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3342 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3344 hfs_free(new_nameptr
, bufsize
);
3347 if (state
->cbs_hasprevdirentry
) {
3348 curlinkref
= ilinkref
; /* save current */
3349 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3352 * Record any hard links for post processing.
3354 if ((ilinkref
!= 0) &&
3355 (state
->cbs_result
== 0) &&
3356 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3357 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3358 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3359 state
->cbs_nlinks
++;
3361 if (state
->cbs_hasprevdirentry
) {
3362 ilinkref
= curlinkref
; /* restore current */
3366 /* Fill the direntry to be used the next time */
3367 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3368 if (stop_after_pack
) {
3369 state
->cbs_eof
= true;
3370 return (0); /* stop */
3372 entry
->d_type
= type
;
3373 entry
->d_namlen
= namelen
;
3374 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3376 /* File number = 0 means skip entry */
3377 entry
->d_fileno
= 0;
3379 entry
->d_fileno
= cnid
;
3381 /* swap the current and previous entry */
3382 struct direntry
* tmp
;
3383 tmp
= state
->cbs_direntry
;
3384 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3385 state
->cbs_prevdirentry
= tmp
;
3386 state
->cbs_hasprevdirentry
= true;
3387 state
->cbs_previlinkref
= ilinkref
;
3390 /* Continue iteration if there's room */
3391 return (state
->cbs_result
== 0 &&
3392 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3397 * getdirentries callback for standard HFS (non HFS+) directories.
3400 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3401 struct packdirentry_state
*state
)
3403 struct hfsmount
*hfsmp
;
3404 const CatalogName
*cnp
;
3407 struct dirent catent
;
3409 u_int8_t type
= DT_UNKNOWN
;
3416 hfsmp
= state
->cbs_hfsmp
;
3418 curID
= ckp
->hfs
.parentID
;
3420 /* We're done when parent directory changes */
3421 if (state
->cbs_parentID
!= curID
) {
3422 state
->cbs_result
= ENOENT
;
3423 return (0); /* stop */
3426 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3427 maxnamelen
= sizeof(catent
.d_name
);
3429 switch(crp
->recordType
) {
3430 case kHFSFolderRecord
:
3432 cnid
= crp
->hfsFolder
.folderID
;
3434 case kHFSFileRecord
:
3436 cnid
= crp
->hfsFile
.fileID
;
3439 return (0); /* stop */
3442 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3443 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3445 * When an HFS name cannot be encoded with the current
3446 * volume encoding we use MacRoman as a fallback.
3449 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3451 catent
.d_type
= type
;
3452 catent
.d_namlen
= namelen
;
3453 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3454 catent
.d_fileno
= cnid
;
3455 uioaddr
= (caddr_t
) &catent
;
3457 /* If this entry won't fit then we're done */
3458 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3459 return (0); /* stop */
3462 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3463 if (state
->cbs_result
== 0) {
3466 /* Remember previous entry */
3467 state
->cbs_desc
->cd_cnid
= cnid
;
3468 if (type
== DT_DIR
) {
3469 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3471 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3473 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3474 state
->cbs_desc
->cd_namelen
= 0;
3476 state
->cbs_desc
->cd_namelen
= namelen
;
3477 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3480 /* Continue iteration if there's room */
3481 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3486 * Pack a uio buffer with directory entries from the catalog
3489 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3490 uio_t uio
, int flags
, int * items
, int * eofflag
)
3493 BTreeIterator
* iterator
;
3495 struct packdirentry_state state
;
3504 extended
= flags
& VNODE_READDIR_EXTENDED
;
3506 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3509 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3512 * Get a buffer for link info array, btree iterator and a direntry.
3514 * We impose an cap of 3000 link entries when trying to compute
3515 * the total number of hardlink entries that we'll allow in the
3518 * Note that in the case where there are very few hardlinks,
3519 * this does not restrict or prevent us from vending out as many entries
3520 * as we can to the uio_resid, because the getdirentries callback
3521 * uiomoves the directory entries to the uio itself and does not use
3522 * this MALLOC'd array. It also limits itself to maxlinks of hardlinks.
3525 /* Now compute the maximum link array size */
3526 maxlinks
= MIN (entrycnt
, MAX_LINKINFO_ENTRIES
);
3527 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3530 bufsize
+= 2*sizeof(struct direntry
);
3532 buffer
= hfs_mallocz(bufsize
);
3534 state
.cbs_flags
= flags
;
3535 state
.cbs_hasprevdirentry
= false;
3536 state
.cbs_previlinkref
= 0;
3537 state
.cbs_nlinks
= 0;
3538 state
.cbs_maxlinks
= maxlinks
;
3539 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3541 * We need to set cbs_eof to false regardless of whether or not the
3542 * control flow is actually in the extended case, since we use this
3543 * field to track whether or not we've returned EOF from the iterator function.
3545 state
.cbs_eof
= false;
3547 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3548 key
= (CatalogKey
*)&iterator
->key
;
3550 index
= dirhint
->dh_index
+ 1;
3552 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3553 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3556 * Attempt to build a key from cached filename
3558 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3559 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3560 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3565 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3567 * Position the iterator at the directory's thread record.
3568 * (i.e. just before the first entry)
3570 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3571 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3572 iterator
->hint
.index
= 0;
3577 * If the last entry wasn't cached then position the btree iterator
3581 * Position the iterator at the directory's thread record.
3582 * (i.e. just before the first entry)
3584 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3585 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3587 result
= MacToVFSError(result
);
3591 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3594 * Iterate until we reach the entry just
3595 * before the one we want to start with.
3598 struct position_state ps
;
3603 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3606 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3607 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3611 result
= MacToVFSError(result
);
3613 result
= MacToVFSError(result
);
3614 if (result
== ENOENT
) {
3616 * ENOENT means we've hit the EOF.
3617 * suppress the error, and set the eof flag.
3620 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3628 state
.cbs_index
= index
;
3629 state
.cbs_hfsmp
= hfsmp
;
3630 state
.cbs_uio
= uio
;
3631 state
.cbs_desc
= &dirhint
->dh_desc
;
3632 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3633 state
.cbs_result
= 0;
3634 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3636 /* Use a temporary buffer to hold intermediate descriptor names. */
3637 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3638 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3639 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3640 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3641 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3644 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3646 enum BTreeIterationOperations op
;
3647 if (extended
&& index
!= 0 && have_key
)
3648 op
= kBTreeCurrentRecord
;
3650 op
= kBTreeNextRecord
;
3653 * Process as many entries as possible starting at iterator->key.
3655 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3657 result
= BTIterateRecords(fcb
, op
, iterator
,
3658 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3660 /* For extended calls, every call to getdirentries_callback()
3661 * transfers the previous directory entry found to the user
3662 * buffer. Therefore when BTIterateRecords reaches the end of
3663 * Catalog BTree, call getdirentries_callback() again with
3664 * dummy values to copy the last directory entry stored in
3665 * packdirentry_state
3667 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3671 bzero(&ckp
, sizeof(ckp
));
3672 bzero(&crp
, sizeof(crp
));
3674 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3679 /* HFS (standard) */
3680 result
= BTIterateRecords(fcb
, op
, iterator
,
3681 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3685 /* Note that state.cbs_index is still valid on errors */
3686 *items
= state
.cbs_index
- index
;
3687 index
= state
.cbs_index
;
3690 * Also note that cbs_eof is set in all cases if we ever hit EOF
3691 * during the enumeration by the catalog callback. Mark the directory's hint
3692 * descriptor as having hit EOF.
3695 if (state
.cbs_eof
) {
3696 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3700 /* Finish updating the catalog iterator. */
3701 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3702 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3703 dirhint
->dh_index
= index
- 1;
3705 /* Fix up the name. */
3706 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3707 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3708 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3710 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3711 dirhint
->dh_desc
.cd_namelen
= 0;
3715 * Post process any hard links to get the real file id.
3717 if (state
.cbs_nlinks
> 0) {
3719 user_addr_t address
;
3722 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3723 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3725 /* This assumes that d_ino is always first field. */
3726 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3727 if (address
== (user_addr_t
)0)
3729 if (uio_isuserspace(uio
)) {
3731 ino64_t fileid_64
= (ino64_t
)fileid
;
3732 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3734 (void) copyout(&fileid
, address
, sizeof(fileid
));
3736 } else /* system space */ {
3738 ino64_t fileid_64
= (ino64_t
)fileid
;
3739 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3741 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3747 if (state
.cbs_result
)
3748 result
= state
.cbs_result
;
3750 result
= MacToVFSError(result
);
3752 if (result
== ENOENT
) {
3757 hfs_free(buffer
, bufsize
);
3764 * Callback to establish directory position.
3765 * Called with position_state for each item in a directory.
3768 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3769 struct position_state
*state
)
3773 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3774 curID
= ckp
->hfsPlus
.parentID
;
3778 curID
= ckp
->hfs
.parentID
;
3782 /* Make sure parent directory didn't change */
3783 if (state
->parentID
!= curID
) {
3785 * The parent ID is different from curID this means we've hit
3786 * the EOF for the directory.
3788 state
->error
= ENOENT
;
3789 return (0); /* stop */
3792 /* Count this entry */
3793 switch(crp
->recordType
) {
3794 case kHFSPlusFolderRecord
:
3795 case kHFSPlusFileRecord
:
3797 case kHFSFolderRecord
:
3798 case kHFSFileRecord
:
3803 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3804 crp
->recordType
, curID
);
3805 state
->error
= EINVAL
;
3806 return (0); /* stop */
3809 return (state
->count
< state
->index
);
3814 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3816 * The name portion of the key is compared using a 16-bit binary comparison.
3817 * This is called from the b-tree code.
3820 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3822 u_int32_t searchParentID
, trialParentID
;
3825 searchParentID
= searchKey
->parentID
;
3826 trialParentID
= trialKey
->parentID
;
3829 if (searchParentID
> trialParentID
) {
3831 } else if (searchParentID
< trialParentID
) {
3834 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3835 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3836 int length1
= searchKey
->nodeName
.length
;
3837 int length2
= trialKey
->nodeName
.length
;
3839 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3848 * Compare two standard HFS catalog keys
3850 * Result: +n search key > trial key
3851 * 0 search key = trial key
3852 * -n search key < trial key
3855 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3857 cnid_t searchParentID
, trialParentID
;
3860 searchParentID
= searchKey
->parentID
;
3861 trialParentID
= trialKey
->parentID
;
3863 if (searchParentID
> trialParentID
)
3865 else if (searchParentID
< trialParentID
)
3867 else /* parent dirID's are equal, compare names */
3868 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3876 * Compare two HFS+ catalog keys
3878 * Result: +n search key > trial key
3879 * 0 search key = trial key
3880 * -n search key < trial key
3883 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3885 cnid_t searchParentID
, trialParentID
;
3888 searchParentID
= searchKey
->parentID
;
3889 trialParentID
= trialKey
->parentID
;
3891 if (searchParentID
> trialParentID
) {
3894 else if (searchParentID
< trialParentID
) {
3897 /* parent node ID's are equal, compare names */
3898 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3899 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3901 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3902 searchKey
->nodeName
.length
,
3903 &trialKey
->nodeName
.unicode
[0],
3904 trialKey
->nodeName
.length
);
3912 * buildkey - build a Catalog b-tree key from a cnode descriptor
3915 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3916 HFSPlusCatalogKey
*key
, int retry
)
3918 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3919 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3921 size_t unicodeBytes
= 0;
3927 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3928 return (EINVAL
); /* invalid name */
3930 key
->parentID
= descp
->cd_parentcnid
;
3931 key
->nodeName
.length
= 0;
3933 * Convert filename from UTF-8 into Unicode
3936 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3937 utf8_flags
|= UTF_DECOMPOSED
;
3938 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3939 key
->nodeName
.unicode
, &unicodeBytes
,
3940 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3941 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3942 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3944 if (result
!= ENAMETOOLONG
)
3945 result
= EINVAL
; /* name has invalid characters */
3951 * For HFS volumes convert to an HFS compatible key
3953 * XXX need to save the encoding that succeeded
3956 HFSCatalogKey hfskey
;
3958 bzero(&hfskey
, sizeof(hfskey
));
3959 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3960 hfskey
.parentID
= key
->parentID
;
3961 hfskey
.nodeName
[0] = 0;
3962 if (key
->nodeName
.length
> 0) {
3964 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3965 key
->nodeName
.length
* 2,
3966 key
->nodeName
.unicode
,
3967 &hfskey
.nodeName
[0], retry
)) != 0) {
3968 if (res
!= ENAMETOOLONG
)
3973 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3975 bcopy(&hfskey
, key
, sizeof(hfskey
));
3984 * Resolve hard link reference to obtain the inode record.
3987 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3989 FSBufferDescriptor btdata
;
3990 struct BTreeIterator
*iterator
;
3991 struct cat_desc idesc
;
3996 BDINIT(btdata
, recp
);
3999 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4000 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4002 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4003 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4006 /* Get space for iterator */
4007 iterator
= hfs_mallocz(sizeof(*iterator
));
4009 /* Build a descriptor for private dir. */
4010 idesc
.cd_parentcnid
= parentcnid
;
4011 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4012 idesc
.cd_namelen
= strlen(inodename
);
4015 idesc
.cd_encoding
= 0;
4016 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4018 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4019 &btdata
, NULL
, NULL
);
4022 /* Make sure there's a reference */
4023 if (recp
->hl_linkCount
== 0)
4024 recp
->hl_linkCount
= 2;
4026 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4029 hfs_free(iterator
, sizeof(*iterator
));
4031 return (result
? ENOENT
: 0);
4035 * Resolve hard link reference to obtain the inode number.
4038 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4040 struct HFSPlusCatalogFile record
;
4044 * Since we know resolvelinkid is only called from
4045 * cat_getdirentries, we can assume that only file
4046 * hardlinks need to be resolved (cat_getdirentries
4047 * can resolve directory hardlinks in place).
4049 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4051 if (record
.fileID
== 0)
4054 *ino
= record
.fileID
;
4060 * getkey - get a key from id by doing a thread lookup
4063 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4065 struct BTreeIterator
* iterator
;
4066 FSBufferDescriptor btdata
;
4069 CatalogRecord
* recp
;
4073 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4075 iterator
= hfs_mallocz(sizeof(*iterator
));
4076 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4078 recp
= hfs_malloc(sizeof(CatalogRecord
));
4079 BDINIT(btdata
, recp
);
4081 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4082 &btdata
, &datasize
, iterator
);
4086 /* Turn thread record into a cnode key (in place) */
4087 switch (recp
->recordType
) {
4090 case kHFSFileThreadRecord
:
4091 case kHFSFolderThreadRecord
:
4092 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4093 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4094 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4098 case kHFSPlusFileThreadRecord
:
4099 case kHFSPlusFolderThreadRecord
:
4100 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4101 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4102 (keyp
->hfsPlus
.nodeName
.length
* 2);
4103 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4112 hfs_free(iterator
, sizeof(*iterator
));
4113 hfs_free(recp
, sizeof(*recp
));
4115 return MacToVFSError(result
);
4119 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4120 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4121 * cat_desc here). Both key and attrp must point to real structures.
4123 * The key's parent id is the only part of the key expected to be used by the caller.
4124 * The name portion of the key may not always be valid (ie in the case of a hard link).
4127 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4131 result
= getkey(hfsmp
, cnid
, key
);
4134 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4137 * Check for a raw file hardlink inode.
4138 * Fix up the parent id in the key if necessary.
4139 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4141 if ((result
== 0) &&
4142 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4143 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4144 cnid_t nextlinkid
= 0;
4145 cnid_t prevlinkid
= 0;
4146 struct cat_desc linkdesc
;
4149 * Pick up the first link in the chain and get a descriptor for it.
4150 * This allows blind bulk access checks to work for hardlinks.
4152 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4153 (nextlinkid
!= 0)) {
4154 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4155 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4156 cat_releasedesc(&linkdesc
);
4160 return MacToVFSError(result
);
4165 * buildrecord - build a default catalog directory or file record
4168 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4169 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4171 int type
= attrp
->ca_mode
& S_IFMT
;
4172 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4175 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4177 if (type
== S_IFDIR
) {
4178 crp
->recordType
= kHFSPlusFolderRecord
;
4179 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4180 crp
->hfsPlusFolder
.valence
= 0;
4181 crp
->hfsPlusFolder
.folderID
= cnid
;
4182 crp
->hfsPlusFolder
.createDate
= createtime
;
4183 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4184 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4185 crp
->hfsPlusFolder
.accessDate
= createtime
;
4186 crp
->hfsPlusFolder
.backupDate
= 0;
4187 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4188 crp
->hfsPlusFolder
.folderCount
= 0;
4189 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4190 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4191 bsdp
->special
.linkCount
= 1;
4192 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4194 crp
->recordType
= kHFSPlusFileRecord
;
4195 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4196 crp
->hfsPlusFile
.reserved1
= 0;
4197 crp
->hfsPlusFile
.fileID
= cnid
;
4198 crp
->hfsPlusFile
.createDate
= createtime
;
4199 crp
->hfsPlusFile
.contentModDate
= createtime
;
4200 crp
->hfsPlusFile
.accessDate
= createtime
;
4201 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4202 crp
->hfsPlusFile
.backupDate
= 0;
4203 crp
->hfsPlusFile
.textEncoding
= encoding
;
4204 crp
->hfsPlusFile
.reserved2
= 0;
4205 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4206 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4207 /* BLK/CHR need to save the device info */
4208 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4209 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4211 bsdp
->special
.linkCount
= 1;
4213 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4214 *recordSize
= sizeof(HFSPlusCatalogFile
);
4216 bsdp
->ownerID
= attrp
->ca_uid
;
4217 bsdp
->groupID
= attrp
->ca_gid
;
4218 bsdp
->fileMode
= attrp
->ca_mode
;
4219 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4220 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4224 createtime
= UTCToLocal(createtime
);
4225 if (type
== S_IFDIR
) {
4226 bzero(crp
, sizeof(HFSCatalogFolder
));
4227 crp
->recordType
= kHFSFolderRecord
;
4228 crp
->hfsFolder
.folderID
= cnid
;
4229 crp
->hfsFolder
.createDate
= createtime
;
4230 crp
->hfsFolder
.modifyDate
= createtime
;
4231 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4232 *recordSize
= sizeof(HFSCatalogFolder
);
4234 bzero(crp
, sizeof(HFSCatalogFile
));
4235 crp
->recordType
= kHFSFileRecord
;
4236 crp
->hfsFile
.fileID
= cnid
;
4237 crp
->hfsFile
.createDate
= createtime
;
4238 crp
->hfsFile
.modifyDate
= createtime
;
4239 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4240 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4241 *recordSize
= sizeof(HFSCatalogFile
);
4250 * builddesc - build a cnode descriptor from an HFS+ key
4253 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4254 int isdir
, struct cat_desc
*descp
)
4257 unsigned char * nameptr
;
4260 unsigned char tmpbuff
[128];
4262 /* guess a size... */
4263 bufsize
= (3 * key
->nodeName
.length
) + 1;
4264 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4265 nameptr
= hfs_malloc(bufsize
);
4267 nameptr
= &tmpbuff
[0];
4270 result
= utf8_encodestr(key
->nodeName
.unicode
,
4271 key
->nodeName
.length
* sizeof(UniChar
),
4272 nameptr
, (size_t *)&utf8len
,
4275 if (result
== ENAMETOOLONG
) {
4276 if (nameptr
!= &tmpbuff
[0])
4277 hfs_free(nameptr
, bufsize
);
4278 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4279 key
->nodeName
.length
* sizeof(UniChar
),
4281 nameptr
= hfs_malloc(bufsize
);
4283 result
= utf8_encodestr(key
->nodeName
.unicode
,
4284 key
->nodeName
.length
* sizeof(UniChar
),
4285 nameptr
, (size_t *)&utf8len
,
4288 descp
->cd_parentcnid
= key
->parentID
;
4289 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4290 descp
->cd_namelen
= utf8len
;
4291 descp
->cd_cnid
= cnid
;
4292 descp
->cd_hint
= hint
;
4293 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4295 descp
->cd_flags
|= CD_ISDIR
;
4296 descp
->cd_encoding
= encoding
;
4297 if (nameptr
!= &tmpbuff
[0]) {
4298 hfs_free(nameptr
, bufsize
);
4305 * getbsdattr - get attributes in bsd format
4309 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4311 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4312 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4314 attrp
->ca_recflags
= crp
->flags
;
4315 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4316 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4317 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4318 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4319 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4320 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4322 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4323 attrp
->ca_flags
= 0;
4324 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4325 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4327 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4329 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4331 attrp
->ca_linkcount
= 1;
4334 attrp
->ca_linkcount
= 1; /* may be overridden below */
4336 attrp
->ca_uid
= bsd
->ownerID
;
4337 attrp
->ca_gid
= bsd
->groupID
;
4338 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4339 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4340 switch (attrp
->ca_mode
& S_IFMT
) {
4341 case S_IFCHR
: /* fall through */
4343 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4349 /* Pick up the hard link count */
4350 if (bsd
->special
.linkCount
> 0)
4351 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4356 * Override the permissions as determined by the mount auguments
4357 * in ALMOST the same way unset permissions are treated but keep
4358 * track of whether or not the file or folder is hfs locked
4359 * by leaving the h_pflags field unchanged from what was unpacked
4360 * out of the catalog.
4363 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4364 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4365 * at the VFS layer, so there is no need to do it here now; this also
4366 * allows VFS to let root see the real UIDs.
4368 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4369 * attrp->ca_uid = hfsmp->hfs_uid;
4370 * attrp->ca_gid = hfsmp->hfs_gid;
4376 if (!S_ISDIR(attrp
->ca_mode
)) {
4377 attrp
->ca_mode
&= ~S_IFMT
;
4378 attrp
->ca_mode
|= S_IFDIR
;
4380 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4381 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4382 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4384 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4385 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4386 attrp
->ca_flags
|= UF_HIDDEN
;
4388 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4389 if (crp
->flags
& kHFSFileLockedMask
) {
4390 /* The file's supposed to be locked:
4391 Make sure at least one of the IMMUTABLE bits is set: */
4392 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4393 attrp
->ca_flags
|= UF_IMMUTABLE
;
4395 /* The file's supposed to be unlocked: */
4396 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4398 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4399 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4400 attrp
->ca_flags
|= UF_HIDDEN
;
4401 /* get total blocks (both forks) */
4402 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4404 /* On HFS+ the ThreadExists flag must always be set. */
4405 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4406 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4408 /* Pick up the hardlink first link, if any. */
4409 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4412 attrp
->ca_fileid
= crp
->fileID
;
4414 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4419 * promotekey - promote hfs key to hfs plus key
4423 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4424 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4426 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4430 *encoding
= hfsmp
->hfs_encoding
;
4432 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4433 kHFSPlusMaxFileNameChars
, &uniCount
);
4435 * When an HFS name cannot be encoded with the current
4436 * encoding use MacRoman as a fallback.
4438 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4440 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4441 keyp
->nodeName
.unicode
,
4442 kHFSPlusMaxFileNameChars
,
4446 keyp
->nodeName
.length
= uniCount
;
4447 keyp
->parentID
= hfskey
->parentID
;
4451 * promotefork - promote hfs fork info to hfs plus
4455 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4456 int resource
, struct cat_fork
* forkp
)
4458 struct HFSPlusExtentDescriptor
*xp
;
4459 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4461 bzero(forkp
, sizeof(*forkp
));
4462 xp
= &forkp
->cf_extents
[0];
4464 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4465 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4466 forkp
->cf_bytesread
= 0;
4467 forkp
->cf_vblocks
= 0;
4468 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4469 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4470 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4471 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4472 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4473 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4475 forkp
->cf_size
= filep
->dataLogicalSize
;
4476 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4477 forkp
->cf_bytesread
= 0;
4478 forkp
->cf_vblocks
= 0;
4479 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4480 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4481 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4482 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4483 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4484 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4489 * promoteattr - promote standard hfs catalog attributes to hfs plus
4493 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4495 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4497 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4498 const struct HFSCatalogFolder
* folder
;
4500 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4501 crp
->recordType
= kHFSPlusFolderRecord
;
4502 crp
->flags
= folder
->flags
;
4503 crp
->fileID
= folder
->folderID
;
4504 crp
->createDate
= LocalToUTC(folder
->createDate
);
4505 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4506 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4507 crp
->reserved1
= folder
->valence
;
4509 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4511 const struct HFSCatalogFile
* file
;
4513 file
= (const struct HFSCatalogFile
*) dataPtr
;
4514 crp
->recordType
= kHFSPlusFileRecord
;
4515 crp
->flags
= file
->flags
;
4516 crp
->fileID
= file
->fileID
;
4517 crp
->createDate
= LocalToUTC(file
->createDate
);
4518 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4519 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4522 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4523 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4524 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4525 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4527 crp
->textEncoding
= 0;
4528 crp
->attributeModDate
= crp
->contentModDate
;
4529 crp
->accessDate
= crp
->contentModDate
;
4530 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4535 * Build a catalog node thread record from a catalog key
4536 * and return the size of the record.
4539 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4544 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4545 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4547 size
= sizeof(HFSPlusCatalogThread
);
4549 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4551 rec
->recordType
= kHFSPlusFileThreadRecord
;
4553 rec
->parentID
= key
->parentID
;
4554 bcopy(&key
->nodeName
, &rec
->nodeName
,
4555 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4557 /* HFS Plus has variable sized thread records */
4558 size
-= (sizeof(rec
->nodeName
.unicode
) -
4559 (rec
->nodeName
.length
* sizeof(UniChar
)));
4564 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4565 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4567 size
= sizeof(HFSCatalogThread
);
4570 rec
->recordType
= kHFSFolderThreadRecord
;
4572 rec
->recordType
= kHFSFileThreadRecord
;
4573 rec
->parentID
= key
->parentID
;
4574 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4583 * Build a catalog node thread key.
4586 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4589 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4590 key
->hfsPlus
.parentID
= parentID
;
4591 key
->hfsPlus
.nodeName
.length
= 0;
4595 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4596 key
->hfs
.reserved
= 0;
4597 key
->hfs
.parentID
= parentID
;
4598 key
->hfs
.nodeName
[0] = 0;
4605 * Extract the text encoding from a catalog node record.
4608 getencoding(const CatalogRecord
*crp
)
4612 if (crp
->recordType
== kHFSPlusFolderRecord
)
4613 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4614 else if (crp
->recordType
== kHFSPlusFileRecord
)
4615 encoding
= crp
->hfsPlusFile
.textEncoding
;
4623 * Extract the CNID from a catalog node record.
4626 getcnid(const CatalogRecord
*crp
)
4630 switch (crp
->recordType
) {
4633 case kHFSFolderRecord
:
4634 cnid
= crp
->hfsFolder
.folderID
;
4636 case kHFSFileRecord
:
4637 cnid
= crp
->hfsFile
.fileID
;
4641 case kHFSPlusFolderRecord
:
4642 cnid
= crp
->hfsPlusFolder
.folderID
;
4644 case kHFSPlusFileRecord
:
4645 cnid
= crp
->hfsPlusFile
.fileID
;
4648 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4656 * Extract the parent ID from a catalog node record.
4659 getparentcnid(const CatalogRecord
*recp
)
4663 switch (recp
->recordType
) {
4666 case kHFSFileThreadRecord
:
4667 case kHFSFolderThreadRecord
:
4668 cnid
= recp
->hfsThread
.parentID
;
4672 case kHFSPlusFileThreadRecord
:
4673 case kHFSPlusFolderThreadRecord
:
4674 cnid
= recp
->hfsPlusThread
.parentID
;
4677 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4685 * Determine if a catalog node record is a directory.
4688 isadir(const CatalogRecord
*crp
)
4690 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4694 if (crp
->recordType
== kHFSFolderRecord
) {
4703 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4704 * (not inode) using catalog record id. Note that this function does
4705 * NOT resolve directory hard link to its directory inode and return
4708 * Note: The caller is responsible for releasing the output catalog
4709 * descriptor (when supplied outdescp is non-null).
4712 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4713 u_int8_t forktype
, struct cat_desc
*outdescp
,
4714 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4716 struct BTreeIterator
*iterator
= NULL
;
4717 FSBufferDescriptor btdata
;
4720 CatalogRecord
*recp
= NULL
;
4723 /* No directory hard links on standard HFS */
4724 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4728 iterator
= hfs_mallocz(sizeof(*iterator
));
4729 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4731 recp
= hfs_malloc(sizeof(CatalogRecord
));
4732 BDINIT(btdata
, recp
);
4734 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4735 &btdata
, &datasize
, iterator
);
4739 /* Directory hard links are catalog file record */
4740 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4745 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4746 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4747 (keyp
->hfsPlus
.nodeName
.length
* 2);
4748 if (forktype
== kHFSResourceForkType
) {
4749 /* Lookup resource fork for directory hard link */
4750 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4752 /* Lookup data fork, if any, for directory hard link */
4753 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4756 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4757 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4760 /* Just for sanity, make sure that id in catalog record and thread record match */
4761 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4762 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4763 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4769 hfs_free(recp
, sizeof(*recp
));
4771 hfs_free(iterator
, sizeof(*iterator
));
4773 return MacToVFSError(error
);
4777 * cnode_update_dirlink - update the catalog node for directory hard link
4778 * described by descp using the data from attrp and forkp.
4781 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4782 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4784 if (forktype
== kHFSResourceForkType
) {
4785 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4787 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);
4791 void hfs_fork_copy(struct cat_fork
*dst
, const struct cat_fork
*src
,
4792 HFSPlusExtentDescriptor
*extents
)
4794 /* Copy everything but the extents into the dest fork */
4795 memcpy(dst
, src
, offsetof(struct cat_fork
, cf_extents
));
4796 /* Then copy the supplied extents into the fork */
4797 memcpy(dst
->cf_extents
, extents
, sizeof(HFSPlusExtentRecord
));