2 * Copyright (c) 2000-2013 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 struct cat_fork
* s_datafork
;
71 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 struct cat_fork
*dataforkp
, 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
);
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_volume_inconsistent (hfsmp
);
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_volume_inconsistent (hfsmp
);
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_volume_inconsistent (hfsmp
);
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_volume_inconsistent(hfsmp
);
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_volume_inconsistent(hfsmp
);
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_volume_inconsistent(hfsmp
);
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_volume_inconsistent(hfsmp
);
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_volume_inconsistent(hfsmp
);
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 struct cat_fork
*dataforkp
, 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 struct cat_fork
*dataforkp
, 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 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. We can change it since we hold the catalog lock.
2465 nextCNID
= hfsmp
->vcbNxtCNID
;
2466 if (nextCNID
== 0xFFFFFFFF) {
2467 hfs_lock_mount (hfsmp
);
2468 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2469 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
2470 hfs_unlock_mount(hfsmp
);
2472 hfsmp
->vcbNxtCNID
++;
2474 MarkVCBDirty(hfsmp
);
2476 /* Get space for iterator, key and data */
2477 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
2478 bto
->iterator
.hint
.nodeNum
= 0;
2479 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2481 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2483 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2487 /* This is our only chance to set the encoding (other than a rename). */
2488 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2490 /* Insert the thread record first. */
2491 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2492 btdata
.bufferAddress
= &bto
->data
;
2493 btdata
.itemSize
= datalen
;
2494 btdata
.itemCount
= 1;
2497 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2500 * If the CNID wraparound bit is set, then we need to validate if there
2501 * is a cnode in the hash already with this ID (even if it no longer exists
2502 * on disk). If so, then just skip this ID and move on to the next one.
2504 if (!std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2505 /* Verify that the CNID does not already exist in the cnode hash... */
2506 if (hfs_chash_snoop (hfsmp
, nextCNID
, 1, NULL
, NULL
) == 0) {
2507 /* It was found in the cnode hash!*/
2513 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2516 if ((result
== btExists
) && (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2518 * Allow CNIDs on HFS Plus volumes to wrap around
2520 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
2521 nextCNID
= kHFSFirstUserCatalogNodeID
;
2526 thread_inserted
= 1;
2534 * CNID is now established. If we have wrapped then
2535 * update the vcbNxtCNID.
2537 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2538 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
2539 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
2540 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2545 * Now insert the link record.
2547 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2549 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2550 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2551 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2553 /* For directory hard links, create alias in resource fork */
2554 if (descp
->cd_flags
& CD_ISDIR
) {
2555 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2558 alias_allocated
= 1;
2560 btdata
.bufferAddress
= &bto
->data
;
2561 btdata
.itemSize
= datalen
;
2562 btdata
.itemCount
= 1;
2564 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2566 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2568 if (result
== btExists
)
2572 if (linkfileid
!= NULL
) {
2573 *linkfileid
= nextCNID
;
2577 if (thread_inserted
) {
2578 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result
), hfsmp
->vcbVN
);
2580 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2581 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2582 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2583 hfs_mark_volume_inconsistent(hfsmp
);
2586 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2587 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2588 rsrcforkp
->extents
[0].blockCount
, 0);
2589 rsrcforkp
->extents
[0].startBlock
= 0;
2590 rsrcforkp
->extents
[0].blockCount
= 0;
2593 (void) BTFlushPath(fcb
);
2596 return MacToVFSError(result
);
2599 /* Directory hard links are visible as aliases on pre-Leopard systems and
2600 * as normal directories on Leopard or later. All directory hard link aliases
2601 * have the same resource fork content except for the three uniquely
2602 * identifying values that are updated in the resource fork data when the alias
2603 * is created. The following array is the constant resource fork data used
2604 * only for creating directory hard link aliases.
2606 static const char hfs_dirlink_alias_rsrc
[] = {
2607 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2609 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2610 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2611 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2613 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2615 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2616 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2617 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2618 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2619 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2622 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2623 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2625 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2627 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2629 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2630 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2631 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2632 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2633 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2634 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2635 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2638 /* Constants for directory hard link alias */
2640 /* Size of resource fork data array for directory hard link alias */
2641 kHFSAliasSize
= 0x1d0,
2643 /* Volume type for ejectable devices like disk image */
2644 kHFSAliasVolTypeEjectable
= 0x5,
2646 /* Offset for volume create date, in Mac OS local time */
2647 kHFSAliasVolCreateDateOffset
= 0x12a,
2649 /* Offset for the type of volume */
2650 kHFSAliasVolTypeOffset
= 0x130,
2652 /* Offset for folder ID of the parent directory of the directory inode */
2653 kHFSAliasParentIDOffset
= 0x132,
2655 /* Offset for folder ID of the directory inode */
2656 kHFSAliasTargetIDOffset
= 0x176,
2659 /* Create and write an alias that points at the directory represented by given
2660 * inode number on the same volume. Directory hard links are visible as
2661 * aliases in pre-Leopard systems and this function creates these aliases.
2663 * Note: This code is very specific to creating alias for the purpose
2664 * of directory hard links only, and should not be generalized.
2667 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2675 HFSPlusForkData
*rsrcforkp
;
2679 rsrcforkp
= &(crp
->resourceFork
);
2681 blksize
= hfsmp
->blockSize
;
2682 blkcount
= howmany(kHFSAliasSize
, blksize
);
2683 sectorsize
= hfsmp
->hfs_logical_block_size
;
2684 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2686 /* Allocate some disk space for the alias content. */
2687 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2688 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2689 &rsrcforkp
->extents
[0].startBlock
,
2690 &rsrcforkp
->extents
[0].blockCount
);
2691 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2692 if (result
== dskFulErr
) {
2693 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2694 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
| HFS_ALLOC_FLUSHTXN
,
2695 &rsrcforkp
->extents
[0].startBlock
,
2696 &rsrcforkp
->extents
[0].blockCount
);
2699 rsrcforkp
->extents
[0].startBlock
= 0;
2703 /* Acquire a buffer cache block for our block. */
2704 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2705 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2707 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2709 journal_modify_block_start(hfsmp
->jnl
, bp
);
2712 /* Generate alias content */
2713 alias
= (char *)buf_dataptr(bp
);
2714 bzero(alias
, buf_size(bp
));
2715 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2717 /* Set the volume create date, local time in Mac OS format */
2718 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2719 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2721 /* If the file system is on a virtual device like disk image,
2722 * update the volume type to be ejectable device.
2724 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2725 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2726 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2729 /* Set id of the parent of the target directory */
2730 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2731 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2733 /* Set id of the target directory */
2734 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2735 *valptr
= OSSwapHostToBigInt32(inode_num
);
2737 /* Write alias content to disk. */
2739 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2740 } else if ((result
= buf_bwrite(bp
))) {
2744 /* Finish initializing the fork data. */
2745 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2746 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2749 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2750 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2751 rsrcforkp
->extents
[0].startBlock
= 0;
2752 rsrcforkp
->extents
[0].blockCount
= 0;
2753 rsrcforkp
->logicalSize
= 0;
2754 rsrcforkp
->totalBlocks
= 0;
2760 * cat_deletelink - delete a link from the catalog
2763 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2765 struct HFSPlusCatalogFile file
;
2766 struct cat_attr cattr
;
2767 uint32_t totalBlocks
;
2771 bzero(&file
, sizeof (file
));
2772 bzero(&cattr
, sizeof (cattr
));
2773 cattr
.ca_fileid
= descp
->cd_cnid
;
2775 /* Directory links have alias content to remove. */
2776 if (descp
->cd_flags
& CD_ISDIR
) {
2778 BTreeIterator
* iterator
;
2779 struct FSBufferDescriptor btdata
;
2781 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2783 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2784 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2785 iterator
->hint
.nodeNum
= 0;
2787 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2790 BDINIT(btdata
, &file
);
2792 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2797 result
= cat_delete(hfsmp
, descp
, &cattr
);
2799 if ((result
== 0) &&
2800 (descp
->cd_flags
& CD_ISDIR
) &&
2801 (file
.recordType
== kHFSPlusFileRecord
)) {
2803 totalBlocks
= file
.resourceFork
.totalBlocks
;
2805 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2806 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2807 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2811 (void) BlockDeallocate(hfsmp
,
2812 file
.resourceFork
.extents
[i
].startBlock
,
2813 file
.resourceFork
.extents
[i
].blockCount
, 0);
2815 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2816 file
.resourceFork
.extents
[i
].startBlock
= 0;
2817 file
.resourceFork
.extents
[i
].blockCount
= 0;
2826 * Callback to collect directory entries.
2827 * Called with readattr_state for each item in a directory.
2829 struct readattr_state
{
2830 struct hfsmount
*hfsmp
;
2831 struct cat_entrylist
*list
;
2838 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2839 struct readattr_state
*state
)
2841 struct cat_entrylist
*list
= state
->list
;
2842 struct hfsmount
*hfsmp
= state
->hfsmp
;
2843 struct cat_entry
*cep
;
2846 if (list
->realentries
>= list
->maxentries
)
2847 return (0); /* stop */
2849 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2851 switch(rec
->recordType
) {
2852 case kHFSPlusFolderRecord
:
2853 case kHFSPlusFileRecord
:
2855 case kHFSFolderRecord
:
2856 case kHFSFileRecord
:
2858 if (parentcnid
!= state
->dir_cnid
) {
2859 state
->error
= ENOENT
;
2860 return (0); /* stop */
2864 state
->error
= ENOENT
;
2865 return (0); /* stop */
2868 /* Hide the private system directories and journal files */
2869 if (parentcnid
== kHFSRootFolderID
) {
2870 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2871 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2872 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2873 list
->skipentries
++;
2874 return (1); /* continue */
2877 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2878 (rec
->recordType
== kHFSPlusFileRecord
) &&
2879 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2880 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2881 list
->skipentries
++;
2882 return (1); /* continue */
2886 cep
= &list
->entry
[list
->realentries
++];
2888 if (state
->stdhfs
== 0) {
2889 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2890 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2891 isadir(rec
), &cep
->ce_desc
);
2893 if (rec
->recordType
== kHFSPlusFileRecord
) {
2894 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2895 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2896 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2897 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2899 /* Save link reference for later processing. */
2900 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2901 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2902 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2903 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2904 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2905 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2906 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2912 struct HFSPlusCatalogFile cnoderec
;
2913 HFSPlusCatalogKey
* pluskey
;
2916 promoteattr(hfsmp
, rec
, &cnoderec
);
2917 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2919 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2920 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2921 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2922 FREE(pluskey
, M_TEMP
);
2924 if (rec
->recordType
== kHFSFileRecord
) {
2925 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2927 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2928 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2929 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2930 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2935 return (list
->realentries
< list
->maxentries
);
2939 * Pack a cat_entrylist buffer with attributes from the catalog
2941 * Note: index is zero relative
2944 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
2948 BTreeIterator
* iterator
;
2949 struct readattr_state state
;
2957 ce_list
->realentries
= 0;
2959 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2960 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2961 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2963 state
.hfsmp
= hfsmp
;
2964 state
.list
= ce_list
;
2965 state
.dir_cnid
= parentcnid
;
2966 state
.stdhfs
= std_hfs
;
2969 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2970 bzero(iterator
, sizeof(*iterator
));
2971 key
= (CatalogKey
*)&iterator
->key
;
2973 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2974 index
= dirhint
->dh_index
+ 1;
2977 * Attempt to build a key from cached filename
2979 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2980 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2986 * If the last entry wasn't cached then position the btree iterator
2988 if ((index
== 0) || !have_key
) {
2990 * Position the iterator at the directory's thread record.
2991 * (i.e. just before the first entry)
2993 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2994 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2996 result
= MacToVFSError(result
);
3001 * Iterate until we reach the entry just
3002 * before the one we want to start with.
3005 struct position_state ps
;
3010 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3013 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3014 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3018 result
= MacToVFSError(result
);
3020 result
= MacToVFSError(result
);
3026 /* Fill list with entries starting at iterator->key. */
3027 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3028 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
3031 result
= state
.error
;
3032 else if (ce_list
->realentries
== 0)
3035 result
= MacToVFSError(result
);
3041 * Resolve any hard links.
3043 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
3044 struct FndrFileInfo
*fip
;
3045 struct cat_entry
*cep
;
3046 struct HFSPlusCatalogFile filerec
;
3050 cep
= &ce_list
->entry
[i
];
3051 if (cep
->ce_attr
.ca_linkref
== 0)
3054 /* Note: Finder info is still in Big Endian */
3055 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
3057 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3058 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
3059 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
3062 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3063 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
3064 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
3065 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
3068 if (isfilelink
|| isdirlink
) {
3069 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
3071 /* Repack entry from inode record. */
3072 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
3073 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
3074 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
3075 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
3076 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
3080 FREE(iterator
, M_TEMP
);
3082 return MacToVFSError(result
);
3085 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3088 * Callback to pack directory entries.
3089 * Called with packdirentry_state for each item in a directory.
3092 /* Hard link information collected during cat_getdirentries. */
3095 user_addr_t dirent_addr
;
3097 typedef struct linkinfo linkinfo_t
;
3099 /* State information for the getdirentries_callback function. */
3100 struct packdirentry_state
{
3101 int cbs_flags
; /* VNODE_READDIR_* flags */
3102 u_int32_t cbs_parentID
;
3103 u_int32_t cbs_index
;
3105 ExtendedVCB
* cbs_hfsmp
;
3108 int32_t cbs_maxlinks
;
3109 linkinfo_t
* cbs_linkinfo
;
3110 struct cat_desc
* cbs_desc
;
3111 u_int8_t
* cbs_namebuf
;
3113 * The following fields are only used for NFS readdir, which
3114 * uses the next file id as the seek offset of each entry.
3116 struct direntry
* cbs_direntry
;
3117 struct direntry
* cbs_prevdirentry
;
3118 u_int32_t cbs_previlinkref
;
3119 Boolean cbs_hasprevdirentry
;
3124 * getdirentries callback for HFS Plus directories.
3127 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3128 struct packdirentry_state
*state
)
3130 struct hfsmount
*hfsmp
;
3131 const CatalogName
*cnp
;
3134 struct dirent catent
;
3135 struct direntry
* entry
= NULL
;
3137 u_int32_t ilinkref
= 0;
3138 u_int32_t curlinkref
= 0;
3141 u_int8_t type
= DT_UNKNOWN
;
3142 u_int8_t is_mangled
= 0;
3143 u_int8_t is_link
= 0;
3145 user_addr_t uiobase
= USER_ADDR_NULL
;
3150 Boolean stop_after_pack
= false;
3152 hfsmp
= state
->cbs_hfsmp
;
3153 curID
= ckp
->hfsPlus
.parentID
;
3155 /* We're done when parent directory changes */
3156 if (state
->cbs_parentID
!= curID
) {
3158 * If the parent ID is different from curID this means we've hit
3159 * the EOF for the directory. To help future callers, we mark
3160 * the cbs_eof boolean. However, we should only mark the EOF
3161 * boolean if we're about to return from this function.
3163 * This is because this callback function does its own uiomove
3164 * to get the data to userspace. If we set the boolean before determining
3165 * whether or not the current entry has enough room to write its
3166 * data to userland, we could fool the callers of this catalog function
3167 * into thinking they've hit EOF earlier than they really would have.
3168 * In that case, we'd know that we have more entries to process and
3169 * send to userland, but we didn't have enough room.
3171 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3172 * about to return and won't write any new data back
3173 * to userland. In the stop_after_pack case, we'll set this boolean
3174 * regardless, so it's slightly safer to let that logic mark the boolean,
3175 * especially since it's closer to the return of this function.
3178 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3179 /* The last record has not been returned yet, so we
3180 * want to stop after packing the last item
3182 if (state
->cbs_hasprevdirentry
) {
3183 stop_after_pack
= true;
3185 state
->cbs_eof
= true;
3186 state
->cbs_result
= ENOENT
;
3187 return (0); /* stop */
3190 state
->cbs_eof
= true;
3191 state
->cbs_result
= ENOENT
;
3192 return (0); /* stop */
3196 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3197 entry
= state
->cbs_direntry
;
3198 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3199 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3201 * The NFS server sometimes needs to make filenames fit in
3202 * NAME_MAX bytes (since its client may not be able to
3203 * handle a longer name). In that case, NFS will ask us
3204 * to mangle the name to keep it short enough.
3206 maxnamelen
= NAME_MAX
+ 1;
3208 maxnamelen
= sizeof(entry
->d_name
);
3211 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3212 maxnamelen
= sizeof(catent
.d_name
);
3215 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3216 /* The last item returns a non-zero invalid cookie */
3219 switch(crp
->recordType
) {
3220 case kHFSPlusFolderRecord
:
3222 cnid
= crp
->hfsPlusFolder
.folderID
;
3223 /* Hide our private system directories. */
3224 if (curID
== kHFSRootFolderID
) {
3225 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3226 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3231 case kHFSPlusFileRecord
:
3232 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3233 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3234 cnid
= crp
->hfsPlusFile
.fileID
;
3236 * When a hardlink link is encountered save its link ref.
3238 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3239 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3240 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3241 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3242 /* If link ref is inode's file id then use it directly. */
3243 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3244 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3246 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3249 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3250 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3251 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3252 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3253 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3254 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3255 /* A directory's link resolves to a directory. */
3257 /* A directory's link ref is always inode's file id. */
3258 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3261 /* Hide the journal files */
3262 if ((curID
== kHFSRootFolderID
) &&
3263 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3264 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3265 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3270 return (0); /* stop */
3273 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3275 namelen
= cnp
->ustr
.length
;
3277 * For MacRoman encoded names, assume that its ascii and
3278 * convert it directly in an attempt to avoid the more
3279 * expensive utf8_encodestr conversion.
3281 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3284 const u_int16_t
*chp
;
3286 chp
= &cnp
->ustr
.unicode
[0];
3287 for (i
= 0; i
< (int)namelen
; ++i
) {
3289 if (ch
> 0x007f || ch
== 0x0000) {
3290 /* Perform expensive utf8_encodestr conversion */
3293 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3295 nameptr
[namelen
] = '\0';
3299 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3300 nameptr
, &namelen
, maxnamelen
, ':', 0);
3303 /* Check result returned from encoding the filename to utf8 */
3304 if (result
== ENAMETOOLONG
) {
3306 * If we were looking at a catalog record for a hardlink (not the inode),
3307 * then we want to use its link ID as opposed to the inode ID for
3308 * a mangled name. For all other cases, they are the same. Note that
3309 * due to the way directory hardlinks are implemented, the actual link
3310 * is going to be counted as a file record, so we can catch both
3313 cnid_t linkid
= cnid
;
3315 linkid
= crp
->hfsPlusFile
.fileID
;
3318 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3319 cnp
->ustr
.unicode
, maxnamelen
,
3320 (ByteCount
*)&namelen
, nameptr
, linkid
);
3325 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3327 * The index is 1 relative and includes "." and ".."
3329 * Also stuff the cnid in the upper 32 bits of the cookie.
3330 * The cookie is stored to the previous entry, which will
3331 * be packed and copied this time
3333 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3334 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3335 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3337 catent
.d_type
= type
;
3338 catent
.d_namlen
= namelen
;
3339 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3341 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3343 catent
.d_fileno
= cnid
;
3344 uioaddr
= (caddr_t
) &catent
;
3347 /* Save current base address for post processing of hard-links. */
3348 if (ilinkref
|| state
->cbs_previlinkref
) {
3349 uiobase
= uio_curriovbase(state
->cbs_uio
);
3351 /* If this entry won't fit then we're done */
3352 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3353 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3354 return (0); /* stop */
3357 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3358 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3359 if (state
->cbs_result
== 0) {
3362 /* Remember previous entry */
3363 state
->cbs_desc
->cd_cnid
= cnid
;
3364 if (type
== DT_DIR
) {
3365 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3367 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3369 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3370 state
->cbs_desc
->cd_namelen
= 0;
3373 state
->cbs_desc
->cd_encoding
= xxxx
;
3376 state
->cbs_desc
->cd_namelen
= namelen
;
3377 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3379 /* Store unmangled name for the directory hint else it will
3380 * restart readdir at the last location again
3382 u_int8_t
*new_nameptr
;
3384 size_t tmp_namelen
= 0;
3386 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3387 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3388 cnp
->ustr
.length
* sizeof(UniChar
),
3390 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
3391 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3392 cnp
->ustr
.length
* sizeof(UniChar
),
3393 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3395 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3396 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3398 FREE(new_nameptr
, M_TEMP
);
3401 if (state
->cbs_hasprevdirentry
) {
3402 curlinkref
= ilinkref
; /* save current */
3403 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3406 * Record any hard links for post processing.
3408 if ((ilinkref
!= 0) &&
3409 (state
->cbs_result
== 0) &&
3410 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3411 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3412 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3413 state
->cbs_nlinks
++;
3415 if (state
->cbs_hasprevdirentry
) {
3416 ilinkref
= curlinkref
; /* restore current */
3420 /* Fill the direntry to be used the next time */
3421 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3422 if (stop_after_pack
) {
3423 state
->cbs_eof
= true;
3424 return (0); /* stop */
3426 entry
->d_type
= type
;
3427 entry
->d_namlen
= namelen
;
3428 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3430 /* File number = 0 means skip entry */
3431 entry
->d_fileno
= 0;
3433 entry
->d_fileno
= cnid
;
3435 /* swap the current and previous entry */
3436 struct direntry
* tmp
;
3437 tmp
= state
->cbs_direntry
;
3438 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3439 state
->cbs_prevdirentry
= tmp
;
3440 state
->cbs_hasprevdirentry
= true;
3441 state
->cbs_previlinkref
= ilinkref
;
3444 /* Continue iteration if there's room */
3445 return (state
->cbs_result
== 0 &&
3446 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3451 * getdirentries callback for standard HFS (non HFS+) directories.
3454 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3455 struct packdirentry_state
*state
)
3457 struct hfsmount
*hfsmp
;
3458 const CatalogName
*cnp
;
3461 struct dirent catent
;
3463 u_int8_t type
= DT_UNKNOWN
;
3470 hfsmp
= state
->cbs_hfsmp
;
3472 curID
= ckp
->hfs
.parentID
;
3474 /* We're done when parent directory changes */
3475 if (state
->cbs_parentID
!= curID
) {
3476 state
->cbs_result
= ENOENT
;
3477 return (0); /* stop */
3480 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3481 maxnamelen
= sizeof(catent
.d_name
);
3483 switch(crp
->recordType
) {
3484 case kHFSFolderRecord
:
3486 cnid
= crp
->hfsFolder
.folderID
;
3488 case kHFSFileRecord
:
3490 cnid
= crp
->hfsFile
.fileID
;
3493 return (0); /* stop */
3496 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3497 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3499 * When an HFS name cannot be encoded with the current
3500 * volume encoding we use MacRoman as a fallback.
3503 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3505 catent
.d_type
= type
;
3506 catent
.d_namlen
= namelen
;
3507 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3508 catent
.d_fileno
= cnid
;
3509 uioaddr
= (caddr_t
) &catent
;
3511 /* If this entry won't fit then we're done */
3512 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3513 return (0); /* stop */
3516 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3517 if (state
->cbs_result
== 0) {
3520 /* Remember previous entry */
3521 state
->cbs_desc
->cd_cnid
= cnid
;
3522 if (type
== DT_DIR
) {
3523 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3525 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3527 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3528 state
->cbs_desc
->cd_namelen
= 0;
3530 state
->cbs_desc
->cd_namelen
= namelen
;
3531 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3534 /* Continue iteration if there's room */
3535 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3540 * Pack a uio buffer with directory entries from the catalog
3543 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3544 uio_t uio
, int flags
, int * items
, int * eofflag
)
3547 BTreeIterator
* iterator
;
3549 struct packdirentry_state state
;
3558 extended
= flags
& VNODE_READDIR_EXTENDED
;
3560 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3563 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3566 * Get a buffer for link info array, btree iterator and a direntry:
3568 maxlinks
= MIN(entrycnt
, (u_int32_t
)(uio_resid(uio
) / SMALL_DIRENTRY_SIZE
));
3569 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3571 bufsize
+= 2*sizeof(struct direntry
);
3573 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3574 bzero(buffer
, bufsize
);
3576 state
.cbs_flags
= flags
;
3577 state
.cbs_hasprevdirentry
= false;
3578 state
.cbs_previlinkref
= 0;
3579 state
.cbs_nlinks
= 0;
3580 state
.cbs_maxlinks
= maxlinks
;
3581 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3583 * We need to set cbs_eof to false regardless of whether or not the
3584 * control flow is actually in the extended case, since we use this
3585 * field to track whether or not we've returned EOF from the iterator function.
3587 state
.cbs_eof
= false;
3589 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3590 key
= (CatalogKey
*)&iterator
->key
;
3592 index
= dirhint
->dh_index
+ 1;
3594 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3595 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3598 * Attempt to build a key from cached filename
3600 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3601 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3602 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3607 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3609 * Position the iterator at the directory's thread record.
3610 * (i.e. just before the first entry)
3612 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3613 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3614 iterator
->hint
.index
= 0;
3619 * If the last entry wasn't cached then position the btree iterator
3623 * Position the iterator at the directory's thread record.
3624 * (i.e. just before the first entry)
3626 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3627 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3629 result
= MacToVFSError(result
);
3633 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3636 * Iterate until we reach the entry just
3637 * before the one we want to start with.
3640 struct position_state ps
;
3645 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3648 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3649 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3653 result
= MacToVFSError(result
);
3655 result
= MacToVFSError(result
);
3661 state
.cbs_index
= index
;
3662 state
.cbs_hfsmp
= hfsmp
;
3663 state
.cbs_uio
= uio
;
3664 state
.cbs_desc
= &dirhint
->dh_desc
;
3665 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3666 state
.cbs_result
= 0;
3667 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3669 /* Use a temporary buffer to hold intermediate descriptor names. */
3670 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3671 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3672 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3673 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3674 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3677 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3679 enum BTreeIterationOperations op
;
3680 if (extended
&& index
!= 0 && have_key
)
3681 op
= kBTreeCurrentRecord
;
3683 op
= kBTreeNextRecord
;
3686 * Process as many entries as possible starting at iterator->key.
3688 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3690 result
= BTIterateRecords(fcb
, op
, iterator
,
3691 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3693 /* For extended calls, every call to getdirentries_callback()
3694 * transfers the previous directory entry found to the user
3695 * buffer. Therefore when BTIterateRecords reaches the end of
3696 * Catalog BTree, call getdirentries_callback() again with
3697 * dummy values to copy the last directory entry stored in
3698 * packdirentry_state
3700 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3704 bzero(&ckp
, sizeof(ckp
));
3705 bzero(&crp
, sizeof(crp
));
3707 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3712 /* HFS (standard) */
3713 result
= BTIterateRecords(fcb
, op
, iterator
,
3714 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3718 /* Note that state.cbs_index is still valid on errors */
3719 *items
= state
.cbs_index
- index
;
3720 index
= state
.cbs_index
;
3723 * Also note that cbs_eof is set in all cases if we ever hit EOF
3724 * during the enumeration by the catalog callback. Mark the directory's hint
3725 * descriptor as having hit EOF.
3728 if (state
.cbs_eof
) {
3729 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3733 /* Finish updating the catalog iterator. */
3734 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3735 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3736 dirhint
->dh_index
= index
- 1;
3738 /* Fix up the name. */
3739 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3740 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3741 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3743 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3744 dirhint
->dh_desc
.cd_namelen
= 0;
3748 * Post process any hard links to get the real file id.
3750 if (state
.cbs_nlinks
> 0) {
3752 user_addr_t address
;
3755 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3756 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3758 /* This assumes that d_ino is always first field. */
3759 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3760 if (address
== (user_addr_t
)0)
3762 if (uio_isuserspace(uio
)) {
3764 ino64_t fileid_64
= (ino64_t
)fileid
;
3765 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3767 (void) copyout(&fileid
, address
, sizeof(fileid
));
3769 } else /* system space */ {
3771 ino64_t fileid_64
= (ino64_t
)fileid
;
3772 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3774 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3780 if (state
.cbs_result
)
3781 result
= state
.cbs_result
;
3783 result
= MacToVFSError(result
);
3785 if (result
== ENOENT
) {
3790 FREE(buffer
, M_TEMP
);
3797 * Callback to establish directory position.
3798 * Called with position_state for each item in a directory.
3801 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3802 struct position_state
*state
)
3806 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3807 curID
= ckp
->hfsPlus
.parentID
;
3811 curID
= ckp
->hfs
.parentID
;
3815 /* Make sure parent directory didn't change */
3816 if (state
->parentID
!= curID
) {
3817 state
->error
= EINVAL
;
3818 return (0); /* stop */
3821 /* Count this entry */
3822 switch(crp
->recordType
) {
3823 case kHFSPlusFolderRecord
:
3824 case kHFSPlusFileRecord
:
3826 case kHFSFolderRecord
:
3827 case kHFSFileRecord
:
3832 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3833 crp
->recordType
, curID
);
3834 state
->error
= EINVAL
;
3835 return (0); /* stop */
3838 return (state
->count
< state
->index
);
3843 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3845 * The name portion of the key is compared using a 16-bit binary comparison.
3846 * This is called from the b-tree code.
3849 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3851 u_int32_t searchParentID
, trialParentID
;
3854 searchParentID
= searchKey
->parentID
;
3855 trialParentID
= trialKey
->parentID
;
3858 if (searchParentID
> trialParentID
) {
3860 } else if (searchParentID
< trialParentID
) {
3863 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3864 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3865 int length1
= searchKey
->nodeName
.length
;
3866 int length2
= trialKey
->nodeName
.length
;
3868 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3877 * Compare two standard HFS catalog keys
3879 * Result: +n search key > trial key
3880 * 0 search key = trial key
3881 * -n search key < trial key
3884 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3886 cnid_t searchParentID
, trialParentID
;
3889 searchParentID
= searchKey
->parentID
;
3890 trialParentID
= trialKey
->parentID
;
3892 if (searchParentID
> trialParentID
)
3894 else if (searchParentID
< trialParentID
)
3896 else /* parent dirID's are equal, compare names */
3897 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3905 * Compare two HFS+ catalog keys
3907 * Result: +n search key > trial key
3908 * 0 search key = trial key
3909 * -n search key < trial key
3912 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3914 cnid_t searchParentID
, trialParentID
;
3917 searchParentID
= searchKey
->parentID
;
3918 trialParentID
= trialKey
->parentID
;
3920 if (searchParentID
> trialParentID
) {
3923 else if (searchParentID
< trialParentID
) {
3926 /* parent node ID's are equal, compare names */
3927 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3928 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3930 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3931 searchKey
->nodeName
.length
,
3932 &trialKey
->nodeName
.unicode
[0],
3933 trialKey
->nodeName
.length
);
3941 * buildkey - build a Catalog b-tree key from a cnode descriptor
3944 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3945 HFSPlusCatalogKey
*key
, int retry
)
3947 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3948 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3950 size_t unicodeBytes
= 0;
3956 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3957 return (EINVAL
); /* invalid name */
3959 key
->parentID
= descp
->cd_parentcnid
;
3960 key
->nodeName
.length
= 0;
3962 * Convert filename from UTF-8 into Unicode
3965 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3966 utf8_flags
|= UTF_DECOMPOSED
;
3967 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3968 key
->nodeName
.unicode
, &unicodeBytes
,
3969 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3970 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3971 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3973 if (result
!= ENAMETOOLONG
)
3974 result
= EINVAL
; /* name has invalid characters */
3980 * For HFS volumes convert to an HFS compatible key
3982 * XXX need to save the encoding that succeeded
3985 HFSCatalogKey hfskey
;
3987 bzero(&hfskey
, sizeof(hfskey
));
3988 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3989 hfskey
.parentID
= key
->parentID
;
3990 hfskey
.nodeName
[0] = 0;
3991 if (key
->nodeName
.length
> 0) {
3993 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3994 key
->nodeName
.length
* 2,
3995 key
->nodeName
.unicode
,
3996 &hfskey
.nodeName
[0], retry
)) != 0) {
3997 if (res
!= ENAMETOOLONG
)
4002 hfskey
.keyLength
+= hfskey
.nodeName
[0];
4004 bcopy(&hfskey
, key
, sizeof(hfskey
));
4013 * Resolve hard link reference to obtain the inode record.
4016 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
4018 FSBufferDescriptor btdata
;
4019 struct BTreeIterator
*iterator
;
4020 struct cat_desc idesc
;
4025 BDINIT(btdata
, recp
);
4028 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4029 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4031 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4032 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4035 /* Get space for iterator */
4036 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4037 bzero(iterator
, sizeof(*iterator
));
4039 /* Build a descriptor for private dir. */
4040 idesc
.cd_parentcnid
= parentcnid
;
4041 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4042 idesc
.cd_namelen
= strlen(inodename
);
4045 idesc
.cd_encoding
= 0;
4046 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4048 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4049 &btdata
, NULL
, NULL
);
4052 /* Make sure there's a reference */
4053 if (recp
->hl_linkCount
== 0)
4054 recp
->hl_linkCount
= 2;
4056 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4059 FREE(iterator
, M_TEMP
);
4061 return (result
? ENOENT
: 0);
4065 * Resolve hard link reference to obtain the inode number.
4068 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4070 struct HFSPlusCatalogFile record
;
4074 * Since we know resolvelinkid is only called from
4075 * cat_getdirentries, we can assume that only file
4076 * hardlinks need to be resolved (cat_getdirentries
4077 * can resolve directory hardlinks in place).
4079 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4081 if (record
.fileID
== 0)
4084 *ino
= record
.fileID
;
4090 * getkey - get a key from id by doing a thread lookup
4093 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4095 struct BTreeIterator
* iterator
;
4096 FSBufferDescriptor btdata
;
4099 CatalogRecord
* recp
;
4103 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4105 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4106 bzero(iterator
, sizeof(*iterator
));
4107 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4109 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4110 BDINIT(btdata
, recp
);
4112 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4113 &btdata
, &datasize
, iterator
);
4117 /* Turn thread record into a cnode key (in place) */
4118 switch (recp
->recordType
) {
4121 case kHFSFileThreadRecord
:
4122 case kHFSFolderThreadRecord
:
4123 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4124 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4125 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4129 case kHFSPlusFileThreadRecord
:
4130 case kHFSPlusFolderThreadRecord
:
4131 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4132 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4133 (keyp
->hfsPlus
.nodeName
.length
* 2);
4134 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4143 FREE(iterator
, M_TEMP
);
4146 return MacToVFSError(result
);
4150 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4151 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4152 * cat_desc here). Both key and attrp must point to real structures.
4154 * The key's parent id is the only part of the key expected to be used by the caller.
4155 * The name portion of the key may not always be valid (ie in the case of a hard link).
4158 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4162 result
= getkey(hfsmp
, cnid
, key
);
4165 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4168 * Check for a raw file hardlink inode.
4169 * Fix up the parent id in the key if necessary.
4170 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4172 if ((result
== 0) &&
4173 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4174 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4175 cnid_t nextlinkid
= 0;
4176 cnid_t prevlinkid
= 0;
4177 struct cat_desc linkdesc
;
4180 * Pick up the first link in the chain and get a descriptor for it.
4181 * This allows blind bulk access checks to work for hardlinks.
4183 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4184 (nextlinkid
!= 0)) {
4185 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4186 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4187 cat_releasedesc(&linkdesc
);
4191 return MacToVFSError(result
);
4196 * buildrecord - build a default catalog directory or file record
4199 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4200 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4202 int type
= attrp
->ca_mode
& S_IFMT
;
4203 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4206 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4208 if (type
== S_IFDIR
) {
4209 crp
->recordType
= kHFSPlusFolderRecord
;
4210 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4211 crp
->hfsPlusFolder
.valence
= 0;
4212 crp
->hfsPlusFolder
.folderID
= cnid
;
4213 crp
->hfsPlusFolder
.createDate
= createtime
;
4214 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4215 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4216 crp
->hfsPlusFolder
.accessDate
= createtime
;
4217 crp
->hfsPlusFolder
.backupDate
= 0;
4218 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4219 crp
->hfsPlusFolder
.folderCount
= 0;
4220 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4221 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4222 bsdp
->special
.linkCount
= 1;
4223 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4225 crp
->recordType
= kHFSPlusFileRecord
;
4226 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4227 crp
->hfsPlusFile
.reserved1
= 0;
4228 crp
->hfsPlusFile
.fileID
= cnid
;
4229 crp
->hfsPlusFile
.createDate
= createtime
;
4230 crp
->hfsPlusFile
.contentModDate
= createtime
;
4231 crp
->hfsPlusFile
.accessDate
= createtime
;
4232 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4233 crp
->hfsPlusFile
.backupDate
= 0;
4234 crp
->hfsPlusFile
.textEncoding
= encoding
;
4235 crp
->hfsPlusFile
.reserved2
= 0;
4236 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4237 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4238 /* BLK/CHR need to save the device info */
4239 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4240 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4242 bsdp
->special
.linkCount
= 1;
4244 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4245 *recordSize
= sizeof(HFSPlusCatalogFile
);
4247 bsdp
->ownerID
= attrp
->ca_uid
;
4248 bsdp
->groupID
= attrp
->ca_gid
;
4249 bsdp
->fileMode
= attrp
->ca_mode
;
4250 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4251 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4255 createtime
= UTCToLocal(createtime
);
4256 if (type
== S_IFDIR
) {
4257 bzero(crp
, sizeof(HFSCatalogFolder
));
4258 crp
->recordType
= kHFSFolderRecord
;
4259 crp
->hfsFolder
.folderID
= cnid
;
4260 crp
->hfsFolder
.createDate
= createtime
;
4261 crp
->hfsFolder
.modifyDate
= createtime
;
4262 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4263 *recordSize
= sizeof(HFSCatalogFolder
);
4265 bzero(crp
, sizeof(HFSCatalogFile
));
4266 crp
->recordType
= kHFSFileRecord
;
4267 crp
->hfsFile
.fileID
= cnid
;
4268 crp
->hfsFile
.createDate
= createtime
;
4269 crp
->hfsFile
.modifyDate
= createtime
;
4270 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4271 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4272 *recordSize
= sizeof(HFSCatalogFile
);
4281 * builddesc - build a cnode descriptor from an HFS+ key
4284 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4285 int isdir
, struct cat_desc
*descp
)
4288 unsigned char * nameptr
;
4291 unsigned char tmpbuff
[128];
4293 /* guess a size... */
4294 bufsize
= (3 * key
->nodeName
.length
) + 1;
4295 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4296 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
4298 nameptr
= &tmpbuff
[0];
4301 result
= utf8_encodestr(key
->nodeName
.unicode
,
4302 key
->nodeName
.length
* sizeof(UniChar
),
4303 nameptr
, (size_t *)&utf8len
,
4306 if (result
== ENAMETOOLONG
) {
4307 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4308 key
->nodeName
.length
* sizeof(UniChar
),
4310 FREE(nameptr
, M_TEMP
);
4311 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
4313 result
= utf8_encodestr(key
->nodeName
.unicode
,
4314 key
->nodeName
.length
* sizeof(UniChar
),
4315 nameptr
, (size_t *)&utf8len
,
4318 descp
->cd_parentcnid
= key
->parentID
;
4319 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4320 descp
->cd_namelen
= utf8len
;
4321 descp
->cd_cnid
= cnid
;
4322 descp
->cd_hint
= hint
;
4323 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4325 descp
->cd_flags
|= CD_ISDIR
;
4326 descp
->cd_encoding
= encoding
;
4327 if (nameptr
!= &tmpbuff
[0]) {
4328 FREE(nameptr
, M_TEMP
);
4335 * getbsdattr - get attributes in bsd format
4339 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4341 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4342 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4344 attrp
->ca_recflags
= crp
->flags
;
4345 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4346 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4347 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4348 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4349 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4350 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4352 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4353 attrp
->ca_flags
= 0;
4354 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4355 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4357 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4359 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4361 attrp
->ca_linkcount
= 1;
4364 attrp
->ca_linkcount
= 1; /* may be overridden below */
4366 attrp
->ca_uid
= bsd
->ownerID
;
4367 attrp
->ca_gid
= bsd
->groupID
;
4368 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4369 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4370 switch (attrp
->ca_mode
& S_IFMT
) {
4371 case S_IFCHR
: /* fall through */
4373 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4376 case S_IFDIR
: /* fall through */
4378 /* Pick up the hard link count */
4379 if (bsd
->special
.linkCount
> 0)
4380 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4385 * Override the permissions as determined by the mount auguments
4386 * in ALMOST the same way unset permissions are treated but keep
4387 * track of whether or not the file or folder is hfs locked
4388 * by leaving the h_pflags field unchanged from what was unpacked
4389 * out of the catalog.
4392 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4393 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4394 * at the VFS layer, so there is no need to do it here now; this also
4395 * allows VFS to let root see the real UIDs.
4397 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4398 * attrp->ca_uid = hfsmp->hfs_uid;
4399 * attrp->ca_gid = hfsmp->hfs_gid;
4405 if (!S_ISDIR(attrp
->ca_mode
)) {
4406 attrp
->ca_mode
&= ~S_IFMT
;
4407 attrp
->ca_mode
|= S_IFDIR
;
4409 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4410 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4411 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4413 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4414 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4415 attrp
->ca_flags
|= UF_HIDDEN
;
4417 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4418 if (crp
->flags
& kHFSFileLockedMask
) {
4419 /* The file's supposed to be locked:
4420 Make sure at least one of the IMMUTABLE bits is set: */
4421 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4422 attrp
->ca_flags
|= UF_IMMUTABLE
;
4424 /* The file's supposed to be unlocked: */
4425 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4427 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4428 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4429 attrp
->ca_flags
|= UF_HIDDEN
;
4430 /* get total blocks (both forks) */
4431 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4433 /* On HFS+ the ThreadExists flag must always be set. */
4434 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4435 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4437 /* Pick up the hardlink first link, if any. */
4438 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4441 attrp
->ca_fileid
= crp
->fileID
;
4443 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4448 * promotekey - promote hfs key to hfs plus key
4452 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4453 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4455 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4459 *encoding
= hfsmp
->hfs_encoding
;
4461 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4462 kHFSPlusMaxFileNameChars
, &uniCount
);
4464 * When an HFS name cannot be encoded with the current
4465 * encoding use MacRoman as a fallback.
4467 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4469 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4470 keyp
->nodeName
.unicode
,
4471 kHFSPlusMaxFileNameChars
,
4475 keyp
->nodeName
.length
= uniCount
;
4476 keyp
->parentID
= hfskey
->parentID
;
4480 * promotefork - promote hfs fork info to hfs plus
4484 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4485 int resource
, struct cat_fork
* forkp
)
4487 struct HFSPlusExtentDescriptor
*xp
;
4488 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4490 bzero(forkp
, sizeof(*forkp
));
4491 xp
= &forkp
->cf_extents
[0];
4493 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4494 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4495 forkp
->cf_bytesread
= 0;
4496 forkp
->cf_vblocks
= 0;
4497 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4498 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4499 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4500 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4501 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4502 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4504 forkp
->cf_size
= filep
->dataLogicalSize
;
4505 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4506 forkp
->cf_bytesread
= 0;
4507 forkp
->cf_vblocks
= 0;
4508 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4509 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4510 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4511 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4512 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4513 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4518 * promoteattr - promote standard hfs catalog attributes to hfs plus
4522 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4524 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4526 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4527 const struct HFSCatalogFolder
* folder
;
4529 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4530 crp
->recordType
= kHFSPlusFolderRecord
;
4531 crp
->flags
= folder
->flags
;
4532 crp
->fileID
= folder
->folderID
;
4533 crp
->createDate
= LocalToUTC(folder
->createDate
);
4534 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4535 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4536 crp
->reserved1
= folder
->valence
;
4538 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4540 const struct HFSCatalogFile
* file
;
4542 file
= (const struct HFSCatalogFile
*) dataPtr
;
4543 crp
->recordType
= kHFSPlusFileRecord
;
4544 crp
->flags
= file
->flags
;
4545 crp
->fileID
= file
->fileID
;
4546 crp
->createDate
= LocalToUTC(file
->createDate
);
4547 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4548 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4551 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4552 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4553 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4554 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4556 crp
->textEncoding
= 0;
4557 crp
->attributeModDate
= crp
->contentModDate
;
4558 crp
->accessDate
= crp
->contentModDate
;
4559 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4564 * Build a catalog node thread record from a catalog key
4565 * and return the size of the record.
4568 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4573 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4574 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4576 size
= sizeof(HFSPlusCatalogThread
);
4578 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4580 rec
->recordType
= kHFSPlusFileThreadRecord
;
4582 rec
->parentID
= key
->parentID
;
4583 bcopy(&key
->nodeName
, &rec
->nodeName
,
4584 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4586 /* HFS Plus has variable sized thread records */
4587 size
-= (sizeof(rec
->nodeName
.unicode
) -
4588 (rec
->nodeName
.length
* sizeof(UniChar
)));
4593 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4594 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4596 size
= sizeof(HFSCatalogThread
);
4599 rec
->recordType
= kHFSFolderThreadRecord
;
4601 rec
->recordType
= kHFSFileThreadRecord
;
4602 rec
->parentID
= key
->parentID
;
4603 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4612 * Build a catalog node thread key.
4615 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4618 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4619 key
->hfsPlus
.parentID
= parentID
;
4620 key
->hfsPlus
.nodeName
.length
= 0;
4624 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4625 key
->hfs
.reserved
= 0;
4626 key
->hfs
.parentID
= parentID
;
4627 key
->hfs
.nodeName
[0] = 0;
4634 * Extract the text encoding from a catalog node record.
4637 getencoding(const CatalogRecord
*crp
)
4641 if (crp
->recordType
== kHFSPlusFolderRecord
)
4642 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4643 else if (crp
->recordType
== kHFSPlusFileRecord
)
4644 encoding
= crp
->hfsPlusFile
.textEncoding
;
4652 * Extract the CNID from a catalog node record.
4655 getcnid(const CatalogRecord
*crp
)
4659 switch (crp
->recordType
) {
4662 case kHFSFolderRecord
:
4663 cnid
= crp
->hfsFolder
.folderID
;
4665 case kHFSFileRecord
:
4666 cnid
= crp
->hfsFile
.fileID
;
4670 case kHFSPlusFolderRecord
:
4671 cnid
= crp
->hfsPlusFolder
.folderID
;
4673 case kHFSPlusFileRecord
:
4674 cnid
= crp
->hfsPlusFile
.fileID
;
4677 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4685 * Extract the parent ID from a catalog node record.
4688 getparentcnid(const CatalogRecord
*recp
)
4692 switch (recp
->recordType
) {
4695 case kHFSFileThreadRecord
:
4696 case kHFSFolderThreadRecord
:
4697 cnid
= recp
->hfsThread
.parentID
;
4701 case kHFSPlusFileThreadRecord
:
4702 case kHFSPlusFolderThreadRecord
:
4703 cnid
= recp
->hfsPlusThread
.parentID
;
4706 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4714 * Determine if a catalog node record is a directory.
4717 isadir(const CatalogRecord
*crp
)
4719 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4723 if (crp
->recordType
== kHFSFolderRecord
) {
4732 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4733 * (not inode) using catalog record id. Note that this function does
4734 * NOT resolve directory hard link to its directory inode and return
4737 * Note: The caller is responsible for releasing the output catalog
4738 * descriptor (when supplied outdescp is non-null).
4741 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4742 u_int8_t forktype
, struct cat_desc
*outdescp
,
4743 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4745 struct BTreeIterator
*iterator
= NULL
;
4746 FSBufferDescriptor btdata
;
4749 CatalogRecord
*recp
= NULL
;
4752 /* No directory hard links on standard HFS */
4753 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4757 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4758 if (iterator
== NULL
) {
4761 bzero(iterator
, sizeof(*iterator
));
4762 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4764 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4769 BDINIT(btdata
, recp
);
4771 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4772 &btdata
, &datasize
, iterator
);
4776 /* Directory hard links are catalog file record */
4777 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4782 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4783 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4784 (keyp
->hfsPlus
.nodeName
.length
* 2);
4785 if (forktype
== kHFSResourceForkType
) {
4786 /* Lookup resource fork for directory hard link */
4787 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4789 /* Lookup data fork, if any, for directory hard link */
4790 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4793 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4794 hfs_mark_volume_inconsistent(hfsmp
);
4797 /* Just for sanity, make sure that id in catalog record and thread record match */
4798 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4799 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4800 hfs_mark_volume_inconsistent(hfsmp
);
4808 FREE(iterator
, M_TEMP
);
4810 return MacToVFSError(error
);
4814 * cnode_update_dirlink - update the catalog node for directory hard link
4815 * described by descp using the data from attrp and forkp.
4818 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4819 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4821 if (forktype
== kHFSResourceForkType
) {
4822 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4824 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);