2 * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
33 #include <sys/mount.h>
34 #include <sys/vnode.h>
35 #include <sys/dirent.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
39 #include <sys/utfconv.h>
42 #include "hfs_catalog.h"
43 #include "hfs_format.h"
44 #include "hfs_endian.h"
46 #include "hfscommon/headers/BTreesInternal.h"
47 #include "hfscommon/headers/BTreesPrivate.h"
48 #include "hfscommon/headers/HFSUnicodeWrappers.h"
52 * Initialization of an FSBufferDescriptor structure.
54 #define BDINIT(bd, addr) { \
55 (bd).bufferAddress = (addr); \
56 (bd).itemSize = sizeof(*(addr)); \
62 BTreeIterator iterator
;
63 HFSPlusCatalogKey key
;
68 struct cat_desc
* s_desc
;
69 struct cat_attr
* s_attr
;
70 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_HFSMNT
, &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_HFSMNT
);
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 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
280 bzero(iterator
, sizeof(*iterator
));
281 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&iterator
->key
);
283 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
284 BDINIT(btdata
, recp
);
286 result
= BTSearchRecord(hfsmp
->hfs_catalog_cp
->c_datafork
, iterator
, &btdata
, &datasize
, iterator
);
288 FREE (iterator
, M_TEMP
);
290 if (result
== btNotFound
) {
291 /* Good. File ID was not in use. Move on to checking EA B-Tree */
292 result
= file_attribute_exist (hfsmp
, nextCNID
);
293 if (result
== EEXIST
) {
294 /* This CNID has orphaned EAs. Skip it and move on to the next one */
299 /* For any other error, return the result */
304 * Now validate that there are no lingering cnodes with this ID. If a cnode
305 * has been removed on-disk (marked C_NOEXISTS), but has not yet been reclaimed,
306 * then it will still have an entry in the cnode hash table. This means that
307 * a subsequent lookup will find THAT entry and believe this one has been deleted
308 * prematurely. If there is a lingering cnode, then just skip this entry and move on.
310 * Note that we pass (existence_only == 1) argument to hfs_chash_snoop.
312 if (!std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
313 if (hfs_chash_snoop (hfsmp
, nextCNID
, 1, NULL
, NULL
) == 0) {
319 * If we get here, then we didn't see any thread records, orphaned EAs,
320 * or stale cnodes. This ID is safe to vend out.
322 *new_cnid
= nextCNID
;
324 else if (result
== noErr
) {
325 /* move on to the next ID */
329 /* For any other situation, just bail out */
338 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, __unused proc_t p
)
343 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
344 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
346 result
= BTReserveSpace(hfsmp
->hfs_catalog_cp
->c_datafork
, ops
, (void*)cookie
);
349 hfs_systemfile_unlock(hfsmp
, lockflags
);
351 return MacToVFSError(result
);
355 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, __unused proc_t p
)
359 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
360 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
362 (void) BTReleaseReserve(hfsmp
->hfs_catalog_cp
->c_datafork
, (void*)cookie
);
365 hfs_systemfile_unlock(hfsmp
, lockflags
);
371 struct hfsmount
*hfsmp
,
372 CatalogRecord
* recp
,
373 struct cat_attr
*attrp
,
374 struct cat_fork
*datafp
,
375 struct cat_fork
*rsrcfp
)
377 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
380 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
384 struct HFSPlusCatalogFile cnoderec
;
386 promoteattr(hfsmp
, recp
, &cnoderec
);
387 getbsdattr(hfsmp
, &cnoderec
, attrp
);
392 bzero(datafp
, sizeof(*datafp
));
396 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
397 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
401 /* Convert the data fork. */
402 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
403 datafp
->cf_new_size
= 0;
404 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
405 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
406 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
407 datafp
->cf_bytesread
=
408 recp
->hfsPlusFile
.dataFork
.clumpSize
*
409 HFSTOVCB(hfsmp
)->blockSize
;
411 datafp
->cf_bytesread
= 0;
413 datafp
->cf_vblocks
= 0;
414 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
415 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
417 /* Convert the resource fork. */
418 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
419 rsrcfp
->cf_new_size
= 0;
420 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
421 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
422 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
423 datafp
->cf_bytesread
=
424 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
425 HFSTOVCB(hfsmp
)->blockSize
;
427 datafp
->cf_bytesread
= 0;
429 rsrcfp
->cf_vblocks
= 0;
430 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
431 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
436 * Convert a raw catalog key and record into an in-core catalog descriptor.
438 * Note: The caller is responsible for releasing the catalog descriptor.
443 struct hfsmount
*hfsmp
,
445 CatalogRecord
* recp
,
446 struct cat_desc
*descp
)
448 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
449 HFSPlusCatalogKey
* pluskey
= NULL
;
455 pluskey
= (HFSPlusCatalogKey
*)key
;
456 encoding
= getencoding(recp
);
460 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
461 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
465 /* Get the CNID before calling builddesc. Need to error check it. */
466 cnid
= getcnid(recp
);
468 /* If ths CNID == 0, it's invalid. Mark as corrupt */
469 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
473 builddesc(pluskey
, cnid
, 0, encoding
, isadir(recp
), descp
);
478 FREE(pluskey
, M_TEMP
);
491 cat_releasedesc(struct cat_desc
*descp
)
493 const u_int8_t
* name
;
498 if ((descp
->cd_flags
& CD_HASBUF
) &&
499 (descp
->cd_nameptr
!= NULL
)) {
500 name
= descp
->cd_nameptr
;
501 descp
->cd_nameptr
= NULL
;
502 descp
->cd_namelen
= 0;
503 vfs_removename((const char *)name
);
505 descp
->cd_nameptr
= NULL
;
506 descp
->cd_namelen
= 0;
507 descp
->cd_flags
&= ~CD_HASBUF
;
511 * These Catalog functions allow access to the HFS Catalog (database).
512 * The catalog b-tree lock must be acquired before calling any of these routines.
516 * cat_lookup - lookup a catalog node using a cnode descriptor
518 * Note: The caller is responsible for releasing the output
519 * catalog descriptor (when supplied outdescp is non-null).
522 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
, int force_casesensitive_lookup
,
523 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
524 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
531 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
532 flags
= force_casesensitive_lookup
? HFS_LOOKUP_CASESENSITIVE
: 0;
534 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
536 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
540 result
= cat_lookupbykey(hfsmp
, keyp
, flags
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
542 if (result
== ENOENT
) {
544 struct cat_desc temp_desc
;
545 if (outdescp
== NULL
) {
546 bzero(&temp_desc
, sizeof(temp_desc
));
547 outdescp
= &temp_desc
;
549 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
551 *desc_cnid
= outdescp
->cd_cnid
;
553 if (outdescp
== &temp_desc
) {
554 /* Release the local copy of desc */
555 cat_releasedesc(outdescp
);
557 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
558 // make MacRoman key from utf-8
559 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
560 // update desc text encoding so that other catalog ops succeed
570 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
572 struct BTreeIterator
*iterator
;
573 struct FSBufferDescriptor file_data
;
574 struct HFSCatalogFile file_rec
;
579 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
582 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
584 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
585 bzero(&iterator
[0], 2* sizeof(*iterator
));
586 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
590 BDINIT(file_data
, &file_rec
);
591 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
595 if (file_rec
.recordType
!= kHFSFileRecord
) {
600 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
601 struct FSBufferDescriptor thread_data
;
602 struct HFSCatalogThread thread_rec
;
604 file_rec
.flags
|= kHFSThreadExistsMask
;
605 BDINIT(thread_data
, &thread_rec
);
606 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
607 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
609 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
613 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
614 (void) BTFlushPath(fcb
);
617 (void) BTFlushPath(fcb
);
618 FREE(iterator
, M_TEMP
);
620 return MacToVFSError(result
);
625 * cat_findname - obtain a descriptor from cnid
627 * Only a thread lookup is performed.
629 * Note: The caller is responsible for releasing the output
630 * catalog descriptor (when supplied outdescp is non-null).
634 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
636 struct BTreeIterator
* iterator
;
637 FSBufferDescriptor btdata
;
639 CatalogRecord
* recp
;
645 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
647 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
648 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
649 iterator
->hint
.nodeNum
= 0;
651 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
652 BDINIT(btdata
, recp
);
654 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
658 /* Turn thread record into a cnode key (in place). */
659 switch (recp
->recordType
) {
662 case kHFSFolderThreadRecord
:
665 case kHFSFileThreadRecord
:
666 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
667 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
671 case kHFSPlusFolderThreadRecord
:
674 case kHFSPlusFileThreadRecord
:
675 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
676 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
677 (keyp
->hfsPlus
.nodeName
.length
* 2);
685 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
689 HFSPlusCatalogKey
* pluskey
= NULL
;
692 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
693 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
694 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
695 FREE(pluskey
, M_TEMP
);
701 FREE(iterator
, M_TEMP
);
703 return MacToVFSError(result
);
707 * cat_idlookup - lookup a catalog node using a cnode id
709 * Note: The caller is responsible for releasing the output
710 * catalog descriptor (when supplied outdescp is non-null).
713 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
, int wantrsrc
,
714 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
716 struct BTreeIterator
* iterator
;
717 FSBufferDescriptor btdata
;
720 CatalogRecord
* recp
;
724 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
726 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
727 bzero(iterator
, sizeof(*iterator
));
728 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
730 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
731 BDINIT(btdata
, recp
);
733 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
734 &btdata
, &datasize
, iterator
);
738 /* Turn thread record into a cnode key (in place) */
739 switch (recp
->recordType
) {
742 case kHFSFileThreadRecord
:
743 case kHFSFolderThreadRecord
:
744 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
746 /* check for NULL name */
747 if (keyp
->hfs
.nodeName
[0] == 0) {
752 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
756 case kHFSPlusFileThreadRecord
:
757 case kHFSPlusFolderThreadRecord
:
758 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
760 /* check for NULL name */
761 if (keyp
->hfsPlus
.nodeName
.length
== 0) {
766 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
767 (keyp
->hfsPlus
.nodeName
.length
* 2);
775 result
= cat_lookupbykey(hfsmp
, keyp
,
776 ((allow_system_files
!= 0) ? HFS_LOOKUP_SYSFILE
: 0),
777 0, wantrsrc
, outdescp
, attrp
, forkp
, NULL
);
778 /* No corresponding file/folder record found for a thread record,
779 * mark the volume inconsistent.
781 if (result
== 0 && outdescp
) {
782 cnid_t dcnid
= outdescp
->cd_cnid
;
784 * Just for sanity's case, let's make sure that
785 * the key in the thread matches the key in the record.
788 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
794 FREE(iterator
, M_TEMP
);
796 return MacToVFSError(result
);
801 * cat_lookupmangled - lookup a catalog node using a mangled name
804 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
805 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
810 u_int8_t utf8
[NAME_MAX
+ 1];
812 u_int16_t unicode
[kHFSPlusMaxFileNameChars
+ 1];
818 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
819 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
822 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
823 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
824 fileID
== hfsmp
->hfs_jnlfileid
||
825 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
829 result
= cat_idlookup(hfsmp
, fileID
, 0, 0, outdescp
, attrp
, forkp
);
832 /* It must be in the correct directory */
833 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
837 * Compare the mangled version of file name looked up from the
838 * disk with the mangled name provided by the user. Note that
839 * this comparison is case-sensitive, which should be fine
840 * since we're trying to prevent user space from constructing
841 * a mangled name that differs from the one they'd get from the
844 result
= utf8_decodestr(outdescp
->cd_nameptr
, outdescp
->cd_namelen
,
845 unicode
, &unicodelen
, sizeof(unicode
), ':', 0);
849 result
= ConvertUnicodeToUTF8Mangled(unicodelen
, unicode
,
850 sizeof(utf8
), &utf8len
, utf8
, fileID
);
852 ((u_int16_t
)descp
->cd_namelen
!= utf8len
) ||
853 (bcmp(descp
->cd_nameptr
, utf8
, utf8len
) != 0)) {
860 cat_releasedesc(outdescp
);
866 * cat_lookupbykey - lookup a catalog node using a cnode key
869 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
870 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
872 struct BTreeIterator
* iterator
;
873 FSBufferDescriptor btdata
;
874 CatalogRecord
* recp
;
880 u_int32_t encoding
= 0;
883 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
885 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
886 BDINIT(btdata
, recp
);
887 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
888 bzero(iterator
, sizeof(*iterator
));
889 iterator
->hint
.nodeNum
= hint
;
890 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
892 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
893 &btdata
, &datasize
, iterator
);
897 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
898 cnid
= getcnid(recp
);
900 /* CNID of 0 is invalid. Mark as corrupt */
901 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
907 parentid
= keyp
->hfsPlus
.parentID
;
910 encoding
= getencoding(recp
);
911 hint
= iterator
->hint
.nodeNum
;
913 /* Hide the journal files (if any) */
914 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
915 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
916 !(flags
& HFS_LOOKUP_SYSFILE
)) {
917 result
= ERESERVEDNAME
;
921 if (!std_hfs
&& !(hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)) {
922 /* Make sure the case of the file was correct if requested */
923 if (flags
& HFS_LOOKUP_CASESENSITIVE
) {
924 if (0 != cat_binarykeycompare(&keyp
->hfsPlus
, (HFSPlusCatalogKey
*)&iterator
->key
)) {
925 result
= ERESERVEDNAME
;
932 * When a hardlink link is encountered, auto resolve it.
934 * The catalog record will change, and possibly its type.
938 && (recp
->recordType
== kHFSPlusFileRecord
)
939 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_itime
) ||
940 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
944 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
945 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
947 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
948 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
949 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
952 if ((isfilelink
|| isdirlink
) && !(flags
& HFS_LOOKUP_HARDLINK
)) {
953 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
954 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
960 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
962 /* Update the inode number for this hard link */
963 attrp
->ca_linkref
= ilink
;
967 * Set kHFSHasLinkChainBit for hard links, and reset it for all
968 * other items. Also set linkCount to 1 for regular files.
970 * Due to some bug (rdar://8505977), some regular files can have
971 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
972 * are not really hard links. The runtime code should not consider
973 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
974 * and linkCount for regular file before we vend it out. This might
975 * also result in repairing the bad files on disk, if the corresponding
976 * file is modified and updated on disk.
979 /* This is a hard link and the link count bit was not set */
980 if (!(attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
981 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp
->vcbVN
, cnid
, ilink
);
982 attrp
->ca_recflags
|= kHFSHasLinkChainMask
;
985 /* Make sure that this non-hard link (regular) record is not
986 * an inode record that was looked up and we do not end up
987 * reseting the hard link bit on it.
989 if ((parentid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
990 (parentid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
991 /* This is not a hard link or inode and the link count bit was set */
992 if (attrp
->ca_recflags
& kHFSHasLinkChainMask
) {
993 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp
->vcbVN
, cnid
);
994 attrp
->ca_recflags
&= ~kHFSHasLinkChainMask
;
996 /* This is a regular file and the link count was more than 1 */
997 if (S_ISREG(attrp
->ca_mode
) && (attrp
->ca_linkcount
> 1)) {
998 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp
->vcbVN
, cnid
, attrp
->ca_linkcount
);
999 attrp
->ca_linkcount
= 1;
1006 struct HFSPlusCatalogFile cnoderec
;
1008 promoteattr(hfsmp
, recp
, &cnoderec
);
1009 getbsdattr(hfsmp
, &cnoderec
, attrp
);
1013 if (forkp
!= NULL
) {
1015 bzero(forkp
, sizeof(*forkp
));
1019 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
1022 else if (wantrsrc
) {
1023 /* Convert the resource fork. */
1024 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
1025 forkp
->cf_new_size
= 0;
1026 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1027 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1028 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1029 forkp
->cf_bytesread
=
1030 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
1031 HFSTOVCB(hfsmp
)->blockSize
;
1033 forkp
->cf_bytesread
= 0;
1035 forkp
->cf_vblocks
= 0;
1036 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
1037 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1040 u_int32_t validblks
;
1042 /* Convert the data fork. */
1043 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
1044 forkp
->cf_new_size
= 0;
1045 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
1046 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1047 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1048 forkp
->cf_bytesread
=
1049 recp
->hfsPlusFile
.dataFork
.clumpSize
*
1050 HFSTOVCB(hfsmp
)->blockSize
;
1052 forkp
->cf_bytesread
= 0;
1054 forkp
->cf_vblocks
= 0;
1055 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
1056 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1058 /* Validate the fork's resident extents. */
1060 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1061 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
1062 /* Suppress any bad extents so a remove can succeed. */
1063 forkp
->cf_extents
[i
].startBlock
= 0;
1064 forkp
->cf_extents
[i
].blockCount
= 0;
1065 /* Disable writes */
1066 if (attrp
!= NULL
) {
1067 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
1070 validblks
+= forkp
->cf_extents
[i
].blockCount
;
1073 /* Adjust for any missing blocks. */
1074 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
1078 * This is technically a volume corruption.
1079 * If the total number of blocks calculated by iterating + summing
1080 * the extents in the resident extent records, is less than that
1081 * which is reported in the catalog entry, we should force a fsck.
1082 * Only modifying ca_blocks here is not guaranteed to make it out
1083 * to disk; it is a runtime-only field.
1085 * Note that we could have gotten into this state if we had invalid ranges
1086 * that existed in borrowed blocks that somehow made it out to disk.
1087 * The cnode's on disk block count should never be greater
1088 * than that which is in its extent records.
1091 (void) hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1093 forkp
->cf_blocks
= validblks
;
1094 if (attrp
!= NULL
) {
1095 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1097 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
1098 if (psize
< forkp
->cf_size
) {
1099 forkp
->cf_size
= psize
;
1105 if (descp
!= NULL
) {
1106 HFSPlusCatalogKey
* pluskey
= NULL
;
1109 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
1113 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1114 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
1119 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
1123 FREE(pluskey
, M_TEMP
);
1129 if (desc_cnid
!= NULL
) {
1133 FREE(iterator
, M_TEMP
);
1136 return MacToVFSError(result
);
1141 * cat_create - create a node in the catalog
1143 * NOTE: both the catalog file and attribute file locks must
1144 * be held before calling this function.
1146 * The caller is responsible for releasing the output
1147 * catalog descriptor (when supplied outdescp is non-null).
1150 cat_create(struct hfsmount
*hfsmp
, cnid_t new_fileid
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1151 struct cat_desc
*out_descp
)
1155 FSBufferDescriptor btdata
;
1159 u_int32_t encoding
= kTextEncodingMacRoman
;
1162 modeformat
= attrp
->ca_mode
& S_IFMT
;
1164 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1165 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1167 /* The caller is expected to reserve a CNID before calling this function! */
1169 /* Get space for iterator, key and data */
1170 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
1171 bto
->iterator
.hint
.nodeNum
= 0;
1173 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
1178 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
1179 bto
->key
.nodeName
.length
);
1180 hfs_setencodingbits(hfsmp
, encoding
);
1184 * Insert the thread record first
1186 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
1187 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
1188 S_ISDIR(attrp
->ca_mode
));
1189 btdata
.bufferAddress
= &bto
->data
;
1190 btdata
.itemSize
= datalen
;
1191 btdata
.itemCount
= 1;
1193 /* Caller asserts the following:
1194 * 1) this CNID is not in use by any orphaned EAs
1195 * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
1196 * 3) There are no thread or catalog records for this ID
1198 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
1199 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1206 * Now insert the file/directory record
1208 buildrecord(attrp
, new_fileid
, std_hfs
, encoding
, &bto
->data
, &datalen
);
1209 btdata
.bufferAddress
= &bto
->data
;
1210 btdata
.itemSize
= datalen
;
1211 btdata
.itemCount
= 1;
1213 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
1215 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1217 if (result
== btExists
)
1220 /* Back out the thread record */
1221 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
1222 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
1223 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
1224 /* Error on deleting extra thread record, mark
1225 * volume inconsistent
1227 printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid
, hfsmp
->vcbVN
);
1228 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1235 * Insert was successful, update name, parent and volume
1237 if (out_descp
!= NULL
) {
1238 HFSPlusCatalogKey
* pluskey
= NULL
;
1241 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1245 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1246 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1250 builddesc(pluskey
, new_fileid
, bto
->iterator
.hint
.nodeNum
,
1251 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1254 FREE(pluskey
, M_TEMP
);
1259 attrp
->ca_fileid
= new_fileid
;
1262 (void) BTFlushPath(fcb
);
1265 return MacToVFSError(result
);
1270 * cnode_rename - rename a catalog node
1272 * Assumes that the target's directory exists.
1274 * Order of B-tree operations:
1275 * 1. BTSearchRecord(from_cnode, &data);
1276 * 2. BTInsertRecord(to_cnode, &data);
1277 * 3. BTDeleteRecord(from_cnode);
1278 * 4. BTDeleteRecord(from_thread);
1279 * 5. BTInsertRecord(to_thread);
1281 * Note: The caller is responsible for releasing the output
1282 * catalog descriptor (when supplied out_cdp is non-null).
1286 struct hfsmount
* hfsmp
,
1287 struct cat_desc
* from_cdp
,
1288 struct cat_desc
* todir_cdp
,
1289 struct cat_desc
* to_cdp
,
1290 struct cat_desc
* out_cdp
)
1292 struct BTreeIterator
* to_iterator
= NULL
;
1293 struct BTreeIterator
* from_iterator
= NULL
;
1294 FSBufferDescriptor btdata
;
1295 CatalogRecord
* recp
= NULL
;
1296 HFSPlusCatalogKey
* to_key
;
1303 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1306 u_int32_t encoding
= 0;
1308 vcb
= HFSTOVCB(hfsmp
);
1309 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1310 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1312 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1315 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
1316 bzero(from_iterator
, sizeof(*from_iterator
));
1317 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1320 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1321 bzero(to_iterator
, sizeof(*to_iterator
));
1322 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1325 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1326 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1327 BDINIT(btdata
, recp
);
1330 * When moving a directory, make sure its a valid move.
1332 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1333 struct BTreeIterator
*dir_iterator
= NULL
;
1335 cnid_t cnid
= from_cdp
->cd_cnid
;
1336 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1338 /* First check the obvious ones */
1339 if (cnid
== fsRtDirID
||
1340 cnid
== to_cdp
->cd_parentcnid
||
1345 /* now allocate the dir_iterator */
1346 MALLOC (dir_iterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
1347 if (dir_iterator
== NULL
) {
1350 bzero(dir_iterator
, sizeof(*dir_iterator
));
1353 * Traverse destination path all the way back to the root
1354 * making sure that source directory is not encountered.
1357 while (pathcnid
> fsRtDirID
) {
1358 buildthreadkey(pathcnid
, std_hfs
, (CatalogKey
*)&dir_iterator
->key
);
1359 result
= BTSearchRecord(fcb
, dir_iterator
, &btdata
, &datasize
, NULL
);
1361 FREE(dir_iterator
, M_TEMP
);
1364 pathcnid
= getparentcnid(recp
);
1365 if (pathcnid
== cnid
|| pathcnid
== 0) {
1367 FREE(dir_iterator
, M_TEMP
);
1371 FREE(dir_iterator
, M_TEMP
);
1375 * Step 1: Find cnode data at old location
1377 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1378 &datasize
, from_iterator
);
1380 if (std_hfs
|| (result
!= btNotFound
))
1383 struct cat_desc temp_desc
;
1385 /* Probably the node has mangled name */
1386 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1390 /* The file has mangled name. Search the cnode data using full name */
1391 bzero(from_iterator
, sizeof(*from_iterator
));
1392 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1394 cat_releasedesc(&temp_desc
);
1398 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1400 cat_releasedesc(&temp_desc
);
1404 cat_releasedesc(&temp_desc
);
1407 /* Check if the source is directory hard link. We do not change
1408 * directory flag because it is later used to initialize result descp
1412 (recp
->recordType
== kHFSPlusFileRecord
) &&
1413 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1418 * Update the text encoding (on disk and in descriptor).
1420 * Note that hardlink inodes don't require a text encoding hint.
1423 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1424 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1425 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1426 hfs_setencodingbits(hfsmp
, encoding
);
1427 recp
->hfsPlusFile
.textEncoding
= encoding
;
1429 out_cdp
->cd_encoding
= encoding
;
1433 if (std_hfs
&& !directory
&&
1434 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
)) {
1441 * If the keys are identical then there's nothing left to do!
1443 * update the hint and exit
1446 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1448 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1452 /* Step 2: Insert cnode at new location */
1453 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1454 if (result
== btExists
) {
1455 int fromtype
= recp
->recordType
;
1458 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1459 goto exit
; /* EEXIST */
1461 /* Find cnode data at new location */
1462 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1466 /* Get the CNID after calling searchrecord */
1467 cnid
= getcnid (recp
);
1469 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1474 if ((fromtype
!= recp
->recordType
) ||
1475 (from_cdp
->cd_cnid
!= cnid
)) {
1477 goto exit
; /* EEXIST */
1479 /* The old name is a case variant and must be removed */
1480 result
= BTDeleteRecord(fcb
, from_iterator
);
1484 /* Insert cnode (now that case duplicate is gone) */
1485 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1487 /* Try and restore original before leaving */
1492 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1494 printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err
);
1495 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1501 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1510 /* Step 3: Remove cnode from old location */
1512 result
= BTDeleteRecord(fcb
, from_iterator
);
1514 /* Try and delete new record before leaving */
1519 err
= BTDeleteRecord(fcb
, to_iterator
);
1521 printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err
);
1522 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1528 (void) BTDeleteRecord(fcb
, to_iterator
);
1534 /* #### POINT OF NO RETURN #### */
1537 * Step 4: Remove cnode's old thread record
1539 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1540 (void) BTDeleteRecord(fcb
, from_iterator
);
1543 * Step 5: Insert cnode's new thread record
1544 * (optional for HFS files)
1547 /* For directory hard links, always create a file thread
1548 * record. For everything else, use the directory flag.
1551 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1553 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1555 btdata
.itemSize
= datasize
;
1556 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1557 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1561 HFSPlusCatalogKey
* pluskey
= NULL
;
1564 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1568 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1569 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1571 /* Save the real encoding hint in the Finder Info (field 4). */
1572 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1575 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1576 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1581 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1582 encoding
, directory
, out_cdp
);
1585 FREE(pluskey
, M_TEMP
);
1591 (void) BTFlushPath(fcb
);
1593 FREE(from_iterator
, M_TEMP
);
1595 FREE(to_iterator
, M_TEMP
);
1598 return MacToVFSError(result
);
1603 * cat_delete - delete a node from the catalog
1605 * Order of B-tree operations:
1606 * 1. BTDeleteRecord(cnode);
1607 * 2. BTDeleteRecord(thread);
1608 * 3. BTUpdateRecord(parent);
1611 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1614 BTreeIterator
*iterator
;
1619 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1620 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1624 * The root directory cannot be deleted
1625 * A directory must be empty
1626 * A file must be zero length (no blocks)
1628 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1629 descp
->cd_parentcnid
== kHFSRootParentID
)
1632 /* XXX Preflight Missing */
1634 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1635 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1636 iterator
->hint
.nodeNum
= 0;
1639 * Derive a key from either the file ID (for a virtual inode)
1640 * or the descriptor.
1642 if (descp
->cd_namelen
== 0) {
1643 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1644 cnid
= attrp
->ca_fileid
;
1646 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1647 cnid
= descp
->cd_cnid
;
1653 result
= BTDeleteRecord(fcb
, iterator
);
1655 if (std_hfs
|| (result
!= btNotFound
))
1658 struct cat_desc temp_desc
;
1660 /* Probably the node has mangled name */
1661 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1665 /* The file has mangled name. Delete the file using full name */
1666 bzero(iterator
, sizeof(*iterator
));
1667 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1668 cnid
= temp_desc
.cd_cnid
;
1670 cat_releasedesc(&temp_desc
);
1674 result
= BTDeleteRecord(fcb
, iterator
);
1676 cat_releasedesc(&temp_desc
);
1680 cat_releasedesc(&temp_desc
);
1683 /* Delete thread record. On error, mark volume inconsistent */
1684 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1685 if (BTDeleteRecord(fcb
, iterator
)) {
1687 printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid
, hfsmp
->vcbVN
);
1688 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
1693 (void) BTFlushPath(fcb
);
1695 return MacToVFSError(result
);
1700 * cat_update_internal - update the catalog node described by descp
1701 * using the data from attrp and forkp.
1702 * If update_hardlink is true, the hard link catalog record is updated
1703 * and not the inode catalog record.
1706 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1707 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1710 BTreeIterator
* iterator
;
1711 struct update_state state
;
1714 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1716 state
.s_desc
= descp
;
1717 state
.s_attr
= attrp
;
1718 state
.s_datafork
= dataforkp
;
1719 state
.s_rsrcfork
= rsrcforkp
;
1720 state
.s_hfsmp
= hfsmp
;
1722 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1723 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1726 * For open-deleted files we need to do a lookup by cnid
1727 * (using thread rec).
1729 * For hard links and if not requested by caller, the target
1730 * of the update is the inode itself (not the link record)
1731 * so a lookup by fileid (i.e. thread rec) is needed.
1733 if ((update_hardlink
== false) &&
1734 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1735 (descp
->cd_namelen
== 0) ||
1736 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1737 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1739 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1744 /* Pass a node hint */
1745 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1747 result
= BTUpdateRecord(fcb
, iterator
,
1748 (IterateCallBackProcPtr
)catrec_update
, &state
);
1752 /* Update the node hint. */
1753 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1756 (void) BTFlushPath(fcb
);
1758 return MacToVFSError(result
);
1762 * cat_update - update the catalog node described by descp
1763 * using the data from attrp and forkp.
1766 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1767 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1769 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1773 * catrec_update - Update the fields of a catalog record
1774 * This is called from within BTUpdateRecord.
1777 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1779 struct cat_desc
*descp
;
1780 struct cat_attr
*attrp
;
1781 const struct cat_fork
*forkp
;
1782 struct hfsmount
*hfsmp
;
1785 descp
= state
->s_desc
;
1786 attrp
= state
->s_attr
;
1787 hfsmp
= state
->s_hfsmp
;
1788 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1790 switch (crp
->recordType
) {
1793 case kHFSFolderRecord
: {
1794 HFSCatalogFolder
*dir
;
1796 dir
= (struct HFSCatalogFolder
*)crp
;
1797 /* Do a quick sanity check */
1798 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1799 (dir
->folderID
!= descp
->cd_cnid
))
1800 return (btNotFound
);
1801 dir
->valence
= attrp
->ca_entries
;
1802 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1803 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1804 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1805 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1806 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1809 case kHFSFileRecord
: {
1810 HFSCatalogFile
*file
;
1813 file
= (struct HFSCatalogFile
*)crp
;
1814 /* Do a quick sanity check */
1815 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1816 (file
->fileID
!= attrp
->ca_fileid
))
1817 return (btNotFound
);
1818 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1819 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1820 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1821 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1822 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1823 if (state
->s_rsrcfork
) {
1824 forkp
= state
->s_rsrcfork
;
1825 file
->rsrcLogicalSize
= forkp
->cf_size
;
1826 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1827 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1828 file
->rsrcExtents
[i
].startBlock
=
1829 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1830 file
->rsrcExtents
[i
].blockCount
=
1831 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1834 if (state
->s_datafork
) {
1835 forkp
= state
->s_datafork
;
1836 file
->dataLogicalSize
= forkp
->cf_size
;
1837 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1838 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1839 file
->dataExtents
[i
].startBlock
=
1840 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1841 file
->dataExtents
[i
].blockCount
=
1842 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1846 /* Synchronize the lock state */
1847 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1848 file
->flags
|= kHFSFileLockedMask
;
1850 file
->flags
&= ~kHFSFileLockedMask
;
1855 case kHFSPlusFolderRecord
: {
1856 HFSPlusCatalogFolder
*dir
;
1858 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1859 /* Do a quick sanity check */
1860 if (dir
->folderID
!= attrp
->ca_fileid
) {
1861 printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir
->folderID
, attrp
->ca_fileid
, hfsmp
->vcbVN
);
1862 return (btNotFound
);
1864 dir
->flags
= attrp
->ca_recflags
;
1865 dir
->valence
= attrp
->ca_entries
;
1866 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1867 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1868 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1869 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1870 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1871 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1872 /* Note: directory hardlink inodes don't require a text encoding hint. */
1873 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1874 dir
->textEncoding
= descp
->cd_encoding
;
1876 dir
->folderCount
= attrp
->ca_dircount
;
1877 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1879 * Update the BSD Info if it was already initialized on
1880 * disk or if the runtime values have been modified.
1882 * If the BSD info was already initialized, but
1883 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1884 * probably different than what was on disk. We don't want
1885 * to overwrite the on-disk values (so if we turn off
1886 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1887 * This way, we can still change fields like the mode or
1888 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1890 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1891 * won't change the uid or gid from their defaults. So, if
1892 * the BSD info wasn't set, and the runtime values are not
1893 * default, then what changed was the mode or flags. We
1894 * have to set the uid and gid to something, so use the
1895 * supplied values (which will be default), which has the
1896 * same effect as creating a new file while
1897 * MNT_UNKNOWNPERMISSIONS is set.
1899 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1900 (attrp
->ca_flags
!= 0) ||
1901 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1902 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1903 ((attrp
->ca_mode
& ALLPERMS
) !=
1904 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1905 if ((dir
->bsdInfo
.fileMode
== 0) ||
1906 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1907 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1908 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1910 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1911 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1912 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1913 /* A directory hardlink has a link count. */
1914 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1915 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1920 case kHFSPlusFileRecord
: {
1921 HFSPlusCatalogFile
*file
;
1924 file
= (struct HFSPlusCatalogFile
*)crp
;
1925 /* Do a quick sanity check */
1926 if (file
->fileID
!= attrp
->ca_fileid
)
1927 return (btNotFound
);
1928 file
->flags
= attrp
->ca_recflags
;
1929 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1930 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1931 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1932 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1933 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1934 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1936 * Note: file hardlink inodes don't require a text encoding
1937 * hint, but they do have a first link value.
1939 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1940 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1942 file
->textEncoding
= descp
->cd_encoding
;
1944 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1946 * Update the BSD Info if it was already initialized on
1947 * disk or if the runtime values have been modified.
1949 * If the BSD info was already initialized, but
1950 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1951 * probably different than what was on disk. We don't want
1952 * to overwrite the on-disk values (so if we turn off
1953 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1954 * This way, we can still change fields like the mode or
1955 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1957 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1958 * won't change the uid or gid from their defaults. So, if
1959 * the BSD info wasn't set, and the runtime values are not
1960 * default, then what changed was the mode or flags. We
1961 * have to set the uid and gid to something, so use the
1962 * supplied values (which will be default), which has the
1963 * same effect as creating a new file while
1964 * MNT_UNKNOWNPERMISSIONS is set.
1966 * Do not modify bsdInfo for directory hard link records.
1967 * They are set during creation and are not modifiable, so just
1970 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1971 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1972 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1975 ((file
->bsdInfo
.fileMode
!= 0) ||
1976 (attrp
->ca_flags
!= 0) ||
1977 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1978 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1979 ((attrp
->ca_mode
& ALLPERMS
) !=
1980 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1981 if ((file
->bsdInfo
.fileMode
== 0) ||
1982 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1983 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1984 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1986 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1987 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1988 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1990 if (state
->s_rsrcfork
) {
1991 forkp
= state
->s_rsrcfork
;
1992 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1993 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1994 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1995 sizeof(HFSPlusExtentRecord
));
1996 /* Push blocks read to disk */
1997 file
->resourceFork
.clumpSize
=
1998 howmany(forkp
->cf_bytesread
, blksize
);
2000 if (state
->s_datafork
) {
2001 forkp
= state
->s_datafork
;
2002 file
->dataFork
.logicalSize
= forkp
->cf_size
;
2003 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
2004 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
2005 sizeof(HFSPlusExtentRecord
));
2006 /* Push blocks read to disk */
2007 file
->dataFork
.clumpSize
=
2008 howmany(forkp
->cf_bytesread
, blksize
);
2011 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
2012 (file
->resourceFork
.extents
[0].startBlock
==
2013 file
->dataFork
.extents
[0].startBlock
)) {
2014 panic("hfs: catrec_update: rsrc fork == data fork");
2017 /* Synchronize the lock state */
2018 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
2019 file
->flags
|= kHFSFileLockedMask
;
2021 file
->flags
&= ~kHFSFileLockedMask
;
2023 /* Push out special field if necessary */
2024 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
2025 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
2029 * Protect against the degenerate case where the descriptor contains the
2030 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
2031 * the linkcount was greater than 1 (the default value), then it must have become
2032 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
2034 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
2035 (file
->hl_linkCount
> 1)) {
2036 file
->hl_linkCount
= attrp
->ca_linkcount
;
2042 return (btNotFound
);
2047 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
2048 * catalog btree of given cnid by walking up the parent chain till it reaches
2049 * either the root folder, or the private metadata directory for storing
2050 * directory hard links. This function updates the corresponding in-core
2051 * cnode, if any, and the directory record in the catalog btree.
2052 * On success, returns zero. On failure, returns non-zero value.
2056 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
2060 struct cat_desc desc
;
2061 struct cat_attr attr
;
2063 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
2064 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
2065 /* Update the bit in corresponding cnode, if any, in the hash.
2066 * If the cnode has the bit already set, stop the traversal.
2068 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
2073 /* Update the catalog record on disk if either cnode was not
2074 * found in the hash, or if a cnode was found and the cnode
2075 * did not have the bit set previously.
2077 retval
= hfs_start_transaction(hfsmp
);
2081 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2083 /* Look up our catalog folder record */
2084 retval
= cat_idlookup(hfsmp
, cnid
, 0, 0, &desc
, &attr
, NULL
);
2086 hfs_systemfile_unlock(hfsmp
, lockflags
);
2087 hfs_end_transaction(hfsmp
);
2091 /* Update the bit in the catalog record */
2092 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
2093 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
2095 hfs_systemfile_unlock(hfsmp
, lockflags
);
2096 hfs_end_transaction(hfsmp
);
2097 cat_releasedesc(&desc
);
2101 hfs_systemfile_unlock(hfsmp
, lockflags
);
2102 hfs_end_transaction(hfsmp
);
2104 cnid
= desc
.cd_parentcnid
;
2105 cat_releasedesc(&desc
);
2111 /* This function traverses the parent directory hierarchy from the given
2112 * directory to one level below root directory and checks if any of its
2114 * 1. A directory hard link.
2115 * 2. The 'pointed at' directory.
2116 * If any of these conditions fail or an internal error is encountered
2117 * during look up of the catalog record, this function returns non-zero value.
2121 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
2123 HFSPlusCatalogKey
*keyp
;
2125 FSBufferDescriptor btdata
;
2126 HFSPlusCatalogFolder folder
;
2132 BDINIT(btdata
, &folder
);
2133 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
2134 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
2135 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2137 while (cnid
!= kHFSRootParentID
) {
2138 /* Check if the 'pointed at' directory is an ancestor */
2139 if (pointed_at_cnid
== cnid
) {
2143 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
2144 printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2145 invalid
= 1; /* On errors, assume an invalid parent */
2148 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
2149 printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2150 invalid
= 1; /* On errors, assume an invalid parent */
2153 /* Check if this ancestor is a directory hard link */
2154 if (folder
.flags
& kHFSHasLinkChainMask
) {
2158 cnid
= keyp
->parentID
;
2166 * update_siblinglinks_callback - update a link's chain
2169 struct linkupdate_state
{
2176 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
2178 HFSPlusCatalogFile
*file
;
2180 if (crp
->recordType
!= kHFSPlusFileRecord
) {
2181 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
2182 return (btNotFound
);
2185 file
= (struct HFSPlusCatalogFile
*)crp
;
2186 if (file
->flags
& kHFSHasLinkChainMask
) {
2187 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
2188 file
->hl_prevLinkID
= state
->prevlinkid
;
2190 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
2191 file
->hl_nextLinkID
= state
->nextlinkid
;
2194 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
2200 * cat_update_siblinglinks - update a link's chain
2203 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
2206 BTreeIterator
* iterator
;
2207 struct linkupdate_state state
;
2210 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2211 state
.filelinkid
= linkfileid
;
2212 state
.prevlinkid
= prevlinkid
;
2213 state
.nextlinkid
= nextlinkid
;
2215 /* Create an iterator for use by us temporarily */
2216 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2217 bzero(iterator
, sizeof(*iterator
));
2219 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
2221 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
2222 (void) BTFlushPath(fcb
);
2224 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid
, hfsmp
->vcbVN
);
2227 FREE (iterator
, M_TEMP
);
2228 return MacToVFSError(result
);
2232 * cat_lookuplink - lookup a link by it's name
2235 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2238 BTreeIterator
* iterator
;
2239 struct FSBufferDescriptor btdata
;
2240 struct HFSPlusCatalogFile file
;
2243 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2245 /* Create an iterator for use by us temporarily */
2246 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2247 bzero(iterator
, sizeof(*iterator
));
2249 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2252 BDINIT(btdata
, &file
);
2254 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2257 if (file
.recordType
!= kHFSPlusFileRecord
) {
2261 *linkfileid
= file
.fileID
;
2263 if (file
.flags
& kHFSHasLinkChainMask
) {
2264 *prevlinkid
= file
.hl_prevLinkID
;
2265 *nextlinkid
= file
.hl_nextLinkID
;
2271 FREE(iterator
, M_TEMP
);
2272 return MacToVFSError(result
);
2277 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2280 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2283 BTreeIterator
* iterator
;
2284 struct FSBufferDescriptor btdata
;
2285 struct HFSPlusCatalogFile file
;
2288 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2290 /* Create an iterator for use by us temporarily */
2291 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2292 bzero(iterator
, sizeof(*iterator
));
2294 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2297 BDINIT(btdata
, &file
);
2299 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2302 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2303 if (file
.flags
& kHFSHasLinkChainMask
) {
2306 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2308 /* directory inodes don't have a chain (its in an EA) */
2309 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2310 result
= ENOLINK
; /* signal to caller to get head of list */
2311 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2313 *nextlinkid
= file
.hl_firstLinkID
;
2315 *prevlinkid
= file
.hl_prevLinkID
;
2316 *nextlinkid
= file
.hl_nextLinkID
;
2323 FREE(iterator
, M_TEMP
);
2324 return MacToVFSError(result
);
2329 * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
2332 cat_lookup_lastlink(struct hfsmount
*hfsmp
, cnid_t linkfileid
,
2333 cnid_t
*lastlink
, struct cat_desc
*cdesc
)
2336 BTreeIterator
* iterator
;
2337 struct FSBufferDescriptor btdata
;
2338 struct HFSPlusCatalogFile file
;
2342 cnid_t currentlink
= linkfileid
;
2344 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2346 /* Create an iterator for use by us temporarily */
2347 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2349 while ((foundlast
== 0) && (itercount
< HFS_LINK_MAX
)) {
2351 bzero(iterator
, sizeof(*iterator
));
2353 if ((result
= getkey(hfsmp
, currentlink
, (CatalogKey
*)&iterator
->key
))) {
2356 BDINIT(btdata
, &file
);
2358 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2362 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2363 if (file
.flags
& kHFSHasLinkChainMask
) {
2366 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2368 * The raw inode for a directory hardlink doesn't have a chain.
2369 * Its link information lives in an EA.
2371 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2372 /* We don't iterate to find the oldest directory hardlink. */
2376 else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2377 /* Raw inode for file hardlink (the base inode) */
2378 currentlink
= file
.hl_firstLinkID
;
2381 * One minor special-casing here is necessary.
2382 * If our ID brought us to the raw hardlink inode, and it does
2383 * not have any siblings, then it's an open-unlinked file, and we
2384 * should not proceed any further.
2386 if (currentlink
== 0) {
2392 /* Otherwise, this item's parent is a legitimate directory in the namespace */
2393 if (file
.hl_nextLinkID
== 0) {
2394 /* If nextLinkID is 0, then we found the end; no more hardlinks */
2396 *lastlink
= currentlink
;
2398 * Since we had to construct a catalog key to do this lookup
2399 * we still hold it in-hand. We might as well use it to build
2400 * the descriptor that the caller asked for.
2402 builddesc ((HFSPlusCatalogKey
*)&iterator
->key
, currentlink
, 0, 0, 0, cdesc
);
2406 currentlink
= file
.hl_nextLinkID
;
2410 /* Sorry, can't help you without a link chain */
2416 /* If we didn't find what we were looking for, zero out the args */
2417 if (foundlast
== 0) {
2419 bzero (cdesc
, sizeof(struct cat_desc
));
2426 FREE(iterator
, M_TEMP
);
2427 return MacToVFSError(result
);
2432 * cat_createlink - create a link in the catalog
2434 * The following cat_attr fields are expected to be set:
2440 * ca_finderinfo (type and creator)
2443 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2444 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2448 FSBufferDescriptor btdata
;
2449 HFSPlusForkData
*rsrcforkp
;
2453 int thread_inserted
= 0;
2454 int alias_allocated
= 0;
2458 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
2460 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2463 * Get the next CNID. Note that we are currently holding catalog lock.
2465 result
= cat_acquire_cnid(hfsmp
, &nextCNID
);
2470 /* Get space for iterator, key and data */
2471 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
2472 bto
->iterator
.hint
.nodeNum
= 0;
2473 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2475 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2477 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2481 /* This is our only chance to set the encoding (other than a rename). */
2482 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2485 * Insert the thread record first.
2487 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2488 btdata
.bufferAddress
= &bto
->data
;
2489 btdata
.itemSize
= datalen
;
2490 btdata
.itemCount
= 1;
2492 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2493 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2497 thread_inserted
= 1;
2500 * Now insert the link record.
2502 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2504 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2505 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2506 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2508 /* For directory hard links, create alias in resource fork */
2509 if (descp
->cd_flags
& CD_ISDIR
) {
2510 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2513 alias_allocated
= 1;
2515 btdata
.bufferAddress
= &bto
->data
;
2516 btdata
.itemSize
= datalen
;
2517 btdata
.itemCount
= 1;
2519 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2521 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2523 if (result
== btExists
)
2527 if (linkfileid
!= NULL
) {
2528 *linkfileid
= nextCNID
;
2532 if (thread_inserted
) {
2533 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result
), hfsmp
->vcbVN
);
2535 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2536 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2537 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2538 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
2541 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2542 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2543 rsrcforkp
->extents
[0].blockCount
, 0);
2544 rsrcforkp
->extents
[0].startBlock
= 0;
2545 rsrcforkp
->extents
[0].blockCount
= 0;
2548 (void) BTFlushPath(fcb
);
2551 return MacToVFSError(result
);
2554 /* Directory hard links are visible as aliases on pre-Leopard systems and
2555 * as normal directories on Leopard or later. All directory hard link aliases
2556 * have the same resource fork content except for the three uniquely
2557 * identifying values that are updated in the resource fork data when the alias
2558 * is created. The following array is the constant resource fork data used
2559 * only for creating directory hard link aliases.
2561 static const char hfs_dirlink_alias_rsrc
[] = {
2562 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2563 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2564 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2575 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2576 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2578 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2579 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2580 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2581 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2582 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2583 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2585 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2586 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2588 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2590 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2593 /* Constants for directory hard link alias */
2595 /* Size of resource fork data array for directory hard link alias */
2596 kHFSAliasSize
= 0x1d0,
2598 /* Volume type for ejectable devices like disk image */
2599 kHFSAliasVolTypeEjectable
= 0x5,
2601 /* Offset for volume create date, in Mac OS local time */
2602 kHFSAliasVolCreateDateOffset
= 0x12a,
2604 /* Offset for the type of volume */
2605 kHFSAliasVolTypeOffset
= 0x130,
2607 /* Offset for folder ID of the parent directory of the directory inode */
2608 kHFSAliasParentIDOffset
= 0x132,
2610 /* Offset for folder ID of the directory inode */
2611 kHFSAliasTargetIDOffset
= 0x176,
2614 /* Create and write an alias that points at the directory represented by given
2615 * inode number on the same volume. Directory hard links are visible as
2616 * aliases in pre-Leopard systems and this function creates these aliases.
2618 * Note: This code is very specific to creating alias for the purpose
2619 * of directory hard links only, and should not be generalized.
2622 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2630 HFSPlusForkData
*rsrcforkp
;
2634 rsrcforkp
= &(crp
->resourceFork
);
2636 blksize
= hfsmp
->blockSize
;
2637 blkcount
= howmany(kHFSAliasSize
, blksize
);
2638 sectorsize
= hfsmp
->hfs_logical_block_size
;
2639 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2641 /* Allocate some disk space for the alias content. */
2642 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2643 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2644 &rsrcforkp
->extents
[0].startBlock
,
2645 &rsrcforkp
->extents
[0].blockCount
);
2646 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2647 if (result
== dskFulErr
) {
2648 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2649 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
| HFS_ALLOC_FLUSHTXN
,
2650 &rsrcforkp
->extents
[0].startBlock
,
2651 &rsrcforkp
->extents
[0].blockCount
);
2654 rsrcforkp
->extents
[0].startBlock
= 0;
2658 /* Acquire a buffer cache block for our block. */
2659 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2660 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2662 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2664 journal_modify_block_start(hfsmp
->jnl
, bp
);
2667 /* Generate alias content */
2668 alias
= (char *)buf_dataptr(bp
);
2669 bzero(alias
, buf_size(bp
));
2670 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2672 /* Set the volume create date, local time in Mac OS format */
2673 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2674 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2676 /* If the file system is on a virtual device like disk image,
2677 * update the volume type to be ejectable device.
2679 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2680 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2681 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2684 /* Set id of the parent of the target directory */
2685 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2686 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2688 /* Set id of the target directory */
2689 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2690 *valptr
= OSSwapHostToBigInt32(inode_num
);
2692 /* Write alias content to disk. */
2694 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2695 } else if ((result
= buf_bwrite(bp
))) {
2699 /* Finish initializing the fork data. */
2700 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2701 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2704 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2705 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2706 rsrcforkp
->extents
[0].startBlock
= 0;
2707 rsrcforkp
->extents
[0].blockCount
= 0;
2708 rsrcforkp
->logicalSize
= 0;
2709 rsrcforkp
->totalBlocks
= 0;
2715 * cat_deletelink - delete a link from the catalog
2718 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2720 struct HFSPlusCatalogFile file
;
2721 struct cat_attr cattr
;
2722 uint32_t totalBlocks
;
2726 bzero(&file
, sizeof (file
));
2727 bzero(&cattr
, sizeof (cattr
));
2728 cattr
.ca_fileid
= descp
->cd_cnid
;
2730 /* Directory links have alias content to remove. */
2731 if (descp
->cd_flags
& CD_ISDIR
) {
2733 BTreeIterator
* iterator
;
2734 struct FSBufferDescriptor btdata
;
2736 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2738 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2739 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2740 iterator
->hint
.nodeNum
= 0;
2742 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2745 BDINIT(btdata
, &file
);
2747 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2752 result
= cat_delete(hfsmp
, descp
, &cattr
);
2754 if ((result
== 0) &&
2755 (descp
->cd_flags
& CD_ISDIR
) &&
2756 (file
.recordType
== kHFSPlusFileRecord
)) {
2758 totalBlocks
= file
.resourceFork
.totalBlocks
;
2760 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2761 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2762 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2766 (void) BlockDeallocate(hfsmp
,
2767 file
.resourceFork
.extents
[i
].startBlock
,
2768 file
.resourceFork
.extents
[i
].blockCount
, 0);
2770 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2771 file
.resourceFork
.extents
[i
].startBlock
= 0;
2772 file
.resourceFork
.extents
[i
].blockCount
= 0;
2781 * Callback to collect directory entries.
2782 * Called with readattr_state for each item in a directory.
2784 struct readattr_state
{
2785 struct hfsmount
*hfsmp
;
2786 struct cat_entrylist
*list
;
2794 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2795 struct readattr_state
*state
)
2797 struct cat_entrylist
*list
= state
->list
;
2798 struct hfsmount
*hfsmp
= state
->hfsmp
;
2799 struct cat_entry
*cep
;
2802 if (list
->realentries
>= list
->maxentries
)
2803 return (0); /* stop */
2805 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2807 switch(rec
->recordType
) {
2808 case kHFSPlusFolderRecord
:
2809 case kHFSPlusFileRecord
:
2811 case kHFSFolderRecord
:
2812 case kHFSFileRecord
:
2814 if (parentcnid
!= state
->dir_cnid
) {
2815 state
->error
= ENOENT
;
2816 state
->reached_eof
= 1;
2817 return (0); /* stop */
2821 state
->error
= ENOENT
;
2822 return (0); /* stop */
2825 /* Hide the private system directories and journal files */
2826 if (parentcnid
== kHFSRootFolderID
) {
2827 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2828 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2829 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2830 list
->skipentries
++;
2831 return (1); /* continue */
2834 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2835 (rec
->recordType
== kHFSPlusFileRecord
) &&
2836 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2837 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2838 list
->skipentries
++;
2839 return (1); /* continue */
2843 cep
= &list
->entry
[list
->realentries
++];
2845 if (state
->stdhfs
== 0) {
2846 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2847 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2848 isadir(rec
), &cep
->ce_desc
);
2850 if (rec
->recordType
== kHFSPlusFileRecord
) {
2851 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2852 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2853 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2854 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2856 /* Save link reference for later processing. */
2857 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2858 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2859 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2860 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2861 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2862 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2863 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2869 struct HFSPlusCatalogFile cnoderec
;
2870 HFSPlusCatalogKey
* pluskey
;
2873 promoteattr(hfsmp
, rec
, &cnoderec
);
2874 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2876 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2877 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2878 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2879 FREE(pluskey
, M_TEMP
);
2881 if (rec
->recordType
== kHFSFileRecord
) {
2882 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2884 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2885 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2886 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2887 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2892 return (list
->realentries
< list
->maxentries
);
2896 * Pack a cat_entrylist buffer with attributes from the catalog
2898 * Note: index is zero relative
2901 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
, int *reachedeof
)
2905 BTreeIterator
* iterator
;
2906 struct readattr_state state
;
2913 int reached_eof
= 0;
2915 ce_list
->realentries
= 0;
2917 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2918 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2919 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2921 bzero (&state
, sizeof(struct readattr_state
));
2923 state
.hfsmp
= hfsmp
;
2924 state
.list
= ce_list
;
2925 state
.dir_cnid
= parentcnid
;
2926 state
.stdhfs
= std_hfs
;
2929 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2930 bzero(iterator
, sizeof(*iterator
));
2931 key
= (CatalogKey
*)&iterator
->key
;
2933 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2934 index
= dirhint
->dh_index
+ 1;
2937 * Attempt to build a key from cached filename
2939 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2940 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2946 * If the last entry wasn't cached then position the btree iterator
2948 if ((index
== 0) || !have_key
) {
2950 * Position the iterator at the directory's thread record.
2951 * (i.e. just before the first entry)
2953 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2954 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2956 result
= MacToVFSError(result
);
2961 * Iterate until we reach the entry just
2962 * before the one we want to start with.
2965 struct position_state ps
;
2970 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2973 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2974 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2978 result
= MacToVFSError(result
);
2982 * Note: the index may now point to EOF if the directory
2983 * was modified in between system calls. We will return
2984 * ENOENT from cat_findposition if this is the case, and
2985 * when we bail out with an error, our caller (hfs_readdirattr_internal)
2986 * will suppress the error and indicate EOF to its caller.
2988 result
= MacToVFSError(result
);
2994 /* Fill list with entries starting at iterator->key. */
2995 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2996 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2999 result
= state
.error
;
3000 reached_eof
= state
.reached_eof
;
3002 else if (ce_list
->realentries
== 0) {
3007 result
= MacToVFSError(result
);
3014 * Resolve any hard links.
3016 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
3017 struct FndrFileInfo
*fip
;
3018 struct cat_entry
*cep
;
3019 struct HFSPlusCatalogFile filerec
;
3023 cep
= &ce_list
->entry
[i
];
3024 if (cep
->ce_attr
.ca_linkref
== 0)
3027 /* Note: Finder info is still in Big Endian */
3028 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
3030 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3031 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
3032 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
3035 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3036 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
3037 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
3038 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
3041 if (isfilelink
|| isdirlink
) {
3042 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
3044 /* Repack entry from inode record. */
3045 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
3046 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
3047 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
3048 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
3049 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
3054 FREE(iterator
, M_TEMP
);
3055 *reachedeof
= reached_eof
;
3056 return MacToVFSError(result
);
3059 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3060 #define MAX_LINKINFO_ENTRIES 3000
3063 * Callback to pack directory entries.
3064 * Called with packdirentry_state for each item in a directory.
3067 /* Hard link information collected during cat_getdirentries. */
3070 user_addr_t dirent_addr
;
3072 typedef struct linkinfo linkinfo_t
;
3074 /* State information for the getdirentries_callback function. */
3075 struct packdirentry_state
{
3076 int cbs_flags
; /* VNODE_READDIR_* flags */
3077 u_int32_t cbs_parentID
;
3078 u_int32_t cbs_index
;
3080 ExtendedVCB
* cbs_hfsmp
;
3083 int32_t cbs_maxlinks
;
3084 linkinfo_t
* cbs_linkinfo
;
3085 struct cat_desc
* cbs_desc
;
3086 u_int8_t
* cbs_namebuf
;
3088 * The following fields are only used for NFS readdir, which
3089 * uses the next file id as the seek offset of each entry.
3091 struct direntry
* cbs_direntry
;
3092 struct direntry
* cbs_prevdirentry
;
3093 u_int32_t cbs_previlinkref
;
3094 Boolean cbs_hasprevdirentry
;
3099 * getdirentries callback for HFS Plus directories.
3102 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3103 struct packdirentry_state
*state
)
3105 struct hfsmount
*hfsmp
;
3106 const CatalogName
*cnp
;
3109 struct dirent catent
;
3110 struct direntry
* entry
= NULL
;
3112 u_int32_t ilinkref
= 0;
3113 u_int32_t curlinkref
= 0;
3116 u_int8_t type
= DT_UNKNOWN
;
3117 u_int8_t is_mangled
= 0;
3118 u_int8_t is_link
= 0;
3120 user_addr_t uiobase
= USER_ADDR_NULL
;
3125 Boolean stop_after_pack
= false;
3127 hfsmp
= state
->cbs_hfsmp
;
3128 curID
= ckp
->hfsPlus
.parentID
;
3130 /* We're done when parent directory changes */
3131 if (state
->cbs_parentID
!= curID
) {
3133 * If the parent ID is different from curID this means we've hit
3134 * the EOF for the directory. To help future callers, we mark
3135 * the cbs_eof boolean. However, we should only mark the EOF
3136 * boolean if we're about to return from this function.
3138 * This is because this callback function does its own uiomove
3139 * to get the data to userspace. If we set the boolean before determining
3140 * whether or not the current entry has enough room to write its
3141 * data to userland, we could fool the callers of this catalog function
3142 * into thinking they've hit EOF earlier than they really would have.
3143 * In that case, we'd know that we have more entries to process and
3144 * send to userland, but we didn't have enough room.
3146 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3147 * about to return and won't write any new data back
3148 * to userland. In the stop_after_pack case, we'll set this boolean
3149 * regardless, so it's slightly safer to let that logic mark the boolean,
3150 * especially since it's closer to the return of this function.
3153 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3154 /* The last record has not been returned yet, so we
3155 * want to stop after packing the last item
3157 if (state
->cbs_hasprevdirentry
) {
3158 stop_after_pack
= true;
3160 state
->cbs_eof
= true;
3161 state
->cbs_result
= ENOENT
;
3162 return (0); /* stop */
3165 state
->cbs_eof
= true;
3166 state
->cbs_result
= ENOENT
;
3167 return (0); /* stop */
3171 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3172 entry
= state
->cbs_direntry
;
3173 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3174 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3176 * The NFS server sometimes needs to make filenames fit in
3177 * NAME_MAX bytes (since its client may not be able to
3178 * handle a longer name). In that case, NFS will ask us
3179 * to mangle the name to keep it short enough.
3181 maxnamelen
= NAME_MAX
+ 1;
3183 maxnamelen
= sizeof(entry
->d_name
);
3186 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3187 maxnamelen
= sizeof(catent
.d_name
);
3190 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3191 /* The last item returns a non-zero invalid cookie */
3194 switch(crp
->recordType
) {
3195 case kHFSPlusFolderRecord
:
3197 cnid
= crp
->hfsPlusFolder
.folderID
;
3198 /* Hide our private system directories. */
3199 if (curID
== kHFSRootFolderID
) {
3200 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3201 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3206 case kHFSPlusFileRecord
:
3207 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3208 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3209 cnid
= crp
->hfsPlusFile
.fileID
;
3211 * When a hardlink link is encountered save its link ref.
3213 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3214 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3215 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3216 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3217 /* If link ref is inode's file id then use it directly. */
3218 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3219 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3221 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3224 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3225 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3226 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3227 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3228 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3229 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3230 /* A directory's link resolves to a directory. */
3232 /* A directory's link ref is always inode's file id. */
3233 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3236 /* Hide the journal files */
3237 if ((curID
== kHFSRootFolderID
) &&
3238 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3239 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3240 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3245 return (0); /* stop */
3248 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3250 namelen
= cnp
->ustr
.length
;
3252 * For MacRoman encoded names, assume that its ascii and
3253 * convert it directly in an attempt to avoid the more
3254 * expensive utf8_encodestr conversion.
3256 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3259 const u_int16_t
*chp
;
3261 chp
= &cnp
->ustr
.unicode
[0];
3262 for (i
= 0; i
< (int)namelen
; ++i
) {
3264 if (ch
> 0x007f || ch
== 0x0000) {
3265 /* Perform expensive utf8_encodestr conversion */
3268 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3270 nameptr
[namelen
] = '\0';
3274 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3275 nameptr
, &namelen
, maxnamelen
, ':', 0);
3278 /* Check result returned from encoding the filename to utf8 */
3279 if (result
== ENAMETOOLONG
) {
3281 * If we were looking at a catalog record for a hardlink (not the inode),
3282 * then we want to use its link ID as opposed to the inode ID for
3283 * a mangled name. For all other cases, they are the same. Note that
3284 * due to the way directory hardlinks are implemented, the actual link
3285 * is going to be counted as a file record, so we can catch both
3288 cnid_t linkid
= cnid
;
3290 linkid
= crp
->hfsPlusFile
.fileID
;
3293 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3294 cnp
->ustr
.unicode
, maxnamelen
,
3295 (ByteCount
*)&namelen
, nameptr
, linkid
);
3300 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3302 * The index is 1 relative and includes "." and ".."
3304 * Also stuff the cnid in the upper 32 bits of the cookie.
3305 * The cookie is stored to the previous entry, which will
3306 * be packed and copied this time
3308 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3309 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3310 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3312 catent
.d_type
= type
;
3313 catent
.d_namlen
= namelen
;
3314 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3316 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3318 catent
.d_fileno
= cnid
;
3319 uioaddr
= (caddr_t
) &catent
;
3322 /* Save current base address for post processing of hard-links. */
3323 if (ilinkref
|| state
->cbs_previlinkref
) {
3324 uiobase
= uio_curriovbase(state
->cbs_uio
);
3326 /* If this entry won't fit then we're done */
3327 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3328 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3329 return (0); /* stop */
3332 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3333 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3334 if (state
->cbs_result
== 0) {
3337 /* Remember previous entry */
3338 state
->cbs_desc
->cd_cnid
= cnid
;
3339 if (type
== DT_DIR
) {
3340 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3342 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3344 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3345 state
->cbs_desc
->cd_namelen
= 0;
3348 state
->cbs_desc
->cd_encoding
= xxxx
;
3351 state
->cbs_desc
->cd_namelen
= namelen
;
3352 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3354 /* Store unmangled name for the directory hint else it will
3355 * restart readdir at the last location again
3357 u_int8_t
*new_nameptr
;
3359 size_t tmp_namelen
= 0;
3361 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3362 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3363 cnp
->ustr
.length
* sizeof(UniChar
),
3365 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
3366 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3367 cnp
->ustr
.length
* sizeof(UniChar
),
3368 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3370 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3371 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3373 FREE(new_nameptr
, M_TEMP
);
3376 if (state
->cbs_hasprevdirentry
) {
3377 curlinkref
= ilinkref
; /* save current */
3378 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3381 * Record any hard links for post processing.
3383 if ((ilinkref
!= 0) &&
3384 (state
->cbs_result
== 0) &&
3385 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3386 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3387 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3388 state
->cbs_nlinks
++;
3390 if (state
->cbs_hasprevdirentry
) {
3391 ilinkref
= curlinkref
; /* restore current */
3395 /* Fill the direntry to be used the next time */
3396 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3397 if (stop_after_pack
) {
3398 state
->cbs_eof
= true;
3399 return (0); /* stop */
3401 entry
->d_type
= type
;
3402 entry
->d_namlen
= namelen
;
3403 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3405 /* File number = 0 means skip entry */
3406 entry
->d_fileno
= 0;
3408 entry
->d_fileno
= cnid
;
3410 /* swap the current and previous entry */
3411 struct direntry
* tmp
;
3412 tmp
= state
->cbs_direntry
;
3413 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3414 state
->cbs_prevdirentry
= tmp
;
3415 state
->cbs_hasprevdirentry
= true;
3416 state
->cbs_previlinkref
= ilinkref
;
3419 /* Continue iteration if there's room */
3420 return (state
->cbs_result
== 0 &&
3421 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3426 * getdirentries callback for standard HFS (non HFS+) directories.
3429 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3430 struct packdirentry_state
*state
)
3432 struct hfsmount
*hfsmp
;
3433 const CatalogName
*cnp
;
3436 struct dirent catent
;
3438 u_int8_t type
= DT_UNKNOWN
;
3445 hfsmp
= state
->cbs_hfsmp
;
3447 curID
= ckp
->hfs
.parentID
;
3449 /* We're done when parent directory changes */
3450 if (state
->cbs_parentID
!= curID
) {
3451 state
->cbs_result
= ENOENT
;
3452 return (0); /* stop */
3455 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3456 maxnamelen
= sizeof(catent
.d_name
);
3458 switch(crp
->recordType
) {
3459 case kHFSFolderRecord
:
3461 cnid
= crp
->hfsFolder
.folderID
;
3463 case kHFSFileRecord
:
3465 cnid
= crp
->hfsFile
.fileID
;
3468 return (0); /* stop */
3471 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3472 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3474 * When an HFS name cannot be encoded with the current
3475 * volume encoding we use MacRoman as a fallback.
3478 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3480 catent
.d_type
= type
;
3481 catent
.d_namlen
= namelen
;
3482 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3483 catent
.d_fileno
= cnid
;
3484 uioaddr
= (caddr_t
) &catent
;
3486 /* If this entry won't fit then we're done */
3487 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3488 return (0); /* stop */
3491 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3492 if (state
->cbs_result
== 0) {
3495 /* Remember previous entry */
3496 state
->cbs_desc
->cd_cnid
= cnid
;
3497 if (type
== DT_DIR
) {
3498 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3500 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3502 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3503 state
->cbs_desc
->cd_namelen
= 0;
3505 state
->cbs_desc
->cd_namelen
= namelen
;
3506 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3509 /* Continue iteration if there's room */
3510 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3515 * Pack a uio buffer with directory entries from the catalog
3518 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3519 uio_t uio
, int flags
, int * items
, int * eofflag
)
3522 BTreeIterator
* iterator
;
3524 struct packdirentry_state state
;
3534 extended
= flags
& VNODE_READDIR_EXTENDED
;
3536 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3539 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3542 * Get a buffer for link info array, btree iterator and a direntry.
3544 * We impose an cap of 3000 link entries when trying to compute
3545 * the total number of hardlink entries that we'll allow in the
3548 * Note that in the case where there are very few hardlinks,
3549 * this does not restrict or prevent us from vending out as many entries
3550 * as we can to the uio_resid, because the getdirentries callback
3551 * uiomoves the directory entries to the uio itself and does not use
3552 * this MALLOC'd array. It also limits itself to maxlinks of hardlinks.
3555 /* Now compute the maximum link array size */
3556 maxlinks
= MIN (entrycnt
, MAX_LINKINFO_ENTRIES
);
3558 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3560 bufsize
+= 2*sizeof(struct direntry
);
3562 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3563 bzero(buffer
, bufsize
);
3565 state
.cbs_flags
= flags
;
3566 state
.cbs_hasprevdirentry
= false;
3567 state
.cbs_previlinkref
= 0;
3568 state
.cbs_nlinks
= 0;
3569 state
.cbs_maxlinks
= maxlinks
;
3570 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3572 * We need to set cbs_eof to false regardless of whether or not the
3573 * control flow is actually in the extended case, since we use this
3574 * field to track whether or not we've returned EOF from the iterator function.
3576 state
.cbs_eof
= false;
3578 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3579 key
= (CatalogKey
*)&iterator
->key
;
3581 index
= dirhint
->dh_index
+ 1;
3583 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3584 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3587 * Attempt to build a key from cached filename
3589 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3590 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3591 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3596 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3598 * Position the iterator at the directory's thread record.
3599 * (i.e. just before the first entry)
3601 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3602 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3603 iterator
->hint
.index
= 0;
3608 * If the last entry wasn't cached then position the btree iterator
3612 * Position the iterator at the directory's thread record.
3613 * (i.e. just before the first entry)
3615 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3616 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3618 result
= MacToVFSError(result
);
3622 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3625 * Iterate until we reach the entry just
3626 * before the one we want to start with.
3629 struct position_state ps
;
3634 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3637 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3638 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3642 result
= MacToVFSError(result
);
3644 result
= MacToVFSError(result
);
3645 if (result
== ENOENT
) {
3647 * ENOENT means we've hit the EOF.
3648 * suppress the error, and set the eof flag.
3651 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3659 state
.cbs_index
= index
;
3660 state
.cbs_hfsmp
= hfsmp
;
3661 state
.cbs_uio
= uio
;
3662 state
.cbs_desc
= &dirhint
->dh_desc
;
3663 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3664 state
.cbs_result
= 0;
3665 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3667 /* Use a temporary buffer to hold intermediate descriptor names. */
3668 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3669 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3670 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3671 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3672 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3675 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3677 enum BTreeIterationOperations op
;
3678 if (extended
&& index
!= 0 && have_key
)
3679 op
= kBTreeCurrentRecord
;
3681 op
= kBTreeNextRecord
;
3684 * Process as many entries as possible starting at iterator->key.
3686 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3688 result
= BTIterateRecords(fcb
, op
, iterator
,
3689 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3691 /* For extended calls, every call to getdirentries_callback()
3692 * transfers the previous directory entry found to the user
3693 * buffer. Therefore when BTIterateRecords reaches the end of
3694 * Catalog BTree, call getdirentries_callback() again with
3695 * dummy values to copy the last directory entry stored in
3696 * packdirentry_state
3698 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3702 bzero(&ckp
, sizeof(ckp
));
3703 bzero(&crp
, sizeof(crp
));
3705 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3710 /* HFS (standard) */
3711 result
= BTIterateRecords(fcb
, op
, iterator
,
3712 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3716 /* Note that state.cbs_index is still valid on errors */
3717 *items
= state
.cbs_index
- index
;
3718 index
= state
.cbs_index
;
3721 * Also note that cbs_eof is set in all cases if we ever hit EOF
3722 * during the enumeration by the catalog callback. Mark the directory's hint
3723 * descriptor as having hit EOF.
3726 if (state
.cbs_eof
) {
3727 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3731 /* Finish updating the catalog iterator. */
3732 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3733 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3734 dirhint
->dh_index
= index
- 1;
3736 /* Fix up the name. */
3737 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3738 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3739 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3741 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3742 dirhint
->dh_desc
.cd_namelen
= 0;
3746 * Post process any hard links to get the real file id.
3748 if (state
.cbs_nlinks
> 0) {
3750 user_addr_t address
;
3753 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3754 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3756 /* This assumes that d_ino is always first field. */
3757 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3758 if (address
== (user_addr_t
)0)
3760 if (uio_isuserspace(uio
)) {
3762 ino64_t fileid_64
= (ino64_t
)fileid
;
3763 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3765 (void) copyout(&fileid
, address
, sizeof(fileid
));
3767 } else /* system space */ {
3769 ino64_t fileid_64
= (ino64_t
)fileid
;
3770 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3772 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3778 if (state
.cbs_result
)
3779 result
= state
.cbs_result
;
3781 result
= MacToVFSError(result
);
3783 if (result
== ENOENT
) {
3788 FREE(buffer
, M_TEMP
);
3795 * Callback to establish directory position.
3796 * Called with position_state for each item in a directory.
3799 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3800 struct position_state
*state
)
3804 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3805 curID
= ckp
->hfsPlus
.parentID
;
3809 curID
= ckp
->hfs
.parentID
;
3813 /* Make sure parent directory didn't change */
3814 if (state
->parentID
!= curID
) {
3816 * The parent ID is different from curID this means we've hit
3817 * the EOF for the directory.
3819 state
->error
= ENOENT
;
3820 return (0); /* stop */
3823 /* Count this entry */
3824 switch(crp
->recordType
) {
3825 case kHFSPlusFolderRecord
:
3826 case kHFSPlusFileRecord
:
3828 case kHFSFolderRecord
:
3829 case kHFSFileRecord
:
3834 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3835 crp
->recordType
, curID
);
3836 state
->error
= EINVAL
;
3837 return (0); /* stop */
3840 return (state
->count
< state
->index
);
3845 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3847 * The name portion of the key is compared using a 16-bit binary comparison.
3848 * This is called from the b-tree code.
3851 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3853 u_int32_t searchParentID
, trialParentID
;
3856 searchParentID
= searchKey
->parentID
;
3857 trialParentID
= trialKey
->parentID
;
3860 if (searchParentID
> trialParentID
) {
3862 } else if (searchParentID
< trialParentID
) {
3865 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3866 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3867 int length1
= searchKey
->nodeName
.length
;
3868 int length2
= trialKey
->nodeName
.length
;
3870 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3879 * Compare two standard HFS catalog keys
3881 * Result: +n search key > trial key
3882 * 0 search key = trial key
3883 * -n search key < trial key
3886 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3888 cnid_t searchParentID
, trialParentID
;
3891 searchParentID
= searchKey
->parentID
;
3892 trialParentID
= trialKey
->parentID
;
3894 if (searchParentID
> trialParentID
)
3896 else if (searchParentID
< trialParentID
)
3898 else /* parent dirID's are equal, compare names */
3899 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3907 * Compare two HFS+ catalog keys
3909 * Result: +n search key > trial key
3910 * 0 search key = trial key
3911 * -n search key < trial key
3914 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3916 cnid_t searchParentID
, trialParentID
;
3919 searchParentID
= searchKey
->parentID
;
3920 trialParentID
= trialKey
->parentID
;
3922 if (searchParentID
> trialParentID
) {
3925 else if (searchParentID
< trialParentID
) {
3928 /* parent node ID's are equal, compare names */
3929 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3930 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3932 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3933 searchKey
->nodeName
.length
,
3934 &trialKey
->nodeName
.unicode
[0],
3935 trialKey
->nodeName
.length
);
3943 * buildkey - build a Catalog b-tree key from a cnode descriptor
3946 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3947 HFSPlusCatalogKey
*key
, int retry
)
3949 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3950 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3952 size_t unicodeBytes
= 0;
3958 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3959 return (EINVAL
); /* invalid name */
3961 key
->parentID
= descp
->cd_parentcnid
;
3962 key
->nodeName
.length
= 0;
3964 * Convert filename from UTF-8 into Unicode
3967 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3968 utf8_flags
|= UTF_DECOMPOSED
;
3969 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3970 key
->nodeName
.unicode
, &unicodeBytes
,
3971 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3972 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3973 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3975 if (result
!= ENAMETOOLONG
)
3976 result
= EINVAL
; /* name has invalid characters */
3982 * For HFS volumes convert to an HFS compatible key
3984 * XXX need to save the encoding that succeeded
3987 HFSCatalogKey hfskey
;
3989 bzero(&hfskey
, sizeof(hfskey
));
3990 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3991 hfskey
.parentID
= key
->parentID
;
3992 hfskey
.nodeName
[0] = 0;
3993 if (key
->nodeName
.length
> 0) {
3995 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3996 key
->nodeName
.length
* 2,
3997 key
->nodeName
.unicode
,
3998 &hfskey
.nodeName
[0], retry
)) != 0) {
3999 if (res
!= ENAMETOOLONG
)
4004 hfskey
.keyLength
+= hfskey
.nodeName
[0];
4006 bcopy(&hfskey
, key
, sizeof(hfskey
));
4015 * Resolve hard link reference to obtain the inode record.
4018 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
4020 FSBufferDescriptor btdata
;
4021 struct BTreeIterator
*iterator
;
4022 struct cat_desc idesc
;
4027 BDINIT(btdata
, recp
);
4030 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4031 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4033 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4034 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4037 /* Get space for iterator */
4038 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4039 bzero(iterator
, sizeof(*iterator
));
4041 /* Build a descriptor for private dir. */
4042 idesc
.cd_parentcnid
= parentcnid
;
4043 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4044 idesc
.cd_namelen
= strlen(inodename
);
4047 idesc
.cd_encoding
= 0;
4048 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4050 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4051 &btdata
, NULL
, NULL
);
4054 /* Make sure there's a reference */
4055 if (recp
->hl_linkCount
== 0)
4056 recp
->hl_linkCount
= 2;
4058 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4061 FREE(iterator
, M_TEMP
);
4063 return (result
? ENOENT
: 0);
4067 * Resolve hard link reference to obtain the inode number.
4070 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4072 struct HFSPlusCatalogFile record
;
4076 * Since we know resolvelinkid is only called from
4077 * cat_getdirentries, we can assume that only file
4078 * hardlinks need to be resolved (cat_getdirentries
4079 * can resolve directory hardlinks in place).
4081 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4083 if (record
.fileID
== 0)
4086 *ino
= record
.fileID
;
4092 * getkey - get a key from id by doing a thread lookup
4095 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4097 struct BTreeIterator
* iterator
;
4098 FSBufferDescriptor btdata
;
4101 CatalogRecord
* recp
;
4105 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4107 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4108 bzero(iterator
, sizeof(*iterator
));
4109 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4111 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4112 BDINIT(btdata
, recp
);
4114 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4115 &btdata
, &datasize
, iterator
);
4119 /* Turn thread record into a cnode key (in place) */
4120 switch (recp
->recordType
) {
4123 case kHFSFileThreadRecord
:
4124 case kHFSFolderThreadRecord
:
4125 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4126 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4127 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4131 case kHFSPlusFileThreadRecord
:
4132 case kHFSPlusFolderThreadRecord
:
4133 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4134 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4135 (keyp
->hfsPlus
.nodeName
.length
* 2);
4136 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4145 FREE(iterator
, M_TEMP
);
4148 return MacToVFSError(result
);
4152 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4153 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4154 * cat_desc here). Both key and attrp must point to real structures.
4156 * The key's parent id is the only part of the key expected to be used by the caller.
4157 * The name portion of the key may not always be valid (ie in the case of a hard link).
4160 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4164 result
= getkey(hfsmp
, cnid
, key
);
4167 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4170 * Check for a raw file hardlink inode.
4171 * Fix up the parent id in the key if necessary.
4172 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4174 if ((result
== 0) &&
4175 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4176 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4177 cnid_t nextlinkid
= 0;
4178 cnid_t prevlinkid
= 0;
4179 struct cat_desc linkdesc
;
4182 * Pick up the first link in the chain and get a descriptor for it.
4183 * This allows blind bulk access checks to work for hardlinks.
4185 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4186 (nextlinkid
!= 0)) {
4187 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4188 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4189 cat_releasedesc(&linkdesc
);
4193 return MacToVFSError(result
);
4198 * buildrecord - build a default catalog directory or file record
4201 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4202 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4204 int type
= attrp
->ca_mode
& S_IFMT
;
4205 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4208 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4210 if (type
== S_IFDIR
) {
4211 crp
->recordType
= kHFSPlusFolderRecord
;
4212 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4213 crp
->hfsPlusFolder
.valence
= 0;
4214 crp
->hfsPlusFolder
.folderID
= cnid
;
4215 crp
->hfsPlusFolder
.createDate
= createtime
;
4216 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4217 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4218 crp
->hfsPlusFolder
.accessDate
= createtime
;
4219 crp
->hfsPlusFolder
.backupDate
= 0;
4220 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4221 crp
->hfsPlusFolder
.folderCount
= 0;
4222 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4223 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4224 bsdp
->special
.linkCount
= 1;
4225 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4227 crp
->recordType
= kHFSPlusFileRecord
;
4228 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4229 crp
->hfsPlusFile
.reserved1
= 0;
4230 crp
->hfsPlusFile
.fileID
= cnid
;
4231 crp
->hfsPlusFile
.createDate
= createtime
;
4232 crp
->hfsPlusFile
.contentModDate
= createtime
;
4233 crp
->hfsPlusFile
.accessDate
= createtime
;
4234 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4235 crp
->hfsPlusFile
.backupDate
= 0;
4236 crp
->hfsPlusFile
.textEncoding
= encoding
;
4237 crp
->hfsPlusFile
.reserved2
= 0;
4238 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4239 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4240 /* BLK/CHR need to save the device info */
4241 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4242 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4244 bsdp
->special
.linkCount
= 1;
4246 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4247 *recordSize
= sizeof(HFSPlusCatalogFile
);
4249 bsdp
->ownerID
= attrp
->ca_uid
;
4250 bsdp
->groupID
= attrp
->ca_gid
;
4251 bsdp
->fileMode
= attrp
->ca_mode
;
4252 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4253 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4257 createtime
= UTCToLocal(createtime
);
4258 if (type
== S_IFDIR
) {
4259 bzero(crp
, sizeof(HFSCatalogFolder
));
4260 crp
->recordType
= kHFSFolderRecord
;
4261 crp
->hfsFolder
.folderID
= cnid
;
4262 crp
->hfsFolder
.createDate
= createtime
;
4263 crp
->hfsFolder
.modifyDate
= createtime
;
4264 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4265 *recordSize
= sizeof(HFSCatalogFolder
);
4267 bzero(crp
, sizeof(HFSCatalogFile
));
4268 crp
->recordType
= kHFSFileRecord
;
4269 crp
->hfsFile
.fileID
= cnid
;
4270 crp
->hfsFile
.createDate
= createtime
;
4271 crp
->hfsFile
.modifyDate
= createtime
;
4272 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4273 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4274 *recordSize
= sizeof(HFSCatalogFile
);
4283 * builddesc - build a cnode descriptor from an HFS+ key
4286 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4287 int isdir
, struct cat_desc
*descp
)
4290 unsigned char * nameptr
;
4293 unsigned char tmpbuff
[128];
4295 /* guess a size... */
4296 bufsize
= (3 * key
->nodeName
.length
) + 1;
4297 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4298 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
4300 nameptr
= &tmpbuff
[0];
4303 result
= utf8_encodestr(key
->nodeName
.unicode
,
4304 key
->nodeName
.length
* sizeof(UniChar
),
4305 nameptr
, (size_t *)&utf8len
,
4308 if (result
== ENAMETOOLONG
) {
4309 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4310 key
->nodeName
.length
* sizeof(UniChar
),
4312 FREE(nameptr
, M_TEMP
);
4313 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
4315 result
= utf8_encodestr(key
->nodeName
.unicode
,
4316 key
->nodeName
.length
* sizeof(UniChar
),
4317 nameptr
, (size_t *)&utf8len
,
4320 descp
->cd_parentcnid
= key
->parentID
;
4321 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4322 descp
->cd_namelen
= utf8len
;
4323 descp
->cd_cnid
= cnid
;
4324 descp
->cd_hint
= hint
;
4325 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4327 descp
->cd_flags
|= CD_ISDIR
;
4328 descp
->cd_encoding
= encoding
;
4329 if (nameptr
!= &tmpbuff
[0]) {
4330 FREE(nameptr
, M_TEMP
);
4337 * getbsdattr - get attributes in bsd format
4341 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4343 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4344 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4346 attrp
->ca_recflags
= crp
->flags
;
4347 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4348 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4349 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4350 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4351 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4352 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4354 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4355 attrp
->ca_flags
= 0;
4356 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4357 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4359 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4361 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4363 attrp
->ca_linkcount
= 1;
4366 attrp
->ca_linkcount
= 1; /* may be overridden below */
4368 attrp
->ca_uid
= bsd
->ownerID
;
4369 attrp
->ca_gid
= bsd
->groupID
;
4370 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4371 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4372 switch (attrp
->ca_mode
& S_IFMT
) {
4373 case S_IFCHR
: /* fall through */
4375 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4381 /* Pick up the hard link count */
4382 if (bsd
->special
.linkCount
> 0)
4383 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4388 * Override the permissions as determined by the mount auguments
4389 * in ALMOST the same way unset permissions are treated but keep
4390 * track of whether or not the file or folder is hfs locked
4391 * by leaving the h_pflags field unchanged from what was unpacked
4392 * out of the catalog.
4395 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4396 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4397 * at the VFS layer, so there is no need to do it here now; this also
4398 * allows VFS to let root see the real UIDs.
4400 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4401 * attrp->ca_uid = hfsmp->hfs_uid;
4402 * attrp->ca_gid = hfsmp->hfs_gid;
4408 if (!S_ISDIR(attrp
->ca_mode
)) {
4409 attrp
->ca_mode
&= ~S_IFMT
;
4410 attrp
->ca_mode
|= S_IFDIR
;
4412 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4413 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4414 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4416 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4417 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4418 attrp
->ca_flags
|= UF_HIDDEN
;
4420 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4421 if (crp
->flags
& kHFSFileLockedMask
) {
4422 /* The file's supposed to be locked:
4423 Make sure at least one of the IMMUTABLE bits is set: */
4424 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4425 attrp
->ca_flags
|= UF_IMMUTABLE
;
4427 /* The file's supposed to be unlocked: */
4428 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4430 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4431 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4432 attrp
->ca_flags
|= UF_HIDDEN
;
4433 /* get total blocks (both forks) */
4434 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4436 /* On HFS+ the ThreadExists flag must always be set. */
4437 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4438 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4440 /* Pick up the hardlink first link, if any. */
4441 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4444 attrp
->ca_fileid
= crp
->fileID
;
4446 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4451 * promotekey - promote hfs key to hfs plus key
4455 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4456 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4458 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4462 *encoding
= hfsmp
->hfs_encoding
;
4464 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4465 kHFSPlusMaxFileNameChars
, &uniCount
);
4467 * When an HFS name cannot be encoded with the current
4468 * encoding use MacRoman as a fallback.
4470 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4472 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4473 keyp
->nodeName
.unicode
,
4474 kHFSPlusMaxFileNameChars
,
4478 keyp
->nodeName
.length
= uniCount
;
4479 keyp
->parentID
= hfskey
->parentID
;
4483 * promotefork - promote hfs fork info to hfs plus
4487 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4488 int resource
, struct cat_fork
* forkp
)
4490 struct HFSPlusExtentDescriptor
*xp
;
4491 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4493 bzero(forkp
, sizeof(*forkp
));
4494 xp
= &forkp
->cf_extents
[0];
4496 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4497 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4498 forkp
->cf_bytesread
= 0;
4499 forkp
->cf_vblocks
= 0;
4500 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4501 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4502 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4503 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4504 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4505 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4507 forkp
->cf_size
= filep
->dataLogicalSize
;
4508 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4509 forkp
->cf_bytesread
= 0;
4510 forkp
->cf_vblocks
= 0;
4511 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4512 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4513 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4514 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4515 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4516 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4521 * promoteattr - promote standard hfs catalog attributes to hfs plus
4525 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4527 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4529 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4530 const struct HFSCatalogFolder
* folder
;
4532 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4533 crp
->recordType
= kHFSPlusFolderRecord
;
4534 crp
->flags
= folder
->flags
;
4535 crp
->fileID
= folder
->folderID
;
4536 crp
->createDate
= LocalToUTC(folder
->createDate
);
4537 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4538 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4539 crp
->reserved1
= folder
->valence
;
4541 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4543 const struct HFSCatalogFile
* file
;
4545 file
= (const struct HFSCatalogFile
*) dataPtr
;
4546 crp
->recordType
= kHFSPlusFileRecord
;
4547 crp
->flags
= file
->flags
;
4548 crp
->fileID
= file
->fileID
;
4549 crp
->createDate
= LocalToUTC(file
->createDate
);
4550 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4551 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4554 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4555 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4556 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4557 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4559 crp
->textEncoding
= 0;
4560 crp
->attributeModDate
= crp
->contentModDate
;
4561 crp
->accessDate
= crp
->contentModDate
;
4562 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4567 * Build a catalog node thread record from a catalog key
4568 * and return the size of the record.
4571 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4576 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4577 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4579 size
= sizeof(HFSPlusCatalogThread
);
4581 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4583 rec
->recordType
= kHFSPlusFileThreadRecord
;
4585 rec
->parentID
= key
->parentID
;
4586 bcopy(&key
->nodeName
, &rec
->nodeName
,
4587 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4589 /* HFS Plus has variable sized thread records */
4590 size
-= (sizeof(rec
->nodeName
.unicode
) -
4591 (rec
->nodeName
.length
* sizeof(UniChar
)));
4596 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4597 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4599 size
= sizeof(HFSCatalogThread
);
4602 rec
->recordType
= kHFSFolderThreadRecord
;
4604 rec
->recordType
= kHFSFileThreadRecord
;
4605 rec
->parentID
= key
->parentID
;
4606 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4615 * Build a catalog node thread key.
4618 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4621 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4622 key
->hfsPlus
.parentID
= parentID
;
4623 key
->hfsPlus
.nodeName
.length
= 0;
4627 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4628 key
->hfs
.reserved
= 0;
4629 key
->hfs
.parentID
= parentID
;
4630 key
->hfs
.nodeName
[0] = 0;
4637 * Extract the text encoding from a catalog node record.
4640 getencoding(const CatalogRecord
*crp
)
4644 if (crp
->recordType
== kHFSPlusFolderRecord
)
4645 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4646 else if (crp
->recordType
== kHFSPlusFileRecord
)
4647 encoding
= crp
->hfsPlusFile
.textEncoding
;
4655 * Extract the CNID from a catalog node record.
4658 getcnid(const CatalogRecord
*crp
)
4662 switch (crp
->recordType
) {
4665 case kHFSFolderRecord
:
4666 cnid
= crp
->hfsFolder
.folderID
;
4668 case kHFSFileRecord
:
4669 cnid
= crp
->hfsFile
.fileID
;
4673 case kHFSPlusFolderRecord
:
4674 cnid
= crp
->hfsPlusFolder
.folderID
;
4676 case kHFSPlusFileRecord
:
4677 cnid
= crp
->hfsPlusFile
.fileID
;
4680 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4688 * Extract the parent ID from a catalog node record.
4691 getparentcnid(const CatalogRecord
*recp
)
4695 switch (recp
->recordType
) {
4698 case kHFSFileThreadRecord
:
4699 case kHFSFolderThreadRecord
:
4700 cnid
= recp
->hfsThread
.parentID
;
4704 case kHFSPlusFileThreadRecord
:
4705 case kHFSPlusFolderThreadRecord
:
4706 cnid
= recp
->hfsPlusThread
.parentID
;
4709 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4717 * Determine if a catalog node record is a directory.
4720 isadir(const CatalogRecord
*crp
)
4722 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4726 if (crp
->recordType
== kHFSFolderRecord
) {
4735 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4736 * (not inode) using catalog record id. Note that this function does
4737 * NOT resolve directory hard link to its directory inode and return
4740 * Note: The caller is responsible for releasing the output catalog
4741 * descriptor (when supplied outdescp is non-null).
4744 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4745 u_int8_t forktype
, struct cat_desc
*outdescp
,
4746 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4748 struct BTreeIterator
*iterator
= NULL
;
4749 FSBufferDescriptor btdata
;
4752 CatalogRecord
*recp
= NULL
;
4755 /* No directory hard links on standard HFS */
4756 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4760 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4761 if (iterator
== NULL
) {
4764 bzero(iterator
, sizeof(*iterator
));
4765 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4767 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4772 BDINIT(btdata
, recp
);
4774 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4775 &btdata
, &datasize
, iterator
);
4779 /* Directory hard links are catalog file record */
4780 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4785 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4786 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4787 (keyp
->hfsPlus
.nodeName
.length
* 2);
4788 if (forktype
== kHFSResourceForkType
) {
4789 /* Lookup resource fork for directory hard link */
4790 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4792 /* Lookup data fork, if any, for directory hard link */
4793 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4796 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4797 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4800 /* Just for sanity, make sure that id in catalog record and thread record match */
4801 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4802 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4803 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4811 FREE(iterator
, M_TEMP
);
4813 return MacToVFSError(error
);
4817 * cnode_update_dirlink - update the catalog node for directory hard link
4818 * described by descp using the data from attrp and forkp.
4821 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4822 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4824 if (forktype
== kHFSResourceForkType
) {
4825 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4827 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);
4831 void hfs_fork_copy(struct cat_fork
*dst
, const struct cat_fork
*src
,
4832 HFSPlusExtentDescriptor
*extents
)
4834 /* Copy everything but the extents into the dest fork */
4835 memcpy(dst
, src
, offsetof(struct cat_fork
, cf_extents
));
4836 /* Then copy the supplied extents into the fork */
4837 memcpy(dst
->cf_extents
, extents
, sizeof(HFSPlusExtentRecord
));