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
;
447 u_int32_t encoding
= 0;
452 pluskey
= (HFSPlusCatalogKey
*)key
;
453 encoding
= getencoding(recp
);
457 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
458 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
462 /* Get the CNID before calling builddesc. Need to error check it. */
463 cnid
= getcnid(recp
);
465 /* If ths CNID == 0, it's invalid. Mark as corrupt */
466 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
470 builddesc(pluskey
, cnid
, 0, encoding
, isadir(recp
), descp
);
475 hfs_free(pluskey
, sizeof(*pluskey
));
487 cat_releasedesc(struct cat_desc
*descp
)
489 const u_int8_t
* name
;
494 if ((descp
->cd_flags
& CD_HASBUF
) &&
495 (descp
->cd_nameptr
!= NULL
)) {
496 name
= descp
->cd_nameptr
;
497 descp
->cd_nameptr
= NULL
;
498 descp
->cd_namelen
= 0;
499 vfs_removename((const char *)name
);
501 descp
->cd_nameptr
= NULL
;
502 descp
->cd_namelen
= 0;
503 descp
->cd_flags
&= ~CD_HASBUF
;
507 * These Catalog functions allow access to the HFS Catalog (database).
508 * The catalog b-tree lock must be acquired before calling any of these routines.
512 * cat_lookup - lookup a catalog node using a cnode descriptor
514 * Note: The caller is responsible for releasing the output
515 * catalog descriptor (when supplied outdescp is non-null).
518 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
, int force_casesensitive_lookup
,
519 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
520 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
527 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
528 flags
= force_casesensitive_lookup
? HFS_LOOKUP_CASESENSITIVE
: 0;
530 keyp
= hfs_malloc(sizeof(CatalogKey
));
532 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
536 result
= cat_lookupbykey(hfsmp
, keyp
, flags
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
538 if (result
== ENOENT
) {
540 struct cat_desc temp_desc
;
541 if (outdescp
== NULL
) {
542 bzero(&temp_desc
, sizeof(temp_desc
));
543 outdescp
= &temp_desc
;
545 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
547 *desc_cnid
= outdescp
->cd_cnid
;
549 if (outdescp
== &temp_desc
) {
550 /* Release the local copy of desc */
551 cat_releasedesc(outdescp
);
553 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
554 // make MacRoman key from utf-8
555 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
556 // update desc text encoding so that other catalog ops succeed
560 hfs_free(keyp
, sizeof(*keyp
));
566 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
568 struct BTreeIterator
*iterator
;
569 struct FSBufferDescriptor file_data
;
570 struct HFSCatalogFile file_rec
;
575 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
578 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
580 iterator
= hfs_mallocz(2 * sizeof(*iterator
));
581 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
585 BDINIT(file_data
, &file_rec
);
586 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
590 if (file_rec
.recordType
!= kHFSFileRecord
) {
595 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
596 struct FSBufferDescriptor thread_data
;
597 struct HFSCatalogThread thread_rec
;
599 file_rec
.flags
|= kHFSThreadExistsMask
;
600 BDINIT(thread_data
, &thread_rec
);
601 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
602 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
604 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
608 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
609 (void) BTFlushPath(fcb
);
612 (void) BTFlushPath(fcb
);
613 hfs_free(iterator
, 2 * sizeof(*iterator
));
615 return MacToVFSError(result
);
620 * cat_findname - obtain a descriptor from cnid
622 * Only a thread lookup is performed.
624 * Note: The caller is responsible for releasing the output
625 * catalog descriptor (when supplied outdescp is non-null).
629 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
631 struct BTreeIterator
* iterator
;
632 FSBufferDescriptor btdata
;
634 CatalogRecord
* recp
;
641 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
646 iterator
= hfs_malloc(sizeof(*iterator
));
647 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
648 iterator
->hint
.nodeNum
= 0;
650 recp
= hfs_malloc(sizeof(CatalogRecord
));
651 BDINIT(btdata
, recp
);
653 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
657 /* Turn thread record into a cnode key (in place). */
658 switch (recp
->recordType
) {
661 case kHFSFolderThreadRecord
:
664 case kHFSFileThreadRecord
:
665 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
666 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
670 case kHFSPlusFolderThreadRecord
:
673 case kHFSPlusFileThreadRecord
:
674 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
675 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
676 (keyp
->hfsPlus
.nodeName
.length
* 2);
685 HFSPlusCatalogKey
* pluskey
= NULL
;
688 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
689 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
690 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
691 hfs_free(pluskey
, sizeof(*pluskey
));
695 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
699 hfs_free(recp
, sizeof(*recp
));
700 hfs_free(iterator
, sizeof(*iterator
));
702 return MacToVFSError(result
);
706 * cat_idlookup - lookup a catalog node using a cnode id
708 * Note: The caller is responsible for releasing the output
709 * catalog descriptor (when supplied outdescp is non-null).
712 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
, int wantrsrc
,
713 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
715 struct BTreeIterator
* iterator
;
716 FSBufferDescriptor btdata
;
719 CatalogRecord
* recp
;
723 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
725 iterator
= hfs_mallocz(sizeof(*iterator
));
726 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
728 recp
= hfs_malloc(sizeof(CatalogRecord
));
729 BDINIT(btdata
, recp
);
731 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
732 &btdata
, &datasize
, iterator
);
736 /* Turn thread record into a cnode key (in place) */
737 switch (recp
->recordType
) {
740 case kHFSFileThreadRecord
:
741 case kHFSFolderThreadRecord
:
742 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
744 /* check for NULL name */
745 if (keyp
->hfs
.nodeName
[0] == 0) {
750 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
754 case kHFSPlusFileThreadRecord
:
755 case kHFSPlusFolderThreadRecord
:
756 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
758 /* check for NULL name */
759 if (keyp
->hfsPlus
.nodeName
.length
== 0) {
764 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
765 (keyp
->hfsPlus
.nodeName
.length
* 2);
773 result
= cat_lookupbykey(hfsmp
, keyp
,
774 ((allow_system_files
!= 0) ? HFS_LOOKUP_SYSFILE
: 0),
775 0, wantrsrc
, outdescp
, attrp
, forkp
, NULL
);
776 /* No corresponding file/folder record found for a thread record,
777 * mark the volume inconsistent.
779 if (result
== 0 && outdescp
) {
780 cnid_t dcnid
= outdescp
->cd_cnid
;
782 * Just for sanity's case, let's make sure that
783 * the key in the thread matches the key in the record.
786 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
791 hfs_free(recp
, sizeof(*recp
));
792 hfs_free(iterator
, sizeof(*iterator
));
794 return MacToVFSError(result
);
799 * cat_lookupmangled - lookup a catalog node using a mangled name
802 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
803 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
808 u_int8_t utf8
[NAME_MAX
+ 1];
810 u_int16_t unicode
[kHFSPlusMaxFileNameChars
+ 1];
816 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
817 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
820 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
821 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
822 fileID
== hfsmp
->hfs_jnlfileid
||
823 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
827 result
= cat_idlookup(hfsmp
, fileID
, 0, 0, outdescp
, attrp
, forkp
);
830 /* It must be in the correct directory */
831 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
835 * Compare the mangled version of file name looked up from the
836 * disk with the mangled name provided by the user. Note that
837 * this comparison is case-sensitive, which should be fine
838 * since we're trying to prevent user space from constructing
839 * a mangled name that differs from the one they'd get from the
842 result
= utf8_decodestr(outdescp
->cd_nameptr
, outdescp
->cd_namelen
,
843 unicode
, &unicodelen
, sizeof(unicode
), ':', 0);
847 result
= ConvertUnicodeToUTF8Mangled(unicodelen
, unicode
,
848 sizeof(utf8
), &utf8len
, utf8
, fileID
);
850 ((u_int16_t
)descp
->cd_namelen
!= utf8len
) ||
851 (bcmp(descp
->cd_nameptr
, utf8
, utf8len
) != 0)) {
858 cat_releasedesc(outdescp
);
864 * cat_lookupbykey - lookup a catalog node using a cnode key
867 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
868 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
870 struct BTreeIterator
* iterator
;
871 FSBufferDescriptor btdata
;
872 CatalogRecord
* recp
;
878 u_int32_t encoding
= 0;
881 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
883 recp
= hfs_malloc(sizeof(CatalogRecord
));
884 BDINIT(btdata
, recp
);
885 iterator
= hfs_mallocz(sizeof(*iterator
));
886 iterator
->hint
.nodeNum
= hint
;
887 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
889 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
890 &btdata
, &datasize
, iterator
);
894 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
895 cnid
= getcnid(recp
);
897 /* CNID of 0 is invalid. Mark as corrupt */
898 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
904 parentid
= keyp
->hfsPlus
.parentID
;
907 encoding
= getencoding(recp
);
908 hint
= iterator
->hint
.nodeNum
;
910 /* Hide the journal files (if any) */
911 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
912 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
913 !(flags
& HFS_LOOKUP_SYSFILE
)) {
914 result
= HFS_ERESERVEDNAME
;
918 if (!std_hfs
&& !(hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)) {
919 /* Make sure the case of the file was correct if requested */
920 if (flags
& HFS_LOOKUP_CASESENSITIVE
) {
921 if (0 != cat_binarykeycompare(&keyp
->hfsPlus
, (HFSPlusCatalogKey
*)&iterator
->key
)) {
922 result
= HFS_ERESERVEDNAME
;
929 * When a hardlink link is encountered, auto resolve it.
931 * The catalog record will change, and possibly its type.
935 && (recp
->recordType
== kHFSPlusFileRecord
)
936 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_itime
) ||
937 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
941 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
942 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
944 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
945 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
946 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
949 if ((isfilelink
|| isdirlink
) && !(flags
& HFS_LOOKUP_HARDLINK
)) {
950 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
951 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
957 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
959 /* Update the inode number for this hard link */
960 attrp
->ca_linkref
= ilink
;
964 * Set kHFSHasLinkChainBit for hard links, and reset it for all
965 * other items. Also set linkCount to 1 for regular files.
967 * Due to some bug (rdar://8505977), some regular files can have
968 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
969 * are not really hard links. The runtime code should not consider
970 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
971 * and linkCount for regular file before we vend it out. This might
972 * also result in repairing the bad files on disk, if the corresponding
973 * file is modified and updated on disk.
976 /* This is a hard link and the link count bit was not set */
977 if (!(attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
978 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp
->vcbVN
, cnid
, ilink
);
979 attrp
->ca_recflags
|= kHFSHasLinkChainMask
;
982 /* Make sure that this non-hard link (regular) record is not
983 * an inode record that was looked up and we do not end up
984 * reseting the hard link bit on it.
986 if ((parentid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
987 (parentid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
988 /* This is not a hard link or inode and the link count bit was set */
989 if (attrp
->ca_recflags
& kHFSHasLinkChainMask
) {
990 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp
->vcbVN
, cnid
);
991 attrp
->ca_recflags
&= ~kHFSHasLinkChainMask
;
993 /* This is a regular file and the link count was more than 1 */
994 if (S_ISREG(attrp
->ca_mode
) && (attrp
->ca_linkcount
> 1)) {
995 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp
->vcbVN
, cnid
, attrp
->ca_linkcount
);
996 attrp
->ca_linkcount
= 1;
1003 struct HFSPlusCatalogFile cnoderec
;
1005 promoteattr(hfsmp
, recp
, &cnoderec
);
1006 getbsdattr(hfsmp
, &cnoderec
, attrp
);
1010 if (forkp
!= NULL
) {
1012 bzero(forkp
, sizeof(*forkp
));
1016 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
1019 else if (wantrsrc
) {
1020 /* Convert the resource fork. */
1021 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
1022 forkp
->cf_new_size
= 0;
1023 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1024 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1025 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1026 forkp
->cf_bytesread
=
1027 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
1028 HFSTOVCB(hfsmp
)->blockSize
;
1030 forkp
->cf_bytesread
= 0;
1032 forkp
->cf_vblocks
= 0;
1033 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
1034 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1037 u_int32_t validblks
;
1039 /* Convert the data fork. */
1040 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
1041 forkp
->cf_new_size
= 0;
1042 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
1043 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1044 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1045 forkp
->cf_bytesread
=
1046 recp
->hfsPlusFile
.dataFork
.clumpSize
*
1047 HFSTOVCB(hfsmp
)->blockSize
;
1049 forkp
->cf_bytesread
= 0;
1051 forkp
->cf_vblocks
= 0;
1052 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
1053 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1055 /* Validate the fork's resident extents. */
1057 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1058 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
1059 /* Suppress any bad extents so a remove can succeed. */
1060 forkp
->cf_extents
[i
].startBlock
= 0;
1061 forkp
->cf_extents
[i
].blockCount
= 0;
1062 /* Disable writes */
1063 if (attrp
!= NULL
) {
1064 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
1067 validblks
+= forkp
->cf_extents
[i
].blockCount
;
1070 /* Adjust for any missing blocks. */
1071 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
1075 * This is technically a volume corruption.
1076 * If the total number of blocks calculated by iterating + summing
1077 * the extents in the resident extent records, is less than that
1078 * which is reported in the catalog entry, we should force a fsck.
1079 * Only modifying ca_blocks here is not guaranteed to make it out
1080 * to disk; it is a runtime-only field.
1082 * Note that we could have gotten into this state if we had invalid ranges
1083 * that existed in borrowed blocks that somehow made it out to disk.
1084 * The cnode's on disk block count should never be greater
1085 * than that which is in its extent records.
1088 (void) hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1090 forkp
->cf_blocks
= validblks
;
1091 if (attrp
!= NULL
) {
1092 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1094 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
1095 if (psize
< forkp
->cf_size
) {
1096 forkp
->cf_size
= psize
;
1102 if (descp
!= NULL
) {
1103 HFSPlusCatalogKey
* pluskey
= NULL
;
1106 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
1110 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1111 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
1116 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
1120 hfs_free(pluskey
, sizeof(*pluskey
));
1126 if (desc_cnid
!= NULL
) {
1130 hfs_free(iterator
, sizeof(*iterator
));
1131 hfs_free(recp
, sizeof(*recp
));
1133 return MacToVFSError(result
);
1138 * cat_create - create a node in the catalog
1139 * using MacRoman encoding
1141 * NOTE: both the catalog file and attribute file locks must
1142 * be held before calling this function.
1144 * The caller is responsible for releasing the output
1145 * catalog descriptor (when supplied outdescp is non-null).
1148 cat_create(struct hfsmount
*hfsmp
, cnid_t new_fileid
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1149 struct cat_desc
*out_descp
)
1153 FSBufferDescriptor btdata
;
1157 u_int32_t encoding
= kTextEncodingMacRoman
;
1160 modeformat
= attrp
->ca_mode
& S_IFMT
;
1162 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1163 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1165 /* The caller is expected to reserve a CNID before calling this function! */
1167 /* Get space for iterator, key and data */
1168 bto
= hfs_malloc(sizeof(struct btobj
));
1169 bto
->iterator
.hint
.nodeNum
= 0;
1171 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
1176 * Insert the thread record first
1178 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
1179 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
1180 S_ISDIR(attrp
->ca_mode
));
1181 btdata
.bufferAddress
= &bto
->data
;
1182 btdata
.itemSize
= datalen
;
1183 btdata
.itemCount
= 1;
1185 /* Caller asserts the following:
1186 * 1) this CNID is not in use by any orphaned EAs
1187 * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
1188 * 3) There are no thread or catalog records for this ID
1190 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
1191 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1198 * Now insert the file/directory record
1200 buildrecord(attrp
, new_fileid
, std_hfs
, encoding
, &bto
->data
, &datalen
);
1201 btdata
.bufferAddress
= &bto
->data
;
1202 btdata
.itemSize
= datalen
;
1203 btdata
.itemCount
= 1;
1205 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
1207 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1209 if (result
== btExists
)
1212 /* Back out the thread record */
1213 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
1214 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
1215 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
1216 /* Error on deleting extra thread record, mark
1217 * volume inconsistent
1219 printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid
, hfsmp
->vcbVN
);
1220 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1227 * Insert was successful, update name, parent and volume
1229 if (out_descp
!= NULL
) {
1230 HFSPlusCatalogKey
* pluskey
= NULL
;
1233 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1237 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1238 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1242 builddesc(pluskey
, new_fileid
, bto
->iterator
.hint
.nodeNum
,
1243 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1246 hfs_free(pluskey
, sizeof(*pluskey
));
1251 attrp
->ca_fileid
= new_fileid
;
1254 (void) BTFlushPath(fcb
);
1255 hfs_free(bto
, sizeof(*bto
));
1257 return MacToVFSError(result
);
1262 * cnode_rename - rename a catalog node
1264 * Assumes that the target's directory exists.
1266 * Order of B-tree operations:
1267 * 1. BTSearchRecord(from_cnode, &data);
1268 * 2. BTInsertRecord(to_cnode, &data);
1269 * 3. BTDeleteRecord(from_cnode);
1270 * 4. BTDeleteRecord(from_thread);
1271 * 5. BTInsertRecord(to_thread);
1273 * Note: The caller is responsible for releasing the output
1274 * catalog descriptor (when supplied out_cdp is non-null).
1278 struct hfsmount
* hfsmp
,
1279 struct cat_desc
* from_cdp
,
1280 struct cat_desc
* todir_cdp
,
1281 struct cat_desc
* to_cdp
,
1282 struct cat_desc
* out_cdp
)
1284 struct BTreeIterator
* to_iterator
= NULL
;
1285 struct BTreeIterator
* from_iterator
= NULL
;
1286 FSBufferDescriptor btdata
;
1287 CatalogRecord
* recp
= NULL
;
1288 HFSPlusCatalogKey
* to_key
;
1295 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1298 u_int32_t encoding
= 0;
1300 vcb
= HFSTOVCB(hfsmp
);
1301 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1302 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1304 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1307 from_iterator
= hfs_mallocz(sizeof(*from_iterator
));
1308 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1311 to_iterator
= hfs_mallocz(sizeof(*to_iterator
));
1312 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1315 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1316 recp
= hfs_malloc(sizeof(CatalogRecord
));
1317 BDINIT(btdata
, recp
);
1320 * When moving a directory, make sure its a valid move.
1322 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1323 struct BTreeIterator
*dir_iterator
= NULL
;
1325 cnid_t cnid
= from_cdp
->cd_cnid
;
1326 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1328 /* First check the obvious ones */
1329 if (cnid
== fsRtDirID
||
1330 cnid
== to_cdp
->cd_parentcnid
||
1335 /* now allocate the dir_iterator */
1336 dir_iterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
1339 * Traverse destination path all the way back to the root
1340 * making sure that source directory is not encountered.
1343 while (pathcnid
> fsRtDirID
) {
1344 buildthreadkey(pathcnid
, std_hfs
, (CatalogKey
*)&dir_iterator
->key
);
1345 result
= BTSearchRecord(fcb
, dir_iterator
, &btdata
, &datasize
, NULL
);
1347 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1350 pathcnid
= getparentcnid(recp
);
1351 if (pathcnid
== cnid
|| pathcnid
== 0) {
1353 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1357 hfs_free(dir_iterator
, sizeof(*dir_iterator
));
1361 * Step 1: Find cnode data at old location
1363 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1364 &datasize
, from_iterator
);
1366 if (std_hfs
|| (result
!= btNotFound
))
1369 struct cat_desc temp_desc
;
1371 /* Probably the node has mangled name */
1372 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1376 /* The file has mangled name. Search the cnode data using full name */
1377 bzero(from_iterator
, sizeof(*from_iterator
));
1378 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1380 cat_releasedesc(&temp_desc
);
1384 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1386 cat_releasedesc(&temp_desc
);
1390 cat_releasedesc(&temp_desc
);
1393 /* Check if the source is directory hard link. We do not change
1394 * directory flag because it is later used to initialize result descp
1398 (recp
->recordType
== kHFSPlusFileRecord
) &&
1399 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1404 * Update the text encoding (on disk and in descriptor),
1405 * using hfs_pickencoding to get the new encoding when available.
1407 * Note that hardlink inodes don't require a text encoding hint.
1410 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1411 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1414 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1415 #else // !TARGET_OS_OSX
1416 encoding
= kTextEncodingMacRoman
;
1417 #endif // TARGET_OS_OSX
1419 hfs_setencodingbits(hfsmp
, encoding
);
1420 recp
->hfsPlusFile
.textEncoding
= encoding
;
1422 out_cdp
->cd_encoding
= encoding
;
1426 if (std_hfs
&& !directory
&&
1427 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
)) {
1434 * If the keys are identical then there's nothing left to do!
1436 * update the hint and exit
1439 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1441 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1445 /* Step 2: Insert cnode at new location */
1446 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1447 if (result
== btExists
) {
1448 int fromtype
= recp
->recordType
;
1451 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1452 goto exit
; /* EEXIST */
1454 /* Find cnode data at new location */
1455 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1459 /* Get the CNID after calling searchrecord */
1460 cnid
= getcnid (recp
);
1462 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1467 if ((fromtype
!= recp
->recordType
) ||
1468 (from_cdp
->cd_cnid
!= cnid
)) {
1470 goto exit
; /* EEXIST */
1472 /* The old name is a case variant and must be removed */
1473 result
= BTDeleteRecord(fcb
, from_iterator
);
1477 /* Insert cnode (now that case duplicate is gone) */
1478 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1480 /* Try and restore original before leaving */
1485 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1487 printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err
);
1488 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1494 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1503 /* Step 3: Remove cnode from old location */
1505 result
= BTDeleteRecord(fcb
, from_iterator
);
1507 /* Try and delete new record before leaving */
1512 err
= BTDeleteRecord(fcb
, to_iterator
);
1514 printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err
);
1515 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1521 (void) BTDeleteRecord(fcb
, to_iterator
);
1527 /* #### POINT OF NO RETURN #### */
1530 * Step 4: Remove cnode's old thread record
1532 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1533 (void) BTDeleteRecord(fcb
, from_iterator
);
1536 * Step 5: Insert cnode's new thread record
1537 * (optional for HFS files)
1540 /* For directory hard links, always create a file thread
1541 * record. For everything else, use the directory flag.
1544 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1546 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1548 btdata
.itemSize
= datasize
;
1549 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1550 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1554 HFSPlusCatalogKey
* pluskey
= NULL
;
1557 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1561 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
1562 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1564 /* Save the real encoding hint in the Finder Info (field 4). */
1565 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1568 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1569 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1574 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1575 encoding
, directory
, out_cdp
);
1578 hfs_free(pluskey
, sizeof(*pluskey
));
1584 (void) BTFlushPath(fcb
);
1586 hfs_free(from_iterator
, sizeof(*from_iterator
));
1588 hfs_free(to_iterator
, sizeof(*to_iterator
));
1590 hfs_free(recp
, sizeof(*recp
));
1591 return MacToVFSError(result
);
1596 * cat_delete - delete a node from the catalog
1598 * Order of B-tree operations:
1599 * 1. BTDeleteRecord(cnode);
1600 * 2. BTDeleteRecord(thread);
1601 * 3. BTUpdateRecord(parent);
1604 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1607 BTreeIterator
*iterator
;
1612 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1613 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1617 * The root directory cannot be deleted
1618 * A directory must be empty
1619 * A file must be zero length (no blocks)
1621 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1622 descp
->cd_parentcnid
== kHFSRootParentID
)
1625 /* XXX Preflight Missing */
1627 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1628 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1629 iterator
->hint
.nodeNum
= 0;
1632 * Derive a key from either the file ID (for a virtual inode)
1633 * or the descriptor.
1635 if (descp
->cd_namelen
== 0) {
1636 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1637 cnid
= attrp
->ca_fileid
;
1639 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1640 cnid
= descp
->cd_cnid
;
1646 result
= BTDeleteRecord(fcb
, iterator
);
1648 if (std_hfs
|| (result
!= btNotFound
))
1651 struct cat_desc temp_desc
;
1653 /* Probably the node has mangled name */
1654 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1658 /* The file has mangled name. Delete the file using full name */
1659 bzero(iterator
, sizeof(*iterator
));
1660 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1661 cnid
= temp_desc
.cd_cnid
;
1663 cat_releasedesc(&temp_desc
);
1667 result
= BTDeleteRecord(fcb
, iterator
);
1669 cat_releasedesc(&temp_desc
);
1673 cat_releasedesc(&temp_desc
);
1676 /* Delete thread record. On error, mark volume inconsistent */
1677 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1678 if (BTDeleteRecord(fcb
, iterator
)) {
1680 printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid
, hfsmp
->vcbVN
);
1681 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
1686 (void) BTFlushPath(fcb
);
1688 return MacToVFSError(result
);
1693 * cat_update_internal - update the catalog node described by descp
1694 * using the data from attrp and forkp.
1695 * If update_hardlink is true, the hard link catalog record is updated
1696 * and not the inode catalog record.
1699 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1700 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1703 BTreeIterator
* iterator
;
1704 struct update_state state
;
1707 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1709 state
.s_desc
= descp
;
1710 state
.s_attr
= attrp
;
1711 state
.s_datafork
= dataforkp
;
1712 state
.s_rsrcfork
= rsrcforkp
;
1713 state
.s_hfsmp
= hfsmp
;
1715 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1716 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1719 * For open-deleted files we need to do a lookup by cnid
1720 * (using thread rec).
1722 * For hard links and if not requested by caller, the target
1723 * of the update is the inode itself (not the link record)
1724 * so a lookup by fileid (i.e. thread rec) is needed.
1726 if ((update_hardlink
== false) &&
1727 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1728 (descp
->cd_namelen
== 0) ||
1729 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1730 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1732 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1737 /* Pass a node hint */
1738 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1740 result
= BTUpdateRecord(fcb
, iterator
,
1741 (IterateCallBackProcPtr
)catrec_update
, &state
);
1745 /* Update the node hint. */
1746 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1749 (void) BTFlushPath(fcb
);
1751 return MacToVFSError(result
);
1755 * cat_update - update the catalog node described by descp
1756 * using the data from attrp and forkp.
1759 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1760 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1762 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1766 * catrec_update - Update the fields of a catalog record
1767 * This is called from within BTUpdateRecord.
1770 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1772 struct cat_desc
*descp
;
1773 struct cat_attr
*attrp
;
1774 const struct cat_fork
*forkp
;
1775 struct hfsmount
*hfsmp
;
1778 descp
= state
->s_desc
;
1779 attrp
= state
->s_attr
;
1780 hfsmp
= state
->s_hfsmp
;
1781 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1783 switch (crp
->recordType
) {
1786 case kHFSFolderRecord
: {
1787 HFSCatalogFolder
*dir
;
1789 dir
= (struct HFSCatalogFolder
*)crp
;
1790 /* Do a quick sanity check */
1791 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1792 (dir
->folderID
!= descp
->cd_cnid
))
1793 return (btNotFound
);
1794 dir
->valence
= attrp
->ca_entries
;
1795 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1796 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1797 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1798 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1799 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1802 case kHFSFileRecord
: {
1803 HFSCatalogFile
*file
;
1806 file
= (struct HFSCatalogFile
*)crp
;
1807 /* Do a quick sanity check */
1808 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1809 (file
->fileID
!= attrp
->ca_fileid
))
1810 return (btNotFound
);
1811 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1812 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1813 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1814 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1815 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1816 if (state
->s_rsrcfork
) {
1817 forkp
= state
->s_rsrcfork
;
1818 file
->rsrcLogicalSize
= forkp
->cf_size
;
1819 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1820 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1821 file
->rsrcExtents
[i
].startBlock
=
1822 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1823 file
->rsrcExtents
[i
].blockCount
=
1824 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1827 if (state
->s_datafork
) {
1828 forkp
= state
->s_datafork
;
1829 file
->dataLogicalSize
= forkp
->cf_size
;
1830 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1831 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1832 file
->dataExtents
[i
].startBlock
=
1833 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1834 file
->dataExtents
[i
].blockCount
=
1835 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1839 /* Synchronize the lock state */
1840 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1841 file
->flags
|= kHFSFileLockedMask
;
1843 file
->flags
&= ~kHFSFileLockedMask
;
1848 case kHFSPlusFolderRecord
: {
1849 HFSPlusCatalogFolder
*dir
;
1851 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1852 /* Do a quick sanity check */
1853 if (dir
->folderID
!= attrp
->ca_fileid
) {
1854 printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir
->folderID
, attrp
->ca_fileid
, hfsmp
->vcbVN
);
1855 return (btNotFound
);
1857 dir
->flags
= attrp
->ca_recflags
;
1858 dir
->valence
= attrp
->ca_entries
;
1859 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1860 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1861 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1862 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1863 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1864 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1865 /* Note: directory hardlink inodes don't require a text encoding hint. */
1866 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1867 dir
->textEncoding
= descp
->cd_encoding
;
1869 dir
->folderCount
= attrp
->ca_dircount
;
1870 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1872 * Update the BSD Info if it was already initialized on
1873 * disk or if the runtime values have been modified.
1875 * If the BSD info was already initialized, but
1876 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1877 * probably different than what was on disk. We don't want
1878 * to overwrite the on-disk values (so if we turn off
1879 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1880 * This way, we can still change fields like the mode or
1881 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1883 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1884 * won't change the uid or gid from their defaults. So, if
1885 * the BSD info wasn't set, and the runtime values are not
1886 * default, then what changed was the mode or flags. We
1887 * have to set the uid and gid to something, so use the
1888 * supplied values (which will be default), which has the
1889 * same effect as creating a new file while
1890 * MNT_UNKNOWNPERMISSIONS is set.
1892 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1893 (attrp
->ca_flags
!= 0) ||
1894 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1895 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1896 ((attrp
->ca_mode
& ALLPERMS
) !=
1897 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1898 if ((dir
->bsdInfo
.fileMode
== 0) ||
1899 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1900 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1901 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1903 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1904 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1905 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1906 /* A directory hardlink has a link count. */
1907 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1908 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1913 case kHFSPlusFileRecord
: {
1914 HFSPlusCatalogFile
*file
;
1917 file
= (struct HFSPlusCatalogFile
*)crp
;
1918 /* Do a quick sanity check */
1919 if (file
->fileID
!= attrp
->ca_fileid
)
1920 return (btNotFound
);
1921 file
->flags
= attrp
->ca_recflags
;
1922 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1923 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1924 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1925 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1926 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1927 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1929 * Note: file hardlink inodes don't require a text encoding
1930 * hint, but they do have a first link value.
1932 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1933 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1935 file
->textEncoding
= descp
->cd_encoding
;
1937 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1939 * Update the BSD Info if it was already initialized on
1940 * disk or if the runtime values have been modified.
1942 * If the BSD info was already initialized, but
1943 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1944 * probably different than what was on disk. We don't want
1945 * to overwrite the on-disk values (so if we turn off
1946 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1947 * This way, we can still change fields like the mode or
1948 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1950 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1951 * won't change the uid or gid from their defaults. So, if
1952 * the BSD info wasn't set, and the runtime values are not
1953 * default, then what changed was the mode or flags. We
1954 * have to set the uid and gid to something, so use the
1955 * supplied values (which will be default), which has the
1956 * same effect as creating a new file while
1957 * MNT_UNKNOWNPERMISSIONS is set.
1959 * Do not modify bsdInfo for directory hard link records.
1960 * They are set during creation and are not modifiable, so just
1963 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1964 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1965 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1968 ((file
->bsdInfo
.fileMode
!= 0) ||
1969 (attrp
->ca_flags
!= 0) ||
1970 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1971 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1972 ((attrp
->ca_mode
& ALLPERMS
) !=
1973 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1974 if ((file
->bsdInfo
.fileMode
== 0) ||
1975 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1976 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1977 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1979 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1980 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1981 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1983 if (state
->s_rsrcfork
) {
1984 forkp
= state
->s_rsrcfork
;
1985 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1986 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1987 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1988 sizeof(HFSPlusExtentRecord
));
1989 /* Push blocks read to disk */
1990 file
->resourceFork
.clumpSize
=
1991 howmany(forkp
->cf_bytesread
, blksize
);
1993 if (state
->s_datafork
) {
1994 forkp
= state
->s_datafork
;
1995 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1996 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1997 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1998 sizeof(HFSPlusExtentRecord
));
1999 /* Push blocks read to disk */
2000 file
->dataFork
.clumpSize
=
2001 howmany(forkp
->cf_bytesread
, blksize
);
2004 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
2005 (file
->resourceFork
.extents
[0].startBlock
==
2006 file
->dataFork
.extents
[0].startBlock
)) {
2007 panic("hfs: catrec_update: rsrc fork == data fork");
2010 /* Synchronize the lock state */
2011 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
2012 file
->flags
|= kHFSFileLockedMask
;
2014 file
->flags
&= ~kHFSFileLockedMask
;
2016 /* Push out special field if necessary */
2017 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
2018 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
2022 * Protect against the degenerate case where the descriptor contains the
2023 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
2024 * the linkcount was greater than 1 (the default value), then it must have become
2025 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
2027 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
2028 (file
->hl_linkCount
> 1)) {
2029 file
->hl_linkCount
= attrp
->ca_linkcount
;
2035 return (btNotFound
);
2040 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
2041 * catalog btree of given cnid by walking up the parent chain till it reaches
2042 * either the root folder, or the private metadata directory for storing
2043 * directory hard links. This function updates the corresponding in-core
2044 * cnode, if any, and the directory record in the catalog btree.
2045 * On success, returns zero. On failure, returns non-zero value.
2048 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
2052 struct cat_desc desc
;
2053 struct cat_attr attr
;
2055 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
2056 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
2057 /* Update the bit in corresponding cnode, if any, in the hash.
2058 * If the cnode has the bit already set, stop the traversal.
2060 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
2065 /* Update the catalog record on disk if either cnode was not
2066 * found in the hash, or if a cnode was found and the cnode
2067 * did not have the bit set previously.
2069 retval
= hfs_start_transaction(hfsmp
);
2073 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2075 /* Look up our catalog folder record */
2076 retval
= cat_idlookup(hfsmp
, cnid
, 0, 0, &desc
, &attr
, NULL
);
2078 hfs_systemfile_unlock(hfsmp
, lockflags
);
2079 hfs_end_transaction(hfsmp
);
2083 /* Update the bit in the catalog record */
2084 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
2085 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
2087 hfs_systemfile_unlock(hfsmp
, lockflags
);
2088 hfs_end_transaction(hfsmp
);
2089 cat_releasedesc(&desc
);
2093 hfs_systemfile_unlock(hfsmp
, lockflags
);
2094 hfs_end_transaction(hfsmp
);
2096 cnid
= desc
.cd_parentcnid
;
2097 cat_releasedesc(&desc
);
2103 /* This function traverses the parent directory hierarchy from the given
2104 * directory to one level below root directory and checks if any of its
2106 * 1. A directory hard link.
2107 * 2. The 'pointed at' directory.
2108 * If any of these conditions fail or an internal error is encountered
2109 * during look up of the catalog record, this function returns non-zero value.
2112 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
2114 HFSPlusCatalogKey
*keyp
;
2116 FSBufferDescriptor btdata
;
2117 HFSPlusCatalogFolder folder
;
2123 BDINIT(btdata
, &folder
);
2124 ip
= hfs_malloc(sizeof(*ip
));
2125 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
2126 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2128 while (cnid
!= kHFSRootParentID
) {
2129 /* Check if the 'pointed at' directory is an ancestor */
2130 if (pointed_at_cnid
== cnid
) {
2134 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
2135 printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2136 invalid
= 1; /* On errors, assume an invalid parent */
2139 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
2140 printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2141 invalid
= 1; /* On errors, assume an invalid parent */
2144 /* Check if this ancestor is a directory hard link */
2145 if (folder
.flags
& kHFSHasLinkChainMask
) {
2149 cnid
= keyp
->parentID
;
2151 hfs_free(ip
, sizeof(*ip
));
2157 * update_siblinglinks_callback - update a link's chain
2160 struct linkupdate_state
{
2167 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
2169 HFSPlusCatalogFile
*file
;
2171 if (crp
->recordType
!= kHFSPlusFileRecord
) {
2172 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
2173 return (btNotFound
);
2176 file
= (struct HFSPlusCatalogFile
*)crp
;
2177 if (file
->flags
& kHFSHasLinkChainMask
) {
2178 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
2179 file
->hl_prevLinkID
= state
->prevlinkid
;
2181 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
2182 file
->hl_nextLinkID
= state
->nextlinkid
;
2185 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
2191 * cat_update_siblinglinks - update a link's chain
2194 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
2197 BTreeIterator
* iterator
;
2198 struct linkupdate_state state
;
2201 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2202 state
.filelinkid
= linkfileid
;
2203 state
.prevlinkid
= prevlinkid
;
2204 state
.nextlinkid
= nextlinkid
;
2206 /* Create an iterator for use by us temporarily */
2207 iterator
= hfs_mallocz(sizeof(*iterator
));
2209 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
2211 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
2212 (void) BTFlushPath(fcb
);
2214 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid
, hfsmp
->vcbVN
);
2217 hfs_free(iterator
, sizeof(*iterator
));
2218 return MacToVFSError(result
);
2222 * cat_lookuplink - lookup a link by it's name
2225 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2228 BTreeIterator
* iterator
;
2229 struct FSBufferDescriptor btdata
;
2230 struct HFSPlusCatalogFile file
;
2233 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2235 /* Create an iterator for use by us temporarily */
2236 iterator
= hfs_mallocz(sizeof(*iterator
));
2238 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2241 BDINIT(btdata
, &file
);
2243 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2246 if (file
.recordType
!= kHFSPlusFileRecord
) {
2250 *linkfileid
= file
.fileID
;
2252 if (file
.flags
& kHFSHasLinkChainMask
) {
2253 *prevlinkid
= file
.hl_prevLinkID
;
2254 *nextlinkid
= file
.hl_nextLinkID
;
2260 hfs_free(iterator
, sizeof(*iterator
));
2261 return MacToVFSError(result
);
2266 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2269 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2272 BTreeIterator
* iterator
;
2273 struct FSBufferDescriptor btdata
;
2274 struct HFSPlusCatalogFile file
;
2277 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2279 /* Create an iterator for use by us temporarily */
2280 iterator
= hfs_mallocz(sizeof(*iterator
));
2282 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2285 BDINIT(btdata
, &file
);
2287 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2290 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2291 if (file
.flags
& kHFSHasLinkChainMask
) {
2294 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2296 /* directory inodes don't have a chain (its in an EA) */
2297 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2298 result
= ENOLINK
; /* signal to caller to get head of list */
2299 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2301 *nextlinkid
= file
.hl_firstLinkID
;
2303 *prevlinkid
= file
.hl_prevLinkID
;
2304 *nextlinkid
= file
.hl_nextLinkID
;
2311 hfs_free(iterator
, sizeof(*iterator
));
2312 return MacToVFSError(result
);
2317 * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
2320 cat_lookup_lastlink(struct hfsmount
*hfsmp
, cnid_t linkfileid
,
2321 cnid_t
*lastlink
, struct cat_desc
*cdesc
)
2324 BTreeIterator
* iterator
;
2325 struct FSBufferDescriptor btdata
;
2326 struct HFSPlusCatalogFile file
;
2330 cnid_t currentlink
= linkfileid
;
2332 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2334 /* Create an iterator for use by us temporarily */
2335 iterator
= hfs_malloc(sizeof(*iterator
));
2337 while ((foundlast
== 0) && (itercount
< HFS_LINK_MAX
)) {
2339 bzero(iterator
, sizeof(*iterator
));
2341 if ((result
= getkey(hfsmp
, currentlink
, (CatalogKey
*)&iterator
->key
))) {
2344 BDINIT(btdata
, &file
);
2346 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2350 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2351 if (file
.flags
& kHFSHasLinkChainMask
) {
2354 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2356 * The raw inode for a directory hardlink doesn't have a chain.
2357 * Its link information lives in an EA.
2359 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2360 /* We don't iterate to find the oldest directory hardlink. */
2364 else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2365 /* Raw inode for file hardlink (the base inode) */
2366 currentlink
= file
.hl_firstLinkID
;
2369 * One minor special-casing here is necessary.
2370 * If our ID brought us to the raw hardlink inode, and it does
2371 * not have any siblings, then it's an open-unlinked file, and we
2372 * should not proceed any further.
2374 if (currentlink
== 0) {
2380 /* Otherwise, this item's parent is a legitimate directory in the namespace */
2381 if (file
.hl_nextLinkID
== 0) {
2382 /* If nextLinkID is 0, then we found the end; no more hardlinks */
2384 *lastlink
= currentlink
;
2386 * Since we had to construct a catalog key to do this lookup
2387 * we still hold it in-hand. We might as well use it to build
2388 * the descriptor that the caller asked for.
2390 builddesc ((HFSPlusCatalogKey
*)&iterator
->key
, currentlink
, 0, 0, 0, cdesc
);
2394 currentlink
= file
.hl_nextLinkID
;
2398 /* Sorry, can't help you without a link chain */
2404 /* If we didn't find what we were looking for, zero out the args */
2405 if (foundlast
== 0) {
2407 bzero (cdesc
, sizeof(struct cat_desc
));
2414 hfs_free(iterator
, sizeof(*iterator
));
2415 return MacToVFSError(result
);
2420 * cat_createlink - create a link in the catalog
2422 * The following cat_attr fields are expected to be set:
2428 * ca_finderinfo (type and creator)
2431 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2432 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2436 FSBufferDescriptor btdata
;
2437 HFSPlusForkData
*rsrcforkp
;
2440 int thread_inserted
= 0;
2441 int alias_allocated
= 0;
2445 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
2447 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2450 * Get the next CNID. Note that we are currently holding catalog lock.
2452 result
= cat_acquire_cnid(hfsmp
, &nextCNID
);
2457 /* Get space for iterator, key and data */
2458 bto
= hfs_malloc(sizeof(struct btobj
));
2459 bto
->iterator
.hint
.nodeNum
= 0;
2460 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2462 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2464 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2469 * Insert the thread record first.
2471 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2472 btdata
.bufferAddress
= &bto
->data
;
2473 btdata
.itemSize
= datalen
;
2474 btdata
.itemCount
= 1;
2476 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2477 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2481 thread_inserted
= 1;
2484 * Now insert the link record.
2486 buildrecord(attrp
, nextCNID
, 0, kTextEncodingMacUnicode
, &bto
->data
, &datalen
);
2488 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2489 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2490 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2492 /* For directory hard links, create alias in resource fork */
2493 if (descp
->cd_flags
& CD_ISDIR
) {
2494 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2497 alias_allocated
= 1;
2499 btdata
.bufferAddress
= &bto
->data
;
2500 btdata
.itemSize
= datalen
;
2501 btdata
.itemCount
= 1;
2503 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2505 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2507 if (result
== btExists
)
2511 if (linkfileid
!= NULL
) {
2512 *linkfileid
= nextCNID
;
2516 if (thread_inserted
) {
2517 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result
), hfsmp
->vcbVN
);
2519 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2520 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2521 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2522 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
2525 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2526 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2527 rsrcforkp
->extents
[0].blockCount
, 0);
2528 rsrcforkp
->extents
[0].startBlock
= 0;
2529 rsrcforkp
->extents
[0].blockCount
= 0;
2532 (void) BTFlushPath(fcb
);
2533 hfs_free(bto
, sizeof(*bto
));
2535 return MacToVFSError(result
);
2538 /* Directory hard links are visible as aliases on pre-Leopard systems and
2539 * as normal directories on Leopard or later. All directory hard link aliases
2540 * have the same resource fork content except for the three uniquely
2541 * identifying values that are updated in the resource fork data when the alias
2542 * is created. The following array is the constant resource fork data used
2543 * only for creating directory hard link aliases.
2545 static const char hfs_dirlink_alias_rsrc
[] = {
2546 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2547 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2548 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2549 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2550 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2551 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2552 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2553 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2554 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2555 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2557 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2558 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2560 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2562 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2563 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2564 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2572 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2574 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2577 /* Constants for directory hard link alias */
2579 /* Size of resource fork data array for directory hard link alias */
2580 kHFSAliasSize
= 0x1d0,
2582 /* Volume type for ejectable devices like disk image */
2583 kHFSAliasVolTypeEjectable
= 0x5,
2585 /* Offset for volume create date, in Mac OS local time */
2586 kHFSAliasVolCreateDateOffset
= 0x12a,
2588 /* Offset for the type of volume */
2589 kHFSAliasVolTypeOffset
= 0x130,
2591 /* Offset for folder ID of the parent directory of the directory inode */
2592 kHFSAliasParentIDOffset
= 0x132,
2594 /* Offset for folder ID of the directory inode */
2595 kHFSAliasTargetIDOffset
= 0x176,
2598 /* Create and write an alias that points at the directory represented by given
2599 * inode number on the same volume. Directory hard links are visible as
2600 * aliases in pre-Leopard systems and this function creates these aliases.
2602 * Note: This code is very specific to creating alias for the purpose
2603 * of directory hard links only, and should not be generalized.
2606 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2614 HFSPlusForkData
*rsrcforkp
;
2618 rsrcforkp
= &(crp
->resourceFork
);
2620 blksize
= hfsmp
->blockSize
;
2621 blkcount
= howmany(kHFSAliasSize
, blksize
);
2622 sectorsize
= hfsmp
->hfs_logical_block_size
;
2623 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2625 /* Allocate some disk space for the alias content. */
2626 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2627 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2628 &rsrcforkp
->extents
[0].startBlock
,
2629 &rsrcforkp
->extents
[0].blockCount
);
2630 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2631 if (result
== dskFulErr
) {
2632 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2633 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
| HFS_ALLOC_FLUSHTXN
,
2634 &rsrcforkp
->extents
[0].startBlock
,
2635 &rsrcforkp
->extents
[0].blockCount
);
2638 rsrcforkp
->extents
[0].startBlock
= 0;
2642 /* Acquire a buffer cache block for our block. */
2643 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2644 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2646 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2648 journal_modify_block_start(hfsmp
->jnl
, bp
);
2651 /* Generate alias content */
2652 alias
= (char *)buf_dataptr(bp
);
2653 bzero(alias
, buf_size(bp
));
2654 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2656 /* Set the volume create date, local time in Mac OS format */
2657 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2658 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2660 /* If the file system is on a virtual device like disk image,
2661 * update the volume type to be ejectable device.
2663 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2664 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2665 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2668 /* Set id of the parent of the target directory */
2669 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2670 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2672 /* Set id of the target directory */
2673 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2674 *valptr
= OSSwapHostToBigInt32(inode_num
);
2676 /* Write alias content to disk. */
2678 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2679 } else if ((result
= buf_bwrite(bp
))) {
2683 /* Finish initializing the fork data. */
2684 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2685 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2688 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2689 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2690 rsrcforkp
->extents
[0].startBlock
= 0;
2691 rsrcforkp
->extents
[0].blockCount
= 0;
2692 rsrcforkp
->logicalSize
= 0;
2693 rsrcforkp
->totalBlocks
= 0;
2699 * cat_deletelink - delete a link from the catalog
2702 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2704 struct HFSPlusCatalogFile file
;
2705 struct cat_attr cattr
;
2706 uint32_t totalBlocks
;
2710 bzero(&file
, sizeof (file
));
2711 bzero(&cattr
, sizeof (cattr
));
2712 cattr
.ca_fileid
= descp
->cd_cnid
;
2714 /* Directory links have alias content to remove. */
2715 if (descp
->cd_flags
& CD_ISDIR
) {
2717 BTreeIterator
* iterator
;
2718 struct FSBufferDescriptor btdata
;
2720 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2722 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2723 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2724 iterator
->hint
.nodeNum
= 0;
2726 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2729 BDINIT(btdata
, &file
);
2731 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2736 result
= cat_delete(hfsmp
, descp
, &cattr
);
2738 if ((result
== 0) &&
2739 (descp
->cd_flags
& CD_ISDIR
) &&
2740 (file
.recordType
== kHFSPlusFileRecord
)) {
2742 totalBlocks
= file
.resourceFork
.totalBlocks
;
2744 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2745 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2746 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2750 (void) BlockDeallocate(hfsmp
,
2751 file
.resourceFork
.extents
[i
].startBlock
,
2752 file
.resourceFork
.extents
[i
].blockCount
, 0);
2754 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2755 file
.resourceFork
.extents
[i
].startBlock
= 0;
2756 file
.resourceFork
.extents
[i
].blockCount
= 0;
2765 * Callback to collect directory entries.
2766 * Called with readattr_state for each item in a directory.
2768 struct readattr_state
{
2769 struct hfsmount
*hfsmp
;
2770 struct cat_entrylist
*list
;
2778 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2779 struct readattr_state
*state
)
2781 struct cat_entrylist
*list
= state
->list
;
2782 struct hfsmount
*hfsmp
= state
->hfsmp
;
2783 struct cat_entry
*cep
;
2786 if (list
->realentries
>= list
->maxentries
)
2787 return (0); /* stop */
2789 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2791 switch(rec
->recordType
) {
2792 case kHFSPlusFolderRecord
:
2793 case kHFSPlusFileRecord
:
2795 case kHFSFolderRecord
:
2796 case kHFSFileRecord
:
2798 if (parentcnid
!= state
->dir_cnid
) {
2799 state
->error
= ENOENT
;
2800 state
->reached_eof
= 1;
2801 return (0); /* stop */
2805 state
->error
= ENOENT
;
2806 return (0); /* stop */
2809 /* Hide the private system directories and journal files */
2810 if (parentcnid
== kHFSRootFolderID
) {
2811 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2812 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2813 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2814 list
->skipentries
++;
2815 return (1); /* continue */
2818 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2819 (rec
->recordType
== kHFSPlusFileRecord
) &&
2820 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2821 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2822 list
->skipentries
++;
2823 return (1); /* continue */
2827 cep
= &list
->entry
[list
->realentries
++];
2829 if (state
->stdhfs
== 0) {
2830 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2831 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2832 isadir(rec
), &cep
->ce_desc
);
2834 if (rec
->recordType
== kHFSPlusFileRecord
) {
2835 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2836 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2837 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2838 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2840 /* Save link reference for later processing. */
2841 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2842 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2843 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2844 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2845 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2846 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2847 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2853 struct HFSPlusCatalogFile cnoderec
;
2854 HFSPlusCatalogKey
* pluskey
;
2857 promoteattr(hfsmp
, rec
, &cnoderec
);
2858 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2860 pluskey
= hfs_malloc(sizeof(HFSPlusCatalogKey
));
2861 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2862 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2863 hfs_free(pluskey
, sizeof(*pluskey
));
2865 if (rec
->recordType
== kHFSFileRecord
) {
2866 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2868 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2869 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2870 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2871 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2876 return (list
->realentries
< list
->maxentries
);
2880 * Pack a cat_entrylist buffer with attributes from the catalog
2882 * Note: index is zero relative
2885 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
, int *reachedeof
)
2889 BTreeIterator
* iterator
;
2890 struct readattr_state state
;
2897 int reached_eof
= 0;
2899 ce_list
->realentries
= 0;
2901 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2902 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2903 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2905 bzero (&state
, sizeof(struct readattr_state
));
2907 state
.hfsmp
= hfsmp
;
2908 state
.list
= ce_list
;
2909 state
.dir_cnid
= parentcnid
;
2910 state
.stdhfs
= std_hfs
;
2913 iterator
= hfs_mallocz(sizeof(*iterator
));
2914 key
= (CatalogKey
*)&iterator
->key
;
2916 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2917 index
= dirhint
->dh_index
+ 1;
2920 * Attempt to build a key from cached filename
2922 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2923 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2929 * If the last entry wasn't cached then position the btree iterator
2931 if ((index
== 0) || !have_key
) {
2933 * Position the iterator at the directory's thread record.
2934 * (i.e. just before the first entry)
2936 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2937 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2939 result
= MacToVFSError(result
);
2944 * Iterate until we reach the entry just
2945 * before the one we want to start with.
2948 struct position_state ps
;
2953 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2956 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2957 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2961 result
= MacToVFSError(result
);
2965 * Note: the index may now point to EOF if the directory
2966 * was modified in between system calls. We will return
2967 * ENOENT from cat_findposition if this is the case, and
2968 * when we bail out with an error, our caller (hfs_readdirattr_internal)
2969 * will suppress the error and indicate EOF to its caller.
2971 result
= MacToVFSError(result
);
2977 /* Fill list with entries starting at iterator->key. */
2978 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2979 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2982 result
= state
.error
;
2983 reached_eof
= state
.reached_eof
;
2985 else if (ce_list
->realentries
== 0) {
2990 result
= MacToVFSError(result
);
2997 * Resolve any hard links.
2999 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
3000 struct FndrFileInfo
*fip
;
3001 struct cat_entry
*cep
;
3002 struct HFSPlusCatalogFile filerec
;
3006 cep
= &ce_list
->entry
[i
];
3007 if (cep
->ce_attr
.ca_linkref
== 0)
3010 /* Note: Finder info is still in Big Endian */
3011 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
3013 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3014 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
3015 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
3018 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3019 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
3020 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
3021 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
3024 if (isfilelink
|| isdirlink
) {
3025 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
3027 /* Repack entry from inode record. */
3028 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
3029 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
3030 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
3031 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
3032 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
3037 hfs_free(iterator
, sizeof(*iterator
));
3038 *reachedeof
= reached_eof
;
3039 return MacToVFSError(result
);
3042 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3045 * Callback to pack directory entries.
3046 * Called with packdirentry_state for each item in a directory.
3049 /* Hard link information collected during cat_getdirentries. */
3052 user_addr_t dirent_addr
;
3054 typedef struct linkinfo linkinfo_t
;
3056 /* State information for the getdirentries_callback function. */
3057 struct packdirentry_state
{
3058 int cbs_flags
; /* VNODE_READDIR_* flags */
3059 u_int32_t cbs_parentID
;
3060 u_int32_t cbs_index
;
3062 ExtendedVCB
* cbs_hfsmp
;
3065 int32_t cbs_maxlinks
;
3066 linkinfo_t
* cbs_linkinfo
;
3067 struct cat_desc
* cbs_desc
;
3068 u_int8_t
* cbs_namebuf
;
3070 * The following fields are only used for NFS readdir, which
3071 * uses the next file id as the seek offset of each entry.
3073 struct direntry
* cbs_direntry
;
3074 struct direntry
* cbs_prevdirentry
;
3075 u_int32_t cbs_previlinkref
;
3076 Boolean cbs_hasprevdirentry
;
3081 * getdirentries callback for HFS Plus directories.
3084 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3085 struct packdirentry_state
*state
)
3087 struct hfsmount
*hfsmp
;
3088 const CatalogName
*cnp
;
3091 struct dirent catent
;
3092 struct direntry
* entry
= NULL
;
3094 u_int32_t ilinkref
= 0;
3095 u_int32_t curlinkref
= 0;
3098 u_int8_t type
= DT_UNKNOWN
;
3099 u_int8_t is_mangled
= 0;
3100 u_int8_t is_link
= 0;
3102 user_addr_t uiobase
= USER_ADDR_NULL
;
3107 Boolean stop_after_pack
= false;
3109 hfsmp
= state
->cbs_hfsmp
;
3110 curID
= ckp
->hfsPlus
.parentID
;
3112 /* We're done when parent directory changes */
3113 if (state
->cbs_parentID
!= curID
) {
3115 * If the parent ID is different from curID this means we've hit
3116 * the EOF for the directory. To help future callers, we mark
3117 * the cbs_eof boolean. However, we should only mark the EOF
3118 * boolean if we're about to return from this function.
3120 * This is because this callback function does its own uiomove
3121 * to get the data to userspace. If we set the boolean before determining
3122 * whether or not the current entry has enough room to write its
3123 * data to userland, we could fool the callers of this catalog function
3124 * into thinking they've hit EOF earlier than they really would have.
3125 * In that case, we'd know that we have more entries to process and
3126 * send to userland, but we didn't have enough room.
3128 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3129 * about to return and won't write any new data back
3130 * to userland. In the stop_after_pack case, we'll set this boolean
3131 * regardless, so it's slightly safer to let that logic mark the boolean,
3132 * especially since it's closer to the return of this function.
3135 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3136 /* The last record has not been returned yet, so we
3137 * want to stop after packing the last item
3139 if (state
->cbs_hasprevdirentry
) {
3140 stop_after_pack
= true;
3142 state
->cbs_eof
= true;
3143 state
->cbs_result
= ENOENT
;
3144 return (0); /* stop */
3147 state
->cbs_eof
= true;
3148 state
->cbs_result
= ENOENT
;
3149 return (0); /* stop */
3153 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3154 entry
= state
->cbs_direntry
;
3155 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3156 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3158 * The NFS server sometimes needs to make filenames fit in
3159 * NAME_MAX bytes (since its client may not be able to
3160 * handle a longer name). In that case, NFS will ask us
3161 * to mangle the name to keep it short enough.
3163 maxnamelen
= NAME_MAX
+ 1;
3165 maxnamelen
= sizeof(entry
->d_name
);
3168 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3169 maxnamelen
= sizeof(catent
.d_name
);
3172 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3173 /* The last item returns a non-zero invalid cookie */
3176 switch(crp
->recordType
) {
3177 case kHFSPlusFolderRecord
:
3179 cnid
= crp
->hfsPlusFolder
.folderID
;
3180 /* Hide our private system directories. */
3181 if (curID
== kHFSRootFolderID
) {
3182 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3183 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3188 case kHFSPlusFileRecord
:
3189 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3190 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3191 cnid
= crp
->hfsPlusFile
.fileID
;
3193 * When a hardlink link is encountered save its link ref.
3195 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3196 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3197 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3198 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3199 /* If link ref is inode's file id then use it directly. */
3200 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3201 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3203 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3206 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3207 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3208 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3209 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3210 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3211 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3212 /* A directory's link resolves to a directory. */
3214 /* A directory's link ref is always inode's file id. */
3215 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3218 /* Hide the journal files */
3219 if ((curID
== kHFSRootFolderID
) &&
3220 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3221 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3222 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3227 return (0); /* stop */
3230 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3232 namelen
= cnp
->ustr
.length
;
3234 * For MacRoman encoded names (textEncoding == 0), assume that it's ascii
3235 * and convert it directly in an attempt to avoid the more
3236 * expensive utf8_encodestr conversion.
3238 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3241 const u_int16_t
*chp
;
3243 chp
= &cnp
->ustr
.unicode
[0];
3244 for (i
= 0; i
< (int)namelen
; ++i
) {
3246 if (ch
> 0x007f || ch
== 0x0000) {
3247 /* Perform expensive utf8_encodestr conversion */
3250 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3252 nameptr
[namelen
] = '\0';
3256 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3257 nameptr
, &namelen
, maxnamelen
, ':', 0);
3260 /* Check result returned from encoding the filename to utf8 */
3261 if (result
== ENAMETOOLONG
) {
3263 * If we were looking at a catalog record for a hardlink (not the inode),
3264 * then we want to use its link ID as opposed to the inode ID for
3265 * a mangled name. For all other cases, they are the same. Note that
3266 * due to the way directory hardlinks are implemented, the actual link
3267 * is going to be counted as a file record, so we can catch both
3270 cnid_t linkid
= cnid
;
3272 linkid
= crp
->hfsPlusFile
.fileID
;
3275 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3276 cnp
->ustr
.unicode
, maxnamelen
,
3277 (ByteCount
*)&namelen
, nameptr
, linkid
);
3282 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3284 * The index is 1 relative and includes "." and ".."
3286 * Also stuff the cnid in the upper 32 bits of the cookie.
3287 * The cookie is stored to the previous entry, which will
3288 * be packed and copied this time
3290 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3291 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3292 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3294 catent
.d_type
= type
;
3295 catent
.d_namlen
= namelen
;
3296 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3298 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3300 catent
.d_fileno
= cnid
;
3301 uioaddr
= (caddr_t
) &catent
;
3304 /* Save current base address for post processing of hard-links. */
3305 if (ilinkref
|| state
->cbs_previlinkref
) {
3306 uiobase
= uio_curriovbase(state
->cbs_uio
);
3308 /* If this entry won't fit then we're done */
3309 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3310 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3311 return (0); /* stop */
3314 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3315 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3316 if (state
->cbs_result
== 0) {
3319 /* Remember previous entry */
3320 state
->cbs_desc
->cd_cnid
= cnid
;
3321 if (type
== DT_DIR
) {
3322 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3324 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3326 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3327 state
->cbs_desc
->cd_namelen
= 0;
3330 state
->cbs_desc
->cd_encoding
= xxxx
;
3333 state
->cbs_desc
->cd_namelen
= namelen
;
3334 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3336 /* Store unmangled name for the directory hint else it will
3337 * restart readdir at the last location again
3339 u_int8_t
*new_nameptr
;
3341 size_t tmp_namelen
= 0;
3343 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3344 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3345 cnp
->ustr
.length
* sizeof(UniChar
),
3347 new_nameptr
= hfs_malloc(bufsize
);
3348 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3349 cnp
->ustr
.length
* sizeof(UniChar
),
3350 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3352 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3353 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3355 hfs_free(new_nameptr
, bufsize
);
3358 if (state
->cbs_hasprevdirentry
) {
3359 curlinkref
= ilinkref
; /* save current */
3360 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3363 * Record any hard links for post processing.
3365 if ((ilinkref
!= 0) &&
3366 (state
->cbs_result
== 0) &&
3367 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3368 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3369 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3370 state
->cbs_nlinks
++;
3372 if (state
->cbs_hasprevdirentry
) {
3373 ilinkref
= curlinkref
; /* restore current */
3377 /* Fill the direntry to be used the next time */
3378 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3379 if (stop_after_pack
) {
3380 state
->cbs_eof
= true;
3381 return (0); /* stop */
3383 entry
->d_type
= type
;
3384 entry
->d_namlen
= namelen
;
3385 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3387 /* File number = 0 means skip entry */
3388 entry
->d_fileno
= 0;
3390 entry
->d_fileno
= cnid
;
3392 /* swap the current and previous entry */
3393 struct direntry
* tmp
;
3394 tmp
= state
->cbs_direntry
;
3395 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3396 state
->cbs_prevdirentry
= tmp
;
3397 state
->cbs_hasprevdirentry
= true;
3398 state
->cbs_previlinkref
= ilinkref
;
3401 /* Continue iteration if there's room */
3402 return (state
->cbs_result
== 0 &&
3403 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3408 * getdirentries callback for standard HFS (non HFS+) directories.
3411 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3412 struct packdirentry_state
*state
)
3414 struct hfsmount
*hfsmp
;
3415 const CatalogName
*cnp
;
3418 struct dirent catent
;
3420 u_int8_t type
= DT_UNKNOWN
;
3427 hfsmp
= state
->cbs_hfsmp
;
3429 curID
= ckp
->hfs
.parentID
;
3431 /* We're done when parent directory changes */
3432 if (state
->cbs_parentID
!= curID
) {
3433 state
->cbs_result
= ENOENT
;
3434 return (0); /* stop */
3437 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3438 maxnamelen
= sizeof(catent
.d_name
);
3440 switch(crp
->recordType
) {
3441 case kHFSFolderRecord
:
3443 cnid
= crp
->hfsFolder
.folderID
;
3445 case kHFSFileRecord
:
3447 cnid
= crp
->hfsFile
.fileID
;
3450 return (0); /* stop */
3453 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3454 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3456 * When an HFS name cannot be encoded with the current
3457 * volume encoding we use MacRoman as a fallback.
3460 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3462 catent
.d_type
= type
;
3463 catent
.d_namlen
= namelen
;
3464 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3465 catent
.d_fileno
= cnid
;
3466 uioaddr
= (caddr_t
) &catent
;
3468 /* If this entry won't fit then we're done */
3469 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3470 return (0); /* stop */
3473 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3474 if (state
->cbs_result
== 0) {
3477 /* Remember previous entry */
3478 state
->cbs_desc
->cd_cnid
= cnid
;
3479 if (type
== DT_DIR
) {
3480 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3482 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3484 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3485 state
->cbs_desc
->cd_namelen
= 0;
3487 state
->cbs_desc
->cd_namelen
= namelen
;
3488 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3491 /* Continue iteration if there's room */
3492 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3497 * Pack a uio buffer with directory entries from the catalog
3500 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3501 uio_t uio
, int flags
, int * items
, int * eofflag
)
3504 BTreeIterator
* iterator
;
3506 struct packdirentry_state state
;
3515 extended
= flags
& VNODE_READDIR_EXTENDED
;
3517 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3520 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3522 #define MAX_LINKINFO_ENTRIES 275
3524 * Get a buffer for link info array, btree iterator and a direntry.
3526 * We impose an cap of 275 link entries when trying to compute
3527 * the total number of hardlink entries that we'll allow in the
3528 * linkinfo array, as this has been shown to noticeably impact performance.
3530 * Note that in the case where there are very few hardlinks,
3531 * this does not restrict or prevent us from vending out as many entries
3532 * as we can to the uio_resid, because the getdirentries callback
3533 * uiomoves the directory entries to the uio itself and does not use
3534 * this MALLOC'd array. It also limits itself to maxlinks of hardlinks.
3537 // This value cannot underflow: both entrycnt and the rhs are unsigned 32-bit
3538 // ints, so the worst-case MIN of them is 0.
3539 maxlinks
= MIN (entrycnt
, (u_int32_t
)(uio_resid(uio
) / SMALL_DIRENTRY_SIZE
));
3540 // Prevent overflow.
3541 maxlinks
= MIN (maxlinks
, MAX_LINKINFO_ENTRIES
);
3542 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3545 bufsize
+= 2*sizeof(struct direntry
);
3547 buffer
= hfs_mallocz(bufsize
);
3549 state
.cbs_flags
= flags
;
3550 state
.cbs_hasprevdirentry
= false;
3551 state
.cbs_previlinkref
= 0;
3552 state
.cbs_nlinks
= 0;
3553 state
.cbs_maxlinks
= maxlinks
;
3554 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3556 * We need to set cbs_eof to false regardless of whether or not the
3557 * control flow is actually in the extended case, since we use this
3558 * field to track whether or not we've returned EOF from the iterator function.
3560 state
.cbs_eof
= false;
3562 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3563 key
= (CatalogKey
*)&iterator
->key
;
3565 index
= dirhint
->dh_index
+ 1;
3567 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3568 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3571 * Attempt to build a key from cached filename
3573 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3574 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3575 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3580 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3582 * Position the iterator at the directory's thread record.
3583 * (i.e. just before the first entry)
3585 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3586 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3587 iterator
->hint
.index
= 0;
3592 * If the last entry wasn't cached then position the btree iterator
3596 * Position the iterator at the directory's thread record.
3597 * (i.e. just before the first entry)
3599 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3600 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3602 result
= MacToVFSError(result
);
3606 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3609 * Iterate until we reach the entry just
3610 * before the one we want to start with.
3613 struct position_state ps
;
3618 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3621 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3622 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3626 result
= MacToVFSError(result
);
3628 result
= MacToVFSError(result
);
3629 if (result
== ENOENT
) {
3631 * ENOENT means we've hit the EOF.
3632 * suppress the error, and set the eof flag.
3635 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3643 state
.cbs_index
= index
;
3644 state
.cbs_hfsmp
= hfsmp
;
3645 state
.cbs_uio
= uio
;
3646 state
.cbs_desc
= &dirhint
->dh_desc
;
3647 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3648 state
.cbs_result
= 0;
3649 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3651 /* Use a temporary buffer to hold intermediate descriptor names. */
3652 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3653 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3654 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3655 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3656 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3659 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3661 enum BTreeIterationOperations op
;
3662 if (extended
&& index
!= 0 && have_key
)
3663 op
= kBTreeCurrentRecord
;
3665 op
= kBTreeNextRecord
;
3668 * Process as many entries as possible starting at iterator->key.
3670 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3672 result
= BTIterateRecords(fcb
, op
, iterator
,
3673 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3675 /* For extended calls, every call to getdirentries_callback()
3676 * transfers the previous directory entry found to the user
3677 * buffer. Therefore when BTIterateRecords reaches the end of
3678 * Catalog BTree, call getdirentries_callback() again with
3679 * dummy values to copy the last directory entry stored in
3680 * packdirentry_state
3682 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3686 bzero(&ckp
, sizeof(ckp
));
3687 bzero(&crp
, sizeof(crp
));
3689 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3694 /* HFS (standard) */
3695 result
= BTIterateRecords(fcb
, op
, iterator
,
3696 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3700 /* Note that state.cbs_index is still valid on errors */
3701 *items
= state
.cbs_index
- index
;
3702 index
= state
.cbs_index
;
3705 * Also note that cbs_eof is set in all cases if we ever hit EOF
3706 * during the enumeration by the catalog callback. Mark the directory's hint
3707 * descriptor as having hit EOF.
3710 if (state
.cbs_eof
) {
3711 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3715 /* Finish updating the catalog iterator. */
3716 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3717 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3718 dirhint
->dh_index
= index
- 1;
3720 /* Fix up the name. */
3721 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3722 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3723 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3725 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3726 dirhint
->dh_desc
.cd_namelen
= 0;
3730 * Post process any hard links to get the real file id.
3732 if (state
.cbs_nlinks
> 0) {
3734 user_addr_t address
;
3737 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3738 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3740 /* This assumes that d_ino is always first field. */
3741 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3742 if (address
== (user_addr_t
)0)
3744 if (uio_isuserspace(uio
)) {
3746 ino64_t fileid_64
= (ino64_t
)fileid
;
3747 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3749 (void) copyout(&fileid
, address
, sizeof(fileid
));
3751 } else /* system space */ {
3753 ino64_t fileid_64
= (ino64_t
)fileid
;
3754 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3756 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3762 if (state
.cbs_result
)
3763 result
= state
.cbs_result
;
3765 result
= MacToVFSError(result
);
3767 if (result
== ENOENT
) {
3772 hfs_free(buffer
, bufsize
);
3779 * Callback to establish directory position.
3780 * Called with position_state for each item in a directory.
3783 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3784 struct position_state
*state
)
3788 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3789 curID
= ckp
->hfsPlus
.parentID
;
3793 curID
= ckp
->hfs
.parentID
;
3797 /* Make sure parent directory didn't change */
3798 if (state
->parentID
!= curID
) {
3800 * The parent ID is different from curID this means we've hit
3801 * the EOF for the directory.
3803 state
->error
= ENOENT
;
3804 return (0); /* stop */
3807 /* Count this entry */
3808 switch(crp
->recordType
) {
3809 case kHFSPlusFolderRecord
:
3810 case kHFSPlusFileRecord
:
3812 case kHFSFolderRecord
:
3813 case kHFSFileRecord
:
3818 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3819 crp
->recordType
, curID
);
3820 state
->error
= EINVAL
;
3821 return (0); /* stop */
3824 return (state
->count
< state
->index
);
3829 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3831 * The name portion of the key is compared using a 16-bit binary comparison.
3832 * This is called from the b-tree code.
3835 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3837 u_int32_t searchParentID
, trialParentID
;
3840 searchParentID
= searchKey
->parentID
;
3841 trialParentID
= trialKey
->parentID
;
3844 if (searchParentID
> trialParentID
) {
3846 } else if (searchParentID
< trialParentID
) {
3849 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3850 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3851 int length1
= searchKey
->nodeName
.length
;
3852 int length2
= trialKey
->nodeName
.length
;
3854 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3863 * Compare two standard HFS catalog keys
3865 * Result: +n search key > trial key
3866 * 0 search key = trial key
3867 * -n search key < trial key
3870 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3872 cnid_t searchParentID
, trialParentID
;
3875 searchParentID
= searchKey
->parentID
;
3876 trialParentID
= trialKey
->parentID
;
3878 if (searchParentID
> trialParentID
)
3880 else if (searchParentID
< trialParentID
)
3882 else /* parent dirID's are equal, compare names */
3883 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3891 * Compare two HFS+ catalog keys
3893 * Result: +n search key > trial key
3894 * 0 search key = trial key
3895 * -n search key < trial key
3898 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3900 cnid_t searchParentID
, trialParentID
;
3903 searchParentID
= searchKey
->parentID
;
3904 trialParentID
= trialKey
->parentID
;
3906 if (searchParentID
> trialParentID
) {
3909 else if (searchParentID
< trialParentID
) {
3912 /* parent node ID's are equal, compare names */
3913 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3914 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3916 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3917 searchKey
->nodeName
.length
,
3918 &trialKey
->nodeName
.unicode
[0],
3919 trialKey
->nodeName
.length
);
3927 * buildkey - build a Catalog b-tree key from a cnode descriptor
3930 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3931 HFSPlusCatalogKey
*key
, int retry
)
3933 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3934 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3936 size_t unicodeBytes
= 0;
3942 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3943 return (EINVAL
); /* invalid name */
3945 key
->parentID
= descp
->cd_parentcnid
;
3946 key
->nodeName
.length
= 0;
3948 * Convert filename from UTF-8 into Unicode
3951 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3952 utf8_flags
|= UTF_DECOMPOSED
;
3953 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3954 key
->nodeName
.unicode
, &unicodeBytes
,
3955 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3956 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3957 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3959 if (result
!= ENAMETOOLONG
)
3960 result
= EINVAL
; /* name has invalid characters */
3966 * For HFS volumes convert to an HFS compatible key
3968 * XXX need to save the encoding that succeeded
3971 HFSCatalogKey hfskey
;
3973 bzero(&hfskey
, sizeof(hfskey
));
3974 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3975 hfskey
.parentID
= key
->parentID
;
3976 hfskey
.nodeName
[0] = 0;
3977 if (key
->nodeName
.length
> 0) {
3979 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3980 key
->nodeName
.length
* 2,
3981 key
->nodeName
.unicode
,
3982 &hfskey
.nodeName
[0], retry
)) != 0) {
3983 if (res
!= ENAMETOOLONG
)
3988 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3990 bcopy(&hfskey
, key
, sizeof(hfskey
));
3999 * Resolve hard link reference to obtain the inode record.
4002 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
4004 FSBufferDescriptor btdata
;
4005 struct BTreeIterator
*iterator
;
4006 struct cat_desc idesc
;
4011 BDINIT(btdata
, recp
);
4014 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4015 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4017 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4018 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4021 /* Get space for iterator */
4022 iterator
= hfs_mallocz(sizeof(*iterator
));
4024 /* Build a descriptor for private dir. */
4025 idesc
.cd_parentcnid
= parentcnid
;
4026 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4027 idesc
.cd_namelen
= strlen(inodename
);
4030 idesc
.cd_encoding
= 0;
4031 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4033 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4034 &btdata
, NULL
, NULL
);
4037 /* Make sure there's a reference */
4038 if (recp
->hl_linkCount
== 0)
4039 recp
->hl_linkCount
= 2;
4041 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4044 hfs_free(iterator
, sizeof(*iterator
));
4046 return (result
? ENOENT
: 0);
4050 * Resolve hard link reference to obtain the inode number.
4053 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4055 struct HFSPlusCatalogFile record
;
4059 * Since we know resolvelinkid is only called from
4060 * cat_getdirentries, we can assume that only file
4061 * hardlinks need to be resolved (cat_getdirentries
4062 * can resolve directory hardlinks in place).
4064 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4066 if (record
.fileID
== 0)
4069 *ino
= record
.fileID
;
4075 * getkey - get a key from id by doing a thread lookup
4078 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4080 struct BTreeIterator
* iterator
;
4081 FSBufferDescriptor btdata
;
4084 CatalogRecord
* recp
;
4088 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4090 iterator
= hfs_mallocz(sizeof(*iterator
));
4091 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4093 recp
= hfs_malloc(sizeof(CatalogRecord
));
4094 BDINIT(btdata
, recp
);
4096 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4097 &btdata
, &datasize
, iterator
);
4101 /* Turn thread record into a cnode key (in place) */
4102 switch (recp
->recordType
) {
4105 case kHFSFileThreadRecord
:
4106 case kHFSFolderThreadRecord
:
4107 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4108 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4109 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4113 case kHFSPlusFileThreadRecord
:
4114 case kHFSPlusFolderThreadRecord
:
4115 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4116 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4117 (keyp
->hfsPlus
.nodeName
.length
* 2);
4118 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4127 hfs_free(iterator
, sizeof(*iterator
));
4128 hfs_free(recp
, sizeof(*recp
));
4130 return MacToVFSError(result
);
4134 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4135 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4136 * cat_desc here). Both key and attrp must point to real structures.
4138 * The key's parent id is the only part of the key expected to be used by the caller.
4139 * The name portion of the key may not always be valid (ie in the case of a hard link).
4142 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4146 result
= getkey(hfsmp
, cnid
, key
);
4149 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4152 * Check for a raw file hardlink inode.
4153 * Fix up the parent id in the key if necessary.
4154 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4156 if ((result
== 0) &&
4157 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4158 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4159 cnid_t nextlinkid
= 0;
4160 cnid_t prevlinkid
= 0;
4161 struct cat_desc linkdesc
;
4164 * Pick up the first link in the chain and get a descriptor for it.
4165 * This allows blind bulk access checks to work for hardlinks.
4167 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4168 (nextlinkid
!= 0)) {
4169 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4170 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4171 cat_releasedesc(&linkdesc
);
4175 return MacToVFSError(result
);
4180 * buildrecord - build a default catalog directory or file record
4183 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4184 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4186 int type
= attrp
->ca_mode
& S_IFMT
;
4187 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4190 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4192 if (type
== S_IFDIR
) {
4193 crp
->recordType
= kHFSPlusFolderRecord
;
4194 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4195 crp
->hfsPlusFolder
.valence
= 0;
4196 crp
->hfsPlusFolder
.folderID
= cnid
;
4197 crp
->hfsPlusFolder
.createDate
= createtime
;
4198 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4199 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4200 crp
->hfsPlusFolder
.accessDate
= createtime
;
4201 crp
->hfsPlusFolder
.backupDate
= 0;
4202 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4203 crp
->hfsPlusFolder
.folderCount
= 0;
4204 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4205 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4206 bsdp
->special
.linkCount
= 1;
4207 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4209 crp
->recordType
= kHFSPlusFileRecord
;
4210 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4211 crp
->hfsPlusFile
.reserved1
= 0;
4212 crp
->hfsPlusFile
.fileID
= cnid
;
4213 crp
->hfsPlusFile
.createDate
= createtime
;
4214 crp
->hfsPlusFile
.contentModDate
= createtime
;
4215 crp
->hfsPlusFile
.accessDate
= createtime
;
4216 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4217 crp
->hfsPlusFile
.backupDate
= 0;
4218 crp
->hfsPlusFile
.textEncoding
= encoding
;
4219 crp
->hfsPlusFile
.reserved2
= 0;
4220 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4221 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4222 /* BLK/CHR need to save the device info */
4223 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4224 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4226 bsdp
->special
.linkCount
= 1;
4228 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4229 *recordSize
= sizeof(HFSPlusCatalogFile
);
4231 bsdp
->ownerID
= attrp
->ca_uid
;
4232 bsdp
->groupID
= attrp
->ca_gid
;
4233 bsdp
->fileMode
= attrp
->ca_mode
;
4234 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4235 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4239 createtime
= UTCToLocal(createtime
);
4240 if (type
== S_IFDIR
) {
4241 bzero(crp
, sizeof(HFSCatalogFolder
));
4242 crp
->recordType
= kHFSFolderRecord
;
4243 crp
->hfsFolder
.folderID
= cnid
;
4244 crp
->hfsFolder
.createDate
= createtime
;
4245 crp
->hfsFolder
.modifyDate
= createtime
;
4246 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4247 *recordSize
= sizeof(HFSCatalogFolder
);
4249 bzero(crp
, sizeof(HFSCatalogFile
));
4250 crp
->recordType
= kHFSFileRecord
;
4251 crp
->hfsFile
.fileID
= cnid
;
4252 crp
->hfsFile
.createDate
= createtime
;
4253 crp
->hfsFile
.modifyDate
= createtime
;
4254 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4255 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4256 *recordSize
= sizeof(HFSCatalogFile
);
4265 * builddesc - build a cnode descriptor from an HFS+ key
4268 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4269 int isdir
, struct cat_desc
*descp
)
4272 unsigned char * nameptr
;
4275 unsigned char tmpbuff
[128];
4277 /* guess a size... */
4278 bufsize
= (3 * key
->nodeName
.length
) + 1;
4279 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4280 nameptr
= hfs_malloc(bufsize
);
4282 nameptr
= &tmpbuff
[0];
4285 result
= utf8_encodestr(key
->nodeName
.unicode
,
4286 key
->nodeName
.length
* sizeof(UniChar
),
4287 nameptr
, (size_t *)&utf8len
,
4290 if (result
== ENAMETOOLONG
) {
4291 if (nameptr
!= &tmpbuff
[0])
4292 hfs_free(nameptr
, bufsize
);
4293 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4294 key
->nodeName
.length
* sizeof(UniChar
),
4296 nameptr
= hfs_malloc(bufsize
);
4298 result
= utf8_encodestr(key
->nodeName
.unicode
,
4299 key
->nodeName
.length
* sizeof(UniChar
),
4300 nameptr
, (size_t *)&utf8len
,
4303 descp
->cd_parentcnid
= key
->parentID
;
4304 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4305 descp
->cd_namelen
= utf8len
;
4306 descp
->cd_cnid
= cnid
;
4307 descp
->cd_hint
= hint
;
4308 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4310 descp
->cd_flags
|= CD_ISDIR
;
4311 descp
->cd_encoding
= encoding
;
4312 if (nameptr
!= &tmpbuff
[0]) {
4313 hfs_free(nameptr
, bufsize
);
4320 * getbsdattr - get attributes in bsd format
4324 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4326 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4327 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4329 attrp
->ca_recflags
= crp
->flags
;
4330 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4331 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4332 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4333 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4334 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4335 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4337 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4338 attrp
->ca_flags
= 0;
4339 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4340 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4342 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4344 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4346 attrp
->ca_linkcount
= 1;
4349 attrp
->ca_linkcount
= 1; /* may be overridden below */
4351 attrp
->ca_uid
= bsd
->ownerID
;
4352 attrp
->ca_gid
= bsd
->groupID
;
4353 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4354 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4355 switch (attrp
->ca_mode
& S_IFMT
) {
4356 case S_IFCHR
: /* fall through */
4358 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4364 /* Pick up the hard link count */
4365 if (bsd
->special
.linkCount
> 0)
4366 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4371 * Override the permissions as determined by the mount auguments
4372 * in ALMOST the same way unset permissions are treated but keep
4373 * track of whether or not the file or folder is hfs locked
4374 * by leaving the h_pflags field unchanged from what was unpacked
4375 * out of the catalog.
4378 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4379 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4380 * at the VFS layer, so there is no need to do it here now; this also
4381 * allows VFS to let root see the real UIDs.
4383 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4384 * attrp->ca_uid = hfsmp->hfs_uid;
4385 * attrp->ca_gid = hfsmp->hfs_gid;
4391 if (!S_ISDIR(attrp
->ca_mode
)) {
4392 attrp
->ca_mode
&= ~S_IFMT
;
4393 attrp
->ca_mode
|= S_IFDIR
;
4395 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4396 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4397 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4399 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4400 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4401 attrp
->ca_flags
|= UF_HIDDEN
;
4403 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4404 if (crp
->flags
& kHFSFileLockedMask
) {
4405 /* The file's supposed to be locked:
4406 Make sure at least one of the IMMUTABLE bits is set: */
4407 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4408 attrp
->ca_flags
|= UF_IMMUTABLE
;
4410 /* The file's supposed to be unlocked: */
4411 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4413 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4414 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4415 attrp
->ca_flags
|= UF_HIDDEN
;
4416 /* get total blocks (both forks) */
4417 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4419 /* On HFS+ the ThreadExists flag must always be set. */
4420 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4421 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4423 /* Pick up the hardlink first link, if any. */
4424 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4427 attrp
->ca_fileid
= crp
->fileID
;
4429 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4434 * promotekey - promote hfs key to hfs plus key
4438 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4439 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4441 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4445 *encoding
= hfsmp
->hfs_encoding
;
4447 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4448 kHFSPlusMaxFileNameChars
, &uniCount
);
4450 * When an HFS name cannot be encoded with the current
4451 * encoding use MacRoman as a fallback.
4453 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4455 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4456 keyp
->nodeName
.unicode
,
4457 kHFSPlusMaxFileNameChars
,
4461 keyp
->nodeName
.length
= uniCount
;
4462 keyp
->parentID
= hfskey
->parentID
;
4466 * promotefork - promote hfs fork info to hfs plus
4470 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4471 int resource
, struct cat_fork
* forkp
)
4473 struct HFSPlusExtentDescriptor
*xp
;
4474 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4476 bzero(forkp
, sizeof(*forkp
));
4477 xp
= &forkp
->cf_extents
[0];
4479 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4480 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4481 forkp
->cf_bytesread
= 0;
4482 forkp
->cf_vblocks
= 0;
4483 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4484 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4485 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4486 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4487 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4488 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4490 forkp
->cf_size
= filep
->dataLogicalSize
;
4491 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4492 forkp
->cf_bytesread
= 0;
4493 forkp
->cf_vblocks
= 0;
4494 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4495 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4496 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4497 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4498 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4499 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4504 * promoteattr - promote standard hfs catalog attributes to hfs plus
4508 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4510 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4512 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4513 const struct HFSCatalogFolder
* folder
;
4515 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4516 crp
->recordType
= kHFSPlusFolderRecord
;
4517 crp
->flags
= folder
->flags
;
4518 crp
->fileID
= folder
->folderID
;
4519 crp
->createDate
= LocalToUTC(folder
->createDate
);
4520 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4521 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4522 crp
->reserved1
= folder
->valence
;
4524 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4526 const struct HFSCatalogFile
* file
;
4528 file
= (const struct HFSCatalogFile
*) dataPtr
;
4529 crp
->recordType
= kHFSPlusFileRecord
;
4530 crp
->flags
= file
->flags
;
4531 crp
->fileID
= file
->fileID
;
4532 crp
->createDate
= LocalToUTC(file
->createDate
);
4533 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4534 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4537 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4538 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4539 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4540 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4542 crp
->textEncoding
= 0;
4543 crp
->attributeModDate
= crp
->contentModDate
;
4544 crp
->accessDate
= crp
->contentModDate
;
4545 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4550 * Build a catalog node thread record from a catalog key
4551 * and return the size of the record.
4554 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4559 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4560 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4562 size
= sizeof(HFSPlusCatalogThread
);
4564 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4566 rec
->recordType
= kHFSPlusFileThreadRecord
;
4568 rec
->parentID
= key
->parentID
;
4569 bcopy(&key
->nodeName
, &rec
->nodeName
,
4570 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4572 /* HFS Plus has variable sized thread records */
4573 size
-= (sizeof(rec
->nodeName
.unicode
) -
4574 (rec
->nodeName
.length
* sizeof(UniChar
)));
4579 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4580 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4582 size
= sizeof(HFSCatalogThread
);
4585 rec
->recordType
= kHFSFolderThreadRecord
;
4587 rec
->recordType
= kHFSFileThreadRecord
;
4588 rec
->parentID
= key
->parentID
;
4589 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4598 * Build a catalog node thread key.
4601 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4604 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4605 key
->hfsPlus
.parentID
= parentID
;
4606 key
->hfsPlus
.nodeName
.length
= 0;
4610 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4611 key
->hfs
.reserved
= 0;
4612 key
->hfs
.parentID
= parentID
;
4613 key
->hfs
.nodeName
[0] = 0;
4620 * Extract the text encoding from a catalog node record.
4623 getencoding(const CatalogRecord
*crp
)
4627 if (crp
->recordType
== kHFSPlusFolderRecord
)
4628 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4629 else if (crp
->recordType
== kHFSPlusFileRecord
)
4630 encoding
= crp
->hfsPlusFile
.textEncoding
;
4638 * Extract the CNID from a catalog node record.
4641 getcnid(const CatalogRecord
*crp
)
4645 switch (crp
->recordType
) {
4648 case kHFSFolderRecord
:
4649 cnid
= crp
->hfsFolder
.folderID
;
4651 case kHFSFileRecord
:
4652 cnid
= crp
->hfsFile
.fileID
;
4656 case kHFSPlusFolderRecord
:
4657 cnid
= crp
->hfsPlusFolder
.folderID
;
4659 case kHFSPlusFileRecord
:
4660 cnid
= crp
->hfsPlusFile
.fileID
;
4663 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4671 * Extract the parent ID from a catalog node record.
4674 getparentcnid(const CatalogRecord
*recp
)
4678 switch (recp
->recordType
) {
4681 case kHFSFileThreadRecord
:
4682 case kHFSFolderThreadRecord
:
4683 cnid
= recp
->hfsThread
.parentID
;
4687 case kHFSPlusFileThreadRecord
:
4688 case kHFSPlusFolderThreadRecord
:
4689 cnid
= recp
->hfsPlusThread
.parentID
;
4692 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4700 * Determine if a catalog node record is a directory.
4703 isadir(const CatalogRecord
*crp
)
4705 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4709 if (crp
->recordType
== kHFSFolderRecord
) {
4718 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4719 * (not inode) using catalog record id. Note that this function does
4720 * NOT resolve directory hard link to its directory inode and return
4723 * Note: The caller is responsible for releasing the output catalog
4724 * descriptor (when supplied outdescp is non-null).
4727 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4728 u_int8_t forktype
, struct cat_desc
*outdescp
,
4729 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4731 struct BTreeIterator
*iterator
= NULL
;
4732 FSBufferDescriptor btdata
;
4735 CatalogRecord
*recp
= NULL
;
4738 /* No directory hard links on standard HFS */
4739 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4743 iterator
= hfs_mallocz(sizeof(*iterator
));
4744 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4746 recp
= hfs_malloc(sizeof(CatalogRecord
));
4747 BDINIT(btdata
, recp
);
4749 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4750 &btdata
, &datasize
, iterator
);
4754 /* Directory hard links are catalog file record */
4755 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4760 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4761 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4762 (keyp
->hfsPlus
.nodeName
.length
* 2);
4763 if (forktype
== kHFSResourceForkType
) {
4764 /* Lookup resource fork for directory hard link */
4765 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4767 /* Lookup data fork, if any, for directory hard link */
4768 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4771 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4772 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4775 /* Just for sanity, make sure that id in catalog record and thread record match */
4776 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4777 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4778 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4784 hfs_free(recp
, sizeof(*recp
));
4786 hfs_free(iterator
, sizeof(*iterator
));
4788 return MacToVFSError(error
);
4792 * cnode_update_dirlink - update the catalog node for directory hard link
4793 * described by descp using the data from attrp and forkp.
4796 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4797 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4799 if (forktype
== kHFSResourceForkType
) {
4800 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4802 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);
4806 void hfs_fork_copy(struct cat_fork
*dst
, const struct cat_fork
*src
,
4807 HFSPlusExtentDescriptor
*extents
)
4809 /* Copy everything but the extents into the dest fork */
4810 memcpy(dst
, src
, offsetof(struct cat_fork
, cf_extents
));
4811 /* Then copy the supplied extents into the fork */
4812 memcpy(dst
->cf_extents
, extents
, sizeof(HFSPlusExtentRecord
));