2 * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
33 #include <sys/mount.h>
34 #include <sys/vnode.h>
35 #include <sys/dirent.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
39 #include <sys/utfconv.h>
42 #include "hfs_catalog.h"
43 #include "hfs_format.h"
44 #include "hfs_endian.h"
46 #include "hfscommon/headers/BTreesInternal.h"
47 #include "hfscommon/headers/BTreesPrivate.h"
48 #include "hfscommon/headers/HFSUnicodeWrappers.h"
52 * Initialization of an FSBufferDescriptor structure.
54 #define BDINIT(bd, addr) { \
55 (bd).bufferAddress = (addr); \
56 (bd).itemSize = sizeof(*(addr)); \
62 BTreeIterator iterator
;
63 HFSPlusCatalogKey key
;
68 struct cat_desc
* s_desc
;
69 struct cat_attr
* s_attr
;
70 const struct cat_fork
* s_datafork
;
71 const struct cat_fork
* s_rsrcfork
;
72 struct hfsmount
* s_hfsmp
;
75 struct position_state
{
80 struct hfsmount
*hfsmp
;
83 /* Map file mode type to directory entry types */
84 u_char modetodirtype
[16] = {
85 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
86 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
87 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
88 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
90 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
93 #define HFS_LOOKUP_SYSFILE 0x1 /* If set, allow lookup of system files */
94 #define HFS_LOOKUP_HARDLINK 0x2 /* If set, allow lookup of hard link records and not resolve the hard links */
95 #define HFS_LOOKUP_CASESENSITIVE 0x4 /* If set, verify results of a file/directory record match input case */
96 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
97 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
99 int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
100 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
102 /* Internal catalog support routines */
104 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
105 struct position_state
*state
);
107 static int resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
);
109 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
111 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
112 HFSPlusCatalogKey
*key
, int retry
);
114 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
116 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, u_int32_t
*recordSize
);
118 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
120 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
121 int isdir
, struct cat_desc
*descp
);
123 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
126 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
);
127 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
128 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
131 static cnid_t
getcnid(const CatalogRecord
*crp
);
132 static u_int32_t
getencoding(const CatalogRecord
*crp
);
133 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
135 static int isadir(const CatalogRecord
*crp
);
137 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
139 static int cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
);
141 static int cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
142 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
);
146 /* HFS ID Hashtable Functions */
147 #define IDHASH(hfsmp, inum) (&hfsmp->hfs_idhashtbl[(inum) & hfsmp->hfs_idhash])
149 /* Initialize the HFS ID hash table */
151 hfs_idhash_init (struct hfsmount
*hfsmp
) {
152 /* secured by catalog lock so no lock init needed */
153 hfsmp
->hfs_idhashtbl
= hashinit(HFS_IDHASH_DEFAULT
, M_HFSMNT
, &hfsmp
->hfs_idhash
);
156 /* Free the HFS ID hash table */
158 hfs_idhash_destroy (struct hfsmount
*hfsmp
) {
159 /* during failed mounts & unmounts */
160 FREE(hfsmp
->hfs_idhashtbl
, M_HFSMNT
);
165 typedef struct cat_preflightid {
167 LIST_ENTRY(cat_preflightid) id_hash;
171 u_long hfs_idhash; / size of cnid/fileid hash table -1 /
172 LIST_HEAD(idhashhead, cat_preflightid) *hfs_idhashtbl; / base of ID hash /
176 * Check the run-time ID hashtable.
178 * The catalog lock must be held (like other functions in this file).
181 * 1 if the ID is in the hash table.
182 * 0 if the ID is not in the hash table
184 int cat_check_idhash (struct hfsmount
*hfsmp
, cnid_t test_fileid
) {
186 cat_preflightid_t
*preflight
;
189 for (preflight
= IDHASH(hfsmp
, test_fileid
)->lh_first
; preflight
; preflight
= preflight
->id_hash
.le_next
) {
190 if (preflight
->fileid
== test_fileid
) {
199 /* Insert the supplied preflight into the ID hash table */
200 int cat_insert_idhash (struct hfsmount
*hfsmp
, cat_preflightid_t
*preflight
) {
203 LIST_INSERT_HEAD(IDHASH(hfsmp
, (preflight
->fileid
)), preflight
, id_hash
);
210 /* Remove the data structure with the specified ID from the hashtable */
211 int cat_remove_idhash (cat_preflightid_t
*preflight
) {
213 if ((preflight
) && ((preflight
->id_hash
.le_next
|| preflight
->id_hash
.le_prev
))) {
214 LIST_REMOVE (preflight
, id_hash
);
215 preflight
->id_hash
.le_next
= NULL
;
216 preflight
->id_hash
.le_prev
= NULL
;
225 * Acquire a new CNID for use.
227 * This is slightly more complicated than just pulling the value from the
228 * hfsmount data structure. We need to validate that the ID is not in-use
229 * even if we've not wrapped around and that there are not any lingering
230 * or orphaned fileIDs for this ID.
232 * Also validate that there are not any pending insertions into the
233 * catalog by checking the ID hash table.
236 cat_acquire_cnid (struct hfsmount
*hfsmp
, cnid_t
*new_cnid
)
239 struct BTreeIterator
*iterator
;
240 FSBufferDescriptor btdata
;
247 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
249 * Get the next CNID. We can change it since we hold the catalog lock.
252 nextCNID
= hfsmp
->vcbNxtCNID
;
253 if (nextCNID
== 0xFFFFFFFF) {
259 /* don't allow more than one wrap-around */
262 hfs_lock_mount (hfsmp
);
263 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
264 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
265 hfs_unlock_mount (hfsmp
);
272 /* First check that there are not any entries pending in the hash table with this ID */
273 if (cat_check_idhash (hfsmp
, nextCNID
)) {
274 /* Someone wants to insert this into the catalog but hasn't done so yet. Skip it */
278 /* Check to see if a thread record exists for the target ID we just got */
279 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
280 bzero(iterator
, sizeof(*iterator
));
281 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&iterator
->key
);
283 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
284 BDINIT(btdata
, recp
);
286 result
= BTSearchRecord(hfsmp
->hfs_catalog_cp
->c_datafork
, iterator
, &btdata
, &datasize
, iterator
);
288 FREE (iterator
, M_TEMP
);
290 if (result
== btNotFound
) {
291 /* Good. File ID was not in use. Move on to checking EA B-Tree */
292 result
= file_attribute_exist (hfsmp
, nextCNID
);
293 if (result
== EEXIST
) {
294 /* This CNID has orphaned EAs. Skip it and move on to the next one */
299 /* For any other error, return the result */
304 * Now validate that there are no lingering cnodes with this ID. If a cnode
305 * has been removed on-disk (marked C_NOEXISTS), but has not yet been reclaimed,
306 * then it will still have an entry in the cnode hash table. This means that
307 * a subsequent lookup will find THAT entry and believe this one has been deleted
308 * prematurely. If there is a lingering cnode, then just skip this entry and move on.
310 * Note that we pass (existence_only == 1) argument to hfs_chash_snoop.
312 if (!std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
313 if (hfs_chash_snoop (hfsmp
, nextCNID
, 1, NULL
, NULL
) == 0) {
319 * If we get here, then we didn't see any thread records, orphaned EAs,
320 * or stale cnodes. This ID is safe to vend out.
322 *new_cnid
= nextCNID
;
324 else if (result
== noErr
) {
325 /* move on to the next ID */
329 /* For any other situation, just bail out */
338 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, __unused proc_t p
)
343 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
344 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
346 result
= BTReserveSpace(hfsmp
->hfs_catalog_cp
->c_datafork
, ops
, (void*)cookie
);
349 hfs_systemfile_unlock(hfsmp
, lockflags
);
351 return MacToVFSError(result
);
355 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, __unused proc_t p
)
359 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
360 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
362 (void) BTReleaseReserve(hfsmp
->hfs_catalog_cp
->c_datafork
, (void*)cookie
);
365 hfs_systemfile_unlock(hfsmp
, lockflags
);
371 struct hfsmount
*hfsmp
,
372 CatalogRecord
* recp
,
373 struct cat_attr
*attrp
,
374 struct cat_fork
*datafp
,
375 struct cat_fork
*rsrcfp
)
377 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
380 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
384 struct HFSPlusCatalogFile cnoderec
;
386 promoteattr(hfsmp
, recp
, &cnoderec
);
387 getbsdattr(hfsmp
, &cnoderec
, attrp
);
392 bzero(datafp
, sizeof(*datafp
));
396 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
397 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
401 /* Convert the data fork. */
402 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
403 datafp
->cf_new_size
= 0;
404 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
405 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
406 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
407 datafp
->cf_bytesread
=
408 recp
->hfsPlusFile
.dataFork
.clumpSize
*
409 HFSTOVCB(hfsmp
)->blockSize
;
411 datafp
->cf_bytesread
= 0;
413 datafp
->cf_vblocks
= 0;
414 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
415 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
417 /* Convert the resource fork. */
418 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
419 rsrcfp
->cf_new_size
= 0;
420 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
421 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
422 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
423 datafp
->cf_bytesread
=
424 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
425 HFSTOVCB(hfsmp
)->blockSize
;
427 datafp
->cf_bytesread
= 0;
429 rsrcfp
->cf_vblocks
= 0;
430 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
431 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
436 * Convert a raw catalog key and record into an in-core catalog descriptor.
438 * Note: The caller is responsible for releasing the catalog descriptor.
443 struct hfsmount
*hfsmp
,
445 CatalogRecord
* recp
,
446 struct cat_desc
*descp
)
448 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
449 HFSPlusCatalogKey
* pluskey
= NULL
;
455 pluskey
= (HFSPlusCatalogKey
*)key
;
456 encoding
= getencoding(recp
);
460 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
461 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
465 /* Get the CNID before calling builddesc. Need to error check it. */
466 cnid
= getcnid(recp
);
468 /* If ths CNID == 0, it's invalid. Mark as corrupt */
469 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
473 builddesc(pluskey
, cnid
, 0, encoding
, isadir(recp
), descp
);
478 FREE(pluskey
, M_TEMP
);
491 cat_releasedesc(struct cat_desc
*descp
)
493 const u_int8_t
* name
;
498 if ((descp
->cd_flags
& CD_HASBUF
) &&
499 (descp
->cd_nameptr
!= NULL
)) {
500 name
= descp
->cd_nameptr
;
501 descp
->cd_nameptr
= NULL
;
502 descp
->cd_namelen
= 0;
503 vfs_removename((const char *)name
);
505 descp
->cd_nameptr
= NULL
;
506 descp
->cd_namelen
= 0;
507 descp
->cd_flags
&= ~CD_HASBUF
;
511 * These Catalog functions allow access to the HFS Catalog (database).
512 * The catalog b-tree lock must be acquired before calling any of these routines.
516 * cat_lookup - lookup a catalog node using a cnode descriptor
518 * Note: The caller is responsible for releasing the output
519 * catalog descriptor (when supplied outdescp is non-null).
522 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
, int force_casesensitive_lookup
,
523 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
524 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
531 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
532 flags
= force_casesensitive_lookup
? HFS_LOOKUP_CASESENSITIVE
: 0;
534 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
536 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
540 result
= cat_lookupbykey(hfsmp
, keyp
, flags
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
542 if (result
== ENOENT
) {
544 struct cat_desc temp_desc
;
545 if (outdescp
== NULL
) {
546 bzero(&temp_desc
, sizeof(temp_desc
));
547 outdescp
= &temp_desc
;
549 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
551 *desc_cnid
= outdescp
->cd_cnid
;
553 if (outdescp
== &temp_desc
) {
554 /* Release the local copy of desc */
555 cat_releasedesc(outdescp
);
557 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
558 // make MacRoman key from utf-8
559 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
560 // update desc text encoding so that other catalog ops succeed
570 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
572 struct BTreeIterator
*iterator
;
573 struct FSBufferDescriptor file_data
;
574 struct HFSCatalogFile file_rec
;
579 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
582 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
584 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
585 bzero(&iterator
[0], 2* sizeof(*iterator
));
586 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
590 BDINIT(file_data
, &file_rec
);
591 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
595 if (file_rec
.recordType
!= kHFSFileRecord
) {
600 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
601 struct FSBufferDescriptor thread_data
;
602 struct HFSCatalogThread thread_rec
;
604 file_rec
.flags
|= kHFSThreadExistsMask
;
605 BDINIT(thread_data
, &thread_rec
);
606 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
607 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
609 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
613 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
614 (void) BTFlushPath(fcb
);
617 (void) BTFlushPath(fcb
);
618 FREE(iterator
, M_TEMP
);
620 return MacToVFSError(result
);
625 * cat_findname - obtain a descriptor from cnid
627 * Only a thread lookup is performed.
629 * Note: The caller is responsible for releasing the output
630 * catalog descriptor (when supplied outdescp is non-null).
634 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
636 struct BTreeIterator
* iterator
;
637 FSBufferDescriptor btdata
;
639 CatalogRecord
* recp
;
645 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
647 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
648 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
649 iterator
->hint
.nodeNum
= 0;
651 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
652 BDINIT(btdata
, recp
);
654 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
658 /* Turn thread record into a cnode key (in place). */
659 switch (recp
->recordType
) {
662 case kHFSFolderThreadRecord
:
665 case kHFSFileThreadRecord
:
666 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
667 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
671 case kHFSPlusFolderThreadRecord
:
674 case kHFSPlusFileThreadRecord
:
675 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
676 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
677 (keyp
->hfsPlus
.nodeName
.length
* 2);
685 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
689 HFSPlusCatalogKey
* pluskey
= NULL
;
692 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
693 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
694 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
695 FREE(pluskey
, M_TEMP
);
701 FREE(iterator
, M_TEMP
);
703 return MacToVFSError(result
);
707 * cat_idlookup - lookup a catalog node using a cnode id
709 * Note: The caller is responsible for releasing the output
710 * catalog descriptor (when supplied outdescp is non-null).
713 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
, int wantrsrc
,
714 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
716 struct BTreeIterator
* iterator
;
717 FSBufferDescriptor btdata
;
720 CatalogRecord
* recp
;
724 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
726 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
727 bzero(iterator
, sizeof(*iterator
));
728 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
730 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
731 BDINIT(btdata
, recp
);
733 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
734 &btdata
, &datasize
, iterator
);
738 /* Turn thread record into a cnode key (in place) */
739 switch (recp
->recordType
) {
742 case kHFSFileThreadRecord
:
743 case kHFSFolderThreadRecord
:
744 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
746 /* check for NULL name */
747 if (keyp
->hfs
.nodeName
[0] == 0) {
752 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
756 case kHFSPlusFileThreadRecord
:
757 case kHFSPlusFolderThreadRecord
:
758 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
760 /* check for NULL name */
761 if (keyp
->hfsPlus
.nodeName
.length
== 0) {
766 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
767 (keyp
->hfsPlus
.nodeName
.length
* 2);
775 result
= cat_lookupbykey(hfsmp
, keyp
,
776 ((allow_system_files
!= 0) ? HFS_LOOKUP_SYSFILE
: 0),
777 0, wantrsrc
, outdescp
, attrp
, forkp
, NULL
);
778 /* No corresponding file/folder record found for a thread record,
779 * mark the volume inconsistent.
781 if (result
== 0 && outdescp
) {
782 cnid_t dcnid
= outdescp
->cd_cnid
;
784 * Just for sanity's case, let's make sure that
785 * the key in the thread matches the key in the record.
788 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
794 FREE(iterator
, M_TEMP
);
796 return MacToVFSError(result
);
801 * cat_lookupmangled - lookup a catalog node using a mangled name
804 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
805 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
810 u_int8_t utf8
[NAME_MAX
+ 1];
812 u_int16_t unicode
[kHFSPlusMaxFileNameChars
+ 1];
818 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
819 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
822 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
823 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
824 fileID
== hfsmp
->hfs_jnlfileid
||
825 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
829 result
= cat_idlookup(hfsmp
, fileID
, 0, 0, outdescp
, attrp
, forkp
);
832 /* It must be in the correct directory */
833 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
837 * Compare the mangled version of file name looked up from the
838 * disk with the mangled name provided by the user. Note that
839 * this comparison is case-sensitive, which should be fine
840 * since we're trying to prevent user space from constructing
841 * a mangled name that differs from the one they'd get from the
844 result
= utf8_decodestr(outdescp
->cd_nameptr
, outdescp
->cd_namelen
,
845 unicode
, &unicodelen
, sizeof(unicode
), ':', 0);
849 result
= ConvertUnicodeToUTF8Mangled(unicodelen
, unicode
,
850 sizeof(utf8
), &utf8len
, utf8
, fileID
);
852 ((u_int16_t
)descp
->cd_namelen
!= utf8len
) ||
853 (bcmp(descp
->cd_nameptr
, utf8
, utf8len
) != 0)) {
860 cat_releasedesc(outdescp
);
866 * cat_lookupbykey - lookup a catalog node using a cnode key
869 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
870 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
872 struct BTreeIterator
* iterator
;
873 FSBufferDescriptor btdata
;
874 CatalogRecord
* recp
;
880 u_int32_t encoding
= 0;
883 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
885 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
886 BDINIT(btdata
, recp
);
887 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
888 bzero(iterator
, sizeof(*iterator
));
889 iterator
->hint
.nodeNum
= hint
;
890 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
892 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
893 &btdata
, &datasize
, iterator
);
897 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
898 cnid
= getcnid(recp
);
900 /* CNID of 0 is invalid. Mark as corrupt */
901 hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
907 parentid
= keyp
->hfsPlus
.parentID
;
910 encoding
= getencoding(recp
);
911 hint
= iterator
->hint
.nodeNum
;
913 /* Hide the journal files (if any) */
914 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
915 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
916 !(flags
& HFS_LOOKUP_SYSFILE
)) {
917 result
= ERESERVEDNAME
;
921 if (!std_hfs
&& !(hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)) {
922 /* Make sure the case of the file was correct if requested */
923 if (flags
& HFS_LOOKUP_CASESENSITIVE
) {
924 if (0 != cat_binarykeycompare(&keyp
->hfsPlus
, (HFSPlusCatalogKey
*)&iterator
->key
)) {
925 result
= ERESERVEDNAME
;
932 * When a hardlink link is encountered, auto resolve it.
934 * The catalog record will change, and possibly its type.
938 && (recp
->recordType
== kHFSPlusFileRecord
)
939 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_itime
) ||
940 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
944 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
945 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
947 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
948 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
949 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
952 if ((isfilelink
|| isdirlink
) && !(flags
& HFS_LOOKUP_HARDLINK
)) {
953 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
954 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
960 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
962 /* Update the inode number for this hard link */
963 attrp
->ca_linkref
= ilink
;
967 * Set kHFSHasLinkChainBit for hard links, and reset it for all
968 * other items. Also set linkCount to 1 for regular files.
970 * Due to some bug (rdar://8505977), some regular files can have
971 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
972 * are not really hard links. The runtime code should not consider
973 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
974 * and linkCount for regular file before we vend it out. This might
975 * also result in repairing the bad files on disk, if the corresponding
976 * file is modified and updated on disk.
979 /* This is a hard link and the link count bit was not set */
980 if (!(attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
981 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp
->vcbVN
, cnid
, ilink
);
982 attrp
->ca_recflags
|= kHFSHasLinkChainMask
;
985 /* Make sure that this non-hard link (regular) record is not
986 * an inode record that was looked up and we do not end up
987 * reseting the hard link bit on it.
989 if ((parentid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
990 (parentid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
991 /* This is not a hard link or inode and the link count bit was set */
992 if (attrp
->ca_recflags
& kHFSHasLinkChainMask
) {
993 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp
->vcbVN
, cnid
);
994 attrp
->ca_recflags
&= ~kHFSHasLinkChainMask
;
996 /* This is a regular file and the link count was more than 1 */
997 if (S_ISREG(attrp
->ca_mode
) && (attrp
->ca_linkcount
> 1)) {
998 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp
->vcbVN
, cnid
, attrp
->ca_linkcount
);
999 attrp
->ca_linkcount
= 1;
1006 struct HFSPlusCatalogFile cnoderec
;
1008 promoteattr(hfsmp
, recp
, &cnoderec
);
1009 getbsdattr(hfsmp
, &cnoderec
, attrp
);
1013 if (forkp
!= NULL
) {
1015 bzero(forkp
, sizeof(*forkp
));
1019 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
1022 else if (wantrsrc
) {
1023 /* Convert the resource fork. */
1024 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
1025 forkp
->cf_new_size
= 0;
1026 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1027 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1028 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1029 forkp
->cf_bytesread
=
1030 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
1031 HFSTOVCB(hfsmp
)->blockSize
;
1033 forkp
->cf_bytesread
= 0;
1035 forkp
->cf_vblocks
= 0;
1036 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
1037 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1040 u_int32_t validblks
;
1042 /* Convert the data fork. */
1043 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
1044 forkp
->cf_new_size
= 0;
1045 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
1046 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
1047 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
1048 forkp
->cf_bytesread
=
1049 recp
->hfsPlusFile
.dataFork
.clumpSize
*
1050 HFSTOVCB(hfsmp
)->blockSize
;
1052 forkp
->cf_bytesread
= 0;
1054 forkp
->cf_vblocks
= 0;
1055 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
1056 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
1058 /* Validate the fork's resident extents. */
1060 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1061 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
1062 /* Suppress any bad extents so a remove can succeed. */
1063 forkp
->cf_extents
[i
].startBlock
= 0;
1064 forkp
->cf_extents
[i
].blockCount
= 0;
1065 /* Disable writes */
1066 if (attrp
!= NULL
) {
1067 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
1070 validblks
+= forkp
->cf_extents
[i
].blockCount
;
1073 /* Adjust for any missing blocks. */
1074 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
1078 * This is technically a volume corruption.
1079 * If the total number of blocks calculated by iterating + summing
1080 * the extents in the resident extent records, is less than that
1081 * which is reported in the catalog entry, we should force a fsck.
1082 * Only modifying ca_blocks here is not guaranteed to make it out
1083 * to disk; it is a runtime-only field.
1085 * Note that we could have gotten into this state if we had invalid ranges
1086 * that existed in borrowed blocks that somehow made it out to disk.
1087 * The cnode's on disk block count should never be greater
1088 * than that which is in its extent records.
1091 (void) hfs_mark_inconsistent (hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1093 forkp
->cf_blocks
= validblks
;
1094 if (attrp
!= NULL
) {
1095 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
1097 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
1098 if (psize
< forkp
->cf_size
) {
1099 forkp
->cf_size
= psize
;
1105 if (descp
!= NULL
) {
1106 HFSPlusCatalogKey
* pluskey
= NULL
;
1109 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
1113 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1114 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
1119 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
1123 FREE(pluskey
, M_TEMP
);
1129 if (desc_cnid
!= NULL
) {
1133 FREE(iterator
, M_TEMP
);
1136 return MacToVFSError(result
);
1141 * cat_create - create a node in the catalog
1143 * NOTE: both the catalog file and attribute file locks must
1144 * be held before calling this function.
1146 * The caller is responsible for releasing the output
1147 * catalog descriptor (when supplied outdescp is non-null).
1150 cat_create(struct hfsmount
*hfsmp
, cnid_t new_fileid
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1151 struct cat_desc
*out_descp
)
1155 FSBufferDescriptor btdata
;
1159 u_int32_t encoding
= kTextEncodingMacRoman
;
1162 modeformat
= attrp
->ca_mode
& S_IFMT
;
1164 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1165 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1167 /* The caller is expected to reserve a CNID before calling this function! */
1169 /* Get space for iterator, key and data */
1170 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
1171 bto
->iterator
.hint
.nodeNum
= 0;
1173 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
1178 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
1179 bto
->key
.nodeName
.length
);
1180 hfs_setencodingbits(hfsmp
, encoding
);
1184 * Insert the thread record first
1186 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
1187 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
1188 S_ISDIR(attrp
->ca_mode
));
1189 btdata
.bufferAddress
= &bto
->data
;
1190 btdata
.itemSize
= datalen
;
1191 btdata
.itemCount
= 1;
1193 /* Caller asserts the following:
1194 * 1) this CNID is not in use by any orphaned EAs
1195 * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
1196 * 3) There are no thread or catalog records for this ID
1198 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
1199 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1206 * Now insert the file/directory record
1208 buildrecord(attrp
, new_fileid
, std_hfs
, encoding
, &bto
->data
, &datalen
);
1209 btdata
.bufferAddress
= &bto
->data
;
1210 btdata
.itemSize
= datalen
;
1211 btdata
.itemCount
= 1;
1213 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
1215 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1217 if (result
== btExists
)
1220 /* Back out the thread record */
1221 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
1222 buildthreadkey(new_fileid
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
1223 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
1224 /* Error on deleting extra thread record, mark
1225 * volume inconsistent
1227 printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid
, hfsmp
->vcbVN
);
1228 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1235 * Insert was successful, update name, parent and volume
1237 if (out_descp
!= NULL
) {
1238 HFSPlusCatalogKey
* pluskey
= NULL
;
1241 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1245 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1246 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1250 builddesc(pluskey
, new_fileid
, bto
->iterator
.hint
.nodeNum
,
1251 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1254 FREE(pluskey
, M_TEMP
);
1259 attrp
->ca_fileid
= new_fileid
;
1262 (void) BTFlushPath(fcb
);
1265 return MacToVFSError(result
);
1270 * cnode_rename - rename a catalog node
1272 * Assumes that the target's directory exists.
1274 * Order of B-tree operations:
1275 * 1. BTSearchRecord(from_cnode, &data);
1276 * 2. BTInsertRecord(to_cnode, &data);
1277 * 3. BTDeleteRecord(from_cnode);
1278 * 4. BTDeleteRecord(from_thread);
1279 * 5. BTInsertRecord(to_thread);
1281 * Note: The caller is responsible for releasing the output
1282 * catalog descriptor (when supplied out_cdp is non-null).
1286 struct hfsmount
* hfsmp
,
1287 struct cat_desc
* from_cdp
,
1288 struct cat_desc
* todir_cdp
,
1289 struct cat_desc
* to_cdp
,
1290 struct cat_desc
* out_cdp
)
1292 struct BTreeIterator
* to_iterator
= NULL
;
1293 struct BTreeIterator
* from_iterator
= NULL
;
1294 FSBufferDescriptor btdata
;
1295 CatalogRecord
* recp
= NULL
;
1296 HFSPlusCatalogKey
* to_key
;
1303 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1306 u_int32_t encoding
= 0;
1308 vcb
= HFSTOVCB(hfsmp
);
1309 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1310 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1312 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1315 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
1316 bzero(from_iterator
, sizeof(*from_iterator
));
1317 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1320 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1321 bzero(to_iterator
, sizeof(*to_iterator
));
1322 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1325 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1326 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1327 BDINIT(btdata
, recp
);
1330 * When moving a directory, make sure its a valid move.
1332 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1333 struct BTreeIterator
*dir_iterator
= NULL
;
1335 cnid_t cnid
= from_cdp
->cd_cnid
;
1336 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1338 /* First check the obvious ones */
1339 if (cnid
== fsRtDirID
||
1340 cnid
== to_cdp
->cd_parentcnid
||
1345 /* now allocate the dir_iterator */
1346 MALLOC (dir_iterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
1347 if (dir_iterator
== NULL
) {
1350 bzero(dir_iterator
, sizeof(*dir_iterator
));
1353 * Traverse destination path all the way back to the root
1354 * making sure that source directory is not encountered.
1357 while (pathcnid
> fsRtDirID
) {
1358 buildthreadkey(pathcnid
, std_hfs
, (CatalogKey
*)&dir_iterator
->key
);
1359 result
= BTSearchRecord(fcb
, dir_iterator
, &btdata
, &datasize
, NULL
);
1361 FREE(dir_iterator
, M_TEMP
);
1364 pathcnid
= getparentcnid(recp
);
1365 if (pathcnid
== cnid
|| pathcnid
== 0) {
1367 FREE(dir_iterator
, M_TEMP
);
1371 FREE(dir_iterator
, M_TEMP
);
1375 * Step 1: Find cnode data at old location
1377 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1378 &datasize
, from_iterator
);
1380 if (std_hfs
|| (result
!= btNotFound
))
1383 struct cat_desc temp_desc
;
1385 /* Probably the node has mangled name */
1386 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1390 /* The file has mangled name. Search the cnode data using full name */
1391 bzero(from_iterator
, sizeof(*from_iterator
));
1392 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1394 cat_releasedesc(&temp_desc
);
1398 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1400 cat_releasedesc(&temp_desc
);
1404 cat_releasedesc(&temp_desc
);
1407 /* Check if the source is directory hard link. We do not change
1408 * directory flag because it is later used to initialize result descp
1412 (recp
->recordType
== kHFSPlusFileRecord
) &&
1413 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1418 * Update the text encoding (on disk and in descriptor).
1420 * Note that hardlink inodes don't require a text encoding hint.
1423 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1424 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1425 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1426 hfs_setencodingbits(hfsmp
, encoding
);
1427 recp
->hfsPlusFile
.textEncoding
= encoding
;
1429 out_cdp
->cd_encoding
= encoding
;
1433 if (std_hfs
&& !directory
&&
1434 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
)) {
1441 * If the keys are identical then there's nothing left to do!
1443 * update the hint and exit
1446 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1448 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1452 /* Step 2: Insert cnode at new location */
1453 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1454 if (result
== btExists
) {
1455 int fromtype
= recp
->recordType
;
1458 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1459 goto exit
; /* EEXIST */
1461 /* Find cnode data at new location */
1462 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1466 /* Get the CNID after calling searchrecord */
1467 cnid
= getcnid (recp
);
1469 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
1474 if ((fromtype
!= recp
->recordType
) ||
1475 (from_cdp
->cd_cnid
!= cnid
)) {
1477 goto exit
; /* EEXIST */
1479 /* The old name is a case variant and must be removed */
1480 result
= BTDeleteRecord(fcb
, from_iterator
);
1484 /* Insert cnode (now that case duplicate is gone) */
1485 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1487 /* Try and restore original before leaving */
1492 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1494 printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err
);
1495 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1501 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1510 /* Step 3: Remove cnode from old location */
1512 result
= BTDeleteRecord(fcb
, from_iterator
);
1514 /* Try and delete new record before leaving */
1519 err
= BTDeleteRecord(fcb
, to_iterator
);
1521 printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err
);
1522 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
1528 (void) BTDeleteRecord(fcb
, to_iterator
);
1534 /* #### POINT OF NO RETURN #### */
1537 * Step 4: Remove cnode's old thread record
1539 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1540 (void) BTDeleteRecord(fcb
, from_iterator
);
1543 * Step 5: Insert cnode's new thread record
1544 * (optional for HFS files)
1547 /* For directory hard links, always create a file thread
1548 * record. For everything else, use the directory flag.
1551 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1553 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1555 btdata
.itemSize
= datasize
;
1556 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1557 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1561 HFSPlusCatalogKey
* pluskey
= NULL
;
1564 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1568 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1569 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1571 /* Save the real encoding hint in the Finder Info (field 4). */
1572 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1575 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1576 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1581 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1582 encoding
, directory
, out_cdp
);
1585 FREE(pluskey
, M_TEMP
);
1591 (void) BTFlushPath(fcb
);
1593 FREE(from_iterator
, M_TEMP
);
1595 FREE(to_iterator
, M_TEMP
);
1598 return MacToVFSError(result
);
1603 * cat_delete - delete a node from the catalog
1605 * Order of B-tree operations:
1606 * 1. BTDeleteRecord(cnode);
1607 * 2. BTDeleteRecord(thread);
1608 * 3. BTUpdateRecord(parent);
1611 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1614 BTreeIterator
*iterator
;
1619 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1620 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1624 * The root directory cannot be deleted
1625 * A directory must be empty
1626 * A file must be zero length (no blocks)
1628 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1629 descp
->cd_parentcnid
== kHFSRootParentID
)
1632 /* XXX Preflight Missing */
1634 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1635 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1636 iterator
->hint
.nodeNum
= 0;
1639 * Derive a key from either the file ID (for a virtual inode)
1640 * or the descriptor.
1642 if (descp
->cd_namelen
== 0) {
1643 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1644 cnid
= attrp
->ca_fileid
;
1646 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1647 cnid
= descp
->cd_cnid
;
1653 result
= BTDeleteRecord(fcb
, iterator
);
1655 if (std_hfs
|| (result
!= btNotFound
))
1658 struct cat_desc temp_desc
;
1660 /* Probably the node has mangled name */
1661 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1665 /* The file has mangled name. Delete the file using full name */
1666 bzero(iterator
, sizeof(*iterator
));
1667 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1668 cnid
= temp_desc
.cd_cnid
;
1670 cat_releasedesc(&temp_desc
);
1674 result
= BTDeleteRecord(fcb
, iterator
);
1676 cat_releasedesc(&temp_desc
);
1680 cat_releasedesc(&temp_desc
);
1683 /* Delete thread record. On error, mark volume inconsistent */
1684 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1685 if (BTDeleteRecord(fcb
, iterator
)) {
1687 printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid
, hfsmp
->vcbVN
);
1688 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
1693 (void) BTFlushPath(fcb
);
1695 return MacToVFSError(result
);
1700 * cat_update_internal - update the catalog node described by descp
1701 * using the data from attrp and forkp.
1702 * If update_hardlink is true, the hard link catalog record is updated
1703 * and not the inode catalog record.
1706 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1707 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1710 BTreeIterator
* iterator
;
1711 struct update_state state
;
1714 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1716 state
.s_desc
= descp
;
1717 state
.s_attr
= attrp
;
1718 state
.s_datafork
= dataforkp
;
1719 state
.s_rsrcfork
= rsrcforkp
;
1720 state
.s_hfsmp
= hfsmp
;
1722 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1723 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1726 * For open-deleted files we need to do a lookup by cnid
1727 * (using thread rec).
1729 * For hard links and if not requested by caller, the target
1730 * of the update is the inode itself (not the link record)
1731 * so a lookup by fileid (i.e. thread rec) is needed.
1733 if ((update_hardlink
== false) &&
1734 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1735 (descp
->cd_namelen
== 0) ||
1736 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1737 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1739 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1744 /* Pass a node hint */
1745 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1747 result
= BTUpdateRecord(fcb
, iterator
,
1748 (IterateCallBackProcPtr
)catrec_update
, &state
);
1752 /* Update the node hint. */
1753 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1756 (void) BTFlushPath(fcb
);
1758 return MacToVFSError(result
);
1762 * cat_update - update the catalog node described by descp
1763 * using the data from attrp and forkp.
1766 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1767 const struct cat_fork
*dataforkp
, const struct cat_fork
*rsrcforkp
)
1769 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1773 * catrec_update - Update the fields of a catalog record
1774 * This is called from within BTUpdateRecord.
1777 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1779 struct cat_desc
*descp
;
1780 struct cat_attr
*attrp
;
1781 const struct cat_fork
*forkp
;
1782 struct hfsmount
*hfsmp
;
1785 descp
= state
->s_desc
;
1786 attrp
= state
->s_attr
;
1787 hfsmp
= state
->s_hfsmp
;
1788 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1790 switch (crp
->recordType
) {
1793 case kHFSFolderRecord
: {
1794 HFSCatalogFolder
*dir
;
1796 dir
= (struct HFSCatalogFolder
*)crp
;
1797 /* Do a quick sanity check */
1798 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1799 (dir
->folderID
!= descp
->cd_cnid
))
1800 return (btNotFound
);
1801 dir
->valence
= attrp
->ca_entries
;
1802 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1803 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1804 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1805 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1806 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1809 case kHFSFileRecord
: {
1810 HFSCatalogFile
*file
;
1813 file
= (struct HFSCatalogFile
*)crp
;
1814 /* Do a quick sanity check */
1815 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1816 (file
->fileID
!= attrp
->ca_fileid
))
1817 return (btNotFound
);
1818 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1819 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1820 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1821 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1822 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1823 if (state
->s_rsrcfork
) {
1824 forkp
= state
->s_rsrcfork
;
1825 file
->rsrcLogicalSize
= forkp
->cf_size
;
1826 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1827 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1828 file
->rsrcExtents
[i
].startBlock
=
1829 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1830 file
->rsrcExtents
[i
].blockCount
=
1831 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1834 if (state
->s_datafork
) {
1835 forkp
= state
->s_datafork
;
1836 file
->dataLogicalSize
= forkp
->cf_size
;
1837 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1838 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1839 file
->dataExtents
[i
].startBlock
=
1840 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1841 file
->dataExtents
[i
].blockCount
=
1842 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1846 /* Synchronize the lock state */
1847 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1848 file
->flags
|= kHFSFileLockedMask
;
1850 file
->flags
&= ~kHFSFileLockedMask
;
1855 case kHFSPlusFolderRecord
: {
1856 HFSPlusCatalogFolder
*dir
;
1858 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1859 /* Do a quick sanity check */
1860 if (dir
->folderID
!= attrp
->ca_fileid
) {
1861 printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir
->folderID
, attrp
->ca_fileid
, hfsmp
->vcbVN
);
1862 return (btNotFound
);
1864 dir
->flags
= attrp
->ca_recflags
;
1865 dir
->valence
= attrp
->ca_entries
;
1866 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1867 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1868 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1869 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1870 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1871 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1872 /* Note: directory hardlink inodes don't require a text encoding hint. */
1873 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1874 dir
->textEncoding
= descp
->cd_encoding
;
1876 dir
->folderCount
= attrp
->ca_dircount
;
1877 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1879 * Update the BSD Info if it was already initialized on
1880 * disk or if the runtime values have been modified.
1882 * If the BSD info was already initialized, but
1883 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1884 * probably different than what was on disk. We don't want
1885 * to overwrite the on-disk values (so if we turn off
1886 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1887 * This way, we can still change fields like the mode or
1888 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1890 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1891 * won't change the uid or gid from their defaults. So, if
1892 * the BSD info wasn't set, and the runtime values are not
1893 * default, then what changed was the mode or flags. We
1894 * have to set the uid and gid to something, so use the
1895 * supplied values (which will be default), which has the
1896 * same effect as creating a new file while
1897 * MNT_UNKNOWNPERMISSIONS is set.
1899 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1900 (attrp
->ca_flags
!= 0) ||
1901 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1902 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1903 ((attrp
->ca_mode
& ALLPERMS
) !=
1904 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1905 if ((dir
->bsdInfo
.fileMode
== 0) ||
1906 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1907 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1908 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1910 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1911 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1912 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1913 /* A directory hardlink has a link count. */
1914 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1915 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1920 case kHFSPlusFileRecord
: {
1921 HFSPlusCatalogFile
*file
;
1924 file
= (struct HFSPlusCatalogFile
*)crp
;
1925 /* Do a quick sanity check */
1926 if (file
->fileID
!= attrp
->ca_fileid
)
1927 return (btNotFound
);
1928 file
->flags
= attrp
->ca_recflags
;
1929 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1930 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1931 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1932 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1933 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1934 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1936 * Note: file hardlink inodes don't require a text encoding
1937 * hint, but they do have a first link value.
1939 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1940 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1942 file
->textEncoding
= descp
->cd_encoding
;
1944 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1946 * Update the BSD Info if it was already initialized on
1947 * disk or if the runtime values have been modified.
1949 * If the BSD info was already initialized, but
1950 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1951 * probably different than what was on disk. We don't want
1952 * to overwrite the on-disk values (so if we turn off
1953 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1954 * This way, we can still change fields like the mode or
1955 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1957 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1958 * won't change the uid or gid from their defaults. So, if
1959 * the BSD info wasn't set, and the runtime values are not
1960 * default, then what changed was the mode or flags. We
1961 * have to set the uid and gid to something, so use the
1962 * supplied values (which will be default), which has the
1963 * same effect as creating a new file while
1964 * MNT_UNKNOWNPERMISSIONS is set.
1966 * Do not modify bsdInfo for directory hard link records.
1967 * They are set during creation and are not modifiable, so just
1970 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1971 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1972 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1975 ((file
->bsdInfo
.fileMode
!= 0) ||
1976 (attrp
->ca_flags
!= 0) ||
1977 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1978 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1979 ((attrp
->ca_mode
& ALLPERMS
) !=
1980 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1981 if ((file
->bsdInfo
.fileMode
== 0) ||
1982 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1983 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1984 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1986 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1987 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1988 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1990 if (state
->s_rsrcfork
) {
1991 forkp
= state
->s_rsrcfork
;
1992 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1993 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1994 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1995 sizeof(HFSPlusExtentRecord
));
1996 /* Push blocks read to disk */
1997 file
->resourceFork
.clumpSize
=
1998 howmany(forkp
->cf_bytesread
, blksize
);
2000 if (state
->s_datafork
) {
2001 forkp
= state
->s_datafork
;
2002 file
->dataFork
.logicalSize
= forkp
->cf_size
;
2003 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
2004 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
2005 sizeof(HFSPlusExtentRecord
));
2006 /* Push blocks read to disk */
2007 file
->dataFork
.clumpSize
=
2008 howmany(forkp
->cf_bytesread
, blksize
);
2011 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
2012 (file
->resourceFork
.extents
[0].startBlock
==
2013 file
->dataFork
.extents
[0].startBlock
)) {
2014 panic("hfs: catrec_update: rsrc fork == data fork");
2017 /* Synchronize the lock state */
2018 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
2019 file
->flags
|= kHFSFileLockedMask
;
2021 file
->flags
&= ~kHFSFileLockedMask
;
2023 /* Push out special field if necessary */
2024 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
2025 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
2029 * Protect against the degenerate case where the descriptor contains the
2030 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
2031 * the linkcount was greater than 1 (the default value), then it must have become
2032 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
2034 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
2035 (file
->hl_linkCount
> 1)) {
2036 file
->hl_linkCount
= attrp
->ca_linkcount
;
2042 return (btNotFound
);
2047 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
2048 * catalog btree of given cnid by walking up the parent chain till it reaches
2049 * either the root folder, or the private metadata directory for storing
2050 * directory hard links. This function updates the corresponding in-core
2051 * cnode, if any, and the directory record in the catalog btree.
2052 * On success, returns zero. On failure, returns non-zero value.
2056 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
2060 struct cat_desc desc
;
2061 struct cat_attr attr
;
2063 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
2064 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
2065 /* Update the bit in corresponding cnode, if any, in the hash.
2066 * If the cnode has the bit already set, stop the traversal.
2068 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
2073 /* Update the catalog record on disk if either cnode was not
2074 * found in the hash, or if a cnode was found and the cnode
2075 * did not have the bit set previously.
2077 retval
= hfs_start_transaction(hfsmp
);
2081 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2083 /* Look up our catalog folder record */
2084 retval
= cat_idlookup(hfsmp
, cnid
, 0, 0, &desc
, &attr
, NULL
);
2086 hfs_systemfile_unlock(hfsmp
, lockflags
);
2087 hfs_end_transaction(hfsmp
);
2091 /* Update the bit in the catalog record */
2092 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
2093 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
2095 hfs_systemfile_unlock(hfsmp
, lockflags
);
2096 hfs_end_transaction(hfsmp
);
2097 cat_releasedesc(&desc
);
2101 hfs_systemfile_unlock(hfsmp
, lockflags
);
2102 hfs_end_transaction(hfsmp
);
2104 cnid
= desc
.cd_parentcnid
;
2105 cat_releasedesc(&desc
);
2111 /* This function traverses the parent directory hierarchy from the given
2112 * directory to one level below root directory and checks if any of its
2114 * 1. A directory hard link.
2115 * 2. The 'pointed at' directory.
2116 * If any of these conditions fail or an internal error is encountered
2117 * during look up of the catalog record, this function returns non-zero value.
2121 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
2123 HFSPlusCatalogKey
*keyp
;
2125 FSBufferDescriptor btdata
;
2126 HFSPlusCatalogFolder folder
;
2132 BDINIT(btdata
, &folder
);
2133 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
2134 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
2135 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2137 while (cnid
!= kHFSRootParentID
) {
2138 /* Check if the 'pointed at' directory is an ancestor */
2139 if (pointed_at_cnid
== cnid
) {
2143 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
2144 printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2145 invalid
= 1; /* On errors, assume an invalid parent */
2148 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
2149 printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid
, hfsmp
->vcbVN
);
2150 invalid
= 1; /* On errors, assume an invalid parent */
2153 /* Check if this ancestor is a directory hard link */
2154 if (folder
.flags
& kHFSHasLinkChainMask
) {
2158 cnid
= keyp
->parentID
;
2166 * update_siblinglinks_callback - update a link's chain
2169 struct linkupdate_state
{
2176 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
2178 HFSPlusCatalogFile
*file
;
2180 if (crp
->recordType
!= kHFSPlusFileRecord
) {
2181 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
2182 return (btNotFound
);
2185 file
= (struct HFSPlusCatalogFile
*)crp
;
2186 if (file
->flags
& kHFSHasLinkChainMask
) {
2187 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
2188 file
->hl_prevLinkID
= state
->prevlinkid
;
2190 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
2191 file
->hl_nextLinkID
= state
->nextlinkid
;
2194 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
2200 * cat_update_siblinglinks - update a link's chain
2203 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
2206 BTreeIterator
* iterator
;
2207 struct linkupdate_state state
;
2210 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2211 state
.filelinkid
= linkfileid
;
2212 state
.prevlinkid
= prevlinkid
;
2213 state
.nextlinkid
= nextlinkid
;
2215 /* Create an iterator for use by us temporarily */
2216 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2217 bzero(iterator
, sizeof(*iterator
));
2219 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
2221 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
2222 (void) BTFlushPath(fcb
);
2224 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid
, hfsmp
->vcbVN
);
2227 FREE (iterator
, M_TEMP
);
2228 return MacToVFSError(result
);
2232 * cat_lookuplink - lookup a link by it's name
2235 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2238 BTreeIterator
* iterator
;
2239 struct FSBufferDescriptor btdata
;
2240 struct HFSPlusCatalogFile file
;
2243 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2245 /* Create an iterator for use by us temporarily */
2246 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2247 bzero(iterator
, sizeof(*iterator
));
2249 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2252 BDINIT(btdata
, &file
);
2254 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2257 if (file
.recordType
!= kHFSPlusFileRecord
) {
2261 *linkfileid
= file
.fileID
;
2263 if (file
.flags
& kHFSHasLinkChainMask
) {
2264 *prevlinkid
= file
.hl_prevLinkID
;
2265 *nextlinkid
= file
.hl_nextLinkID
;
2271 FREE(iterator
, M_TEMP
);
2272 return MacToVFSError(result
);
2277 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2280 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2283 BTreeIterator
* iterator
;
2284 struct FSBufferDescriptor btdata
;
2285 struct HFSPlusCatalogFile file
;
2288 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2290 /* Create an iterator for use by us temporarily */
2291 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2292 bzero(iterator
, sizeof(*iterator
));
2294 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2297 BDINIT(btdata
, &file
);
2299 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2302 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2303 if (file
.flags
& kHFSHasLinkChainMask
) {
2306 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2308 /* directory inodes don't have a chain (its in an EA) */
2309 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2310 result
= ENOLINK
; /* signal to caller to get head of list */
2311 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2313 *nextlinkid
= file
.hl_firstLinkID
;
2315 *prevlinkid
= file
.hl_prevLinkID
;
2316 *nextlinkid
= file
.hl_nextLinkID
;
2323 FREE(iterator
, M_TEMP
);
2324 return MacToVFSError(result
);
2329 * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
2332 cat_lookup_lastlink(struct hfsmount
*hfsmp
, cnid_t linkfileid
,
2333 cnid_t
*lastlink
, struct cat_desc
*cdesc
)
2336 BTreeIterator
* iterator
;
2337 struct FSBufferDescriptor btdata
;
2338 struct HFSPlusCatalogFile file
;
2342 cnid_t currentlink
= linkfileid
;
2344 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2346 /* Create an iterator for use by us temporarily */
2347 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2349 while ((foundlast
== 0) && (itercount
< HFS_LINK_MAX
)) {
2351 bzero(iterator
, sizeof(*iterator
));
2353 if ((result
= getkey(hfsmp
, currentlink
, (CatalogKey
*)&iterator
->key
))) {
2356 BDINIT(btdata
, &file
);
2358 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2362 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2363 if (file
.flags
& kHFSHasLinkChainMask
) {
2366 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2368 * The raw inode for a directory hardlink doesn't have a chain.
2369 * Its link information lives in an EA.
2371 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2372 /* We don't iterate to find the oldest directory hardlink. */
2376 else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2377 /* Raw inode for file hardlink (the base inode) */
2378 currentlink
= file
.hl_firstLinkID
;
2381 * One minor special-casing here is necessary.
2382 * If our ID brought us to the raw hardlink inode, and it does
2383 * not have any siblings, then it's an open-unlinked file, and we
2384 * should not proceed any further.
2386 if (currentlink
== 0) {
2392 /* Otherwise, this item's parent is a legitimate directory in the namespace */
2393 if (file
.hl_nextLinkID
== 0) {
2394 /* If nextLinkID is 0, then we found the end; no more hardlinks */
2396 *lastlink
= currentlink
;
2398 * Since we had to construct a catalog key to do this lookup
2399 * we still hold it in-hand. We might as well use it to build
2400 * the descriptor that the caller asked for.
2402 builddesc ((HFSPlusCatalogKey
*)&iterator
->key
, currentlink
, 0, 0, 0, cdesc
);
2406 currentlink
= file
.hl_nextLinkID
;
2410 /* Sorry, can't help you without a link chain */
2416 /* If we didn't find what we were looking for, zero out the args */
2417 if (foundlast
== 0) {
2419 bzero (cdesc
, sizeof(struct cat_desc
));
2426 FREE(iterator
, M_TEMP
);
2427 return MacToVFSError(result
);
2432 * cat_createlink - create a link in the catalog
2434 * The following cat_attr fields are expected to be set:
2440 * ca_finderinfo (type and creator)
2443 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2444 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2448 FSBufferDescriptor btdata
;
2449 HFSPlusForkData
*rsrcforkp
;
2453 int thread_inserted
= 0;
2454 int alias_allocated
= 0;
2458 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
2460 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2463 * Get the next CNID. Note that we are currently holding catalog lock.
2465 result
= cat_acquire_cnid(hfsmp
, &nextCNID
);
2470 /* Get space for iterator, key and data */
2471 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
2472 bto
->iterator
.hint
.nodeNum
= 0;
2473 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2475 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2477 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2481 /* This is our only chance to set the encoding (other than a rename). */
2482 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2485 * Insert the thread record first.
2487 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2488 btdata
.bufferAddress
= &bto
->data
;
2489 btdata
.itemSize
= datalen
;
2490 btdata
.itemCount
= 1;
2492 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2493 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2497 thread_inserted
= 1;
2500 * Now insert the link record.
2502 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2504 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2505 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2506 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2508 /* For directory hard links, create alias in resource fork */
2509 if (descp
->cd_flags
& CD_ISDIR
) {
2510 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2513 alias_allocated
= 1;
2515 btdata
.bufferAddress
= &bto
->data
;
2516 btdata
.itemSize
= datalen
;
2517 btdata
.itemCount
= 1;
2519 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2521 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2523 if (result
== btExists
)
2527 if (linkfileid
!= NULL
) {
2528 *linkfileid
= nextCNID
;
2532 if (thread_inserted
) {
2533 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result
), hfsmp
->vcbVN
);
2535 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2536 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2537 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2538 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
2541 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2542 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2543 rsrcforkp
->extents
[0].blockCount
, 0);
2544 rsrcforkp
->extents
[0].startBlock
= 0;
2545 rsrcforkp
->extents
[0].blockCount
= 0;
2548 (void) BTFlushPath(fcb
);
2551 return MacToVFSError(result
);
2554 /* Directory hard links are visible as aliases on pre-Leopard systems and
2555 * as normal directories on Leopard or later. All directory hard link aliases
2556 * have the same resource fork content except for the three uniquely
2557 * identifying values that are updated in the resource fork data when the alias
2558 * is created. The following array is the constant resource fork data used
2559 * only for creating directory hard link aliases.
2561 static const char hfs_dirlink_alias_rsrc
[] = {
2562 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2563 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2564 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2575 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2576 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2578 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2579 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2580 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2581 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2582 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2583 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2585 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2586 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2588 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2590 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2593 /* Constants for directory hard link alias */
2595 /* Size of resource fork data array for directory hard link alias */
2596 kHFSAliasSize
= 0x1d0,
2598 /* Volume type for ejectable devices like disk image */
2599 kHFSAliasVolTypeEjectable
= 0x5,
2601 /* Offset for volume create date, in Mac OS local time */
2602 kHFSAliasVolCreateDateOffset
= 0x12a,
2604 /* Offset for the type of volume */
2605 kHFSAliasVolTypeOffset
= 0x130,
2607 /* Offset for folder ID of the parent directory of the directory inode */
2608 kHFSAliasParentIDOffset
= 0x132,
2610 /* Offset for folder ID of the directory inode */
2611 kHFSAliasTargetIDOffset
= 0x176,
2614 /* Create and write an alias that points at the directory represented by given
2615 * inode number on the same volume. Directory hard links are visible as
2616 * aliases in pre-Leopard systems and this function creates these aliases.
2618 * Note: This code is very specific to creating alias for the purpose
2619 * of directory hard links only, and should not be generalized.
2622 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2630 HFSPlusForkData
*rsrcforkp
;
2634 rsrcforkp
= &(crp
->resourceFork
);
2636 blksize
= hfsmp
->blockSize
;
2637 blkcount
= howmany(kHFSAliasSize
, blksize
);
2638 sectorsize
= hfsmp
->hfs_logical_block_size
;
2639 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2641 /* Allocate some disk space for the alias content. */
2642 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2643 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2644 &rsrcforkp
->extents
[0].startBlock
,
2645 &rsrcforkp
->extents
[0].blockCount
);
2646 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2647 if (result
== dskFulErr
) {
2648 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2649 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
| HFS_ALLOC_FLUSHTXN
,
2650 &rsrcforkp
->extents
[0].startBlock
,
2651 &rsrcforkp
->extents
[0].blockCount
);
2654 rsrcforkp
->extents
[0].startBlock
= 0;
2658 /* Acquire a buffer cache block for our block. */
2659 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2660 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2662 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2664 journal_modify_block_start(hfsmp
->jnl
, bp
);
2667 /* Generate alias content */
2668 alias
= (char *)buf_dataptr(bp
);
2669 bzero(alias
, buf_size(bp
));
2670 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2672 /* Set the volume create date, local time in Mac OS format */
2673 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2674 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2676 /* If the file system is on a virtual device like disk image,
2677 * update the volume type to be ejectable device.
2679 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2680 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2681 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2684 /* Set id of the parent of the target directory */
2685 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2686 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2688 /* Set id of the target directory */
2689 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2690 *valptr
= OSSwapHostToBigInt32(inode_num
);
2692 /* Write alias content to disk. */
2694 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2695 } else if ((result
= buf_bwrite(bp
))) {
2699 /* Finish initializing the fork data. */
2700 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2701 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2704 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2705 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2706 rsrcforkp
->extents
[0].startBlock
= 0;
2707 rsrcforkp
->extents
[0].blockCount
= 0;
2708 rsrcforkp
->logicalSize
= 0;
2709 rsrcforkp
->totalBlocks
= 0;
2715 * cat_deletelink - delete a link from the catalog
2718 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2720 struct HFSPlusCatalogFile file
;
2721 struct cat_attr cattr
;
2722 uint32_t totalBlocks
;
2726 bzero(&file
, sizeof (file
));
2727 bzero(&cattr
, sizeof (cattr
));
2728 cattr
.ca_fileid
= descp
->cd_cnid
;
2730 /* Directory links have alias content to remove. */
2731 if (descp
->cd_flags
& CD_ISDIR
) {
2733 BTreeIterator
* iterator
;
2734 struct FSBufferDescriptor btdata
;
2736 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2738 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2739 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2740 iterator
->hint
.nodeNum
= 0;
2742 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2745 BDINIT(btdata
, &file
);
2747 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2752 result
= cat_delete(hfsmp
, descp
, &cattr
);
2754 if ((result
== 0) &&
2755 (descp
->cd_flags
& CD_ISDIR
) &&
2756 (file
.recordType
== kHFSPlusFileRecord
)) {
2758 totalBlocks
= file
.resourceFork
.totalBlocks
;
2760 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2761 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2762 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2766 (void) BlockDeallocate(hfsmp
,
2767 file
.resourceFork
.extents
[i
].startBlock
,
2768 file
.resourceFork
.extents
[i
].blockCount
, 0);
2770 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2771 file
.resourceFork
.extents
[i
].startBlock
= 0;
2772 file
.resourceFork
.extents
[i
].blockCount
= 0;
2781 * Callback to collect directory entries.
2782 * Called with readattr_state for each item in a directory.
2784 struct readattr_state
{
2785 struct hfsmount
*hfsmp
;
2786 struct cat_entrylist
*list
;
2794 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2795 struct readattr_state
*state
)
2797 struct cat_entrylist
*list
= state
->list
;
2798 struct hfsmount
*hfsmp
= state
->hfsmp
;
2799 struct cat_entry
*cep
;
2802 if (list
->realentries
>= list
->maxentries
)
2803 return (0); /* stop */
2805 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2807 switch(rec
->recordType
) {
2808 case kHFSPlusFolderRecord
:
2809 case kHFSPlusFileRecord
:
2811 case kHFSFolderRecord
:
2812 case kHFSFileRecord
:
2814 if (parentcnid
!= state
->dir_cnid
) {
2815 state
->error
= ENOENT
;
2816 state
->reached_eof
= 1;
2817 return (0); /* stop */
2821 state
->error
= ENOENT
;
2822 return (0); /* stop */
2825 /* Hide the private system directories and journal files */
2826 if (parentcnid
== kHFSRootFolderID
) {
2827 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2828 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2829 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2830 list
->skipentries
++;
2831 return (1); /* continue */
2834 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2835 (rec
->recordType
== kHFSPlusFileRecord
) &&
2836 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2837 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2838 list
->skipentries
++;
2839 return (1); /* continue */
2843 cep
= &list
->entry
[list
->realentries
++];
2845 if (state
->stdhfs
== 0) {
2846 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2847 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2848 isadir(rec
), &cep
->ce_desc
);
2850 if (rec
->recordType
== kHFSPlusFileRecord
) {
2851 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2852 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2853 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2854 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2856 /* Save link reference for later processing. */
2857 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2858 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2859 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2860 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2861 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2862 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2863 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2869 struct HFSPlusCatalogFile cnoderec
;
2870 HFSPlusCatalogKey
* pluskey
;
2873 promoteattr(hfsmp
, rec
, &cnoderec
);
2874 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2876 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2877 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2878 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2879 FREE(pluskey
, M_TEMP
);
2881 if (rec
->recordType
== kHFSFileRecord
) {
2882 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2884 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2885 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2886 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2887 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2892 return (list
->realentries
< list
->maxentries
);
2896 * Pack a cat_entrylist buffer with attributes from the catalog
2898 * Note: index is zero relative
2901 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
, int *reachedeof
)
2905 BTreeIterator
* iterator
;
2906 struct readattr_state state
;
2913 int reached_eof
= 0;
2915 ce_list
->realentries
= 0;
2917 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2918 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2919 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2921 bzero (&state
, sizeof(struct readattr_state
));
2923 state
.hfsmp
= hfsmp
;
2924 state
.list
= ce_list
;
2925 state
.dir_cnid
= parentcnid
;
2926 state
.stdhfs
= std_hfs
;
2929 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2930 bzero(iterator
, sizeof(*iterator
));
2931 key
= (CatalogKey
*)&iterator
->key
;
2933 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2934 index
= dirhint
->dh_index
+ 1;
2937 * Attempt to build a key from cached filename
2939 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2940 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2946 * If the last entry wasn't cached then position the btree iterator
2948 if ((index
== 0) || !have_key
) {
2950 * Position the iterator at the directory's thread record.
2951 * (i.e. just before the first entry)
2953 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2954 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2956 result
= MacToVFSError(result
);
2961 * Iterate until we reach the entry just
2962 * before the one we want to start with.
2965 struct position_state ps
;
2970 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2973 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2974 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2978 result
= MacToVFSError(result
);
2982 * Note: the index may now point to EOF if the directory
2983 * was modified in between system calls. We will return
2984 * ENOENT from cat_findposition if this is the case, and
2985 * when we bail out with an error, our caller (hfs_readdirattr_internal)
2986 * will suppress the error and indicate EOF to its caller.
2988 result
= MacToVFSError(result
);
2994 /* Fill list with entries starting at iterator->key. */
2995 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2996 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2999 result
= state
.error
;
3000 reached_eof
= state
.reached_eof
;
3002 else if (ce_list
->realentries
== 0) {
3007 result
= MacToVFSError(result
);
3014 * Resolve any hard links.
3016 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
3017 struct FndrFileInfo
*fip
;
3018 struct cat_entry
*cep
;
3019 struct HFSPlusCatalogFile filerec
;
3023 cep
= &ce_list
->entry
[i
];
3024 if (cep
->ce_attr
.ca_linkref
== 0)
3027 /* Note: Finder info is still in Big Endian */
3028 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
3030 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3031 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
3032 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
3035 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
3036 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
3037 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
3038 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
3041 if (isfilelink
|| isdirlink
) {
3042 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
3044 /* Repack entry from inode record. */
3045 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
3046 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
3047 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
3048 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
3049 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
3054 FREE(iterator
, M_TEMP
);
3055 *reachedeof
= reached_eof
;
3056 return MacToVFSError(result
);
3059 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3062 * Callback to pack directory entries.
3063 * Called with packdirentry_state for each item in a directory.
3066 /* Hard link information collected during cat_getdirentries. */
3069 user_addr_t dirent_addr
;
3071 typedef struct linkinfo linkinfo_t
;
3073 /* State information for the getdirentries_callback function. */
3074 struct packdirentry_state
{
3075 int cbs_flags
; /* VNODE_READDIR_* flags */
3076 u_int32_t cbs_parentID
;
3077 u_int32_t cbs_index
;
3079 ExtendedVCB
* cbs_hfsmp
;
3082 int32_t cbs_maxlinks
;
3083 linkinfo_t
* cbs_linkinfo
;
3084 struct cat_desc
* cbs_desc
;
3085 u_int8_t
* cbs_namebuf
;
3087 * The following fields are only used for NFS readdir, which
3088 * uses the next file id as the seek offset of each entry.
3090 struct direntry
* cbs_direntry
;
3091 struct direntry
* cbs_prevdirentry
;
3092 u_int32_t cbs_previlinkref
;
3093 Boolean cbs_hasprevdirentry
;
3098 * getdirentries callback for HFS Plus directories.
3101 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3102 struct packdirentry_state
*state
)
3104 struct hfsmount
*hfsmp
;
3105 const CatalogName
*cnp
;
3108 struct dirent catent
;
3109 struct direntry
* entry
= NULL
;
3111 u_int32_t ilinkref
= 0;
3112 u_int32_t curlinkref
= 0;
3115 u_int8_t type
= DT_UNKNOWN
;
3116 u_int8_t is_mangled
= 0;
3117 u_int8_t is_link
= 0;
3119 user_addr_t uiobase
= USER_ADDR_NULL
;
3124 Boolean stop_after_pack
= false;
3126 hfsmp
= state
->cbs_hfsmp
;
3127 curID
= ckp
->hfsPlus
.parentID
;
3129 /* We're done when parent directory changes */
3130 if (state
->cbs_parentID
!= curID
) {
3132 * If the parent ID is different from curID this means we've hit
3133 * the EOF for the directory. To help future callers, we mark
3134 * the cbs_eof boolean. However, we should only mark the EOF
3135 * boolean if we're about to return from this function.
3137 * This is because this callback function does its own uiomove
3138 * to get the data to userspace. If we set the boolean before determining
3139 * whether or not the current entry has enough room to write its
3140 * data to userland, we could fool the callers of this catalog function
3141 * into thinking they've hit EOF earlier than they really would have.
3142 * In that case, we'd know that we have more entries to process and
3143 * send to userland, but we didn't have enough room.
3145 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3146 * about to return and won't write any new data back
3147 * to userland. In the stop_after_pack case, we'll set this boolean
3148 * regardless, so it's slightly safer to let that logic mark the boolean,
3149 * especially since it's closer to the return of this function.
3152 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3153 /* The last record has not been returned yet, so we
3154 * want to stop after packing the last item
3156 if (state
->cbs_hasprevdirentry
) {
3157 stop_after_pack
= true;
3159 state
->cbs_eof
= true;
3160 state
->cbs_result
= ENOENT
;
3161 return (0); /* stop */
3164 state
->cbs_eof
= true;
3165 state
->cbs_result
= ENOENT
;
3166 return (0); /* stop */
3170 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3171 entry
= state
->cbs_direntry
;
3172 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
3173 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
3175 * The NFS server sometimes needs to make filenames fit in
3176 * NAME_MAX bytes (since its client may not be able to
3177 * handle a longer name). In that case, NFS will ask us
3178 * to mangle the name to keep it short enough.
3180 maxnamelen
= NAME_MAX
+ 1;
3182 maxnamelen
= sizeof(entry
->d_name
);
3185 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3186 maxnamelen
= sizeof(catent
.d_name
);
3189 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
3190 /* The last item returns a non-zero invalid cookie */
3193 switch(crp
->recordType
) {
3194 case kHFSPlusFolderRecord
:
3196 cnid
= crp
->hfsPlusFolder
.folderID
;
3197 /* Hide our private system directories. */
3198 if (curID
== kHFSRootFolderID
) {
3199 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3200 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3205 case kHFSPlusFileRecord
:
3206 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
3207 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
3208 cnid
= crp
->hfsPlusFile
.fileID
;
3210 * When a hardlink link is encountered save its link ref.
3212 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
3213 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
3214 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3215 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3216 /* If link ref is inode's file id then use it directly. */
3217 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
3218 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3220 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
3223 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
3224 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
3225 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
3226 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
3227 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
3228 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
3229 /* A directory's link resolves to a directory. */
3231 /* A directory's link ref is always inode's file id. */
3232 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
3235 /* Hide the journal files */
3236 if ((curID
== kHFSRootFolderID
) &&
3237 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
3238 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
3239 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
3244 return (0); /* stop */
3247 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3249 namelen
= cnp
->ustr
.length
;
3251 * For MacRoman encoded names, assume that its ascii and
3252 * convert it directly in an attempt to avoid the more
3253 * expensive utf8_encodestr conversion.
3255 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
3258 const u_int16_t
*chp
;
3260 chp
= &cnp
->ustr
.unicode
[0];
3261 for (i
= 0; i
< (int)namelen
; ++i
) {
3263 if (ch
> 0x007f || ch
== 0x0000) {
3264 /* Perform expensive utf8_encodestr conversion */
3267 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
3269 nameptr
[namelen
] = '\0';
3273 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
3274 nameptr
, &namelen
, maxnamelen
, ':', 0);
3277 /* Check result returned from encoding the filename to utf8 */
3278 if (result
== ENAMETOOLONG
) {
3280 * If we were looking at a catalog record for a hardlink (not the inode),
3281 * then we want to use its link ID as opposed to the inode ID for
3282 * a mangled name. For all other cases, they are the same. Note that
3283 * due to the way directory hardlinks are implemented, the actual link
3284 * is going to be counted as a file record, so we can catch both
3287 cnid_t linkid
= cnid
;
3289 linkid
= crp
->hfsPlusFile
.fileID
;
3292 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3293 cnp
->ustr
.unicode
, maxnamelen
,
3294 (ByteCount
*)&namelen
, nameptr
, linkid
);
3299 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3301 * The index is 1 relative and includes "." and ".."
3303 * Also stuff the cnid in the upper 32 bits of the cookie.
3304 * The cookie is stored to the previous entry, which will
3305 * be packed and copied this time
3307 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
3308 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
3309 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
3311 catent
.d_type
= type
;
3312 catent
.d_namlen
= namelen
;
3313 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3315 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
3317 catent
.d_fileno
= cnid
;
3318 uioaddr
= (caddr_t
) &catent
;
3321 /* Save current base address for post processing of hard-links. */
3322 if (ilinkref
|| state
->cbs_previlinkref
) {
3323 uiobase
= uio_curriovbase(state
->cbs_uio
);
3325 /* If this entry won't fit then we're done */
3326 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3327 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3328 return (0); /* stop */
3331 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3332 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3333 if (state
->cbs_result
== 0) {
3336 /* Remember previous entry */
3337 state
->cbs_desc
->cd_cnid
= cnid
;
3338 if (type
== DT_DIR
) {
3339 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3341 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3343 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3344 state
->cbs_desc
->cd_namelen
= 0;
3347 state
->cbs_desc
->cd_encoding
= xxxx
;
3350 state
->cbs_desc
->cd_namelen
= namelen
;
3351 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3353 /* Store unmangled name for the directory hint else it will
3354 * restart readdir at the last location again
3356 u_int8_t
*new_nameptr
;
3358 size_t tmp_namelen
= 0;
3360 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3361 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3362 cnp
->ustr
.length
* sizeof(UniChar
),
3364 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
3365 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3366 cnp
->ustr
.length
* sizeof(UniChar
),
3367 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3369 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3370 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3372 FREE(new_nameptr
, M_TEMP
);
3375 if (state
->cbs_hasprevdirentry
) {
3376 curlinkref
= ilinkref
; /* save current */
3377 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3380 * Record any hard links for post processing.
3382 if ((ilinkref
!= 0) &&
3383 (state
->cbs_result
== 0) &&
3384 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3385 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3386 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3387 state
->cbs_nlinks
++;
3389 if (state
->cbs_hasprevdirentry
) {
3390 ilinkref
= curlinkref
; /* restore current */
3394 /* Fill the direntry to be used the next time */
3395 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3396 if (stop_after_pack
) {
3397 state
->cbs_eof
= true;
3398 return (0); /* stop */
3400 entry
->d_type
= type
;
3401 entry
->d_namlen
= namelen
;
3402 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3404 /* File number = 0 means skip entry */
3405 entry
->d_fileno
= 0;
3407 entry
->d_fileno
= cnid
;
3409 /* swap the current and previous entry */
3410 struct direntry
* tmp
;
3411 tmp
= state
->cbs_direntry
;
3412 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3413 state
->cbs_prevdirentry
= tmp
;
3414 state
->cbs_hasprevdirentry
= true;
3415 state
->cbs_previlinkref
= ilinkref
;
3418 /* Continue iteration if there's room */
3419 return (state
->cbs_result
== 0 &&
3420 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3425 * getdirentries callback for standard HFS (non HFS+) directories.
3428 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3429 struct packdirentry_state
*state
)
3431 struct hfsmount
*hfsmp
;
3432 const CatalogName
*cnp
;
3435 struct dirent catent
;
3437 u_int8_t type
= DT_UNKNOWN
;
3444 hfsmp
= state
->cbs_hfsmp
;
3446 curID
= ckp
->hfs
.parentID
;
3448 /* We're done when parent directory changes */
3449 if (state
->cbs_parentID
!= curID
) {
3450 state
->cbs_result
= ENOENT
;
3451 return (0); /* stop */
3454 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3455 maxnamelen
= sizeof(catent
.d_name
);
3457 switch(crp
->recordType
) {
3458 case kHFSFolderRecord
:
3460 cnid
= crp
->hfsFolder
.folderID
;
3462 case kHFSFileRecord
:
3464 cnid
= crp
->hfsFile
.fileID
;
3467 return (0); /* stop */
3470 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3471 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3473 * When an HFS name cannot be encoded with the current
3474 * volume encoding we use MacRoman as a fallback.
3477 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
, (ByteCount
*)&namelen
, nameptr
);
3479 catent
.d_type
= type
;
3480 catent
.d_namlen
= namelen
;
3481 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3482 catent
.d_fileno
= cnid
;
3483 uioaddr
= (caddr_t
) &catent
;
3485 /* If this entry won't fit then we're done */
3486 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3487 return (0); /* stop */
3490 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3491 if (state
->cbs_result
== 0) {
3494 /* Remember previous entry */
3495 state
->cbs_desc
->cd_cnid
= cnid
;
3496 if (type
== DT_DIR
) {
3497 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3499 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3501 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3502 state
->cbs_desc
->cd_namelen
= 0;
3504 state
->cbs_desc
->cd_namelen
= namelen
;
3505 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3508 /* Continue iteration if there's room */
3509 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3514 * Pack a uio buffer with directory entries from the catalog
3517 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3518 uio_t uio
, int flags
, int * items
, int * eofflag
)
3521 BTreeIterator
* iterator
;
3523 struct packdirentry_state state
;
3532 extended
= flags
& VNODE_READDIR_EXTENDED
;
3534 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3537 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3540 * Get a buffer for link info array, btree iterator and a direntry:
3542 maxlinks
= MIN(entrycnt
, (u_int32_t
)(uio_resid(uio
) / SMALL_DIRENTRY_SIZE
));
3543 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3545 bufsize
+= 2*sizeof(struct direntry
);
3547 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3548 bzero(buffer
, bufsize
);
3550 state
.cbs_flags
= flags
;
3551 state
.cbs_hasprevdirentry
= false;
3552 state
.cbs_previlinkref
= 0;
3553 state
.cbs_nlinks
= 0;
3554 state
.cbs_maxlinks
= maxlinks
;
3555 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3557 * We need to set cbs_eof to false regardless of whether or not the
3558 * control flow is actually in the extended case, since we use this
3559 * field to track whether or not we've returned EOF from the iterator function.
3561 state
.cbs_eof
= false;
3563 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3564 key
= (CatalogKey
*)&iterator
->key
;
3566 index
= dirhint
->dh_index
+ 1;
3568 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3569 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3572 * Attempt to build a key from cached filename
3574 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3575 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3576 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3581 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3583 * Position the iterator at the directory's thread record.
3584 * (i.e. just before the first entry)
3586 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3587 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3588 iterator
->hint
.index
= 0;
3593 * If the last entry wasn't cached then position the btree iterator
3597 * Position the iterator at the directory's thread record.
3598 * (i.e. just before the first entry)
3600 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3601 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3603 result
= MacToVFSError(result
);
3607 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3610 * Iterate until we reach the entry just
3611 * before the one we want to start with.
3614 struct position_state ps
;
3619 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3622 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3623 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3627 result
= MacToVFSError(result
);
3629 result
= MacToVFSError(result
);
3630 if (result
== ENOENT
) {
3632 * ENOENT means we've hit the EOF.
3633 * suppress the error, and set the eof flag.
3636 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3644 state
.cbs_index
= index
;
3645 state
.cbs_hfsmp
= hfsmp
;
3646 state
.cbs_uio
= uio
;
3647 state
.cbs_desc
= &dirhint
->dh_desc
;
3648 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3649 state
.cbs_result
= 0;
3650 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3652 /* Use a temporary buffer to hold intermediate descriptor names. */
3653 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3654 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3655 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3656 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3657 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3660 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3662 enum BTreeIterationOperations op
;
3663 if (extended
&& index
!= 0 && have_key
)
3664 op
= kBTreeCurrentRecord
;
3666 op
= kBTreeNextRecord
;
3669 * Process as many entries as possible starting at iterator->key.
3671 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3673 result
= BTIterateRecords(fcb
, op
, iterator
,
3674 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3676 /* For extended calls, every call to getdirentries_callback()
3677 * transfers the previous directory entry found to the user
3678 * buffer. Therefore when BTIterateRecords reaches the end of
3679 * Catalog BTree, call getdirentries_callback() again with
3680 * dummy values to copy the last directory entry stored in
3681 * packdirentry_state
3683 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3687 bzero(&ckp
, sizeof(ckp
));
3688 bzero(&crp
, sizeof(crp
));
3690 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3695 /* HFS (standard) */
3696 result
= BTIterateRecords(fcb
, op
, iterator
,
3697 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3701 /* Note that state.cbs_index is still valid on errors */
3702 *items
= state
.cbs_index
- index
;
3703 index
= state
.cbs_index
;
3706 * Also note that cbs_eof is set in all cases if we ever hit EOF
3707 * during the enumeration by the catalog callback. Mark the directory's hint
3708 * descriptor as having hit EOF.
3711 if (state
.cbs_eof
) {
3712 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3716 /* Finish updating the catalog iterator. */
3717 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3718 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3719 dirhint
->dh_index
= index
- 1;
3721 /* Fix up the name. */
3722 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3723 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3724 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3726 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3727 dirhint
->dh_desc
.cd_namelen
= 0;
3731 * Post process any hard links to get the real file id.
3733 if (state
.cbs_nlinks
> 0) {
3735 user_addr_t address
;
3738 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3739 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3741 /* This assumes that d_ino is always first field. */
3742 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3743 if (address
== (user_addr_t
)0)
3745 if (uio_isuserspace(uio
)) {
3747 ino64_t fileid_64
= (ino64_t
)fileid
;
3748 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3750 (void) copyout(&fileid
, address
, sizeof(fileid
));
3752 } else /* system space */ {
3754 ino64_t fileid_64
= (ino64_t
)fileid
;
3755 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3757 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3763 if (state
.cbs_result
)
3764 result
= state
.cbs_result
;
3766 result
= MacToVFSError(result
);
3768 if (result
== ENOENT
) {
3773 FREE(buffer
, M_TEMP
);
3780 * Callback to establish directory position.
3781 * Called with position_state for each item in a directory.
3784 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3785 struct position_state
*state
)
3789 if ((state
->hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
3790 curID
= ckp
->hfsPlus
.parentID
;
3794 curID
= ckp
->hfs
.parentID
;
3798 /* Make sure parent directory didn't change */
3799 if (state
->parentID
!= curID
) {
3801 * The parent ID is different from curID this means we've hit
3802 * the EOF for the directory.
3804 state
->error
= ENOENT
;
3805 return (0); /* stop */
3808 /* Count this entry */
3809 switch(crp
->recordType
) {
3810 case kHFSPlusFolderRecord
:
3811 case kHFSPlusFileRecord
:
3813 case kHFSFolderRecord
:
3814 case kHFSFileRecord
:
3819 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3820 crp
->recordType
, curID
);
3821 state
->error
= EINVAL
;
3822 return (0); /* stop */
3825 return (state
->count
< state
->index
);
3830 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3832 * The name portion of the key is compared using a 16-bit binary comparison.
3833 * This is called from the b-tree code.
3836 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3838 u_int32_t searchParentID
, trialParentID
;
3841 searchParentID
= searchKey
->parentID
;
3842 trialParentID
= trialKey
->parentID
;
3845 if (searchParentID
> trialParentID
) {
3847 } else if (searchParentID
< trialParentID
) {
3850 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3851 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3852 int length1
= searchKey
->nodeName
.length
;
3853 int length2
= trialKey
->nodeName
.length
;
3855 result
= UnicodeBinaryCompare (str1
, length1
, str2
, length2
);
3864 * Compare two standard HFS catalog keys
3866 * Result: +n search key > trial key
3867 * 0 search key = trial key
3868 * -n search key < trial key
3871 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3873 cnid_t searchParentID
, trialParentID
;
3876 searchParentID
= searchKey
->parentID
;
3877 trialParentID
= trialKey
->parentID
;
3879 if (searchParentID
> trialParentID
)
3881 else if (searchParentID
< trialParentID
)
3883 else /* parent dirID's are equal, compare names */
3884 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3892 * Compare two HFS+ catalog keys
3894 * Result: +n search key > trial key
3895 * 0 search key = trial key
3896 * -n search key < trial key
3899 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3901 cnid_t searchParentID
, trialParentID
;
3904 searchParentID
= searchKey
->parentID
;
3905 trialParentID
= trialKey
->parentID
;
3907 if (searchParentID
> trialParentID
) {
3910 else if (searchParentID
< trialParentID
) {
3913 /* parent node ID's are equal, compare names */
3914 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3915 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3917 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3918 searchKey
->nodeName
.length
,
3919 &trialKey
->nodeName
.unicode
[0],
3920 trialKey
->nodeName
.length
);
3928 * buildkey - build a Catalog b-tree key from a cnode descriptor
3931 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3932 HFSPlusCatalogKey
*key
, int retry
)
3934 int std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
3935 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3937 size_t unicodeBytes
= 0;
3943 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3944 return (EINVAL
); /* invalid name */
3946 key
->parentID
= descp
->cd_parentcnid
;
3947 key
->nodeName
.length
= 0;
3949 * Convert filename from UTF-8 into Unicode
3952 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3953 utf8_flags
|= UTF_DECOMPOSED
;
3954 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3955 key
->nodeName
.unicode
, &unicodeBytes
,
3956 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3957 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3958 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3960 if (result
!= ENAMETOOLONG
)
3961 result
= EINVAL
; /* name has invalid characters */
3967 * For HFS volumes convert to an HFS compatible key
3969 * XXX need to save the encoding that succeeded
3972 HFSCatalogKey hfskey
;
3974 bzero(&hfskey
, sizeof(hfskey
));
3975 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3976 hfskey
.parentID
= key
->parentID
;
3977 hfskey
.nodeName
[0] = 0;
3978 if (key
->nodeName
.length
> 0) {
3980 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3981 key
->nodeName
.length
* 2,
3982 key
->nodeName
.unicode
,
3983 &hfskey
.nodeName
[0], retry
)) != 0) {
3984 if (res
!= ENAMETOOLONG
)
3989 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3991 bcopy(&hfskey
, key
, sizeof(hfskey
));
4000 * Resolve hard link reference to obtain the inode record.
4003 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
4005 FSBufferDescriptor btdata
;
4006 struct BTreeIterator
*iterator
;
4007 struct cat_desc idesc
;
4012 BDINIT(btdata
, recp
);
4015 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4016 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
4018 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
4019 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
4022 /* Get space for iterator */
4023 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4024 bzero(iterator
, sizeof(*iterator
));
4026 /* Build a descriptor for private dir. */
4027 idesc
.cd_parentcnid
= parentcnid
;
4028 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
4029 idesc
.cd_namelen
= strlen(inodename
);
4032 idesc
.cd_encoding
= 0;
4033 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
4035 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4036 &btdata
, NULL
, NULL
);
4039 /* Make sure there's a reference */
4040 if (recp
->hl_linkCount
== 0)
4041 recp
->hl_linkCount
= 2;
4043 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename
, hfsmp
->vcbVN
);
4046 FREE(iterator
, M_TEMP
);
4048 return (result
? ENOENT
: 0);
4052 * Resolve hard link reference to obtain the inode number.
4055 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
4057 struct HFSPlusCatalogFile record
;
4061 * Since we know resolvelinkid is only called from
4062 * cat_getdirentries, we can assume that only file
4063 * hardlinks need to be resolved (cat_getdirentries
4064 * can resolve directory hardlinks in place).
4066 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
4068 if (record
.fileID
== 0)
4071 *ino
= record
.fileID
;
4077 * getkey - get a key from id by doing a thread lookup
4080 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
4082 struct BTreeIterator
* iterator
;
4083 FSBufferDescriptor btdata
;
4086 CatalogRecord
* recp
;
4090 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
4092 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4093 bzero(iterator
, sizeof(*iterator
));
4094 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
4096 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4097 BDINIT(btdata
, recp
);
4099 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4100 &btdata
, &datasize
, iterator
);
4104 /* Turn thread record into a cnode key (in place) */
4105 switch (recp
->recordType
) {
4108 case kHFSFileThreadRecord
:
4109 case kHFSFolderThreadRecord
:
4110 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
4111 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
4112 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
4116 case kHFSPlusFileThreadRecord
:
4117 case kHFSPlusFolderThreadRecord
:
4118 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4119 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4120 (keyp
->hfsPlus
.nodeName
.length
* 2);
4121 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
4130 FREE(iterator
, M_TEMP
);
4133 return MacToVFSError(result
);
4137 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4138 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4139 * cat_desc here). Both key and attrp must point to real structures.
4141 * The key's parent id is the only part of the key expected to be used by the caller.
4142 * The name portion of the key may not always be valid (ie in the case of a hard link).
4145 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
4149 result
= getkey(hfsmp
, cnid
, key
);
4152 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
4155 * Check for a raw file hardlink inode.
4156 * Fix up the parent id in the key if necessary.
4157 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4159 if ((result
== 0) &&
4160 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
4161 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
4162 cnid_t nextlinkid
= 0;
4163 cnid_t prevlinkid
= 0;
4164 struct cat_desc linkdesc
;
4167 * Pick up the first link in the chain and get a descriptor for it.
4168 * This allows blind bulk access checks to work for hardlinks.
4170 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
4171 (nextlinkid
!= 0)) {
4172 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
4173 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
4174 cat_releasedesc(&linkdesc
);
4178 return MacToVFSError(result
);
4183 * buildrecord - build a default catalog directory or file record
4186 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
4187 CatalogRecord
*crp
, u_int32_t
*recordSize
)
4189 int type
= attrp
->ca_mode
& S_IFMT
;
4190 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
4193 struct HFSPlusBSDInfo
* bsdp
= NULL
;
4195 if (type
== S_IFDIR
) {
4196 crp
->recordType
= kHFSPlusFolderRecord
;
4197 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
4198 crp
->hfsPlusFolder
.valence
= 0;
4199 crp
->hfsPlusFolder
.folderID
= cnid
;
4200 crp
->hfsPlusFolder
.createDate
= createtime
;
4201 crp
->hfsPlusFolder
.contentModDate
= createtime
;
4202 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
4203 crp
->hfsPlusFolder
.accessDate
= createtime
;
4204 crp
->hfsPlusFolder
.backupDate
= 0;
4205 crp
->hfsPlusFolder
.textEncoding
= encoding
;
4206 crp
->hfsPlusFolder
.folderCount
= 0;
4207 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
4208 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
4209 bsdp
->special
.linkCount
= 1;
4210 *recordSize
= sizeof(HFSPlusCatalogFolder
);
4212 crp
->recordType
= kHFSPlusFileRecord
;
4213 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
4214 crp
->hfsPlusFile
.reserved1
= 0;
4215 crp
->hfsPlusFile
.fileID
= cnid
;
4216 crp
->hfsPlusFile
.createDate
= createtime
;
4217 crp
->hfsPlusFile
.contentModDate
= createtime
;
4218 crp
->hfsPlusFile
.accessDate
= createtime
;
4219 crp
->hfsPlusFile
.attributeModDate
= createtime
;
4220 crp
->hfsPlusFile
.backupDate
= 0;
4221 crp
->hfsPlusFile
.textEncoding
= encoding
;
4222 crp
->hfsPlusFile
.reserved2
= 0;
4223 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
4224 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
4225 /* BLK/CHR need to save the device info */
4226 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
4227 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
4229 bsdp
->special
.linkCount
= 1;
4231 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
4232 *recordSize
= sizeof(HFSPlusCatalogFile
);
4234 bsdp
->ownerID
= attrp
->ca_uid
;
4235 bsdp
->groupID
= attrp
->ca_gid
;
4236 bsdp
->fileMode
= attrp
->ca_mode
;
4237 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
4238 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
4242 createtime
= UTCToLocal(createtime
);
4243 if (type
== S_IFDIR
) {
4244 bzero(crp
, sizeof(HFSCatalogFolder
));
4245 crp
->recordType
= kHFSFolderRecord
;
4246 crp
->hfsFolder
.folderID
= cnid
;
4247 crp
->hfsFolder
.createDate
= createtime
;
4248 crp
->hfsFolder
.modifyDate
= createtime
;
4249 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
4250 *recordSize
= sizeof(HFSCatalogFolder
);
4252 bzero(crp
, sizeof(HFSCatalogFile
));
4253 crp
->recordType
= kHFSFileRecord
;
4254 crp
->hfsFile
.fileID
= cnid
;
4255 crp
->hfsFile
.createDate
= createtime
;
4256 crp
->hfsFile
.modifyDate
= createtime
;
4257 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
4258 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
4259 *recordSize
= sizeof(HFSCatalogFile
);
4268 * builddesc - build a cnode descriptor from an HFS+ key
4271 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
4272 int isdir
, struct cat_desc
*descp
)
4275 unsigned char * nameptr
;
4278 unsigned char tmpbuff
[128];
4280 /* guess a size... */
4281 bufsize
= (3 * key
->nodeName
.length
) + 1;
4282 if (bufsize
>= sizeof(tmpbuff
) - 1) {
4283 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
4285 nameptr
= &tmpbuff
[0];
4288 result
= utf8_encodestr(key
->nodeName
.unicode
,
4289 key
->nodeName
.length
* sizeof(UniChar
),
4290 nameptr
, (size_t *)&utf8len
,
4293 if (result
== ENAMETOOLONG
) {
4294 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
4295 key
->nodeName
.length
* sizeof(UniChar
),
4297 FREE(nameptr
, M_TEMP
);
4298 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
4300 result
= utf8_encodestr(key
->nodeName
.unicode
,
4301 key
->nodeName
.length
* sizeof(UniChar
),
4302 nameptr
, (size_t *)&utf8len
,
4305 descp
->cd_parentcnid
= key
->parentID
;
4306 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
4307 descp
->cd_namelen
= utf8len
;
4308 descp
->cd_cnid
= cnid
;
4309 descp
->cd_hint
= hint
;
4310 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
4312 descp
->cd_flags
|= CD_ISDIR
;
4313 descp
->cd_encoding
= encoding
;
4314 if (nameptr
!= &tmpbuff
[0]) {
4315 FREE(nameptr
, M_TEMP
);
4322 * getbsdattr - get attributes in bsd format
4326 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
4328 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
4329 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
4331 attrp
->ca_recflags
= crp
->flags
;
4332 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
4333 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
4334 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
4335 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
4336 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
4337 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4339 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4340 attrp
->ca_flags
= 0;
4341 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4342 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4344 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4346 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4348 attrp
->ca_linkcount
= 1;
4351 attrp
->ca_linkcount
= 1; /* may be overridden below */
4353 attrp
->ca_uid
= bsd
->ownerID
;
4354 attrp
->ca_gid
= bsd
->groupID
;
4355 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4356 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4357 switch (attrp
->ca_mode
& S_IFMT
) {
4358 case S_IFCHR
: /* fall through */
4360 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4363 case S_IFDIR
: /* fall through */
4365 /* Pick up the hard link count */
4366 if (bsd
->special
.linkCount
> 0)
4367 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4372 * Override the permissions as determined by the mount auguments
4373 * in ALMOST the same way unset permissions are treated but keep
4374 * track of whether or not the file or folder is hfs locked
4375 * by leaving the h_pflags field unchanged from what was unpacked
4376 * out of the catalog.
4379 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4380 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4381 * at the VFS layer, so there is no need to do it here now; this also
4382 * allows VFS to let root see the real UIDs.
4384 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4385 * attrp->ca_uid = hfsmp->hfs_uid;
4386 * attrp->ca_gid = hfsmp->hfs_gid;
4392 if (!S_ISDIR(attrp
->ca_mode
)) {
4393 attrp
->ca_mode
&= ~S_IFMT
;
4394 attrp
->ca_mode
|= S_IFDIR
;
4396 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4397 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4398 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4400 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4401 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4402 attrp
->ca_flags
|= UF_HIDDEN
;
4404 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4405 if (crp
->flags
& kHFSFileLockedMask
) {
4406 /* The file's supposed to be locked:
4407 Make sure at least one of the IMMUTABLE bits is set: */
4408 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4409 attrp
->ca_flags
|= UF_IMMUTABLE
;
4411 /* The file's supposed to be unlocked: */
4412 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4414 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4415 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4416 attrp
->ca_flags
|= UF_HIDDEN
;
4417 /* get total blocks (both forks) */
4418 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4420 /* On HFS+ the ThreadExists flag must always be set. */
4421 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4422 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4424 /* Pick up the hardlink first link, if any. */
4425 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4428 attrp
->ca_fileid
= crp
->fileID
;
4430 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4435 * promotekey - promote hfs key to hfs plus key
4439 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4440 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4442 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4446 *encoding
= hfsmp
->hfs_encoding
;
4448 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4449 kHFSPlusMaxFileNameChars
, &uniCount
);
4451 * When an HFS name cannot be encoded with the current
4452 * encoding use MacRoman as a fallback.
4454 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4456 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4457 keyp
->nodeName
.unicode
,
4458 kHFSPlusMaxFileNameChars
,
4462 keyp
->nodeName
.length
= uniCount
;
4463 keyp
->parentID
= hfskey
->parentID
;
4467 * promotefork - promote hfs fork info to hfs plus
4471 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4472 int resource
, struct cat_fork
* forkp
)
4474 struct HFSPlusExtentDescriptor
*xp
;
4475 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4477 bzero(forkp
, sizeof(*forkp
));
4478 xp
= &forkp
->cf_extents
[0];
4480 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4481 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4482 forkp
->cf_bytesread
= 0;
4483 forkp
->cf_vblocks
= 0;
4484 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4485 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4486 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4487 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4488 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4489 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4491 forkp
->cf_size
= filep
->dataLogicalSize
;
4492 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4493 forkp
->cf_bytesread
= 0;
4494 forkp
->cf_vblocks
= 0;
4495 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4496 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4497 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4498 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4499 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4500 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4505 * promoteattr - promote standard hfs catalog attributes to hfs plus
4509 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4511 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4513 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4514 const struct HFSCatalogFolder
* folder
;
4516 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4517 crp
->recordType
= kHFSPlusFolderRecord
;
4518 crp
->flags
= folder
->flags
;
4519 crp
->fileID
= folder
->folderID
;
4520 crp
->createDate
= LocalToUTC(folder
->createDate
);
4521 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4522 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4523 crp
->reserved1
= folder
->valence
;
4525 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4527 const struct HFSCatalogFile
* file
;
4529 file
= (const struct HFSCatalogFile
*) dataPtr
;
4530 crp
->recordType
= kHFSPlusFileRecord
;
4531 crp
->flags
= file
->flags
;
4532 crp
->fileID
= file
->fileID
;
4533 crp
->createDate
= LocalToUTC(file
->createDate
);
4534 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4535 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4538 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4539 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4540 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4541 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4543 crp
->textEncoding
= 0;
4544 crp
->attributeModDate
= crp
->contentModDate
;
4545 crp
->accessDate
= crp
->contentModDate
;
4546 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4551 * Build a catalog node thread record from a catalog key
4552 * and return the size of the record.
4555 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4560 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4561 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4563 size
= sizeof(HFSPlusCatalogThread
);
4565 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4567 rec
->recordType
= kHFSPlusFileThreadRecord
;
4569 rec
->parentID
= key
->parentID
;
4570 bcopy(&key
->nodeName
, &rec
->nodeName
,
4571 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4573 /* HFS Plus has variable sized thread records */
4574 size
-= (sizeof(rec
->nodeName
.unicode
) -
4575 (rec
->nodeName
.length
* sizeof(UniChar
)));
4580 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4581 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4583 size
= sizeof(HFSCatalogThread
);
4586 rec
->recordType
= kHFSFolderThreadRecord
;
4588 rec
->recordType
= kHFSFileThreadRecord
;
4589 rec
->parentID
= key
->parentID
;
4590 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4599 * Build a catalog node thread key.
4602 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4605 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4606 key
->hfsPlus
.parentID
= parentID
;
4607 key
->hfsPlus
.nodeName
.length
= 0;
4611 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4612 key
->hfs
.reserved
= 0;
4613 key
->hfs
.parentID
= parentID
;
4614 key
->hfs
.nodeName
[0] = 0;
4621 * Extract the text encoding from a catalog node record.
4624 getencoding(const CatalogRecord
*crp
)
4628 if (crp
->recordType
== kHFSPlusFolderRecord
)
4629 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4630 else if (crp
->recordType
== kHFSPlusFileRecord
)
4631 encoding
= crp
->hfsPlusFile
.textEncoding
;
4639 * Extract the CNID from a catalog node record.
4642 getcnid(const CatalogRecord
*crp
)
4646 switch (crp
->recordType
) {
4649 case kHFSFolderRecord
:
4650 cnid
= crp
->hfsFolder
.folderID
;
4652 case kHFSFileRecord
:
4653 cnid
= crp
->hfsFile
.fileID
;
4657 case kHFSPlusFolderRecord
:
4658 cnid
= crp
->hfsPlusFolder
.folderID
;
4660 case kHFSPlusFileRecord
:
4661 cnid
= crp
->hfsPlusFile
.fileID
;
4664 printf("hfs: getcnid: unknown recordType=%d\n", crp
->recordType
);
4672 * Extract the parent ID from a catalog node record.
4675 getparentcnid(const CatalogRecord
*recp
)
4679 switch (recp
->recordType
) {
4682 case kHFSFileThreadRecord
:
4683 case kHFSFolderThreadRecord
:
4684 cnid
= recp
->hfsThread
.parentID
;
4688 case kHFSPlusFileThreadRecord
:
4689 case kHFSPlusFolderThreadRecord
:
4690 cnid
= recp
->hfsPlusThread
.parentID
;
4693 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4701 * Determine if a catalog node record is a directory.
4704 isadir(const CatalogRecord
*crp
)
4706 if (crp
->recordType
== kHFSPlusFolderRecord
) {
4710 if (crp
->recordType
== kHFSFolderRecord
) {
4719 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4720 * (not inode) using catalog record id. Note that this function does
4721 * NOT resolve directory hard link to its directory inode and return
4724 * Note: The caller is responsible for releasing the output catalog
4725 * descriptor (when supplied outdescp is non-null).
4728 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4729 u_int8_t forktype
, struct cat_desc
*outdescp
,
4730 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4732 struct BTreeIterator
*iterator
= NULL
;
4733 FSBufferDescriptor btdata
;
4736 CatalogRecord
*recp
= NULL
;
4739 /* No directory hard links on standard HFS */
4740 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4744 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4745 if (iterator
== NULL
) {
4748 bzero(iterator
, sizeof(*iterator
));
4749 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4751 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4756 BDINIT(btdata
, recp
);
4758 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4759 &btdata
, &datasize
, iterator
);
4763 /* Directory hard links are catalog file record */
4764 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4769 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4770 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4771 (keyp
->hfsPlus
.nodeName
.length
* 2);
4772 if (forktype
== kHFSResourceForkType
) {
4773 /* Lookup resource fork for directory hard link */
4774 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4776 /* Lookup data fork, if any, for directory hard link */
4777 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4780 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4781 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4784 /* Just for sanity, make sure that id in catalog record and thread record match */
4785 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4786 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4787 hfs_mark_inconsistent(hfsmp
, HFS_INCONSISTENCY_DETECTED
);
4795 FREE(iterator
, M_TEMP
);
4797 return MacToVFSError(error
);
4801 * cnode_update_dirlink - update the catalog node for directory hard link
4802 * described by descp using the data from attrp and forkp.
4805 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4806 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4808 if (forktype
== kHFSResourceForkType
) {
4809 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4811 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);