2 * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
33 #include <sys/mount.h>
34 #include <sys/vnode.h>
35 #include <sys/dirent.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
39 #include <sys/utfconv.h>
42 #include "hfs_catalog.h"
43 #include "hfs_format.h"
44 #include "hfs_endian.h"
46 #include "hfscommon/headers/BTreesInternal.h"
47 #include "hfscommon/headers/BTreesPrivate.h"
48 #include "hfscommon/headers/HFSUnicodeWrappers.h"
52 * Initialization of an FSBufferDescriptor structure.
54 #define BDINIT(bd, addr) { \
55 (bd).bufferAddress = (addr); \
56 (bd).itemSize = sizeof(*(addr)); \
62 BTreeIterator iterator
;
63 HFSPlusCatalogKey key
;
68 struct cat_desc
* s_desc
;
69 struct cat_attr
* s_attr
;
70 struct cat_fork
* s_datafork
;
71 struct cat_fork
* s_rsrcfork
;
72 struct hfsmount
* s_hfsmp
;
75 struct position_state
{
80 struct hfsmount
*hfsmp
;
83 /* Map file mode type to directory entry types */
84 u_char modetodirtype
[16] = {
85 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
86 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
87 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
88 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
90 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
93 #define HFS_LOOKUP_SYSFILE 0x1 /* If set, allow lookup of system files */
94 #define HFS_LOOKUP_HARDLINK 0x2 /* If set, allow lookup of hard link records and not resolve the hard links */
95 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
96 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
98 int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
99 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
101 /* Internal catalog support routines */
103 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
104 struct position_state
*state
);
106 static int resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
);
108 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
110 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
111 HFSPlusCatalogKey
*key
, int retry
);
113 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
115 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, u_int32_t
*recordSize
);
117 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
119 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
120 int isdir
, struct cat_desc
*descp
);
122 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
124 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
);
125 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
126 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
128 static cnid_t
getcnid(const CatalogRecord
*crp
);
129 static u_int32_t
getencoding(const CatalogRecord
*crp
);
130 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
132 static int isadir(const CatalogRecord
*crp
);
134 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
136 static int cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
);
138 static int cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
139 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
);
142 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, __unused proc_t p
)
147 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
148 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
150 result
= BTReserveSpace(hfsmp
->hfs_catalog_cp
->c_datafork
, ops
, (void*)cookie
);
153 hfs_systemfile_unlock(hfsmp
, lockflags
);
155 return MacToVFSError(result
);
159 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, __unused proc_t p
)
163 if (hfsmp
->hfs_catalog_cp
->c_lockowner
!= current_thread())
164 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
166 (void) BTReleaseReserve(hfsmp
->hfs_catalog_cp
->c_datafork
, (void*)cookie
);
169 hfs_systemfile_unlock(hfsmp
, lockflags
);
175 struct hfsmount
*hfsmp
,
176 CatalogRecord
* recp
,
177 struct cat_attr
*attrp
,
178 struct cat_fork
*datafp
,
179 struct cat_fork
*rsrcfp
)
181 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
184 struct HFSPlusCatalogFile cnoderec
;
186 promoteattr(hfsmp
, recp
, &cnoderec
);
187 getbsdattr(hfsmp
, &cnoderec
, attrp
);
189 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
193 bzero(datafp
, sizeof(*datafp
));
195 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
196 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
198 /* Convert the data fork. */
199 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
200 datafp
->cf_new_size
= 0;
201 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
202 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
203 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
204 datafp
->cf_bytesread
=
205 recp
->hfsPlusFile
.dataFork
.clumpSize
*
206 HFSTOVCB(hfsmp
)->blockSize
;
208 datafp
->cf_bytesread
= 0;
210 datafp
->cf_vblocks
= 0;
211 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
212 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
214 /* Convert the resource fork. */
215 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
216 rsrcfp
->cf_new_size
= 0;
217 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
218 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
219 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
220 datafp
->cf_bytesread
=
221 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
222 HFSTOVCB(hfsmp
)->blockSize
;
224 datafp
->cf_bytesread
= 0;
226 rsrcfp
->cf_vblocks
= 0;
227 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
228 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
233 * Convert a raw catalog key and record into an in-core catalog descriptor.
235 * Note: The caller is responsible for releasing the catalog descriptor.
240 struct hfsmount
*hfsmp
,
242 CatalogRecord
* recp
,
243 struct cat_desc
*descp
)
245 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
246 HFSPlusCatalogKey
* pluskey
= NULL
;
250 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
251 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
254 pluskey
= (HFSPlusCatalogKey
*)key
;
255 encoding
= getencoding(recp
);
258 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
260 FREE(pluskey
, M_TEMP
);
271 cat_releasedesc(struct cat_desc
*descp
)
273 const u_int8_t
* name
;
278 if ((descp
->cd_flags
& CD_HASBUF
) &&
279 (descp
->cd_nameptr
!= NULL
)) {
280 name
= descp
->cd_nameptr
;
281 descp
->cd_nameptr
= NULL
;
282 descp
->cd_namelen
= 0;
283 vfs_removename((const char *)name
);
285 descp
->cd_nameptr
= NULL
;
286 descp
->cd_namelen
= 0;
287 descp
->cd_flags
&= ~CD_HASBUF
;
291 * These Catalog functions allow access to the HFS Catalog (database).
292 * The catalog b-tree lock must be acquired before calling any of these routines.
296 * cat_lookup - lookup a catalog node using a cnode descriptor
298 * Note: The caller is responsible for releasing the output
299 * catalog descriptor (when supplied outdescp is non-null).
302 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
303 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
304 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
310 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
312 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
314 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
318 result
= cat_lookupbykey(hfsmp
, keyp
, 0, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
320 if (result
== ENOENT
) {
322 struct cat_desc temp_desc
;
323 if (outdescp
== NULL
) {
324 bzero(&temp_desc
, sizeof(temp_desc
));
325 outdescp
= &temp_desc
;
327 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
329 *desc_cnid
= outdescp
->cd_cnid
;
331 if (outdescp
== &temp_desc
) {
332 /* Release the local copy of desc */
333 cat_releasedesc(outdescp
);
335 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
336 // make MacRoman key from utf-8
337 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
338 // update desc text encoding so that other catalog ops succeed
348 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
350 struct BTreeIterator
*iterator
;
351 struct FSBufferDescriptor file_data
;
352 struct HFSCatalogFile file_rec
;
357 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
360 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
362 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
363 bzero(&iterator
[0], 2* sizeof(*iterator
));
364 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
368 BDINIT(file_data
, &file_rec
);
369 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
373 if (file_rec
.recordType
!= kHFSFileRecord
) {
378 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
379 struct FSBufferDescriptor thread_data
;
380 struct HFSCatalogThread thread_rec
;
382 file_rec
.flags
|= kHFSThreadExistsMask
;
383 BDINIT(thread_data
, &thread_rec
);
384 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
385 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
387 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
391 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
392 (void) BTFlushPath(fcb
);
395 (void) BTFlushPath(fcb
);
396 FREE(iterator
, M_TEMP
);
398 return MacToVFSError(result
);
403 * cat_findname - obtain a descriptor from cnid
405 * Only a thread lookup is performed.
407 * Note: The caller is responsible for releasing the output
408 * catalog descriptor (when supplied outdescp is non-null).
412 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
414 struct BTreeIterator
* iterator
;
415 FSBufferDescriptor btdata
;
417 CatalogRecord
* recp
;
423 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
425 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
426 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
427 iterator
->hint
.nodeNum
= 0;
429 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
430 BDINIT(btdata
, recp
);
432 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
436 /* Turn thread record into a cnode key (in place). */
437 switch (recp
->recordType
) {
438 case kHFSFolderThreadRecord
:
441 case kHFSFileThreadRecord
:
442 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
443 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
446 case kHFSPlusFolderThreadRecord
:
449 case kHFSPlusFileThreadRecord
:
450 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
451 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
452 (keyp
->hfsPlus
.nodeName
.length
* 2);
459 HFSPlusCatalogKey
* pluskey
= NULL
;
462 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
463 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
464 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
465 FREE(pluskey
, M_TEMP
);
468 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
472 FREE(iterator
, M_TEMP
);
474 return MacToVFSError(result
);
478 * cat_idlookup - lookup a catalog node using a cnode id
480 * Note: The caller is responsible for releasing the output
481 * catalog descriptor (when supplied outdescp is non-null).
484 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, int allow_system_files
, int wantrsrc
,
485 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
487 struct BTreeIterator
* iterator
;
488 FSBufferDescriptor btdata
;
491 CatalogRecord
* recp
;
495 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
497 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
498 bzero(iterator
, sizeof(*iterator
));
499 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
501 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
502 BDINIT(btdata
, recp
);
504 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
505 &btdata
, &datasize
, iterator
);
509 /* Turn thread record into a cnode key (in place) */
510 switch (recp
->recordType
) {
511 case kHFSFileThreadRecord
:
512 case kHFSFolderThreadRecord
:
513 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
515 /* check for NULL name */
516 if (keyp
->hfs
.nodeName
[0] == 0) {
521 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
524 case kHFSPlusFileThreadRecord
:
525 case kHFSPlusFolderThreadRecord
:
526 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
528 /* check for NULL name */
529 if (keyp
->hfsPlus
.nodeName
.length
== 0) {
534 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
535 (keyp
->hfsPlus
.nodeName
.length
* 2);
543 result
= cat_lookupbykey(hfsmp
, keyp
,
544 ((allow_system_files
!= 0) ? HFS_LOOKUP_SYSFILE
: 0),
545 0, wantrsrc
, outdescp
, attrp
, forkp
, NULL
);
546 /* No corresponding file/folder record found for a thread record,
547 * mark the volume inconsistent.
549 if (result
== 0 && outdescp
) {
550 cnid_t dcnid
= outdescp
->cd_cnid
;
552 * Just for sanity's case, let's make sure that
553 * the key in the thread matches the key in the record.
556 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid
, cnid
, dcnid
, dcnid
);
562 FREE(iterator
, M_TEMP
);
564 return MacToVFSError(result
);
569 * cat_lookupmangled - lookup a catalog node using a mangled name
572 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
573 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
578 u_int8_t utf8
[NAME_MAX
+ 1];
580 u_int16_t unicode
[kHFSPlusMaxFileNameChars
+ 1];
586 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
587 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
590 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
591 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
592 fileID
== hfsmp
->hfs_jnlfileid
||
593 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
597 result
= cat_idlookup(hfsmp
, fileID
, 0,0, outdescp
, attrp
, forkp
);
600 /* It must be in the correct directory */
601 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
605 * Compare the mangled version of file name looked up from the
606 * disk with the mangled name provided by the user. Note that
607 * this comparison is case-sensitive, which should be fine
608 * since we're trying to prevent user space from constructing
609 * a mangled name that differs from the one they'd get from the
612 result
= utf8_decodestr(outdescp
->cd_nameptr
, outdescp
->cd_namelen
,
613 unicode
, &unicodelen
, sizeof(unicode
), ':', 0);
617 result
= ConvertUnicodeToUTF8Mangled(unicodelen
, unicode
,
618 sizeof(utf8
), &utf8len
, utf8
, fileID
);
620 ((u_int16_t
)descp
->cd_namelen
!= utf8len
) ||
621 (bcmp(descp
->cd_nameptr
, utf8
, utf8len
) != 0)) {
628 cat_releasedesc(outdescp
);
634 * cat_lookupbykey - lookup a catalog node using a cnode key
637 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
638 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
640 struct BTreeIterator
* iterator
;
641 FSBufferDescriptor btdata
;
642 CatalogRecord
* recp
;
648 u_int32_t encoding
= 0;
651 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
653 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
654 BDINIT(btdata
, recp
);
655 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
656 bzero(iterator
, sizeof(*iterator
));
657 iterator
->hint
.nodeNum
= hint
;
658 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
660 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
661 &btdata
, &datasize
, iterator
);
665 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
666 cnid
= getcnid(recp
);
668 parentid
= keyp
->hfsPlus
.parentID
;
671 encoding
= getencoding(recp
);
672 hint
= iterator
->hint
.nodeNum
;
674 /* Hide the journal files (if any) */
675 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
676 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
677 !(flags
& HFS_LOOKUP_SYSFILE
)) {
683 * When a hardlink link is encountered, auto resolve it.
685 * The catalog record will change, and possibly its type.
689 && (recp
->recordType
== kHFSPlusFileRecord
)
690 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_itime
) ||
691 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
695 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
696 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
698 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
699 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
700 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
703 if ((isfilelink
|| isdirlink
) && !(flags
& HFS_LOOKUP_HARDLINK
)) {
704 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
705 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
711 struct HFSPlusCatalogFile cnoderec
;
713 promoteattr(hfsmp
, recp
, &cnoderec
);
714 getbsdattr(hfsmp
, &cnoderec
, attrp
);
716 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
718 /* Update the inode number for this hard link */
719 attrp
->ca_linkref
= ilink
;
723 * Set kHFSHasLinkChainBit for hard links, and reset it for all
724 * other items. Also set linkCount to 1 for regular files.
726 * Due to some bug (rdar://8505977), some regular files can have
727 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
728 * are not really hard links. The runtime code should not consider
729 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
730 * and linkCount for regular file before we vend it out. This might
731 * also result in repairing the bad files on disk, if the corresponding
732 * file is modified and updated on disk.
735 /* This is a hard link and the link count bit was not set */
736 if (!(attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
737 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp
->vcbVN
, cnid
, ilink
);
738 attrp
->ca_recflags
|= kHFSHasLinkChainMask
;
741 /* Make sure that this non-hard link (regular) record is not
742 * an inode record that was looked up and we do not end up
743 * reseting the hard link bit on it.
745 if ((parentid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
746 (parentid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
747 /* This is not a hard link or inode and the link count bit was set */
748 if (attrp
->ca_recflags
& kHFSHasLinkChainMask
) {
749 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp
->vcbVN
, cnid
);
750 attrp
->ca_recflags
&= ~kHFSHasLinkChainMask
;
752 /* This is a regular file and the link count was more than 1 */
753 if (S_ISREG(attrp
->ca_mode
) && (attrp
->ca_linkcount
> 1)) {
754 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp
->vcbVN
, cnid
, attrp
->ca_linkcount
);
755 attrp
->ca_linkcount
= 1;
763 bzero(forkp
, sizeof(*forkp
));
764 } else if (std_hfs
) {
765 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
766 } else if (wantrsrc
) {
767 /* Convert the resource fork. */
768 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
769 forkp
->cf_new_size
= 0;
770 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
771 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
772 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
773 forkp
->cf_bytesread
=
774 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
775 HFSTOVCB(hfsmp
)->blockSize
;
777 forkp
->cf_bytesread
= 0;
779 forkp
->cf_vblocks
= 0;
780 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
781 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
786 /* Convert the data fork. */
787 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
788 forkp
->cf_new_size
= 0;
789 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
790 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
791 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
792 forkp
->cf_bytesread
=
793 recp
->hfsPlusFile
.dataFork
.clumpSize
*
794 HFSTOVCB(hfsmp
)->blockSize
;
796 forkp
->cf_bytesread
= 0;
798 forkp
->cf_vblocks
= 0;
799 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
800 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
802 /* Validate the fork's resident extents. */
804 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
805 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
806 /* Suppress any bad extents so a remove can succeed. */
807 forkp
->cf_extents
[i
].startBlock
= 0;
808 forkp
->cf_extents
[i
].blockCount
= 0;
811 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
814 validblks
+= forkp
->cf_extents
[i
].blockCount
;
817 /* Adjust for any missing blocks. */
818 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
822 * This is technically a volume corruption.
823 * If the total number of blocks calculated by iterating + summing
824 * the extents in the resident extent records, is less than that
825 * which is reported in the catalog entry, we should force a fsck.
826 * Only modifying ca_blocks here is not guaranteed to make it out
827 * to disk; it is a runtime-only field.
829 * Note that we could have gotten into this state if we had invalid ranges
830 * that existed in borrowed blocks that somehow made it out to disk.
831 * The cnode's on disk block count should never be greater
832 * than that which is in its extent records.
835 (void) hfs_mark_volume_inconsistent (hfsmp
);
837 forkp
->cf_blocks
= validblks
;
839 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
841 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
842 if (psize
< forkp
->cf_size
) {
843 forkp
->cf_size
= psize
;
850 HFSPlusCatalogKey
* pluskey
= NULL
;
853 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
854 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
857 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
859 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
861 FREE(pluskey
, M_TEMP
);
865 if (desc_cnid
!= NULL
) {
869 FREE(iterator
, M_TEMP
);
872 return MacToVFSError(result
);
877 * cat_create - create a node in the catalog
879 * NOTE: both the catalog file and attribute file locks must
880 * be held before calling this function.
882 * The caller is responsible for releasing the output
883 * catalog descriptor (when supplied outdescp is non-null).
886 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
887 struct cat_desc
*out_descp
)
891 FSBufferDescriptor btdata
;
896 u_int32_t encoding
= kTextEncodingMacRoman
;
899 modeformat
= attrp
->ca_mode
& S_IFMT
;
901 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
902 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
905 * Get the next CNID. We can change it since we hold the catalog lock.
907 nextCNID
= hfsmp
->vcbNxtCNID
;
908 if (nextCNID
== 0xFFFFFFFF) {
912 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
913 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
914 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
915 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
922 /* Get space for iterator, key and data */
923 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
924 bto
->iterator
.hint
.nodeNum
= 0;
926 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
931 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
932 bto
->key
.nodeName
.length
);
933 hfs_setencodingbits(hfsmp
, encoding
);
937 * Insert the thread record first
939 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
940 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
941 S_ISDIR(attrp
->ca_mode
));
942 btdata
.bufferAddress
= &bto
->data
;
943 btdata
.itemSize
= datalen
;
944 btdata
.itemCount
= 1;
947 // this call requires the attribute file lock to be held
948 result
= file_attribute_exist(hfsmp
, nextCNID
);
949 if (result
== EEXIST
) {
950 // that cnid has orphaned attributes so just skip it.
951 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
952 nextCNID
= kHFSFirstUserCatalogNodeID
;
956 if (result
) goto exit
;
958 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
961 * If the CNID wraparound bit is set, then we need to validate if there
962 * is a cnode in the hash already with this ID (even if it no longer exists
963 * on disk). If so, then just skip this ID and move on to the next one.
965 if (!std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
966 if (hfs_chash_snoop (hfsmp
, nextCNID
, 1, NULL
, NULL
) == 0) {
967 /* It was found in the cnode hash!*/
973 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
976 if ((result
== btExists
) && !std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
978 * Allow CNIDs on HFS Plus volumes to wrap around
980 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
981 nextCNID
= kHFSFirstUserCatalogNodeID
;
987 if (result
) goto exit
;
991 * CNID is now established. If we have wrapped then
992 * update the vcbNxtCNID.
994 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
995 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
996 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
997 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
1002 * Now insert the file/directory record
1004 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
1005 btdata
.bufferAddress
= &bto
->data
;
1006 btdata
.itemSize
= datalen
;
1007 btdata
.itemCount
= 1;
1009 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
1011 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
1013 if (result
== btExists
)
1016 /* Back out the thread record */
1017 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
1018 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
1019 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
1020 /* Error on deleting extra thread record, mark
1021 * volume inconsistent
1023 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1024 hfs_mark_volume_inconsistent(hfsmp
);
1031 * Insert was successful, update name, parent and volume
1033 if (out_descp
!= NULL
) {
1034 HFSPlusCatalogKey
* pluskey
= NULL
;
1037 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1038 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1041 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1043 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
1044 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1046 FREE(pluskey
, M_TEMP
);
1049 attrp
->ca_fileid
= nextCNID
;
1052 (void) BTFlushPath(fcb
);
1055 return MacToVFSError(result
);
1060 * cnode_rename - rename a catalog node
1062 * Assumes that the target's directory exists.
1064 * Order of B-tree operations:
1065 * 1. BTSearchRecord(from_cnode, &data);
1066 * 2. BTInsertRecord(to_cnode, &data);
1067 * 3. BTDeleteRecord(from_cnode);
1068 * 4. BTDeleteRecord(from_thread);
1069 * 5. BTInsertRecord(to_thread);
1071 * Note: The caller is responsible for releasing the output
1072 * catalog descriptor (when supplied out_cdp is non-null).
1076 struct hfsmount
* hfsmp
,
1077 struct cat_desc
* from_cdp
,
1078 struct cat_desc
* todir_cdp
,
1079 struct cat_desc
* to_cdp
,
1080 struct cat_desc
* out_cdp
)
1082 struct BTreeIterator
* to_iterator
= NULL
;
1083 struct BTreeIterator
* from_iterator
= NULL
;
1084 FSBufferDescriptor btdata
;
1085 CatalogRecord
* recp
= NULL
;
1086 HFSPlusCatalogKey
* to_key
;
1093 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1096 u_int32_t encoding
= 0;
1098 vcb
= HFSTOVCB(hfsmp
);
1099 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1100 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1102 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1105 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
1106 bzero(from_iterator
, sizeof(*from_iterator
));
1107 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1110 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1111 bzero(to_iterator
, sizeof(*to_iterator
));
1112 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1115 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1116 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1117 BDINIT(btdata
, recp
);
1120 * When moving a directory, make sure its a valid move.
1122 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1123 struct BTreeIterator
*dir_iterator
= NULL
;
1125 cnid_t cnid
= from_cdp
->cd_cnid
;
1126 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1128 /* First check the obvious ones */
1129 if (cnid
== fsRtDirID
||
1130 cnid
== to_cdp
->cd_parentcnid
||
1135 /* now allocate the dir_iterator */
1136 MALLOC (dir_iterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
1137 if (dir_iterator
== NULL
) {
1140 bzero(dir_iterator
, sizeof(*dir_iterator
));
1143 * Traverse destination path all the way back to the root
1144 * making sure that source directory is not encountered.
1147 while (pathcnid
> fsRtDirID
) {
1148 buildthreadkey(pathcnid
, std_hfs
, (CatalogKey
*)&dir_iterator
->key
);
1149 result
= BTSearchRecord(fcb
, dir_iterator
, &btdata
, &datasize
, NULL
);
1151 FREE(dir_iterator
, M_TEMP
);
1154 pathcnid
= getparentcnid(recp
);
1155 if (pathcnid
== cnid
|| pathcnid
== 0) {
1157 FREE(dir_iterator
, M_TEMP
);
1161 FREE(dir_iterator
, M_TEMP
);
1165 * Step 1: Find cnode data at old location
1167 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1168 &datasize
, from_iterator
);
1170 if (std_hfs
|| (result
!= btNotFound
))
1173 struct cat_desc temp_desc
;
1175 /* Probably the node has mangled name */
1176 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1180 /* The file has mangled name. Search the cnode data using full name */
1181 bzero(from_iterator
, sizeof(*from_iterator
));
1182 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1184 cat_releasedesc(&temp_desc
);
1188 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1190 cat_releasedesc(&temp_desc
);
1194 cat_releasedesc(&temp_desc
);
1197 /* Check if the source is directory hard link. We do not change
1198 * directory flag because it is later used to initialize result descp
1202 (recp
->recordType
== kHFSPlusFileRecord
) &&
1203 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1208 * Update the text encoding (on disk and in descriptor).
1210 * Note that hardlink inodes don't require a text encoding hint.
1213 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1214 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1215 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1216 hfs_setencodingbits(hfsmp
, encoding
);
1217 recp
->hfsPlusFile
.textEncoding
= encoding
;
1219 out_cdp
->cd_encoding
= encoding
;
1222 if (std_hfs
&& !directory
&&
1223 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1227 * If the keys are identical then there's nothing left to do!
1229 * update the hint and exit
1232 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1234 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1238 /* Step 2: Insert cnode at new location */
1239 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1240 if (result
== btExists
) {
1241 int fromtype
= recp
->recordType
;
1243 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1244 goto exit
; /* EEXIST */
1246 /* Find cnode data at new location */
1247 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1251 if ((fromtype
!= recp
->recordType
) ||
1252 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1254 goto exit
; /* EEXIST */
1256 /* The old name is a case variant and must be removed */
1257 result
= BTDeleteRecord(fcb
, from_iterator
);
1261 /* Insert cnode (now that case duplicate is gone) */
1262 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1264 /* Try and restore original before leaving */
1269 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1271 printf("hfs: cat_create: could not undo (BTInsert = %d)", err
);
1272 hfs_mark_volume_inconsistent(hfsmp
);
1278 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1287 /* Step 3: Remove cnode from old location */
1289 result
= BTDeleteRecord(fcb
, from_iterator
);
1291 /* Try and delete new record before leaving */
1296 err
= BTDeleteRecord(fcb
, to_iterator
);
1298 printf("hfs: cat_create: could not undo (BTDelete = %d)", err
);
1299 hfs_mark_volume_inconsistent(hfsmp
);
1305 (void) BTDeleteRecord(fcb
, to_iterator
);
1311 /* #### POINT OF NO RETURN #### */
1314 * Step 4: Remove cnode's old thread record
1316 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1317 (void) BTDeleteRecord(fcb
, from_iterator
);
1320 * Step 5: Insert cnode's new thread record
1321 * (optional for HFS files)
1324 /* For directory hard links, always create a file thread
1325 * record. For everything else, use the directory flag.
1328 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1330 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1332 btdata
.itemSize
= datasize
;
1333 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1334 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1338 HFSPlusCatalogKey
* pluskey
= NULL
;
1341 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1342 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1344 /* Save the real encoding hint in the Finder Info (field 4). */
1345 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1348 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1349 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1353 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1355 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1356 encoding
, directory
, out_cdp
);
1358 FREE(pluskey
, M_TEMP
);
1362 (void) BTFlushPath(fcb
);
1364 FREE(from_iterator
, M_TEMP
);
1366 FREE(to_iterator
, M_TEMP
);
1369 return MacToVFSError(result
);
1374 * cat_delete - delete a node from the catalog
1376 * Order of B-tree operations:
1377 * 1. BTDeleteRecord(cnode);
1378 * 2. BTDeleteRecord(thread);
1379 * 3. BTUpdateRecord(parent);
1382 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1385 BTreeIterator
*iterator
;
1390 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1391 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1395 * The root directory cannot be deleted
1396 * A directory must be empty
1397 * A file must be zero length (no blocks)
1399 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1400 descp
->cd_parentcnid
== kHFSRootParentID
)
1403 /* XXX Preflight Missing */
1405 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1406 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1407 iterator
->hint
.nodeNum
= 0;
1410 * Derive a key from either the file ID (for a virtual inode)
1411 * or the descriptor.
1413 if (descp
->cd_namelen
== 0) {
1414 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1415 cnid
= attrp
->ca_fileid
;
1417 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1418 cnid
= descp
->cd_cnid
;
1424 result
= BTDeleteRecord(fcb
, iterator
);
1426 if (std_hfs
|| (result
!= btNotFound
))
1429 struct cat_desc temp_desc
;
1431 /* Probably the node has mangled name */
1432 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1436 /* The file has mangled name. Delete the file using full name */
1437 bzero(iterator
, sizeof(*iterator
));
1438 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1439 cnid
= temp_desc
.cd_cnid
;
1441 cat_releasedesc(&temp_desc
);
1445 result
= BTDeleteRecord(fcb
, iterator
);
1447 cat_releasedesc(&temp_desc
);
1451 cat_releasedesc(&temp_desc
);
1454 /* Delete thread record. On error, mark volume inconsistent */
1455 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1456 if (BTDeleteRecord(fcb
, iterator
)) {
1458 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1459 hfs_mark_volume_inconsistent(hfsmp
);
1464 (void) BTFlushPath(fcb
);
1466 return MacToVFSError(result
);
1471 * cat_update_internal - update the catalog node described by descp
1472 * using the data from attrp and forkp.
1473 * If update_hardlink is true, the hard link catalog record is updated
1474 * and not the inode catalog record.
1477 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1478 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1481 BTreeIterator
* iterator
;
1482 struct update_state state
;
1486 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1487 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1489 state
.s_desc
= descp
;
1490 state
.s_attr
= attrp
;
1491 state
.s_datafork
= dataforkp
;
1492 state
.s_rsrcfork
= rsrcforkp
;
1493 state
.s_hfsmp
= hfsmp
;
1495 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1496 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1499 * For open-deleted files we need to do a lookup by cnid
1500 * (using thread rec).
1502 * For hard links and if not requested by caller, the target
1503 * of the update is the inode itself (not the link record)
1504 * so a lookup by fileid (i.e. thread rec) is needed.
1506 if ((update_hardlink
== false) &&
1507 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1508 (descp
->cd_namelen
== 0) ||
1509 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1510 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1512 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1517 /* Pass a node hint */
1518 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1520 result
= BTUpdateRecord(fcb
, iterator
,
1521 (IterateCallBackProcPtr
)catrec_update
, &state
);
1525 /* Update the node hint. */
1526 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1529 (void) BTFlushPath(fcb
);
1531 return MacToVFSError(result
);
1535 * cat_update - update the catalog node described by descp
1536 * using the data from attrp and forkp.
1539 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1540 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1542 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1546 * catrec_update - Update the fields of a catalog record
1547 * This is called from within BTUpdateRecord.
1550 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1552 struct cat_desc
*descp
;
1553 struct cat_attr
*attrp
;
1554 struct cat_fork
*forkp
;
1555 struct hfsmount
*hfsmp
;
1559 descp
= state
->s_desc
;
1560 attrp
= state
->s_attr
;
1561 hfsmp
= state
->s_hfsmp
;
1562 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1564 switch (crp
->recordType
) {
1565 case kHFSFolderRecord
: {
1566 HFSCatalogFolder
*dir
;
1568 dir
= (struct HFSCatalogFolder
*)crp
;
1569 /* Do a quick sanity check */
1570 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1571 (dir
->folderID
!= descp
->cd_cnid
))
1572 return (btNotFound
);
1573 dir
->valence
= attrp
->ca_entries
;
1574 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1575 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1576 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1577 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1578 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1581 case kHFSFileRecord
: {
1582 HFSCatalogFile
*file
;
1584 file
= (struct HFSCatalogFile
*)crp
;
1585 /* Do a quick sanity check */
1586 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1587 (file
->fileID
!= attrp
->ca_fileid
))
1588 return (btNotFound
);
1589 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1590 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1591 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1592 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1593 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1594 if (state
->s_rsrcfork
) {
1595 forkp
= state
->s_rsrcfork
;
1596 file
->rsrcLogicalSize
= forkp
->cf_size
;
1597 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1598 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1599 file
->rsrcExtents
[i
].startBlock
=
1600 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1601 file
->rsrcExtents
[i
].blockCount
=
1602 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1605 if (state
->s_datafork
) {
1606 forkp
= state
->s_datafork
;
1607 file
->dataLogicalSize
= forkp
->cf_size
;
1608 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1609 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1610 file
->dataExtents
[i
].startBlock
=
1611 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1612 file
->dataExtents
[i
].blockCount
=
1613 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1617 /* Synchronize the lock state */
1618 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1619 file
->flags
|= kHFSFileLockedMask
;
1621 file
->flags
&= ~kHFSFileLockedMask
;
1624 case kHFSPlusFolderRecord
: {
1625 HFSPlusCatalogFolder
*dir
;
1627 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1628 /* Do a quick sanity check */
1629 if (dir
->folderID
!= attrp
->ca_fileid
) {
1630 printf("hfs: catrec_update: id %d != %d\n", dir
->folderID
, attrp
->ca_fileid
);
1631 return (btNotFound
);
1633 dir
->flags
= attrp
->ca_recflags
;
1634 dir
->valence
= attrp
->ca_entries
;
1635 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1636 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1637 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1638 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1639 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1640 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1641 /* Note: directory hardlink inodes don't require a text encoding hint. */
1642 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1643 dir
->textEncoding
= descp
->cd_encoding
;
1645 dir
->folderCount
= attrp
->ca_dircount
;
1646 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1648 * Update the BSD Info if it was already initialized on
1649 * disk or if the runtime values have been modified.
1651 * If the BSD info was already initialized, but
1652 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1653 * probably different than what was on disk. We don't want
1654 * to overwrite the on-disk values (so if we turn off
1655 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1656 * This way, we can still change fields like the mode or
1657 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1659 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1660 * won't change the uid or gid from their defaults. So, if
1661 * the BSD info wasn't set, and the runtime values are not
1662 * default, then what changed was the mode or flags. We
1663 * have to set the uid and gid to something, so use the
1664 * supplied values (which will be default), which has the
1665 * same effect as creating a new file while
1666 * MNT_UNKNOWNPERMISSIONS is set.
1668 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1669 (attrp
->ca_flags
!= 0) ||
1670 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1671 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1672 ((attrp
->ca_mode
& ALLPERMS
) !=
1673 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1674 if ((dir
->bsdInfo
.fileMode
== 0) ||
1675 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1676 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1677 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1679 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1680 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1681 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1682 /* A directory hardlink has a link count. */
1683 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1684 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1689 case kHFSPlusFileRecord
: {
1690 HFSPlusCatalogFile
*file
;
1693 file
= (struct HFSPlusCatalogFile
*)crp
;
1694 /* Do a quick sanity check */
1695 if (file
->fileID
!= attrp
->ca_fileid
)
1696 return (btNotFound
);
1697 file
->flags
= attrp
->ca_recflags
;
1698 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1699 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1700 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1701 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1702 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1703 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1705 * Note: file hardlink inodes don't require a text encoding
1706 * hint, but they do have a first link value.
1708 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1709 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1711 file
->textEncoding
= descp
->cd_encoding
;
1713 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1715 * Update the BSD Info if it was already initialized on
1716 * disk or if the runtime values have been modified.
1718 * If the BSD info was already initialized, but
1719 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1720 * probably different than what was on disk. We don't want
1721 * to overwrite the on-disk values (so if we turn off
1722 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1723 * This way, we can still change fields like the mode or
1724 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1726 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1727 * won't change the uid or gid from their defaults. So, if
1728 * the BSD info wasn't set, and the runtime values are not
1729 * default, then what changed was the mode or flags. We
1730 * have to set the uid and gid to something, so use the
1731 * supplied values (which will be default), which has the
1732 * same effect as creating a new file while
1733 * MNT_UNKNOWNPERMISSIONS is set.
1735 * Do not modify bsdInfo for directory hard link records.
1736 * They are set during creation and are not modifiable, so just
1739 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1740 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1741 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1744 ((file
->bsdInfo
.fileMode
!= 0) ||
1745 (attrp
->ca_flags
!= 0) ||
1746 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1747 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1748 ((attrp
->ca_mode
& ALLPERMS
) !=
1749 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1750 if ((file
->bsdInfo
.fileMode
== 0) ||
1751 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1752 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1753 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1755 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1756 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1757 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1759 if (state
->s_rsrcfork
) {
1760 forkp
= state
->s_rsrcfork
;
1761 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1762 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1763 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1764 sizeof(HFSPlusExtentRecord
));
1765 /* Push blocks read to disk */
1766 file
->resourceFork
.clumpSize
=
1767 howmany(forkp
->cf_bytesread
, blksize
);
1769 if (state
->s_datafork
) {
1770 forkp
= state
->s_datafork
;
1771 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1772 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1773 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1774 sizeof(HFSPlusExtentRecord
));
1775 /* Push blocks read to disk */
1776 file
->dataFork
.clumpSize
=
1777 howmany(forkp
->cf_bytesread
, blksize
);
1780 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1781 (file
->resourceFork
.extents
[0].startBlock
==
1782 file
->dataFork
.extents
[0].startBlock
)) {
1783 panic("hfs: catrec_update: rsrc fork == data fork");
1786 /* Synchronize the lock state */
1787 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1788 file
->flags
|= kHFSFileLockedMask
;
1790 file
->flags
&= ~kHFSFileLockedMask
;
1792 /* Push out special field if necessary */
1793 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
1794 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1798 * Protect against the degenerate case where the descriptor contains the
1799 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
1800 * the linkcount was greater than 1 (the default value), then it must have become
1801 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
1803 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
1804 (file
->hl_linkCount
> 1)) {
1805 file
->hl_linkCount
= attrp
->ca_linkcount
;
1811 return (btNotFound
);
1816 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
1817 * catalog btree of given cnid by walking up the parent chain till it reaches
1818 * either the root folder, or the private metadata directory for storing
1819 * directory hard links. This function updates the corresponding in-core
1820 * cnode, if any, and the directory record in the catalog btree.
1821 * On success, returns zero. On failure, returns non-zero value.
1825 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
1829 struct cat_desc desc
;
1830 struct cat_attr attr
;
1832 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
1833 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
1834 /* Update the bit in corresponding cnode, if any, in the hash.
1835 * If the cnode has the bit already set, stop the traversal.
1837 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
1842 /* Update the catalog record on disk if either cnode was not
1843 * found in the hash, or if a cnode was found and the cnode
1844 * did not have the bit set previously.
1846 retval
= hfs_start_transaction(hfsmp
);
1850 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1852 /* Look up our catalog folder record */
1853 retval
= cat_idlookup(hfsmp
, cnid
, 0, 0, &desc
, &attr
, NULL
);
1855 hfs_systemfile_unlock(hfsmp
, lockflags
);
1856 hfs_end_transaction(hfsmp
);
1860 /* Update the bit in the catalog record */
1861 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
1862 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
1864 hfs_systemfile_unlock(hfsmp
, lockflags
);
1865 hfs_end_transaction(hfsmp
);
1866 cat_releasedesc(&desc
);
1870 hfs_systemfile_unlock(hfsmp
, lockflags
);
1871 hfs_end_transaction(hfsmp
);
1873 cnid
= desc
.cd_parentcnid
;
1874 cat_releasedesc(&desc
);
1880 /* This function traverses the parent directory hierarchy from the given
1881 * directory to one level below root directory and checks if any of its
1883 * 1. A directory hard link.
1884 * 2. The 'pointed at' directory.
1885 * If any of these conditions fail or an internal error is encountered
1886 * during look up of the catalog record, this function returns non-zero value.
1890 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
1892 HFSPlusCatalogKey
*keyp
;
1894 FSBufferDescriptor btdata
;
1895 HFSPlusCatalogFolder folder
;
1901 BDINIT(btdata
, &folder
);
1902 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
1903 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
1904 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1906 while (cnid
!= kHFSRootParentID
) {
1907 /* Check if the 'pointed at' directory is an ancestor */
1908 if (pointed_at_cnid
== cnid
) {
1912 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
1913 printf("hfs: cat_check_link_ancestry: getkey for %u failed\n", cnid
);
1914 invalid
= 1; /* On errors, assume an invalid parent */
1917 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
1918 printf("hfs: cat_check_link_ancestry: cannot find %u\n", cnid
);
1919 invalid
= 1; /* On errors, assume an invalid parent */
1922 /* Check if this ancestor is a directory hard link */
1923 if (folder
.flags
& kHFSHasLinkChainMask
) {
1927 cnid
= keyp
->parentID
;
1935 * update_siblinglinks_callback - update a link's chain
1938 struct linkupdate_state
{
1945 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
1947 HFSPlusCatalogFile
*file
;
1949 if (crp
->recordType
!= kHFSPlusFileRecord
) {
1950 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
1951 return (btNotFound
);
1954 file
= (struct HFSPlusCatalogFile
*)crp
;
1955 if (file
->flags
& kHFSHasLinkChainMask
) {
1956 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
1957 file
->hl_prevLinkID
= state
->prevlinkid
;
1959 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
1960 file
->hl_nextLinkID
= state
->nextlinkid
;
1963 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
1969 * cat_update_siblinglinks - update a link's chain
1972 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
1975 BTreeIterator
* iterator
;
1976 struct linkupdate_state state
;
1979 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1980 state
.filelinkid
= linkfileid
;
1981 state
.prevlinkid
= prevlinkid
;
1982 state
.nextlinkid
= nextlinkid
;
1984 /* Create an iterator for use by us temporarily */
1985 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1986 bzero(iterator
, sizeof(*iterator
));
1988 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
1990 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
1991 (void) BTFlushPath(fcb
);
1993 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid %d\n", linkfileid
);
1996 FREE (iterator
, M_TEMP
);
1997 return MacToVFSError(result
);
2001 * cat_lookuplink - lookup a link by it's name
2004 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2007 BTreeIterator
* iterator
;
2008 struct FSBufferDescriptor btdata
;
2009 struct HFSPlusCatalogFile file
;
2012 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2014 /* Create an iterator for use by us temporarily */
2015 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2016 bzero(iterator
, sizeof(*iterator
));
2018 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2021 BDINIT(btdata
, &file
);
2023 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2026 if (file
.recordType
!= kHFSPlusFileRecord
) {
2030 *linkfileid
= file
.fileID
;
2032 if (file
.flags
& kHFSHasLinkChainMask
) {
2033 *prevlinkid
= file
.hl_prevLinkID
;
2034 *nextlinkid
= file
.hl_nextLinkID
;
2040 FREE(iterator
, M_TEMP
);
2041 return MacToVFSError(result
);
2046 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2049 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2052 BTreeIterator
* iterator
;
2053 struct FSBufferDescriptor btdata
;
2054 struct HFSPlusCatalogFile file
;
2057 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2059 /* Create an iterator for use by us temporarily */
2060 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2061 bzero(iterator
, sizeof(*iterator
));
2063 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2066 BDINIT(btdata
, &file
);
2068 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2071 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2072 if (file
.flags
& kHFSHasLinkChainMask
) {
2075 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2077 /* directory inodes don't have a chain (its in an EA) */
2078 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2079 result
= ENOLINK
; /* signal to caller to get head of list */
2080 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2082 *nextlinkid
= file
.hl_firstLinkID
;
2084 *prevlinkid
= file
.hl_prevLinkID
;
2085 *nextlinkid
= file
.hl_nextLinkID
;
2092 FREE(iterator
, M_TEMP
);
2093 return MacToVFSError(result
);
2098 * cat_createlink - create a link in the catalog
2100 * The following cat_attr fields are expected to be set:
2106 * ca_finderinfo (type and creator)
2109 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2110 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2114 FSBufferDescriptor btdata
;
2115 HFSPlusForkData
*rsrcforkp
;
2119 int thread_inserted
= 0;
2120 int alias_allocated
= 0;
2124 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
2126 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2129 * Get the next CNID. We can change it since we hold the catalog lock.
2131 nextCNID
= hfsmp
->vcbNxtCNID
;
2132 if (nextCNID
== 0xFFFFFFFF) {
2133 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
2134 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2135 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
2136 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2138 hfsmp
->vcbNxtCNID
++;
2140 MarkVCBDirty(hfsmp
);
2142 /* Get space for iterator, key and data */
2143 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
2144 bto
->iterator
.hint
.nodeNum
= 0;
2145 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2147 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2149 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2153 /* This is our only chance to set the encoding (other than a rename). */
2154 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2156 /* Insert the thread record first. */
2157 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2158 btdata
.bufferAddress
= &bto
->data
;
2159 btdata
.itemSize
= datalen
;
2160 btdata
.itemCount
= 1;
2163 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2166 * If the CNID wraparound bit is set, then we need to validate if there
2167 * is a cnode in the hash already with this ID (even if it no longer exists
2168 * on disk). If so, then just skip this ID and move on to the next one.
2170 if (!std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2171 /* Verify that the CNID does not already exist in the cnode hash... */
2172 if (hfs_chash_snoop (hfsmp
, nextCNID
, 1, NULL
, NULL
) == 0) {
2173 /* It was found in the cnode hash!*/
2179 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2182 if ((result
== btExists
) && (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2184 * Allow CNIDs on HFS Plus volumes to wrap around
2186 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
2187 nextCNID
= kHFSFirstUserCatalogNodeID
;
2192 thread_inserted
= 1;
2200 * CNID is now established. If we have wrapped then
2201 * update the vcbNxtCNID.
2203 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2204 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
2205 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
2206 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2211 * Now insert the link record.
2213 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2215 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2216 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2217 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2219 /* For directory hard links, create alias in resource fork */
2220 if (descp
->cd_flags
& CD_ISDIR
) {
2221 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2224 alias_allocated
= 1;
2226 btdata
.bufferAddress
= &bto
->data
;
2227 btdata
.itemSize
= datalen
;
2228 btdata
.itemCount
= 1;
2230 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2232 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2234 if (result
== btExists
)
2238 if (linkfileid
!= NULL
) {
2239 *linkfileid
= nextCNID
;
2243 if (thread_inserted
) {
2244 printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result
));
2246 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2247 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2248 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2249 hfs_mark_volume_inconsistent(hfsmp
);
2252 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2253 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2254 rsrcforkp
->extents
[0].blockCount
, 0);
2255 rsrcforkp
->extents
[0].startBlock
= 0;
2256 rsrcforkp
->extents
[0].blockCount
= 0;
2259 (void) BTFlushPath(fcb
);
2262 return MacToVFSError(result
);
2265 /* Directory hard links are visible as aliases on pre-Leopard systems and
2266 * as normal directories on Leopard or later. All directory hard link aliases
2267 * have the same resource fork content except for the three uniquely
2268 * identifying values that are updated in the resource fork data when the alias
2269 * is created. The following array is the constant resource fork data used
2270 * only for creating directory hard link aliases.
2272 static const char hfs_dirlink_alias_rsrc
[] = {
2273 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2276 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2278 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2280 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2281 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2282 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2283 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2284 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2285 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2289 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2290 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2291 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2296 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2297 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2298 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2299 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2301 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2304 /* Constants for directory hard link alias */
2306 /* Size of resource fork data array for directory hard link alias */
2307 kHFSAliasSize
= 0x1d0,
2309 /* Volume type for ejectable devices like disk image */
2310 kHFSAliasVolTypeEjectable
= 0x5,
2312 /* Offset for volume create date, in Mac OS local time */
2313 kHFSAliasVolCreateDateOffset
= 0x12a,
2315 /* Offset for the type of volume */
2316 kHFSAliasVolTypeOffset
= 0x130,
2318 /* Offset for folder ID of the parent directory of the directory inode */
2319 kHFSAliasParentIDOffset
= 0x132,
2321 /* Offset for folder ID of the directory inode */
2322 kHFSAliasTargetIDOffset
= 0x176,
2325 /* Create and write an alias that points at the directory represented by given
2326 * inode number on the same volume. Directory hard links are visible as
2327 * aliases in pre-Leopard systems and this function creates these aliases.
2329 * Note: This code is very specific to creating alias for the purpose
2330 * of directory hard links only, and should not be generalized.
2333 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2341 HFSPlusForkData
*rsrcforkp
;
2345 rsrcforkp
= &(crp
->resourceFork
);
2347 blksize
= hfsmp
->blockSize
;
2348 blkcount
= howmany(kHFSAliasSize
, blksize
);
2349 sectorsize
= hfsmp
->hfs_logical_block_size
;
2350 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2352 /* Allocate some disk space for the alias content. */
2353 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2354 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2355 &rsrcforkp
->extents
[0].startBlock
,
2356 &rsrcforkp
->extents
[0].blockCount
);
2358 rsrcforkp
->extents
[0].startBlock
= 0;
2362 /* Acquire a buffer cache block for our block. */
2363 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2364 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2366 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2368 journal_modify_block_start(hfsmp
->jnl
, bp
);
2371 /* Generate alias content */
2372 alias
= (char *)buf_dataptr(bp
);
2373 bzero(alias
, buf_size(bp
));
2374 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2376 /* Set the volume create date, local time in Mac OS format */
2377 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2378 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2380 /* If the file system is on a virtual device like disk image,
2381 * update the volume type to be ejectable device.
2383 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2384 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2385 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2388 /* Set id of the parent of the target directory */
2389 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2390 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2392 /* Set id of the target directory */
2393 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2394 *valptr
= OSSwapHostToBigInt32(inode_num
);
2396 /* Write alias content to disk. */
2398 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2399 } else if ((result
= buf_bwrite(bp
))) {
2403 /* Finish initializing the fork data. */
2404 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2405 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2408 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2409 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2410 rsrcforkp
->extents
[0].startBlock
= 0;
2411 rsrcforkp
->extents
[0].blockCount
= 0;
2412 rsrcforkp
->logicalSize
= 0;
2413 rsrcforkp
->totalBlocks
= 0;
2419 * cat_deletelink - delete a link from the catalog
2422 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2424 struct HFSPlusCatalogFile file
;
2425 struct cat_attr cattr
;
2426 uint32_t totalBlocks
;
2430 bzero(&file
, sizeof (file
));
2431 bzero(&cattr
, sizeof (cattr
));
2432 cattr
.ca_fileid
= descp
->cd_cnid
;
2434 /* Directory links have alias content to remove. */
2435 if (descp
->cd_flags
& CD_ISDIR
) {
2437 BTreeIterator
* iterator
;
2438 struct FSBufferDescriptor btdata
;
2440 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2442 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2443 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2444 iterator
->hint
.nodeNum
= 0;
2446 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2449 BDINIT(btdata
, &file
);
2451 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2456 result
= cat_delete(hfsmp
, descp
, &cattr
);
2458 if ((result
== 0) &&
2459 (descp
->cd_flags
& CD_ISDIR
) &&
2460 (file
.recordType
== kHFSPlusFileRecord
)) {
2462 totalBlocks
= file
.resourceFork
.totalBlocks
;
2464 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2465 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2466 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2470 (void) BlockDeallocate(hfsmp
,
2471 file
.resourceFork
.extents
[i
].startBlock
,
2472 file
.resourceFork
.extents
[i
].blockCount
, 0);
2474 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2475 file
.resourceFork
.extents
[i
].startBlock
= 0;
2476 file
.resourceFork
.extents
[i
].blockCount
= 0;
2485 * Callback to collect directory entries.
2486 * Called with readattr_state for each item in a directory.
2488 struct readattr_state
{
2489 struct hfsmount
*hfsmp
;
2490 struct cat_entrylist
*list
;
2497 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2498 struct readattr_state
*state
)
2500 struct cat_entrylist
*list
= state
->list
;
2501 struct hfsmount
*hfsmp
= state
->hfsmp
;
2502 struct cat_entry
*cep
;
2505 if (list
->realentries
>= list
->maxentries
)
2506 return (0); /* stop */
2508 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2510 switch(rec
->recordType
) {
2511 case kHFSPlusFolderRecord
:
2512 case kHFSPlusFileRecord
:
2513 case kHFSFolderRecord
:
2514 case kHFSFileRecord
:
2515 if (parentcnid
!= state
->dir_cnid
) {
2516 state
->error
= ENOENT
;
2517 return (0); /* stop */
2521 state
->error
= ENOENT
;
2522 return (0); /* stop */
2525 /* Hide the private system directories and journal files */
2526 if (parentcnid
== kHFSRootFolderID
) {
2527 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2528 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2529 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2530 list
->skipentries
++;
2531 return (1); /* continue */
2534 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2535 (rec
->recordType
== kHFSPlusFileRecord
) &&
2536 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2537 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2538 list
->skipentries
++;
2539 return (1); /* continue */
2543 cep
= &list
->entry
[list
->realentries
++];
2545 if (state
->stdhfs
) {
2546 struct HFSPlusCatalogFile cnoderec
;
2547 HFSPlusCatalogKey
* pluskey
;
2550 promoteattr(hfsmp
, rec
, &cnoderec
);
2551 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2553 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2554 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2555 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2556 FREE(pluskey
, M_TEMP
);
2558 if (rec
->recordType
== kHFSFileRecord
) {
2559 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2561 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2562 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2563 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2564 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2567 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2568 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2569 isadir(rec
), &cep
->ce_desc
);
2571 if (rec
->recordType
== kHFSPlusFileRecord
) {
2572 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2573 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2574 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2575 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2577 /* Save link reference for later processing. */
2578 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2579 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2580 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2581 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2582 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2583 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2584 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2589 return (list
->realentries
< list
->maxentries
);
2593 * Pack a cat_entrylist buffer with attributes from the catalog
2595 * Note: index is zero relative
2598 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
2602 BTreeIterator
* iterator
;
2603 struct readattr_state state
;
2611 ce_list
->realentries
= 0;
2613 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2614 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2615 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2617 state
.hfsmp
= hfsmp
;
2618 state
.list
= ce_list
;
2619 state
.dir_cnid
= parentcnid
;
2620 state
.stdhfs
= std_hfs
;
2623 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2624 bzero(iterator
, sizeof(*iterator
));
2625 key
= (CatalogKey
*)&iterator
->key
;
2627 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2628 index
= dirhint
->dh_index
+ 1;
2631 * Attempt to build a key from cached filename
2633 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2634 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2640 * If the last entry wasn't cached then position the btree iterator
2642 if ((index
== 0) || !have_key
) {
2644 * Position the iterator at the directory's thread record.
2645 * (i.e. just before the first entry)
2647 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2648 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2650 result
= MacToVFSError(result
);
2655 * Iterate until we reach the entry just
2656 * before the one we want to start with.
2659 struct position_state ps
;
2664 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2667 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2668 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2672 result
= MacToVFSError(result
);
2674 result
= MacToVFSError(result
);
2680 /* Fill list with entries starting at iterator->key. */
2681 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2682 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2685 result
= state
.error
;
2686 else if (ce_list
->realentries
== 0)
2689 result
= MacToVFSError(result
);
2695 * Resolve any hard links.
2697 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2698 struct FndrFileInfo
*fip
;
2699 struct cat_entry
*cep
;
2700 struct HFSPlusCatalogFile filerec
;
2704 cep
= &ce_list
->entry
[i
];
2705 if (cep
->ce_attr
.ca_linkref
== 0)
2708 /* Note: Finder info is still in Big Endian */
2709 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
2711 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2712 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2713 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2716 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2717 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
2718 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
2719 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
2722 if (isfilelink
|| isdirlink
) {
2723 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
2725 /* Repack entry from inode record. */
2726 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
2727 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
2728 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
2729 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
2730 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
2734 FREE(iterator
, M_TEMP
);
2736 return MacToVFSError(result
);
2739 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
2742 * Callback to pack directory entries.
2743 * Called with packdirentry_state for each item in a directory.
2746 /* Hard link information collected during cat_getdirentries. */
2749 user_addr_t dirent_addr
;
2751 typedef struct linkinfo linkinfo_t
;
2753 /* State information for the getdirentries_callback function. */
2754 struct packdirentry_state
{
2755 int cbs_flags
; /* VNODE_READDIR_* flags */
2756 u_int32_t cbs_parentID
;
2757 u_int32_t cbs_index
;
2759 ExtendedVCB
* cbs_hfsmp
;
2762 int32_t cbs_maxlinks
;
2763 linkinfo_t
* cbs_linkinfo
;
2764 struct cat_desc
* cbs_desc
;
2765 u_int8_t
* cbs_namebuf
;
2767 * The following fields are only used for NFS readdir, which
2768 * uses the next file id as the seek offset of each entry.
2770 struct direntry
* cbs_direntry
;
2771 struct direntry
* cbs_prevdirentry
;
2772 u_int32_t cbs_previlinkref
;
2773 Boolean cbs_hasprevdirentry
;
2778 * getdirentries callback for HFS Plus directories.
2781 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2782 struct packdirentry_state
*state
)
2784 struct hfsmount
*hfsmp
;
2785 const CatalogName
*cnp
;
2788 struct dirent catent
;
2789 struct direntry
* entry
= NULL
;
2791 u_int32_t ilinkref
= 0;
2792 u_int32_t curlinkref
= 0;
2795 u_int8_t type
= DT_UNKNOWN
;
2796 u_int8_t is_mangled
= 0;
2797 u_int8_t is_link
= 0;
2799 user_addr_t uiobase
= USER_ADDR_NULL
;
2804 Boolean stop_after_pack
= false;
2806 hfsmp
= state
->cbs_hfsmp
;
2807 curID
= ckp
->hfsPlus
.parentID
;
2809 /* We're done when parent directory changes */
2810 if (state
->cbs_parentID
!= curID
) {
2812 * If the parent ID is different from curID this means we've hit
2813 * the EOF for the directory. To help future callers, we mark
2814 * the cbs_eof boolean. However, we should only mark the EOF
2815 * boolean if we're about to return from this function.
2817 * This is because this callback function does its own uiomove
2818 * to get the data to userspace. If we set the boolean before determining
2819 * whether or not the current entry has enough room to write its
2820 * data to userland, we could fool the callers of this catalog function
2821 * into thinking they've hit EOF earlier than they really would have.
2822 * In that case, we'd know that we have more entries to process and
2823 * send to userland, but we didn't have enough room.
2825 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
2826 * about to return and won't write any new data back
2827 * to userland. In the stop_after_pack case, we'll set this boolean
2828 * regardless, so it's slightly safer to let that logic mark the boolean,
2829 * especially since it's closer to the return of this function.
2832 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
2833 /* The last record has not been returned yet, so we
2834 * want to stop after packing the last item
2836 if (state
->cbs_hasprevdirentry
) {
2837 stop_after_pack
= true;
2839 state
->cbs_eof
= true;
2840 state
->cbs_result
= ENOENT
;
2841 return (0); /* stop */
2844 state
->cbs_eof
= true;
2845 state
->cbs_result
= ENOENT
;
2846 return (0); /* stop */
2850 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
2851 entry
= state
->cbs_direntry
;
2852 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
2853 if (state
->cbs_flags
& VNODE_READDIR_NAMEMAX
) {
2855 * The NFS server sometimes needs to make filenames fit in
2856 * NAME_MAX bytes (since its client may not be able to
2857 * handle a longer name). In that case, NFS will ask us
2858 * to mangle the name to keep it short enough.
2860 maxnamelen
= NAME_MAX
;
2862 maxnamelen
= sizeof(entry
->d_name
);
2865 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2866 maxnamelen
= sizeof(catent
.d_name
);
2869 if ((state
->cbs_flags
& VNODE_READDIR_EXTENDED
) && stop_after_pack
) {
2870 /* The last item returns a non-zero invalid cookie */
2873 switch(crp
->recordType
) {
2874 case kHFSPlusFolderRecord
:
2876 cnid
= crp
->hfsPlusFolder
.folderID
;
2877 /* Hide our private system directories. */
2878 if (curID
== kHFSRootFolderID
) {
2879 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2880 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2885 case kHFSPlusFileRecord
:
2886 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
2887 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
2888 cnid
= crp
->hfsPlusFile
.fileID
;
2890 * When a hardlink link is encountered save its link ref.
2892 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2893 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
2894 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2895 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2896 /* If link ref is inode's file id then use it directly. */
2897 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
2898 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2900 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
2903 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2904 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
2905 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2906 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
2907 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2908 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2909 /* A directory's link resolves to a directory. */
2911 /* A directory's link ref is always inode's file id. */
2912 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2915 /* Hide the journal files */
2916 if ((curID
== kHFSRootFolderID
) &&
2917 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
2918 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
2919 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
2924 return (0); /* stop */
2927 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
2929 namelen
= cnp
->ustr
.length
;
2931 * For MacRoman encoded names, assume that its ascii and
2932 * convert it directly in an attempt to avoid the more
2933 * expensive utf8_encodestr conversion.
2935 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
2938 const u_int16_t
*chp
;
2940 chp
= &cnp
->ustr
.unicode
[0];
2941 for (i
= 0; i
< (int)namelen
; ++i
) {
2943 if (ch
> 0x007f || ch
== 0x0000) {
2944 /* Perform expensive utf8_encodestr conversion */
2947 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
2949 nameptr
[namelen
] = '\0';
2953 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
2954 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
2957 /* Check result returned from encoding the filename to utf8 */
2958 if (result
== ENAMETOOLONG
) {
2960 * If we were looking at a catalog record for a hardlink (not the inode),
2961 * then we want to use its link ID as opposed to the inode ID for
2962 * a mangled name. For all other cases, they are the same. Note that
2963 * due to the way directory hardlinks are implemented, the actual link
2964 * is going to be counted as a file record, so we can catch both
2967 cnid_t linkid
= cnid
;
2969 linkid
= crp
->hfsPlusFile
.fileID
;
2972 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
2973 cnp
->ustr
.unicode
, maxnamelen
+ 1,
2974 (ByteCount
*)&namelen
, nameptr
, linkid
);
2979 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
2981 * The index is 1 relative and includes "." and ".."
2983 * Also stuff the cnid in the upper 32 bits of the cookie.
2984 * The cookie is stored to the previous entry, which will
2985 * be packed and copied this time
2987 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
2988 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
2989 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
2991 catent
.d_type
= type
;
2992 catent
.d_namlen
= namelen
;
2993 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2995 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
2997 catent
.d_fileno
= cnid
;
2998 uioaddr
= (caddr_t
) &catent
;
3001 /* Save current base address for post processing of hard-links. */
3002 if (ilinkref
|| state
->cbs_previlinkref
) {
3003 uiobase
= uio_curriovbase(state
->cbs_uio
);
3005 /* If this entry won't fit then we're done */
3006 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
3007 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
3008 return (0); /* stop */
3011 if (!(state
->cbs_flags
& VNODE_READDIR_EXTENDED
) || state
->cbs_hasprevdirentry
) {
3012 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3013 if (state
->cbs_result
== 0) {
3016 /* Remember previous entry */
3017 state
->cbs_desc
->cd_cnid
= cnid
;
3018 if (type
== DT_DIR
) {
3019 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3021 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3023 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3024 state
->cbs_desc
->cd_namelen
= 0;
3027 state
->cbs_desc
->cd_encoding
= xxxx
;
3030 state
->cbs_desc
->cd_namelen
= namelen
;
3031 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3033 /* Store unmangled name for the directory hint else it will
3034 * restart readdir at the last location again
3036 u_int8_t
*new_nameptr
;
3038 size_t tmp_namelen
= 0;
3040 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
3041 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
3042 cnp
->ustr
.length
* sizeof(UniChar
),
3044 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
3045 result
= utf8_encodestr(cnp
->ustr
.unicode
,
3046 cnp
->ustr
.length
* sizeof(UniChar
),
3047 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
3049 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
3050 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
3052 FREE(new_nameptr
, M_TEMP
);
3055 if (state
->cbs_hasprevdirentry
) {
3056 curlinkref
= ilinkref
; /* save current */
3057 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3060 * Record any hard links for post processing.
3062 if ((ilinkref
!= 0) &&
3063 (state
->cbs_result
== 0) &&
3064 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3065 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3066 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3067 state
->cbs_nlinks
++;
3069 if (state
->cbs_hasprevdirentry
) {
3070 ilinkref
= curlinkref
; /* restore current */
3074 /* Fill the direntry to be used the next time */
3075 if (state
->cbs_flags
& VNODE_READDIR_EXTENDED
) {
3076 if (stop_after_pack
) {
3077 state
->cbs_eof
= true;
3078 return (0); /* stop */
3080 entry
->d_type
= type
;
3081 entry
->d_namlen
= namelen
;
3082 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3084 /* File number = 0 means skip entry */
3085 entry
->d_fileno
= 0;
3087 entry
->d_fileno
= cnid
;
3089 /* swap the current and previous entry */
3090 struct direntry
* tmp
;
3091 tmp
= state
->cbs_direntry
;
3092 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3093 state
->cbs_prevdirentry
= tmp
;
3094 state
->cbs_hasprevdirentry
= true;
3095 state
->cbs_previlinkref
= ilinkref
;
3098 /* Continue iteration if there's room */
3099 return (state
->cbs_result
== 0 &&
3100 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3104 * getdirentries callback for standard HFS (non HFS+) directories.
3107 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3108 struct packdirentry_state
*state
)
3110 struct hfsmount
*hfsmp
;
3111 const CatalogName
*cnp
;
3114 struct dirent catent
;
3116 u_int8_t type
= DT_UNKNOWN
;
3123 hfsmp
= state
->cbs_hfsmp
;
3125 curID
= ckp
->hfs
.parentID
;
3127 /* We're done when parent directory changes */
3128 if (state
->cbs_parentID
!= curID
) {
3129 state
->cbs_result
= ENOENT
;
3130 return (0); /* stop */
3133 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3134 maxnamelen
= NAME_MAX
;
3136 switch(crp
->recordType
) {
3137 case kHFSFolderRecord
:
3139 cnid
= crp
->hfsFolder
.folderID
;
3141 case kHFSFileRecord
:
3143 cnid
= crp
->hfsFile
.fileID
;
3146 return (0); /* stop */
3149 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3150 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
3152 * When an HFS name cannot be encoded with the current
3153 * volume encoding we use MacRoman as a fallback.
3156 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
3158 catent
.d_type
= type
;
3159 catent
.d_namlen
= namelen
;
3160 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3161 catent
.d_fileno
= cnid
;
3162 uioaddr
= (caddr_t
) &catent
;
3164 /* If this entry won't fit then we're done */
3165 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3166 return (0); /* stop */
3169 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3170 if (state
->cbs_result
== 0) {
3173 /* Remember previous entry */
3174 state
->cbs_desc
->cd_cnid
= cnid
;
3175 if (type
== DT_DIR
) {
3176 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3178 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3180 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3181 state
->cbs_desc
->cd_namelen
= 0;
3183 state
->cbs_desc
->cd_namelen
= namelen
;
3184 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3187 /* Continue iteration if there's room */
3188 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3192 * Pack a uio buffer with directory entries from the catalog
3195 cat_getdirentries(struct hfsmount
*hfsmp
, u_int32_t entrycnt
, directoryhint_t
*dirhint
,
3196 uio_t uio
, int flags
, int * items
, int * eofflag
)
3199 BTreeIterator
* iterator
;
3201 struct packdirentry_state state
;
3210 extended
= flags
& VNODE_READDIR_EXTENDED
;
3212 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3215 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3218 * Get a buffer for link info array, btree iterator and a direntry:
3220 maxlinks
= MIN(entrycnt
, (u_int32_t
)(uio_resid(uio
) / SMALL_DIRENTRY_SIZE
));
3221 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3223 bufsize
+= 2*sizeof(struct direntry
);
3225 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3226 bzero(buffer
, bufsize
);
3228 state
.cbs_flags
= flags
;
3229 state
.cbs_hasprevdirentry
= false;
3230 state
.cbs_previlinkref
= 0;
3231 state
.cbs_nlinks
= 0;
3232 state
.cbs_maxlinks
= maxlinks
;
3233 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3235 * We need to set cbs_eof to false regardless of whether or not the
3236 * control flow is actually in the extended case, since we use this
3237 * field to track whether or not we've returned EOF from the iterator function.
3239 state
.cbs_eof
= false;
3241 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3242 key
= (CatalogKey
*)&iterator
->key
;
3244 index
= dirhint
->dh_index
+ 1;
3246 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3247 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3250 * Attempt to build a key from cached filename
3252 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3253 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3254 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3259 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3261 * Position the iterator at the directory's thread record.
3262 * (i.e. just before the first entry)
3264 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3265 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3266 iterator
->hint
.index
= 0;
3271 * If the last entry wasn't cached then position the btree iterator
3275 * Position the iterator at the directory's thread record.
3276 * (i.e. just before the first entry)
3278 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3279 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3281 result
= MacToVFSError(result
);
3285 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3288 * Iterate until we reach the entry just
3289 * before the one we want to start with.
3292 struct position_state ps
;
3297 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3300 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3301 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3305 result
= MacToVFSError(result
);
3307 result
= MacToVFSError(result
);
3313 state
.cbs_index
= index
;
3314 state
.cbs_hfsmp
= hfsmp
;
3315 state
.cbs_uio
= uio
;
3316 state
.cbs_desc
= &dirhint
->dh_desc
;
3317 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3318 state
.cbs_result
= 0;
3319 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3321 /* Use a temporary buffer to hold intermediate descriptor names. */
3322 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3323 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3324 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3325 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3326 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3329 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3331 enum BTreeIterationOperations op
;
3332 if (extended
&& index
!= 0 && have_key
)
3333 op
= kBTreeCurrentRecord
;
3335 op
= kBTreeNextRecord
;
3338 * Process as many entries as possible starting at iterator->key.
3340 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
3341 result
= BTIterateRecords(fcb
, op
, iterator
,
3342 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3344 result
= BTIterateRecords(fcb
, op
, iterator
,
3345 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3347 /* For extended calls, every call to getdirentries_callback()
3348 * transfers the previous directory entry found to the user
3349 * buffer. Therefore when BTIterateRecords reaches the end of
3350 * Catalog BTree, call getdirentries_callback() again with
3351 * dummy values to copy the last directory entry stored in
3352 * packdirentry_state
3354 if (extended
&& (result
== fsBTRecordNotFoundErr
)) {
3358 bzero(&ckp
, sizeof(ckp
));
3359 bzero(&crp
, sizeof(crp
));
3361 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3365 /* Note that state.cbs_index is still valid on errors */
3366 *items
= state
.cbs_index
- index
;
3367 index
= state
.cbs_index
;
3370 * Also note that cbs_eof is set in all cases if we ever hit EOF
3371 * during the enumeration by the catalog callback. Mark the directory's hint
3372 * descriptor as having hit EOF.
3375 if (state
.cbs_eof
) {
3376 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3380 /* Finish updating the catalog iterator. */
3381 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3382 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3383 dirhint
->dh_index
= index
- 1;
3385 /* Fix up the name. */
3386 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3387 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3388 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3390 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3391 dirhint
->dh_desc
.cd_namelen
= 0;
3395 * Post process any hard links to get the real file id.
3397 if (state
.cbs_nlinks
> 0) {
3399 user_addr_t address
;
3402 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3403 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3405 /* This assumes that d_ino is always first field. */
3406 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3407 if (address
== (user_addr_t
)0)
3409 if (uio_isuserspace(uio
)) {
3411 ino64_t fileid_64
= (ino64_t
)fileid
;
3412 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3414 (void) copyout(&fileid
, address
, sizeof(fileid
));
3416 } else /* system space */ {
3418 ino64_t fileid_64
= (ino64_t
)fileid
;
3419 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3421 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3427 if (state
.cbs_result
)
3428 result
= state
.cbs_result
;
3430 result
= MacToVFSError(result
);
3432 if (result
== ENOENT
) {
3437 FREE(buffer
, M_TEMP
);
3444 * Callback to establish directory position.
3445 * Called with position_state for each item in a directory.
3448 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3449 struct position_state
*state
)
3453 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
3454 curID
= ckp
->hfs
.parentID
;
3456 curID
= ckp
->hfsPlus
.parentID
;
3458 /* Make sure parent directory didn't change */
3459 if (state
->parentID
!= curID
) {
3460 state
->error
= EINVAL
;
3461 return (0); /* stop */
3464 /* Count this entry */
3465 switch(crp
->recordType
) {
3466 case kHFSPlusFolderRecord
:
3467 case kHFSPlusFileRecord
:
3468 case kHFSFolderRecord
:
3469 case kHFSFileRecord
:
3473 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3474 crp
->recordType
, curID
);
3475 state
->error
= EINVAL
;
3476 return (0); /* stop */
3479 return (state
->count
< state
->index
);
3484 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3486 * The name portion of the key is compared using a 16-bit binary comparison.
3487 * This is called from the b-tree code.
3490 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3492 u_int32_t searchParentID
, trialParentID
;
3495 searchParentID
= searchKey
->parentID
;
3496 trialParentID
= trialKey
->parentID
;
3499 if (searchParentID
> trialParentID
) {
3501 } else if (searchParentID
< trialParentID
) {
3504 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3505 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3506 int length1
= searchKey
->nodeName
.length
;
3507 int length2
= trialKey
->nodeName
.length
;
3511 if (length1
< length2
) {
3514 } else if (length1
> length2
) {
3541 * Compare two standard HFS catalog keys
3543 * Result: +n search key > trial key
3544 * 0 search key = trial key
3545 * -n search key < trial key
3548 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3550 cnid_t searchParentID
, trialParentID
;
3553 searchParentID
= searchKey
->parentID
;
3554 trialParentID
= trialKey
->parentID
;
3556 if (searchParentID
> trialParentID
)
3558 else if (searchParentID
< trialParentID
)
3560 else /* parent dirID's are equal, compare names */
3561 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3568 * Compare two HFS+ catalog keys
3570 * Result: +n search key > trial key
3571 * 0 search key = trial key
3572 * -n search key < trial key
3575 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3577 cnid_t searchParentID
, trialParentID
;
3580 searchParentID
= searchKey
->parentID
;
3581 trialParentID
= trialKey
->parentID
;
3583 if (searchParentID
> trialParentID
) {
3586 else if (searchParentID
< trialParentID
) {
3589 /* parent node ID's are equal, compare names */
3590 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3591 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3593 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3594 searchKey
->nodeName
.length
,
3595 &trialKey
->nodeName
.unicode
[0],
3596 trialKey
->nodeName
.length
);
3604 * buildkey - build a Catalog b-tree key from a cnode descriptor
3607 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3608 HFSPlusCatalogKey
*key
, int retry
)
3610 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3612 size_t unicodeBytes
= 0;
3614 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3615 return (EINVAL
); /* invalid name */
3617 key
->parentID
= descp
->cd_parentcnid
;
3618 key
->nodeName
.length
= 0;
3620 * Convert filename from UTF-8 into Unicode
3623 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3624 utf8_flags
|= UTF_DECOMPOSED
;
3625 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3626 key
->nodeName
.unicode
, &unicodeBytes
,
3627 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3628 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3629 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3631 if (result
!= ENAMETOOLONG
)
3632 result
= EINVAL
; /* name has invalid characters */
3637 * For HFS volumes convert to an HFS compatible key
3639 * XXX need to save the encoding that succeeded
3641 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
3642 HFSCatalogKey hfskey
;
3644 bzero(&hfskey
, sizeof(hfskey
));
3645 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3646 hfskey
.parentID
= key
->parentID
;
3647 hfskey
.nodeName
[0] = 0;
3648 if (key
->nodeName
.length
> 0) {
3650 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3651 key
->nodeName
.length
* 2,
3652 key
->nodeName
.unicode
,
3653 &hfskey
.nodeName
[0], retry
)) != 0) {
3654 if (res
!= ENAMETOOLONG
)
3659 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3661 bcopy(&hfskey
, key
, sizeof(hfskey
));
3668 * Resolve hard link reference to obtain the inode record.
3671 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3673 FSBufferDescriptor btdata
;
3674 struct BTreeIterator
*iterator
;
3675 struct cat_desc idesc
;
3680 BDINIT(btdata
, recp
);
3683 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3684 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
3686 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3687 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
3690 /* Get space for iterator */
3691 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3692 bzero(iterator
, sizeof(*iterator
));
3694 /* Build a descriptor for private dir. */
3695 idesc
.cd_parentcnid
= parentcnid
;
3696 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
3697 idesc
.cd_namelen
= strlen(inodename
);
3700 idesc
.cd_encoding
= 0;
3701 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
3703 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3704 &btdata
, NULL
, NULL
);
3707 /* Make sure there's a reference */
3708 if (recp
->hl_linkCount
== 0)
3709 recp
->hl_linkCount
= 2;
3711 printf("hfs: cat_resolvelink: can't find %s\n", inodename
);
3714 FREE(iterator
, M_TEMP
);
3716 return (result
? ENOENT
: 0);
3720 * Resolve hard link reference to obtain the inode number.
3723 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
3725 struct HFSPlusCatalogFile record
;
3729 * Since we know resolvelinkid is only called from
3730 * cat_getdirentries, we can assume that only file
3731 * hardlinks need to be resolved (cat_getdirentries
3732 * can resolve directory hardlinks in place).
3734 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
3736 if (record
.fileID
== 0)
3739 *ino
= record
.fileID
;
3745 * getkey - get a key from id by doing a thread lookup
3748 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
3750 struct BTreeIterator
* iterator
;
3751 FSBufferDescriptor btdata
;
3754 CatalogRecord
* recp
;
3758 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
3760 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3761 bzero(iterator
, sizeof(*iterator
));
3762 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
3764 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
3765 BDINIT(btdata
, recp
);
3767 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3768 &btdata
, &datasize
, iterator
);
3772 /* Turn thread record into a cnode key (in place) */
3773 switch (recp
->recordType
) {
3774 case kHFSFileThreadRecord
:
3775 case kHFSFolderThreadRecord
:
3776 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
3777 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
3778 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
3781 case kHFSPlusFileThreadRecord
:
3782 case kHFSPlusFolderThreadRecord
:
3783 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
3784 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
3785 (keyp
->hfsPlus
.nodeName
.length
* 2);
3786 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
3795 FREE(iterator
, M_TEMP
);
3798 return MacToVFSError(result
);
3802 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
3803 * null arguments to cat_idlookup instead, but we save around 10% by not building the
3804 * cat_desc here). Both key and attrp must point to real structures.
3806 * The key's parent id is the only part of the key expected to be used by the caller.
3807 * The name portion of the key may not always be valid (ie in the case of a hard link).
3810 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
3814 result
= getkey(hfsmp
, cnid
, key
);
3817 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
3820 * Check for a raw file hardlink inode.
3821 * Fix up the parent id in the key if necessary.
3822 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
3824 if ((result
== 0) &&
3825 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3826 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
3827 cnid_t nextlinkid
= 0;
3828 cnid_t prevlinkid
= 0;
3829 struct cat_desc linkdesc
;
3832 * Pick up the first link in the chain and get a descriptor for it.
3833 * This allows blind bulk access checks to work for hardlinks.
3835 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
3836 (nextlinkid
!= 0)) {
3837 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
3838 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
3839 cat_releasedesc(&linkdesc
);
3843 return MacToVFSError(result
);
3848 * buildrecord - build a default catalog directory or file record
3851 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
3852 CatalogRecord
*crp
, u_int32_t
*recordSize
)
3854 int type
= attrp
->ca_mode
& S_IFMT
;
3855 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
3858 createtime
= UTCToLocal(createtime
);
3859 if (type
== S_IFDIR
) {
3860 bzero(crp
, sizeof(HFSCatalogFolder
));
3861 crp
->recordType
= kHFSFolderRecord
;
3862 crp
->hfsFolder
.folderID
= cnid
;
3863 crp
->hfsFolder
.createDate
= createtime
;
3864 crp
->hfsFolder
.modifyDate
= createtime
;
3865 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
3866 *recordSize
= sizeof(HFSCatalogFolder
);
3868 bzero(crp
, sizeof(HFSCatalogFile
));
3869 crp
->recordType
= kHFSFileRecord
;
3870 crp
->hfsFile
.fileID
= cnid
;
3871 crp
->hfsFile
.createDate
= createtime
;
3872 crp
->hfsFile
.modifyDate
= createtime
;
3873 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
3874 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
3875 *recordSize
= sizeof(HFSCatalogFile
);
3878 struct HFSPlusBSDInfo
* bsdp
= NULL
;
3880 if (type
== S_IFDIR
) {
3881 crp
->recordType
= kHFSPlusFolderRecord
;
3882 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
3883 crp
->hfsPlusFolder
.valence
= 0;
3884 crp
->hfsPlusFolder
.folderID
= cnid
;
3885 crp
->hfsPlusFolder
.createDate
= createtime
;
3886 crp
->hfsPlusFolder
.contentModDate
= createtime
;
3887 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
3888 crp
->hfsPlusFolder
.accessDate
= createtime
;
3889 crp
->hfsPlusFolder
.backupDate
= 0;
3890 crp
->hfsPlusFolder
.textEncoding
= encoding
;
3891 crp
->hfsPlusFolder
.folderCount
= 0;
3892 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
3893 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
3894 bsdp
->special
.linkCount
= 1;
3895 *recordSize
= sizeof(HFSPlusCatalogFolder
);
3897 crp
->recordType
= kHFSPlusFileRecord
;
3898 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
3899 crp
->hfsPlusFile
.reserved1
= 0;
3900 crp
->hfsPlusFile
.fileID
= cnid
;
3901 crp
->hfsPlusFile
.createDate
= createtime
;
3902 crp
->hfsPlusFile
.contentModDate
= createtime
;
3903 crp
->hfsPlusFile
.accessDate
= createtime
;
3904 crp
->hfsPlusFile
.attributeModDate
= createtime
;
3905 crp
->hfsPlusFile
.backupDate
= 0;
3906 crp
->hfsPlusFile
.textEncoding
= encoding
;
3907 crp
->hfsPlusFile
.reserved2
= 0;
3908 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
3909 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
3910 /* BLK/CHR need to save the device info */
3911 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
3912 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
3914 bsdp
->special
.linkCount
= 1;
3916 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
3917 *recordSize
= sizeof(HFSPlusCatalogFile
);
3919 bsdp
->ownerID
= attrp
->ca_uid
;
3920 bsdp
->groupID
= attrp
->ca_gid
;
3921 bsdp
->fileMode
= attrp
->ca_mode
;
3922 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
3923 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
3929 * builddesc - build a cnode descriptor from an HFS+ key
3932 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
3933 int isdir
, struct cat_desc
*descp
)
3936 unsigned char * nameptr
;
3939 unsigned char tmpbuff
[128];
3941 /* guess a size... */
3942 bufsize
= (3 * key
->nodeName
.length
) + 1;
3943 if (bufsize
>= sizeof(tmpbuff
) - 1) {
3944 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3946 nameptr
= &tmpbuff
[0];
3949 result
= utf8_encodestr(key
->nodeName
.unicode
,
3950 key
->nodeName
.length
* sizeof(UniChar
),
3951 nameptr
, (size_t *)&utf8len
,
3954 if (result
== ENAMETOOLONG
) {
3955 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
3956 key
->nodeName
.length
* sizeof(UniChar
),
3958 FREE(nameptr
, M_TEMP
);
3959 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3961 result
= utf8_encodestr(key
->nodeName
.unicode
,
3962 key
->nodeName
.length
* sizeof(UniChar
),
3963 nameptr
, (size_t *)&utf8len
,
3966 descp
->cd_parentcnid
= key
->parentID
;
3967 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
3968 descp
->cd_namelen
= utf8len
;
3969 descp
->cd_cnid
= cnid
;
3970 descp
->cd_hint
= hint
;
3971 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
3973 descp
->cd_flags
|= CD_ISDIR
;
3974 descp
->cd_encoding
= encoding
;
3975 if (nameptr
!= &tmpbuff
[0]) {
3976 FREE(nameptr
, M_TEMP
);
3983 * getbsdattr - get attributes in bsd format
3987 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
3989 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
3990 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
3992 attrp
->ca_recflags
= crp
->flags
;
3993 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
3994 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
3995 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
3996 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
3997 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
3998 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
4000 if ((bsd
->fileMode
& S_IFMT
) == 0) {
4001 attrp
->ca_flags
= 0;
4002 attrp
->ca_uid
= hfsmp
->hfs_uid
;
4003 attrp
->ca_gid
= hfsmp
->hfs_gid
;
4005 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
4007 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
4009 attrp
->ca_linkcount
= 1;
4012 attrp
->ca_linkcount
= 1; /* may be overridden below */
4014 attrp
->ca_uid
= bsd
->ownerID
;
4015 attrp
->ca_gid
= bsd
->groupID
;
4016 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
4017 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
4018 switch (attrp
->ca_mode
& S_IFMT
) {
4019 case S_IFCHR
: /* fall through */
4021 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
4024 case S_IFDIR
: /* fall through */
4026 /* Pick up the hard link count */
4027 if (bsd
->special
.linkCount
> 0)
4028 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
4033 * Override the permissions as determined by the mount auguments
4034 * in ALMOST the same way unset permissions are treated but keep
4035 * track of whether or not the file or folder is hfs locked
4036 * by leaving the h_pflags field unchanged from what was unpacked
4037 * out of the catalog.
4040 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4041 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4042 * at the VFS layer, so there is no need to do it here now; this also
4043 * allows VFS to let root see the real UIDs.
4045 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4046 * attrp->ca_uid = hfsmp->hfs_uid;
4047 * attrp->ca_gid = hfsmp->hfs_gid;
4053 if (!S_ISDIR(attrp
->ca_mode
)) {
4054 attrp
->ca_mode
&= ~S_IFMT
;
4055 attrp
->ca_mode
|= S_IFDIR
;
4057 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
4058 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
4059 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
4061 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4062 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4063 attrp
->ca_flags
|= UF_HIDDEN
;
4065 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4066 if (crp
->flags
& kHFSFileLockedMask
) {
4067 /* The file's supposed to be locked:
4068 Make sure at least one of the IMMUTABLE bits is set: */
4069 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4070 attrp
->ca_flags
|= UF_IMMUTABLE
;
4072 /* The file's supposed to be unlocked: */
4073 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4075 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4076 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4077 attrp
->ca_flags
|= UF_HIDDEN
;
4078 /* get total blocks (both forks) */
4079 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4081 /* On HFS+ the ThreadExists flag must always be set. */
4082 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4083 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4085 /* Pick up the hardlink first link, if any. */
4086 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4089 attrp
->ca_fileid
= crp
->fileID
;
4091 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4095 * promotekey - promote hfs key to hfs plus key
4099 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4100 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4102 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4106 *encoding
= hfsmp
->hfs_encoding
;
4108 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4109 kHFSPlusMaxFileNameChars
, &uniCount
);
4111 * When an HFS name cannot be encoded with the current
4112 * encoding use MacRoman as a fallback.
4114 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4116 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4117 keyp
->nodeName
.unicode
,
4118 kHFSPlusMaxFileNameChars
,
4122 keyp
->nodeName
.length
= uniCount
;
4123 keyp
->parentID
= hfskey
->parentID
;
4127 * promotefork - promote hfs fork info to hfs plus
4131 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4132 int resource
, struct cat_fork
* forkp
)
4134 struct HFSPlusExtentDescriptor
*xp
;
4135 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4137 bzero(forkp
, sizeof(*forkp
));
4138 xp
= &forkp
->cf_extents
[0];
4140 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4141 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4142 forkp
->cf_bytesread
= 0;
4143 forkp
->cf_vblocks
= 0;
4144 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4145 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4146 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4147 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4148 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4149 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4151 forkp
->cf_size
= filep
->dataLogicalSize
;
4152 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4153 forkp
->cf_bytesread
= 0;
4154 forkp
->cf_vblocks
= 0;
4155 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4156 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4157 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4158 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4159 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4160 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4165 * promoteattr - promote standard hfs catalog attributes to hfs plus
4169 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4171 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4173 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4174 const struct HFSCatalogFolder
* folder
;
4176 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4177 crp
->recordType
= kHFSPlusFolderRecord
;
4178 crp
->flags
= folder
->flags
;
4179 crp
->fileID
= folder
->folderID
;
4180 crp
->createDate
= LocalToUTC(folder
->createDate
);
4181 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4182 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4183 crp
->reserved1
= folder
->valence
;
4185 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4187 const struct HFSCatalogFile
* file
;
4189 file
= (const struct HFSCatalogFile
*) dataPtr
;
4190 crp
->recordType
= kHFSPlusFileRecord
;
4191 crp
->flags
= file
->flags
;
4192 crp
->fileID
= file
->fileID
;
4193 crp
->createDate
= LocalToUTC(file
->createDate
);
4194 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4195 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4198 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4199 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4200 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4201 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4203 crp
->textEncoding
= 0;
4204 crp
->attributeModDate
= crp
->contentModDate
;
4205 crp
->accessDate
= crp
->contentModDate
;
4206 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4210 * Build a catalog node thread record from a catalog key
4211 * and return the size of the record.
4214 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4219 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4220 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4222 size
= sizeof(HFSCatalogThread
);
4225 rec
->recordType
= kHFSFolderThreadRecord
;
4227 rec
->recordType
= kHFSFileThreadRecord
;
4228 rec
->parentID
= key
->parentID
;
4229 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4232 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4233 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4235 size
= sizeof(HFSPlusCatalogThread
);
4237 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4239 rec
->recordType
= kHFSPlusFileThreadRecord
;
4241 rec
->parentID
= key
->parentID
;
4242 bcopy(&key
->nodeName
, &rec
->nodeName
,
4243 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4245 /* HFS Plus has varaible sized thread records */
4246 size
-= (sizeof(rec
->nodeName
.unicode
) -
4247 (rec
->nodeName
.length
* sizeof(UniChar
)));
4254 * Build a catalog node thread key.
4257 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4260 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4261 key
->hfs
.reserved
= 0;
4262 key
->hfs
.parentID
= parentID
;
4263 key
->hfs
.nodeName
[0] = 0;
4265 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4266 key
->hfsPlus
.parentID
= parentID
;
4267 key
->hfsPlus
.nodeName
.length
= 0;
4272 * Extract the text encoding from a catalog node record.
4275 getencoding(const CatalogRecord
*crp
)
4279 if (crp
->recordType
== kHFSPlusFolderRecord
)
4280 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4281 else if (crp
->recordType
== kHFSPlusFileRecord
)
4282 encoding
= crp
->hfsPlusFile
.textEncoding
;
4290 * Extract the CNID from a catalog node record.
4293 getcnid(const CatalogRecord
*crp
)
4297 switch (crp
->recordType
) {
4298 case kHFSFolderRecord
:
4299 cnid
= crp
->hfsFolder
.folderID
;
4301 case kHFSFileRecord
:
4302 cnid
= crp
->hfsFile
.fileID
;
4304 case kHFSPlusFolderRecord
:
4305 cnid
= crp
->hfsPlusFolder
.folderID
;
4307 case kHFSPlusFileRecord
:
4308 cnid
= crp
->hfsPlusFile
.fileID
;
4311 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp
);
4319 * Extract the parent ID from a catalog node record.
4322 getparentcnid(const CatalogRecord
*recp
)
4326 switch (recp
->recordType
) {
4327 case kHFSFileThreadRecord
:
4328 case kHFSFolderThreadRecord
:
4329 cnid
= recp
->hfsThread
.parentID
;
4332 case kHFSPlusFileThreadRecord
:
4333 case kHFSPlusFolderThreadRecord
:
4334 cnid
= recp
->hfsPlusThread
.parentID
;
4337 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4345 * Determine if a catalog node record is a directory.
4348 isadir(const CatalogRecord
*crp
)
4350 return (crp
->recordType
== kHFSFolderRecord
||
4351 crp
->recordType
== kHFSPlusFolderRecord
);
4355 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4356 * (not inode) using catalog record id. Note that this function does
4357 * NOT resolve directory hard link to its directory inode and return
4360 * Note: The caller is responsible for releasing the output catalog
4361 * descriptor (when supplied outdescp is non-null).
4364 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4365 u_int8_t forktype
, struct cat_desc
*outdescp
,
4366 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4368 struct BTreeIterator
*iterator
= NULL
;
4369 FSBufferDescriptor btdata
;
4372 CatalogRecord
*recp
= NULL
;
4375 /* No directory hard links on standard HFS */
4376 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4380 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4381 if (iterator
== NULL
) {
4384 bzero(iterator
, sizeof(*iterator
));
4385 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4387 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4392 BDINIT(btdata
, recp
);
4394 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4395 &btdata
, &datasize
, iterator
);
4399 /* Directory hard links are catalog file record */
4400 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4405 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4406 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4407 (keyp
->hfsPlus
.nodeName
.length
* 2);
4408 if (forktype
== kHFSResourceForkType
) {
4409 /* Lookup resource fork for directory hard link */
4410 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4412 /* Lookup data fork, if any, for directory hard link */
4413 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4416 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4417 hfs_mark_volume_inconsistent(hfsmp
);
4420 /* Just for sanity, make sure that id in catalog record and thread record match */
4421 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4422 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4423 hfs_mark_volume_inconsistent(hfsmp
);
4431 FREE(iterator
, M_TEMP
);
4433 return MacToVFSError(error
);
4437 * cnode_update_dirlink - update the catalog node for directory hard link
4438 * described by descp using the data from attrp and forkp.
4441 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4442 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4444 if (forktype
== kHFSResourceForkType
) {
4445 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4447 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);