2 * Copyright (c) 2000-2010 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
,
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, 0, 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 int extlen1
, extlen2
;
583 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
584 if (fileID
< (cnid_t
)kHFSFirstUserCatalogNodeID
)
587 if (fileID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
588 fileID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
||
589 fileID
== hfsmp
->hfs_jnlfileid
||
590 fileID
== hfsmp
->hfs_jnlinfoblkid
) {
594 result
= cat_idlookup(hfsmp
, fileID
, 0, outdescp
, attrp
, forkp
);
597 /* It must be in the correct directory */
598 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
601 if (((u_int16_t
)outdescp
->cd_namelen
< prefixlen
) ||
602 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
605 extlen1
= CountFilenameExtensionChars(descp
->cd_nameptr
, descp
->cd_namelen
);
606 extlen2
= CountFilenameExtensionChars(outdescp
->cd_nameptr
, outdescp
->cd_namelen
);
607 if (extlen1
!= extlen2
)
610 if (bcmp(outdescp
->cd_nameptr
+ (outdescp
->cd_namelen
- extlen2
),
611 descp
->cd_nameptr
+ (descp
->cd_namelen
- extlen1
),
618 cat_releasedesc(outdescp
);
624 * cat_lookupbykey - lookup a catalog node using a cnode key
627 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, int flags
, u_int32_t hint
, int wantrsrc
,
628 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
630 struct BTreeIterator
* iterator
;
631 FSBufferDescriptor btdata
;
632 CatalogRecord
* recp
;
638 u_int32_t encoding
= 0;
641 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
643 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
644 BDINIT(btdata
, recp
);
645 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
646 bzero(iterator
, sizeof(*iterator
));
647 iterator
->hint
.nodeNum
= hint
;
648 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
650 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
651 &btdata
, &datasize
, iterator
);
655 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
656 cnid
= getcnid(recp
);
658 parentid
= keyp
->hfsPlus
.parentID
;
660 encoding
= getencoding(recp
);
661 hint
= iterator
->hint
.nodeNum
;
663 /* Hide the journal files (if any) */
664 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
665 ((cnid
== hfsmp
->hfs_jnlfileid
) || (cnid
== hfsmp
->hfs_jnlinfoblkid
)) &&
666 !(flags
& HFS_LOOKUP_SYSFILE
)) {
672 * When a hardlink link is encountered, auto resolve it.
674 * The catalog record will change, and possibly its type.
678 && (recp
->recordType
== kHFSPlusFileRecord
)
679 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_itime
) ||
680 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
684 if ((SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
685 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
687 } else if ((recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
688 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
689 (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
692 if ((isfilelink
|| isdirlink
) && !(flags
& HFS_LOOKUP_HARDLINK
)) {
693 ilink
= recp
->hfsPlusFile
.hl_linkReference
;
694 (void) cat_resolvelink(hfsmp
, ilink
, isdirlink
, (struct HFSPlusCatalogFile
*)recp
);
700 struct HFSPlusCatalogFile cnoderec
;
702 promoteattr(hfsmp
, recp
, &cnoderec
);
703 getbsdattr(hfsmp
, &cnoderec
, attrp
);
705 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
707 /* Update the inode number for this hard link */
708 attrp
->ca_linkref
= ilink
;
712 * Set kHFSHasLinkChainBit for hard links, and reset it for all
713 * other items. Also set linkCount to 1 for regular files.
715 * Due to some bug (rdar://8505977), some regular files can have
716 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
717 * are not really hard links. The runtime code should not consider
718 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
719 * and linkCount for regular file before we vend it out. This might
720 * also result in repairing the bad files on disk, if the corresponding
721 * file is modified and updated on disk.
724 /* This is a hard link and the link count bit was not set */
725 if (!(attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
726 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp
->vcbVN
, cnid
, ilink
);
727 attrp
->ca_recflags
|= kHFSHasLinkChainMask
;
730 /* Make sure that this non-hard link (regular) record is not
731 * an inode record or a valid hard link being that is not
732 * resolved for volume resize purposes. We do not want to
733 * reset the hard link bit or reset link count on these records.
735 if (!(flags
& HFS_LOOKUP_HARDLINK
) &&
736 (parentid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
737 (parentid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
738 /* This is not a hard link or inode and the link count bit was set */
739 if (attrp
->ca_recflags
& kHFSHasLinkChainMask
) {
740 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp
->vcbVN
, cnid
);
741 attrp
->ca_recflags
&= ~kHFSHasLinkChainMask
;
743 /* This is a regular file and the link count was more than 1 */
744 if (S_ISREG(attrp
->ca_mode
) && (attrp
->ca_linkcount
> 1)) {
745 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp
->vcbVN
, cnid
, attrp
->ca_linkcount
);
746 attrp
->ca_linkcount
= 1;
754 bzero(forkp
, sizeof(*forkp
));
755 } else if (std_hfs
) {
756 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
757 } else if (wantrsrc
) {
758 /* Convert the resource fork. */
759 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
760 forkp
->cf_new_size
= 0;
761 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
762 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
763 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
764 forkp
->cf_bytesread
=
765 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
766 HFSTOVCB(hfsmp
)->blockSize
;
768 forkp
->cf_bytesread
= 0;
770 forkp
->cf_vblocks
= 0;
771 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
772 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
777 /* Convert the data fork. */
778 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
779 forkp
->cf_new_size
= 0;
780 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
781 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
782 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
783 forkp
->cf_bytesread
=
784 recp
->hfsPlusFile
.dataFork
.clumpSize
*
785 HFSTOVCB(hfsmp
)->blockSize
;
787 forkp
->cf_bytesread
= 0;
789 forkp
->cf_vblocks
= 0;
790 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
791 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
793 /* Validate the fork's resident extents. */
795 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
796 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
797 /* Suppress any bad extents so a remove can succeed. */
798 forkp
->cf_extents
[i
].startBlock
= 0;
799 forkp
->cf_extents
[i
].blockCount
= 0;
802 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
805 validblks
+= forkp
->cf_extents
[i
].blockCount
;
808 /* Adjust for any missing blocks. */
809 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
813 * This is technically a volume corruption.
814 * If the total number of blocks calculated by iterating + summing
815 * the extents in the resident extent records, is less than that
816 * which is reported in the catalog entry, we should force a fsck.
817 * Only modifying ca_blocks here is not guaranteed to make it out
818 * to disk; it is a runtime-only field.
820 * Note that we could have gotten into this state if we had invalid ranges
821 * that existed in borrowed blocks that somehow made it out to disk.
822 * The cnode's on disk block count should never be greater
823 * than that which is in its extent records.
826 (void) hfs_mark_volume_inconsistent (hfsmp
);
828 forkp
->cf_blocks
= validblks
;
830 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
832 psize
= (off_t
)validblks
* (off_t
)hfsmp
->blockSize
;
833 if (psize
< forkp
->cf_size
) {
834 forkp
->cf_size
= psize
;
841 HFSPlusCatalogKey
* pluskey
= NULL
;
844 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
845 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
848 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
850 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
852 FREE(pluskey
, M_TEMP
);
856 if (desc_cnid
!= NULL
) {
860 FREE(iterator
, M_TEMP
);
863 return MacToVFSError(result
);
868 * cat_create - create a node in the catalog
870 * NOTE: both the catalog file and attribute file locks must
871 * be held before calling this function.
873 * The caller is responsible for releasing the output
874 * catalog descriptor (when supplied outdescp is non-null).
877 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
878 struct cat_desc
*out_descp
)
882 FSBufferDescriptor btdata
;
887 u_int32_t encoding
= kTextEncodingMacRoman
;
890 modeformat
= attrp
->ca_mode
& S_IFMT
;
892 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
893 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
896 * Get the next CNID. We can change it since we hold the catalog lock.
898 nextCNID
= hfsmp
->vcbNxtCNID
;
899 if (nextCNID
== 0xFFFFFFFF) {
903 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
904 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
905 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
906 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
913 /* Get space for iterator, key and data */
914 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
915 bto
->iterator
.hint
.nodeNum
= 0;
917 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
922 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
923 bto
->key
.nodeName
.length
);
924 hfs_setencodingbits(hfsmp
, encoding
);
928 * Insert the thread record first
930 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
931 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
932 S_ISDIR(attrp
->ca_mode
));
933 btdata
.bufferAddress
= &bto
->data
;
934 btdata
.itemSize
= datalen
;
935 btdata
.itemCount
= 1;
938 // this call requires the attribute file lock to be held
939 result
= file_attribute_exist(hfsmp
, nextCNID
);
940 if (result
== EEXIST
) {
941 // that cnid has orphaned attributes so just skip it.
942 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
943 nextCNID
= kHFSFirstUserCatalogNodeID
;
947 if (result
) goto exit
;
949 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
951 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
952 if ((result
== btExists
) && !std_hfs
&& (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
954 * Allow CNIDs on HFS Plus volumes to wrap around
956 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
957 nextCNID
= kHFSFirstUserCatalogNodeID
;
963 if (result
) goto exit
;
967 * CNID is now established. If we have wrapped then
968 * update the vcbNxtCNID.
970 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
971 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
972 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
973 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
978 * Now insert the file/directory record
980 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
981 btdata
.bufferAddress
= &bto
->data
;
982 btdata
.itemSize
= datalen
;
983 btdata
.itemCount
= 1;
985 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
987 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
989 if (result
== btExists
)
992 /* Back out the thread record */
993 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
994 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
995 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
996 /* Error on deleting extra thread record, mark
997 * volume inconsistent
999 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1000 hfs_mark_volume_inconsistent(hfsmp
);
1007 * Insert was successful, update name, parent and volume
1009 if (out_descp
!= NULL
) {
1010 HFSPlusCatalogKey
* pluskey
= NULL
;
1013 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1014 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
1017 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
1019 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
1020 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
1022 FREE(pluskey
, M_TEMP
);
1025 attrp
->ca_fileid
= nextCNID
;
1028 (void) BTFlushPath(fcb
);
1031 return MacToVFSError(result
);
1036 * cnode_rename - rename a catalog node
1038 * Assumes that the target's directory exists.
1040 * Order of B-tree operations:
1041 * 1. BTSearchRecord(from_cnode, &data);
1042 * 2. BTInsertRecord(to_cnode, &data);
1043 * 3. BTDeleteRecord(from_cnode);
1044 * 4. BTDeleteRecord(from_thread);
1045 * 5. BTInsertRecord(to_thread);
1047 * Note: The caller is responsible for releasing the output
1048 * catalog descriptor (when supplied out_cdp is non-null).
1052 struct hfsmount
* hfsmp
,
1053 struct cat_desc
* from_cdp
,
1054 struct cat_desc
* todir_cdp
,
1055 struct cat_desc
* to_cdp
,
1056 struct cat_desc
* out_cdp
)
1058 struct BTreeIterator
* to_iterator
= NULL
;
1059 struct BTreeIterator
* from_iterator
= NULL
;
1060 FSBufferDescriptor btdata
;
1061 CatalogRecord
* recp
= NULL
;
1062 HFSPlusCatalogKey
* to_key
;
1069 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
1072 u_int32_t encoding
= 0;
1074 vcb
= HFSTOVCB(hfsmp
);
1075 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1076 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1078 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
1081 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
1082 bzero(from_iterator
, sizeof(*from_iterator
));
1083 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
1086 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
1087 bzero(to_iterator
, sizeof(*to_iterator
));
1088 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
1091 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1092 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1093 BDINIT(btdata
, recp
);
1096 * When moving a directory, make sure its a valid move.
1098 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
1099 struct BTreeIterator iterator
;
1100 cnid_t cnid
= from_cdp
->cd_cnid
;
1101 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
1103 /* First check the obvious ones */
1104 if (cnid
== fsRtDirID
||
1105 cnid
== to_cdp
->cd_parentcnid
||
1110 bzero(&iterator
, sizeof(iterator
));
1112 * Traverse destination path all the way back to the root
1113 * making sure that source directory is not encountered.
1116 while (pathcnid
> fsRtDirID
) {
1117 buildthreadkey(pathcnid
, std_hfs
,
1118 (CatalogKey
*)&iterator
.key
);
1119 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
1121 if (result
) goto exit
;
1123 pathcnid
= getparentcnid(recp
);
1124 if (pathcnid
== cnid
|| pathcnid
== 0) {
1132 * Step 1: Find cnode data at old location
1134 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1135 &datasize
, from_iterator
);
1137 if (std_hfs
|| (result
!= btNotFound
))
1140 struct cat_desc temp_desc
;
1142 /* Probably the node has mangled name */
1143 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1147 /* The file has mangled name. Search the cnode data using full name */
1148 bzero(from_iterator
, sizeof(*from_iterator
));
1149 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1151 cat_releasedesc(&temp_desc
);
1155 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1157 cat_releasedesc(&temp_desc
);
1161 cat_releasedesc(&temp_desc
);
1164 /* Check if the source is directory hard link. We do not change
1165 * directory flag because it is later used to initialize result descp
1169 (recp
->recordType
== kHFSPlusFileRecord
) &&
1170 (recp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
)) {
1175 * Update the text encoding (on disk and in descriptor).
1177 * Note that hardlink inodes don't require a text encoding hint.
1180 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
&&
1181 todir_cdp
->cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1182 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
, to_key
->nodeName
.length
);
1183 hfs_setencodingbits(hfsmp
, encoding
);
1184 recp
->hfsPlusFile
.textEncoding
= encoding
;
1186 out_cdp
->cd_encoding
= encoding
;
1189 if (std_hfs
&& !directory
&&
1190 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1194 * If the keys are identical then there's nothing left to do!
1196 * update the hint and exit
1199 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1201 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1205 /* Step 2: Insert cnode at new location */
1206 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1207 if (result
== btExists
) {
1208 int fromtype
= recp
->recordType
;
1210 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1211 goto exit
; /* EEXIST */
1213 /* Find cnode data at new location */
1214 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1218 if ((fromtype
!= recp
->recordType
) ||
1219 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1221 goto exit
; /* EEXIST */
1223 /* The old name is a case variant and must be removed */
1224 result
= BTDeleteRecord(fcb
, from_iterator
);
1228 /* Insert cnode (now that case duplicate is gone) */
1229 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1231 /* Try and restore original before leaving */
1236 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1238 printf("hfs: cat_create: could not undo (BTInsert = %d)", err
);
1239 hfs_mark_volume_inconsistent(hfsmp
);
1245 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1254 /* Step 3: Remove cnode from old location */
1256 result
= BTDeleteRecord(fcb
, from_iterator
);
1258 /* Try and delete new record before leaving */
1263 err
= BTDeleteRecord(fcb
, to_iterator
);
1265 printf("hfs: cat_create: could not undo (BTDelete = %d)", err
);
1266 hfs_mark_volume_inconsistent(hfsmp
);
1272 (void) BTDeleteRecord(fcb
, to_iterator
);
1278 /* #### POINT OF NO RETURN #### */
1281 * Step 4: Remove cnode's old thread record
1283 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1284 (void) BTDeleteRecord(fcb
, from_iterator
);
1287 * Step 5: Insert cnode's new thread record
1288 * (optional for HFS files)
1291 /* For directory hard links, always create a file thread
1292 * record. For everything else, use the directory flag.
1295 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, false);
1297 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1299 btdata
.itemSize
= datasize
;
1300 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1301 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1305 HFSPlusCatalogKey
* pluskey
= NULL
;
1308 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1309 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1311 /* Save the real encoding hint in the Finder Info (field 4). */
1312 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1315 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1316 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1320 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1322 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1323 encoding
, directory
, out_cdp
);
1325 FREE(pluskey
, M_TEMP
);
1329 (void) BTFlushPath(fcb
);
1331 FREE(from_iterator
, M_TEMP
);
1333 FREE(to_iterator
, M_TEMP
);
1336 return MacToVFSError(result
);
1341 * cat_delete - delete a node from the catalog
1343 * Order of B-tree operations:
1344 * 1. BTDeleteRecord(cnode);
1345 * 2. BTDeleteRecord(thread);
1346 * 3. BTUpdateRecord(parent);
1349 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1352 BTreeIterator
*iterator
;
1357 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1358 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1362 * The root directory cannot be deleted
1363 * A directory must be empty
1364 * A file must be zero length (no blocks)
1366 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1367 descp
->cd_parentcnid
== kHFSRootParentID
)
1370 /* XXX Preflight Missing */
1372 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1373 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1374 iterator
->hint
.nodeNum
= 0;
1377 * Derive a key from either the file ID (for a virtual inode)
1378 * or the descriptor.
1380 if (descp
->cd_namelen
== 0) {
1381 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1382 cnid
= attrp
->ca_fileid
;
1384 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1385 cnid
= descp
->cd_cnid
;
1391 result
= BTDeleteRecord(fcb
, iterator
);
1393 if (std_hfs
|| (result
!= btNotFound
))
1396 struct cat_desc temp_desc
;
1398 /* Probably the node has mangled name */
1399 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1403 /* The file has mangled name. Delete the file using full name */
1404 bzero(iterator
, sizeof(*iterator
));
1405 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1406 cnid
= temp_desc
.cd_cnid
;
1408 cat_releasedesc(&temp_desc
);
1412 result
= BTDeleteRecord(fcb
, iterator
);
1414 cat_releasedesc(&temp_desc
);
1418 cat_releasedesc(&temp_desc
);
1421 /* Delete thread record. On error, mark volume inconsistent */
1422 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1423 if (BTDeleteRecord(fcb
, iterator
)) {
1425 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
1426 hfs_mark_volume_inconsistent(hfsmp
);
1431 (void) BTFlushPath(fcb
);
1433 return MacToVFSError(result
);
1438 * cat_update_internal - update the catalog node described by descp
1439 * using the data from attrp and forkp.
1440 * If update_hardlink is true, the hard link catalog record is updated
1441 * and not the inode catalog record.
1444 cat_update_internal(struct hfsmount
*hfsmp
, int update_hardlink
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1445 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1448 BTreeIterator
* iterator
;
1449 struct update_state state
;
1453 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1454 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
1456 state
.s_desc
= descp
;
1457 state
.s_attr
= attrp
;
1458 state
.s_datafork
= dataforkp
;
1459 state
.s_rsrcfork
= rsrcforkp
;
1460 state
.s_hfsmp
= hfsmp
;
1462 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1463 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
1466 * For open-deleted files we need to do a lookup by cnid
1467 * (using thread rec).
1469 * For hard links and if not requested by caller, the target
1470 * of the update is the inode itself (not the link record)
1471 * so a lookup by fileid (i.e. thread rec) is needed.
1473 if ((update_hardlink
== false) &&
1474 ((descp
->cd_cnid
!= attrp
->ca_fileid
) ||
1475 (descp
->cd_namelen
== 0) ||
1476 (attrp
->ca_recflags
& kHFSHasLinkChainMask
))) {
1477 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1479 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1484 /* Pass a node hint */
1485 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1487 result
= BTUpdateRecord(fcb
, iterator
,
1488 (IterateCallBackProcPtr
)catrec_update
, &state
);
1492 /* Update the node hint. */
1493 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1496 (void) BTFlushPath(fcb
);
1498 return MacToVFSError(result
);
1502 * cat_update - update the catalog node described by descp
1503 * using the data from attrp and forkp.
1506 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1507 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1509 return cat_update_internal(hfsmp
, false, descp
, attrp
, dataforkp
, rsrcforkp
);
1513 * catrec_update - Update the fields of a catalog record
1514 * This is called from within BTUpdateRecord.
1517 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1519 struct cat_desc
*descp
;
1520 struct cat_attr
*attrp
;
1521 struct cat_fork
*forkp
;
1522 struct hfsmount
*hfsmp
;
1526 descp
= state
->s_desc
;
1527 attrp
= state
->s_attr
;
1528 hfsmp
= state
->s_hfsmp
;
1529 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1531 switch (crp
->recordType
) {
1532 case kHFSFolderRecord
: {
1533 HFSCatalogFolder
*dir
;
1535 dir
= (struct HFSCatalogFolder
*)crp
;
1536 /* Do a quick sanity check */
1537 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1538 (dir
->folderID
!= descp
->cd_cnid
))
1539 return (btNotFound
);
1540 dir
->valence
= attrp
->ca_entries
;
1541 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1542 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1543 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1544 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1545 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1548 case kHFSFileRecord
: {
1549 HFSCatalogFile
*file
;
1551 file
= (struct HFSCatalogFile
*)crp
;
1552 /* Do a quick sanity check */
1553 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1554 (file
->fileID
!= attrp
->ca_fileid
))
1555 return (btNotFound
);
1556 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1557 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1558 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1559 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1560 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1561 if (state
->s_rsrcfork
) {
1562 forkp
= state
->s_rsrcfork
;
1563 file
->rsrcLogicalSize
= forkp
->cf_size
;
1564 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1565 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1566 file
->rsrcExtents
[i
].startBlock
=
1567 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1568 file
->rsrcExtents
[i
].blockCount
=
1569 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1572 if (state
->s_datafork
) {
1573 forkp
= state
->s_datafork
;
1574 file
->dataLogicalSize
= forkp
->cf_size
;
1575 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1576 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1577 file
->dataExtents
[i
].startBlock
=
1578 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1579 file
->dataExtents
[i
].blockCount
=
1580 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1584 /* Synchronize the lock state */
1585 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1586 file
->flags
|= kHFSFileLockedMask
;
1588 file
->flags
&= ~kHFSFileLockedMask
;
1591 case kHFSPlusFolderRecord
: {
1592 HFSPlusCatalogFolder
*dir
;
1594 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1595 /* Do a quick sanity check */
1596 if (dir
->folderID
!= attrp
->ca_fileid
) {
1597 printf("hfs: catrec_update: id %d != %d\n", dir
->folderID
, attrp
->ca_fileid
);
1598 return (btNotFound
);
1600 dir
->flags
= attrp
->ca_recflags
;
1601 dir
->valence
= attrp
->ca_entries
;
1602 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1603 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1604 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1605 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1606 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1607 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1608 /* Note: directory hardlink inodes don't require a text encoding hint. */
1609 if (ckp
->hfsPlus
.parentID
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1610 dir
->textEncoding
= descp
->cd_encoding
;
1612 dir
->folderCount
= attrp
->ca_dircount
;
1613 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1615 * Update the BSD Info if it was already initialized on
1616 * disk or if the runtime values have been modified.
1618 * If the BSD info was already initialized, but
1619 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1620 * probably different than what was on disk. We don't want
1621 * to overwrite the on-disk values (so if we turn off
1622 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1623 * This way, we can still change fields like the mode or
1624 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1626 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1627 * won't change the uid or gid from their defaults. So, if
1628 * the BSD info wasn't set, and the runtime values are not
1629 * default, then what changed was the mode or flags. We
1630 * have to set the uid and gid to something, so use the
1631 * supplied values (which will be default), which has the
1632 * same effect as creating a new file while
1633 * MNT_UNKNOWNPERMISSIONS is set.
1635 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1636 (attrp
->ca_flags
!= 0) ||
1637 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1638 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1639 ((attrp
->ca_mode
& ALLPERMS
) !=
1640 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1641 if ((dir
->bsdInfo
.fileMode
== 0) ||
1642 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1643 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1644 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1646 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1647 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1648 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1649 /* A directory hardlink has a link count. */
1650 if (attrp
->ca_linkcount
> 1 || dir
->hl_linkCount
> 1) {
1651 dir
->hl_linkCount
= attrp
->ca_linkcount
;
1656 case kHFSPlusFileRecord
: {
1657 HFSPlusCatalogFile
*file
;
1660 file
= (struct HFSPlusCatalogFile
*)crp
;
1661 /* Do a quick sanity check */
1662 if (file
->fileID
!= attrp
->ca_fileid
)
1663 return (btNotFound
);
1664 file
->flags
= attrp
->ca_recflags
;
1665 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1666 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1667 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1668 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1669 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1670 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1672 * Note: file hardlink inodes don't require a text encoding
1673 * hint, but they do have a first link value.
1675 if (ckp
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
1676 file
->hl_firstLinkID
= attrp
->ca_firstlink
;
1678 file
->textEncoding
= descp
->cd_encoding
;
1680 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1682 * Update the BSD Info if it was already initialized on
1683 * disk or if the runtime values have been modified.
1685 * If the BSD info was already initialized, but
1686 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1687 * probably different than what was on disk. We don't want
1688 * to overwrite the on-disk values (so if we turn off
1689 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1690 * This way, we can still change fields like the mode or
1691 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1693 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1694 * won't change the uid or gid from their defaults. So, if
1695 * the BSD info wasn't set, and the runtime values are not
1696 * default, then what changed was the mode or flags. We
1697 * have to set the uid and gid to something, so use the
1698 * supplied values (which will be default), which has the
1699 * same effect as creating a new file while
1700 * MNT_UNKNOWNPERMISSIONS is set.
1702 * Do not modify bsdInfo for directory hard link records.
1703 * They are set during creation and are not modifiable, so just
1706 is_dirlink
= (file
->flags
& kHFSHasLinkChainMask
) &&
1707 (SWAP_BE32(file
->userInfo
.fdType
) == kHFSAliasType
) &&
1708 (SWAP_BE32(file
->userInfo
.fdCreator
) == kHFSAliasCreator
);
1711 ((file
->bsdInfo
.fileMode
!= 0) ||
1712 (attrp
->ca_flags
!= 0) ||
1713 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1714 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1715 ((attrp
->ca_mode
& ALLPERMS
) !=
1716 (hfsmp
->hfs_file_mask
& ACCESSPERMS
)))) {
1717 if ((file
->bsdInfo
.fileMode
== 0) ||
1718 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1719 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1720 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1722 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1723 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1724 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1726 if (state
->s_rsrcfork
) {
1727 forkp
= state
->s_rsrcfork
;
1728 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1729 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1730 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1731 sizeof(HFSPlusExtentRecord
));
1732 /* Push blocks read to disk */
1733 file
->resourceFork
.clumpSize
=
1734 howmany(forkp
->cf_bytesread
, blksize
);
1736 if (state
->s_datafork
) {
1737 forkp
= state
->s_datafork
;
1738 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1739 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1740 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1741 sizeof(HFSPlusExtentRecord
));
1742 /* Push blocks read to disk */
1743 file
->dataFork
.clumpSize
=
1744 howmany(forkp
->cf_bytesread
, blksize
);
1747 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1748 (file
->resourceFork
.extents
[0].startBlock
==
1749 file
->dataFork
.extents
[0].startBlock
)) {
1750 panic("hfs: catrec_update: rsrc fork == data fork");
1753 /* Synchronize the lock state */
1754 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1755 file
->flags
|= kHFSFileLockedMask
;
1757 file
->flags
&= ~kHFSFileLockedMask
;
1759 /* Push out special field if necessary */
1760 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
)) {
1761 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1765 * Protect against the degenerate case where the descriptor contains the
1766 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
1767 * the linkcount was greater than 1 (the default value), then it must have become
1768 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
1770 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (attrp
->ca_linkcount
> 1 ) ||
1771 (file
->hl_linkCount
> 1)) {
1772 file
->hl_linkCount
= attrp
->ca_linkcount
;
1778 return (btNotFound
);
1783 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
1784 * catalog btree of given cnid by walking up the parent chain till it reaches
1785 * either the root folder, or the private metadata directory for storing
1786 * directory hard links. This function updates the corresponding in-core
1787 * cnode, if any, and the directory record in the catalog btree.
1788 * On success, returns zero. On failure, returns non-zero value.
1792 cat_set_childlinkbit(struct hfsmount
*hfsmp
, cnid_t cnid
)
1796 struct cat_desc desc
;
1797 struct cat_attr attr
;
1799 while ((cnid
!= kHFSRootFolderID
) && (cnid
!= kHFSRootParentID
) &&
1800 (cnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
)) {
1801 /* Update the bit in corresponding cnode, if any, in the hash.
1802 * If the cnode has the bit already set, stop the traversal.
1804 retval
= hfs_chash_set_childlinkbit(hfsmp
, cnid
);
1809 /* Update the catalog record on disk if either cnode was not
1810 * found in the hash, or if a cnode was found and the cnode
1811 * did not have the bit set previously.
1813 retval
= hfs_start_transaction(hfsmp
);
1817 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1819 /* Look up our catalog folder record */
1820 retval
= cat_idlookup(hfsmp
, cnid
, 0, &desc
, &attr
, NULL
);
1822 hfs_systemfile_unlock(hfsmp
, lockflags
);
1823 hfs_end_transaction(hfsmp
);
1827 /* Update the bit in the catalog record */
1828 attr
.ca_recflags
|= kHFSHasChildLinkMask
;
1829 retval
= cat_update(hfsmp
, &desc
, &attr
, NULL
, NULL
);
1831 hfs_systemfile_unlock(hfsmp
, lockflags
);
1832 hfs_end_transaction(hfsmp
);
1833 cat_releasedesc(&desc
);
1837 hfs_systemfile_unlock(hfsmp
, lockflags
);
1838 hfs_end_transaction(hfsmp
);
1840 cnid
= desc
.cd_parentcnid
;
1841 cat_releasedesc(&desc
);
1847 /* This function traverses the parent directory hierarchy from the given
1848 * directory to one level below root directory and checks if any of its
1850 * 1. A directory hard link.
1851 * 2. The 'pointed at' directory.
1852 * If any of these conditions fail or an internal error is encountered
1853 * during look up of the catalog record, this function returns non-zero value.
1857 cat_check_link_ancestry(struct hfsmount
*hfsmp
, cnid_t cnid
, cnid_t pointed_at_cnid
)
1859 HFSPlusCatalogKey
*keyp
;
1861 FSBufferDescriptor btdata
;
1862 HFSPlusCatalogFolder folder
;
1868 BDINIT(btdata
, &folder
);
1869 MALLOC(ip
, BTreeIterator
*, sizeof(*ip
), M_TEMP
, M_WAITOK
);
1870 keyp
= (HFSPlusCatalogKey
*)&ip
->key
;
1871 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1873 while (cnid
!= kHFSRootParentID
) {
1874 /* Check if the 'pointed at' directory is an ancestor */
1875 if (pointed_at_cnid
== cnid
) {
1879 if ((result
= getkey(hfsmp
, cnid
, (CatalogKey
*)keyp
))) {
1880 printf("hfs: cat_check_link_ancestry: getkey for %u failed\n", cnid
);
1881 invalid
= 1; /* On errors, assume an invalid parent */
1884 if ((result
= BTSearchRecord(fcb
, ip
, &btdata
, NULL
, NULL
))) {
1885 printf("hfs: cat_check_link_ancestry: cannot find %u\n", cnid
);
1886 invalid
= 1; /* On errors, assume an invalid parent */
1889 /* Check if this ancestor is a directory hard link */
1890 if (folder
.flags
& kHFSHasLinkChainMask
) {
1894 cnid
= keyp
->parentID
;
1902 * update_siblinglinks_callback - update a link's chain
1905 struct linkupdate_state
{
1912 update_siblinglinks_callback(__unused
const CatalogKey
*ckp
, CatalogRecord
*crp
, struct linkupdate_state
*state
)
1914 HFSPlusCatalogFile
*file
;
1916 if (crp
->recordType
!= kHFSPlusFileRecord
) {
1917 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp
->recordType
);
1918 return (btNotFound
);
1921 file
= (struct HFSPlusCatalogFile
*)crp
;
1922 if (file
->flags
& kHFSHasLinkChainMask
) {
1923 if (state
->prevlinkid
!= HFS_IGNORABLE_LINK
) {
1924 file
->hl_prevLinkID
= state
->prevlinkid
;
1926 if (state
->nextlinkid
!= HFS_IGNORABLE_LINK
) {
1927 file
->hl_nextLinkID
= state
->nextlinkid
;
1930 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file
->fileID
);
1936 * cat_update_siblinglinks - update a link's chain
1939 cat_update_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t prevlinkid
, cnid_t nextlinkid
)
1942 BTreeIterator
* iterator
;
1943 struct linkupdate_state state
;
1946 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1947 state
.filelinkid
= linkfileid
;
1948 state
.prevlinkid
= prevlinkid
;
1949 state
.nextlinkid
= nextlinkid
;
1951 /* Create an iterator for use by us temporarily */
1952 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1953 bzero(iterator
, sizeof(*iterator
));
1955 result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
);
1957 result
= BTUpdateRecord(fcb
, iterator
, (IterateCallBackProcPtr
)update_siblinglinks_callback
, &state
);
1958 (void) BTFlushPath(fcb
);
1960 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid %d\n", linkfileid
);
1963 FREE (iterator
, M_TEMP
);
1964 return MacToVFSError(result
);
1968 * cat_lookuplink - lookup a link by it's name
1971 cat_lookuplink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, cnid_t
*linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1974 BTreeIterator
* iterator
;
1975 struct FSBufferDescriptor btdata
;
1976 struct HFSPlusCatalogFile file
;
1979 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
1981 /* Create an iterator for use by us temporarily */
1982 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1983 bzero(iterator
, sizeof(*iterator
));
1985 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
1988 BDINIT(btdata
, &file
);
1990 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
1993 if (file
.recordType
!= kHFSPlusFileRecord
) {
1997 *linkfileid
= file
.fileID
;
1999 if (file
.flags
& kHFSHasLinkChainMask
) {
2000 *prevlinkid
= file
.hl_prevLinkID
;
2001 *nextlinkid
= file
.hl_nextLinkID
;
2007 FREE(iterator
, M_TEMP
);
2008 return MacToVFSError(result
);
2013 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2016 cat_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
2019 BTreeIterator
* iterator
;
2020 struct FSBufferDescriptor btdata
;
2021 struct HFSPlusCatalogFile file
;
2024 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2026 /* Create an iterator for use by us temporarily */
2027 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2028 bzero(iterator
, sizeof(*iterator
));
2031 if ((result
= getkey(hfsmp
, linkfileid
, (CatalogKey
*)&iterator
->key
))) {
2032 printf("hfs: cat_lookup_siblinglinks: getkey for %d failed %d\n", linkfileid
, result
);
2035 BDINIT(btdata
, &file
);
2037 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2038 printf("hfs: cat_lookup_siblinglinks: cannot find %d\n", linkfileid
);
2041 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2042 if (file
.flags
& kHFSHasLinkChainMask
) {
2045 parent
= ((HFSPlusCatalogKey
*)&iterator
->key
)->parentID
;
2047 /* directory inodes don't have a chain (its in an EA) */
2048 if (parent
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2049 result
= ENOLINK
; /* signal to caller to get head of list */
2050 } else if (parent
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
2052 *nextlinkid
= file
.hl_firstLinkID
;
2054 *prevlinkid
= file
.hl_prevLinkID
;
2055 *nextlinkid
= file
.hl_nextLinkID
;
2062 FREE(iterator
, M_TEMP
);
2063 return MacToVFSError(result
);
2068 * cat_createlink - create a link in the catalog
2070 * The following cat_attr fields are expected to be set:
2076 * ca_finderinfo (type and creator)
2079 cat_createlink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
2080 cnid_t nextlinkid
, cnid_t
*linkfileid
)
2084 FSBufferDescriptor btdata
;
2085 HFSPlusForkData
*rsrcforkp
;
2089 int thread_inserted
= 0;
2090 int alias_allocated
= 0;
2093 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2096 * Get the next CNID. We can change it since we hold the catalog lock.
2098 nextCNID
= hfsmp
->vcbNxtCNID
;
2099 if (nextCNID
== 0xFFFFFFFF) {
2100 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
2101 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2102 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
2103 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
2105 hfsmp
->vcbNxtCNID
++;
2107 MarkVCBDirty(hfsmp
);
2109 /* Get space for iterator, key and data */
2110 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
2111 bto
->iterator
.hint
.nodeNum
= 0;
2112 rsrcforkp
= &bto
->data
.hfsPlusFile
.resourceFork
;
2114 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
2116 printf("hfs: cat_createlink: err %d from buildkey\n", result
);
2120 /* This is our only chance to set the encoding (other than a rename). */
2121 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
, bto
->key
.nodeName
.length
);
2123 /* Insert the thread record first. */
2124 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, 0, 0);
2125 btdata
.bufferAddress
= &bto
->data
;
2126 btdata
.itemSize
= datalen
;
2127 btdata
.itemCount
= 1;
2130 buildthreadkey(nextCNID
, 0, (CatalogKey
*) &bto
->iterator
.key
);
2132 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2133 if ((result
== btExists
) && (hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2135 * Allow CNIDs on HFS Plus volumes to wrap around
2137 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
2138 nextCNID
= kHFSFirstUserCatalogNodeID
;
2143 thread_inserted
= 1;
2151 * CNID is now established. If we have wrapped then
2152 * update the vcbNxtCNID.
2154 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
)) {
2155 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
2156 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
2157 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
2162 * Now insert the link record.
2164 buildrecord(attrp
, nextCNID
, 0, encoding
, &bto
->data
, &datalen
);
2166 bto
->data
.hfsPlusFile
.hl_prevLinkID
= 0;
2167 bto
->data
.hfsPlusFile
.hl_nextLinkID
= nextlinkid
;
2168 bto
->data
.hfsPlusFile
.hl_linkReference
= attrp
->ca_linkref
;
2170 /* For directory hard links, create alias in resource fork */
2171 if (descp
->cd_flags
& CD_ISDIR
) {
2172 if ((result
= cat_makealias(hfsmp
, attrp
->ca_linkref
, &bto
->data
.hfsPlusFile
))) {
2175 alias_allocated
= 1;
2177 btdata
.bufferAddress
= &bto
->data
;
2178 btdata
.itemSize
= datalen
;
2179 btdata
.itemCount
= 1;
2181 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
2183 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
2185 if (result
== btExists
)
2189 if (linkfileid
!= NULL
) {
2190 *linkfileid
= nextCNID
;
2194 if (thread_inserted
) {
2195 printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result
));
2197 buildthreadkey(nextCNID
, 0, (CatalogKey
*)&bto
->iterator
.key
);
2198 if (BTDeleteRecord(fcb
, &bto
->iterator
)) {
2199 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp
->vcbVN
);
2200 hfs_mark_volume_inconsistent(hfsmp
);
2203 if (alias_allocated
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2204 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
,
2205 rsrcforkp
->extents
[0].blockCount
, 0);
2206 rsrcforkp
->extents
[0].startBlock
= 0;
2207 rsrcforkp
->extents
[0].blockCount
= 0;
2210 (void) BTFlushPath(fcb
);
2213 return MacToVFSError(result
);
2216 /* Directory hard links are visible as aliases on pre-Leopard systems and
2217 * as normal directories on Leopard or later. All directory hard link aliases
2218 * have the same resource fork content except for the three uniquely
2219 * identifying values that are updated in the resource fork data when the alias
2220 * is created. The following array is the constant resource fork data used
2221 * only for creating directory hard link aliases.
2223 static const char hfs_dirlink_alias_rsrc
[] = {
2224 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2240 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2246 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2249 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2250 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2252 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2255 /* Constants for directory hard link alias */
2257 /* Size of resource fork data array for directory hard link alias */
2258 kHFSAliasSize
= 0x1d0,
2260 /* Volume type for ejectable devices like disk image */
2261 kHFSAliasVolTypeEjectable
= 0x5,
2263 /* Offset for volume create date, in Mac OS local time */
2264 kHFSAliasVolCreateDateOffset
= 0x12a,
2266 /* Offset for the type of volume */
2267 kHFSAliasVolTypeOffset
= 0x130,
2269 /* Offset for folder ID of the parent directory of the directory inode */
2270 kHFSAliasParentIDOffset
= 0x132,
2272 /* Offset for folder ID of the directory inode */
2273 kHFSAliasTargetIDOffset
= 0x176,
2276 /* Create and write an alias that points at the directory represented by given
2277 * inode number on the same volume. Directory hard links are visible as
2278 * aliases in pre-Leopard systems and this function creates these aliases.
2280 * Note: This code is very specific to creating alias for the purpose
2281 * of directory hard links only, and should not be generalized.
2284 cat_makealias(struct hfsmount
*hfsmp
, u_int32_t inode_num
, struct HFSPlusCatalogFile
*crp
)
2292 HFSPlusForkData
*rsrcforkp
;
2296 rsrcforkp
= &(crp
->resourceFork
);
2298 blksize
= hfsmp
->blockSize
;
2299 blkcount
= howmany(kHFSAliasSize
, blksize
);
2300 sectorsize
= hfsmp
->hfs_logical_block_size
;
2301 bzero(rsrcforkp
, sizeof(HFSPlusForkData
));
2303 /* Allocate some disk space for the alias content. */
2304 result
= BlockAllocate(hfsmp
, 0, blkcount
, blkcount
,
2305 HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_METAZONE
,
2306 &rsrcforkp
->extents
[0].startBlock
,
2307 &rsrcforkp
->extents
[0].blockCount
);
2309 rsrcforkp
->extents
[0].startBlock
= 0;
2313 /* Acquire a buffer cache block for our block. */
2314 blkno
= ((u_int64_t
)rsrcforkp
->extents
[0].startBlock
* (u_int64_t
)blksize
) / sectorsize
;
2315 blkno
+= hfsmp
->hfsPlusIOPosOffset
/ sectorsize
;
2317 bp
= buf_getblk(hfsmp
->hfs_devvp
, blkno
, roundup(kHFSAliasSize
, hfsmp
->hfs_logical_block_size
), 0, 0, BLK_META
);
2319 journal_modify_block_start(hfsmp
->jnl
, bp
);
2322 /* Generate alias content */
2323 alias
= (char *)buf_dataptr(bp
);
2324 bzero(alias
, buf_size(bp
));
2325 bcopy(hfs_dirlink_alias_rsrc
, alias
, kHFSAliasSize
);
2327 /* Set the volume create date, local time in Mac OS format */
2328 valptr
= (uint32_t *)(alias
+ kHFSAliasVolCreateDateOffset
);
2329 *valptr
= OSSwapHostToBigInt32(hfsmp
->localCreateDate
);
2331 /* If the file system is on a virtual device like disk image,
2332 * update the volume type to be ejectable device.
2334 if (hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) {
2335 *(uint16_t *)(alias
+ kHFSAliasVolTypeOffset
) =
2336 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable
);
2339 /* Set id of the parent of the target directory */
2340 valptr
= (uint32_t *)(alias
+ kHFSAliasParentIDOffset
);
2341 *valptr
= OSSwapHostToBigInt32(hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
);
2343 /* Set id of the target directory */
2344 valptr
= (uint32_t *)(alias
+ kHFSAliasTargetIDOffset
);
2345 *valptr
= OSSwapHostToBigInt32(inode_num
);
2347 /* Write alias content to disk. */
2349 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
2350 } else if ((result
= buf_bwrite(bp
))) {
2354 /* Finish initializing the fork data. */
2355 rsrcforkp
->logicalSize
= kHFSAliasSize
;
2356 rsrcforkp
->totalBlocks
= rsrcforkp
->extents
[0].blockCount
;
2359 if (result
&& rsrcforkp
->extents
[0].startBlock
!= 0) {
2360 (void) BlockDeallocate(hfsmp
, rsrcforkp
->extents
[0].startBlock
, rsrcforkp
->extents
[0].blockCount
, 0);
2361 rsrcforkp
->extents
[0].startBlock
= 0;
2362 rsrcforkp
->extents
[0].blockCount
= 0;
2363 rsrcforkp
->logicalSize
= 0;
2364 rsrcforkp
->totalBlocks
= 0;
2370 * cat_deletelink - delete a link from the catalog
2373 cat_deletelink(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
2375 struct HFSPlusCatalogFile file
;
2376 struct cat_attr cattr
;
2377 uint32_t totalBlocks
;
2381 bzero(&file
, sizeof (file
));
2382 bzero(&cattr
, sizeof (cattr
));
2383 cattr
.ca_fileid
= descp
->cd_cnid
;
2385 /* Directory links have alias content to remove. */
2386 if (descp
->cd_flags
& CD_ISDIR
) {
2388 BTreeIterator
* iterator
;
2389 struct FSBufferDescriptor btdata
;
2391 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
2393 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2394 iterator
= &((BTreeControlBlockPtr
)(fcb
->ff_sysfileinfo
))->iterator
;
2395 iterator
->hint
.nodeNum
= 0;
2397 if ((result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0))) {
2400 BDINIT(btdata
, &file
);
2402 if ((result
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, NULL
))) {
2407 result
= cat_delete(hfsmp
, descp
, &cattr
);
2409 if ((result
== 0) &&
2410 (descp
->cd_flags
& CD_ISDIR
) &&
2411 (file
.recordType
== kHFSPlusFileRecord
)) {
2413 totalBlocks
= file
.resourceFork
.totalBlocks
;
2415 for (i
= 0; (i
< 8) && (totalBlocks
> 0); i
++) {
2416 if ((file
.resourceFork
.extents
[i
].blockCount
== 0) &&
2417 (file
.resourceFork
.extents
[i
].startBlock
== 0)) {
2421 (void) BlockDeallocate(hfsmp
,
2422 file
.resourceFork
.extents
[i
].startBlock
,
2423 file
.resourceFork
.extents
[i
].blockCount
, 0);
2425 totalBlocks
-= file
.resourceFork
.extents
[i
].blockCount
;
2426 file
.resourceFork
.extents
[i
].startBlock
= 0;
2427 file
.resourceFork
.extents
[i
].blockCount
= 0;
2436 * Callback to collect directory entries.
2437 * Called with readattr_state for each item in a directory.
2439 struct readattr_state
{
2440 struct hfsmount
*hfsmp
;
2441 struct cat_entrylist
*list
;
2448 getentriesattr_callback(const CatalogKey
*key
, const CatalogRecord
*rec
,
2449 struct readattr_state
*state
)
2451 struct cat_entrylist
*list
= state
->list
;
2452 struct hfsmount
*hfsmp
= state
->hfsmp
;
2453 struct cat_entry
*cep
;
2456 if (list
->realentries
>= list
->maxentries
)
2457 return (0); /* stop */
2459 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
2461 switch(rec
->recordType
) {
2462 case kHFSPlusFolderRecord
:
2463 case kHFSPlusFileRecord
:
2464 case kHFSFolderRecord
:
2465 case kHFSFileRecord
:
2466 if (parentcnid
!= state
->dir_cnid
) {
2467 state
->error
= ENOENT
;
2468 return (0); /* stop */
2472 state
->error
= ENOENT
;
2473 return (0); /* stop */
2476 /* Hide the private system directories and journal files */
2477 if (parentcnid
== kHFSRootFolderID
) {
2478 if (rec
->recordType
== kHFSPlusFolderRecord
) {
2479 if (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2480 rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2481 list
->skipentries
++;
2482 return (1); /* continue */
2485 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
2486 (rec
->recordType
== kHFSPlusFileRecord
) &&
2487 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
2488 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
2489 list
->skipentries
++;
2490 return (1); /* continue */
2494 cep
= &list
->entry
[list
->realentries
++];
2496 if (state
->stdhfs
) {
2497 struct HFSPlusCatalogFile cnoderec
;
2498 HFSPlusCatalogKey
* pluskey
;
2501 promoteattr(hfsmp
, rec
, &cnoderec
);
2502 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
2504 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
2505 promotekey(hfsmp
, (const HFSCatalogKey
*)key
, pluskey
, &encoding
);
2506 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
2507 FREE(pluskey
, M_TEMP
);
2509 if (rec
->recordType
== kHFSFileRecord
) {
2510 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
2512 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
2513 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
2514 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
2515 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
2518 getbsdattr(hfsmp
, (const struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
2519 builddesc((const HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
2520 isadir(rec
), &cep
->ce_desc
);
2522 if (rec
->recordType
== kHFSPlusFileRecord
) {
2523 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
2524 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
2525 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
2526 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
2528 /* Save link reference for later processing. */
2529 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2530 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)) {
2531 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2532 } else if ((rec
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2533 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2534 (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
)) {
2535 cep
->ce_attr
.ca_linkref
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
2540 return (list
->realentries
< list
->maxentries
);
2544 * Pack a cat_entrylist buffer with attributes from the catalog
2546 * Note: index is zero relative
2549 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
2553 BTreeIterator
* iterator
;
2554 struct readattr_state state
;
2562 ce_list
->realentries
= 0;
2564 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
2565 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2566 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
2568 state
.hfsmp
= hfsmp
;
2569 state
.list
= ce_list
;
2570 state
.dir_cnid
= parentcnid
;
2571 state
.stdhfs
= std_hfs
;
2574 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2575 bzero(iterator
, sizeof(*iterator
));
2576 key
= (CatalogKey
*)&iterator
->key
;
2578 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
2579 index
= dirhint
->dh_index
+ 1;
2582 * Attempt to build a key from cached filename
2584 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2585 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2591 * If the last entry wasn't cached then position the btree iterator
2593 if ((index
== 0) || !have_key
) {
2595 * Position the iterator at the directory's thread record.
2596 * (i.e. just before the first entry)
2598 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2599 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2601 result
= MacToVFSError(result
);
2606 * Iterate until we reach the entry just
2607 * before the one we want to start with.
2610 struct position_state ps
;
2615 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2618 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2619 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2623 result
= MacToVFSError(result
);
2625 result
= MacToVFSError(result
);
2631 /* Fill list with entries starting at iterator->key. */
2632 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2633 (IterateCallBackProcPtr
)getentriesattr_callback
, &state
);
2636 result
= state
.error
;
2637 else if (ce_list
->realentries
== 0)
2640 result
= MacToVFSError(result
);
2646 * Resolve any hard links.
2648 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
2649 struct FndrFileInfo
*fip
;
2650 struct cat_entry
*cep
;
2651 struct HFSPlusCatalogFile filerec
;
2655 cep
= &ce_list
->entry
[i
];
2656 if (cep
->ce_attr
.ca_linkref
== 0)
2659 /* Note: Finder info is still in Big Endian */
2660 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
2662 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2663 (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2664 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2667 if (S_ISREG(cep
->ce_attr
.ca_mode
) &&
2668 (SWAP_BE32(fip
->fdType
) == kHFSAliasType
) &&
2669 (SWAP_BE32(fip
->fdCreator
) == kHFSAliasCreator
) &&
2670 (cep
->ce_attr
.ca_recflags
& kHFSHasLinkChainMask
)) {
2673 if (isfilelink
|| isdirlink
) {
2674 if (cat_resolvelink(hfsmp
, cep
->ce_attr
.ca_linkref
, isdirlink
, &filerec
) != 0)
2676 /* Repack entry from inode record. */
2677 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
2678 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
2679 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
2680 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
2681 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
2685 FREE(iterator
, M_TEMP
);
2687 return MacToVFSError(result
);
2690 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
2693 * Callback to pack directory entries.
2694 * Called with packdirentry_state for each item in a directory.
2697 /* Hard link information collected during cat_getdirentries. */
2700 user_addr_t dirent_addr
;
2702 typedef struct linkinfo linkinfo_t
;
2704 /* State information for the getdirentries_callback function. */
2705 struct packdirentry_state
{
2707 u_int32_t cbs_parentID
;
2708 u_int32_t cbs_index
;
2710 ExtendedVCB
* cbs_hfsmp
;
2713 int32_t cbs_maxlinks
;
2714 linkinfo_t
* cbs_linkinfo
;
2715 struct cat_desc
* cbs_desc
;
2716 u_int8_t
* cbs_namebuf
;
2718 * The following fields are only used for NFS readdir, which
2719 * uses the next file id as the seek offset of each entry.
2721 struct direntry
* cbs_direntry
;
2722 struct direntry
* cbs_prevdirentry
;
2723 u_int32_t cbs_previlinkref
;
2724 Boolean cbs_hasprevdirentry
;
2729 * getdirentries callback for HFS Plus directories.
2732 getdirentries_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2733 struct packdirentry_state
*state
)
2735 struct hfsmount
*hfsmp
;
2736 const CatalogName
*cnp
;
2739 struct dirent catent
;
2740 struct direntry
* entry
= NULL
;
2742 u_int32_t ilinkref
= 0;
2743 u_int32_t curlinkref
= 0;
2746 u_int8_t type
= DT_UNKNOWN
;
2747 u_int8_t is_mangled
= 0;
2748 u_int8_t is_link
= 0;
2750 user_addr_t uiobase
= USER_ADDR_NULL
;
2755 Boolean stop_after_pack
= false;
2757 hfsmp
= state
->cbs_hfsmp
;
2758 curID
= ckp
->hfsPlus
.parentID
;
2760 /* We're done when parent directory changes */
2761 if (state
->cbs_parentID
!= curID
) {
2763 * If the parent ID is different from curID this means we've hit
2764 * the EOF for the directory. To help future callers, we mark
2765 * the cbs_eof boolean. However, we should only mark the EOF
2766 * boolean if we're about to return from this function.
2768 * This is because this callback function does its own uiomove
2769 * to get the data to userspace. If we set the boolean before determining
2770 * whether or not the current entry has enough room to write its
2771 * data to userland, we could fool the callers of this catalog function
2772 * into thinking they've hit EOF earlier than they really would have.
2773 * In that case, we'd know that we have more entries to process and
2774 * send to userland, but we didn't have enough room.
2776 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
2777 * about to return and won't write any new data back
2778 * to userland. In the stop_after_pack case, we'll set this boolean
2779 * regardless, so it's slightly safer to let that logic mark the boolean,
2780 * especially since it's closer to the return of this function.
2783 if (state
->cbs_extended
) {
2784 /* The last record has not been returned yet, so we
2785 * want to stop after packing the last item
2787 if (state
->cbs_hasprevdirentry
) {
2788 stop_after_pack
= true;
2790 state
->cbs_eof
= true;
2791 state
->cbs_result
= ENOENT
;
2792 return (0); /* stop */
2795 state
->cbs_eof
= true;
2796 state
->cbs_result
= ENOENT
;
2797 return (0); /* stop */
2801 if (state
->cbs_extended
) {
2802 entry
= state
->cbs_direntry
;
2803 nameptr
= (u_int8_t
*)&entry
->d_name
[0];
2804 maxnamelen
= NAME_MAX
;
2806 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
2807 maxnamelen
= NAME_MAX
;
2810 if (state
->cbs_extended
&& stop_after_pack
) {
2811 /* The last item returns a non-zero invalid cookie */
2814 switch(crp
->recordType
) {
2815 case kHFSPlusFolderRecord
:
2817 cnid
= crp
->hfsPlusFolder
.folderID
;
2818 /* Hide our private system directories. */
2819 if (curID
== kHFSRootFolderID
) {
2820 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
2821 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
2826 case kHFSPlusFileRecord
:
2827 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
2828 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
2829 cnid
= crp
->hfsPlusFile
.fileID
;
2831 * When a hardlink link is encountered save its link ref.
2833 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
2834 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
2835 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2836 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2837 /* If link ref is inode's file id then use it directly. */
2838 if (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) {
2839 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2841 ilinkref
= crp
->hfsPlusFile
.hl_linkReference
;
2844 } else if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHFSAliasType
) &&
2845 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSAliasCreator
) &&
2846 (crp
->hfsPlusFile
.flags
& kHFSHasLinkChainMask
) &&
2847 (crp
->hfsPlusFile
.hl_linkReference
>= kHFSFirstUserCatalogNodeID
) &&
2848 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
2849 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
2850 /* A directory's link resolves to a directory. */
2852 /* A directory's link ref is always inode's file id. */
2853 cnid
= crp
->hfsPlusFile
.hl_linkReference
;
2856 /* Hide the journal files */
2857 if ((curID
== kHFSRootFolderID
) &&
2858 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
2859 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
2860 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
2865 return (0); /* stop */
2868 cnp
= (const CatalogName
*) &ckp
->hfsPlus
.nodeName
;
2870 namelen
= cnp
->ustr
.length
;
2872 * For MacRoman encoded names, assume that its ascii and
2873 * convert it directly in an attempt to avoid the more
2874 * expensive utf8_encodestr conversion.
2876 if ((namelen
< maxnamelen
) && (crp
->hfsPlusFile
.textEncoding
== 0)) {
2879 const u_int16_t
*chp
;
2881 chp
= &cnp
->ustr
.unicode
[0];
2882 for (i
= 0; i
< (int)namelen
; ++i
) {
2884 if (ch
> 0x007f || ch
== 0x0000) {
2885 /* Perform expensive utf8_encodestr conversion */
2888 nameptr
[i
] = (ch
== '/') ? ':' : (u_int8_t
)ch
;
2890 nameptr
[namelen
] = '\0';
2894 result
= utf8_encodestr(cnp
->ustr
.unicode
, namelen
* sizeof(UniChar
),
2895 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
2898 /* Check result returned from encoding the filename to utf8 */
2899 if (result
== ENAMETOOLONG
) {
2901 * If we were looking at a catalog record for a hardlink (not the inode),
2902 * then we want to use its link ID as opposed to the inode ID for
2903 * a mangled name. For all other cases, they are the same. Note that
2904 * due to the way directory hardlinks are implemented, the actual link
2905 * is going to be counted as a file record, so we can catch both
2908 cnid_t linkid
= cnid
;
2910 linkid
= crp
->hfsPlusFile
.fileID
;
2913 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
2914 cnp
->ustr
.unicode
, maxnamelen
+ 1,
2915 (ByteCount
*)&namelen
, nameptr
, linkid
);
2920 if (state
->cbs_extended
) {
2922 * The index is 1 relative and includes "." and ".."
2924 * Also stuff the cnid in the upper 32 bits of the cookie.
2925 * The cookie is stored to the previous entry, which will
2926 * be packed and copied this time
2928 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
2929 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
2930 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
2932 catent
.d_type
= type
;
2933 catent
.d_namlen
= namelen
;
2934 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
2936 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
2938 catent
.d_fileno
= cnid
;
2939 uioaddr
= (caddr_t
) &catent
;
2942 /* Save current base address for post processing of hard-links. */
2943 if (ilinkref
|| state
->cbs_previlinkref
) {
2944 uiobase
= uio_curriovbase(state
->cbs_uio
);
2946 /* If this entry won't fit then we're done */
2947 if ((uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) ||
2948 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
2949 return (0); /* stop */
2952 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
2953 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2954 if (state
->cbs_result
== 0) {
2957 /* Remember previous entry */
2958 state
->cbs_desc
->cd_cnid
= cnid
;
2959 if (type
== DT_DIR
) {
2960 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2962 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2964 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2965 state
->cbs_desc
->cd_namelen
= 0;
2968 state
->cbs_desc
->cd_encoding
= xxxx
;
2971 state
->cbs_desc
->cd_namelen
= namelen
;
2972 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
2974 /* Store unmangled name for the directory hint else it will
2975 * restart readdir at the last location again
2977 u_int8_t
*new_nameptr
;
2979 size_t tmp_namelen
= 0;
2981 cnp
= (const CatalogName
*)&ckp
->hfsPlus
.nodeName
;
2982 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
2983 cnp
->ustr
.length
* sizeof(UniChar
),
2985 MALLOC(new_nameptr
, u_int8_t
*, bufsize
, M_TEMP
, M_WAITOK
);
2986 result
= utf8_encodestr(cnp
->ustr
.unicode
,
2987 cnp
->ustr
.length
* sizeof(UniChar
),
2988 new_nameptr
, &tmp_namelen
, bufsize
, ':', 0);
2990 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2991 bcopy(new_nameptr
, state
->cbs_namebuf
, tmp_namelen
+ 1);
2993 FREE(new_nameptr
, M_TEMP
);
2996 if (state
->cbs_hasprevdirentry
) {
2997 curlinkref
= ilinkref
; /* save current */
2998 ilinkref
= state
->cbs_previlinkref
; /* use previous */
3001 * Record any hard links for post processing.
3003 if ((ilinkref
!= 0) &&
3004 (state
->cbs_result
== 0) &&
3005 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
3006 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
3007 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
3008 state
->cbs_nlinks
++;
3010 if (state
->cbs_hasprevdirentry
) {
3011 ilinkref
= curlinkref
; /* restore current */
3015 /* Fill the direntry to be used the next time */
3016 if (state
->cbs_extended
) {
3017 if (stop_after_pack
) {
3018 state
->cbs_eof
= true;
3019 return (0); /* stop */
3021 entry
->d_type
= type
;
3022 entry
->d_namlen
= namelen
;
3023 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
3025 /* File number = 0 means skip entry */
3026 entry
->d_fileno
= 0;
3028 entry
->d_fileno
= cnid
;
3030 /* swap the current and previous entry */
3031 struct direntry
* tmp
;
3032 tmp
= state
->cbs_direntry
;
3033 state
->cbs_direntry
= state
->cbs_prevdirentry
;
3034 state
->cbs_prevdirentry
= tmp
;
3035 state
->cbs_hasprevdirentry
= true;
3036 state
->cbs_previlinkref
= ilinkref
;
3039 /* Continue iteration if there's room */
3040 return (state
->cbs_result
== 0 &&
3041 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3045 * getdirentries callback for standard HFS (non HFS+) directories.
3048 getdirentries_std_callback(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3049 struct packdirentry_state
*state
)
3051 struct hfsmount
*hfsmp
;
3052 const CatalogName
*cnp
;
3055 struct dirent catent
;
3057 u_int8_t type
= DT_UNKNOWN
;
3064 hfsmp
= state
->cbs_hfsmp
;
3066 curID
= ckp
->hfs
.parentID
;
3068 /* We're done when parent directory changes */
3069 if (state
->cbs_parentID
!= curID
) {
3070 state
->cbs_result
= ENOENT
;
3071 return (0); /* stop */
3074 nameptr
= (u_int8_t
*)&catent
.d_name
[0];
3075 maxnamelen
= NAME_MAX
;
3077 switch(crp
->recordType
) {
3078 case kHFSFolderRecord
:
3080 cnid
= crp
->hfsFolder
.folderID
;
3082 case kHFSFileRecord
:
3084 cnid
= crp
->hfsFile
.fileID
;
3087 return (0); /* stop */
3090 cnp
= (const CatalogName
*) ckp
->hfs
.nodeName
;
3091 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
3093 * When an HFS name cannot be encoded with the current
3094 * volume encoding we use MacRoman as a fallback.
3097 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1, (ByteCount
*)&namelen
, nameptr
);
3099 catent
.d_type
= type
;
3100 catent
.d_namlen
= namelen
;
3101 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
3102 catent
.d_fileno
= cnid
;
3103 uioaddr
= (caddr_t
) &catent
;
3105 /* If this entry won't fit then we're done */
3106 if (uiosize
> (user_size_t
)uio_resid(state
->cbs_uio
)) {
3107 return (0); /* stop */
3110 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
3111 if (state
->cbs_result
== 0) {
3114 /* Remember previous entry */
3115 state
->cbs_desc
->cd_cnid
= cnid
;
3116 if (type
== DT_DIR
) {
3117 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
3119 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
3121 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
3122 state
->cbs_desc
->cd_namelen
= 0;
3124 state
->cbs_desc
->cd_namelen
= namelen
;
3125 bcopy(nameptr
, state
->cbs_namebuf
, namelen
+ 1);
3128 /* Continue iteration if there's room */
3129 return (state
->cbs_result
== 0 && uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
3133 * Pack a uio buffer with directory entries from the catalog
3136 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
3137 uio_t uio
, int extended
, int * items
, int * eofflag
)
3140 BTreeIterator
* iterator
;
3142 struct packdirentry_state state
;
3150 if (extended
&& (hfsmp
->hfs_flags
& HFS_STANDARD
)) {
3153 fcb
= hfsmp
->hfs_catalog_cp
->c_datafork
;
3156 * Get a buffer for link info array, btree iterator and a direntry:
3158 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
3159 bufsize
= MAXPATHLEN
+ (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
3161 bufsize
+= 2*sizeof(struct direntry
);
3163 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
3164 bzero(buffer
, bufsize
);
3166 state
.cbs_extended
= extended
;
3167 state
.cbs_hasprevdirentry
= false;
3168 state
.cbs_previlinkref
= 0;
3169 state
.cbs_nlinks
= 0;
3170 state
.cbs_maxlinks
= maxlinks
;
3171 state
.cbs_linkinfo
= (linkinfo_t
*)((char *)buffer
+ MAXPATHLEN
);
3173 * We need to set cbs_eof to false regardless of whether or not the
3174 * control flow is actually in the extended case, since we use this
3175 * field to track whether or not we've returned EOF from the iterator function.
3177 state
.cbs_eof
= false;
3179 iterator
= (BTreeIterator
*) ((char *)state
.cbs_linkinfo
+ (maxlinks
* sizeof(linkinfo_t
)));
3180 key
= (CatalogKey
*)&iterator
->key
;
3182 index
= dirhint
->dh_index
+ 1;
3184 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
3185 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
3188 * Attempt to build a key from cached filename
3190 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
3191 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
3192 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
3197 if (index
== 0 && dirhint
->dh_threadhint
!= 0) {
3199 * Position the iterator at the directory's thread record.
3200 * (i.e. just before the first entry)
3202 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3203 iterator
->hint
.nodeNum
= dirhint
->dh_threadhint
;
3204 iterator
->hint
.index
= 0;
3209 * If the last entry wasn't cached then position the btree iterator
3213 * Position the iterator at the directory's thread record.
3214 * (i.e. just before the first entry)
3216 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
3217 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
3219 result
= MacToVFSError(result
);
3223 dirhint
->dh_threadhint
= iterator
->hint
.nodeNum
;
3226 * Iterate until we reach the entry just
3227 * before the one we want to start with.
3230 struct position_state ps
;
3235 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3238 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
3239 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
3243 result
= MacToVFSError(result
);
3245 result
= MacToVFSError(result
);
3251 state
.cbs_index
= index
;
3252 state
.cbs_hfsmp
= hfsmp
;
3253 state
.cbs_uio
= uio
;
3254 state
.cbs_desc
= &dirhint
->dh_desc
;
3255 state
.cbs_namebuf
= (u_int8_t
*)buffer
;
3256 state
.cbs_result
= 0;
3257 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
3259 /* Use a temporary buffer to hold intermediate descriptor names. */
3260 if (dirhint
->dh_desc
.cd_namelen
> 0 && dirhint
->dh_desc
.cd_nameptr
!= NULL
) {
3261 bcopy(dirhint
->dh_desc
.cd_nameptr
, buffer
, dirhint
->dh_desc
.cd_namelen
+1);
3262 if (dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) {
3263 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
3264 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
3267 dirhint
->dh_desc
.cd_nameptr
= (u_int8_t
*)buffer
;
3269 enum BTreeIterationOperations op
;
3270 if (extended
&& index
!= 0 && have_key
)
3271 op
= kBTreeCurrentRecord
;
3273 op
= kBTreeNextRecord
;
3276 * Process as many entries as possible starting at iterator->key.
3278 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
3279 result
= BTIterateRecords(fcb
, op
, iterator
,
3280 (IterateCallBackProcPtr
)getdirentries_std_callback
, &state
);
3282 result
= BTIterateRecords(fcb
, op
, iterator
,
3283 (IterateCallBackProcPtr
)getdirentries_callback
, &state
);
3285 /* For extended calls, every call to getdirentries_callback()
3286 * transfers the previous directory entry found to the user
3287 * buffer. Therefore when BTIterateRecords reaches the end of
3288 * Catalog BTree, call getdirentries_callback() again with
3289 * dummy values to copy the last directory entry stored in
3290 * packdirentry_state
3292 if (state
.cbs_extended
&& (result
== fsBTRecordNotFoundErr
)) {
3296 bzero(&ckp
, sizeof(ckp
));
3297 bzero(&crp
, sizeof(crp
));
3299 result
= getdirentries_callback(&ckp
, &crp
, &state
);
3303 /* Note that state.cbs_index is still valid on errors */
3304 *items
= state
.cbs_index
- index
;
3305 index
= state
.cbs_index
;
3308 * Also note that cbs_eof is set in all cases if we ever hit EOF
3309 * during the enumeration by the catalog callback. Mark the directory's hint
3310 * descriptor as having hit EOF.
3313 if (state
.cbs_eof
) {
3314 dirhint
->dh_desc
.cd_flags
|= CD_EOF
;
3318 /* Finish updating the catalog iterator. */
3319 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
3320 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
3321 dirhint
->dh_index
= index
- 1;
3323 /* Fix up the name. */
3324 if (dirhint
->dh_desc
.cd_namelen
> 0) {
3325 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)buffer
, dirhint
->dh_desc
.cd_namelen
, 0, 0);
3326 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
3328 dirhint
->dh_desc
.cd_nameptr
= NULL
;
3329 dirhint
->dh_desc
.cd_namelen
= 0;
3333 * Post process any hard links to get the real file id.
3335 if (state
.cbs_nlinks
> 0) {
3337 user_addr_t address
;
3340 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
3341 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
3343 /* This assumes that d_ino is always first field. */
3344 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
3345 if (address
== (user_addr_t
)0)
3347 if (uio_isuserspace(uio
)) {
3349 ino64_t fileid_64
= (ino64_t
)fileid
;
3350 (void) copyout(&fileid_64
, address
, sizeof(fileid_64
));
3352 (void) copyout(&fileid
, address
, sizeof(fileid
));
3354 } else /* system space */ {
3356 ino64_t fileid_64
= (ino64_t
)fileid
;
3357 bcopy(&fileid_64
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid_64
));
3359 bcopy(&fileid
, (void*) CAST_DOWN(caddr_t
, address
), sizeof(fileid
));
3365 if (state
.cbs_result
)
3366 result
= state
.cbs_result
;
3368 result
= MacToVFSError(result
);
3370 if (result
== ENOENT
) {
3375 FREE(buffer
, M_TEMP
);
3382 * Callback to establish directory position.
3383 * Called with position_state for each item in a directory.
3386 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3387 struct position_state
*state
)
3391 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
3392 curID
= ckp
->hfs
.parentID
;
3394 curID
= ckp
->hfsPlus
.parentID
;
3396 /* Make sure parent directory didn't change */
3397 if (state
->parentID
!= curID
) {
3398 state
->error
= EINVAL
;
3399 return (0); /* stop */
3402 /* Count this entry */
3403 switch(crp
->recordType
) {
3404 case kHFSPlusFolderRecord
:
3405 case kHFSPlusFileRecord
:
3406 case kHFSFolderRecord
:
3407 case kHFSFileRecord
:
3411 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3412 crp
->recordType
, curID
);
3413 state
->error
= EINVAL
;
3414 return (0); /* stop */
3417 return (state
->count
< state
->index
);
3422 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3424 * The name portion of the key is compared using a 16-bit binary comparison.
3425 * This is called from the b-tree code.
3428 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3430 u_int32_t searchParentID
, trialParentID
;
3433 searchParentID
= searchKey
->parentID
;
3434 trialParentID
= trialKey
->parentID
;
3437 if (searchParentID
> trialParentID
) {
3439 } else if (searchParentID
< trialParentID
) {
3442 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
3443 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
3444 int length1
= searchKey
->nodeName
.length
;
3445 int length2
= trialKey
->nodeName
.length
;
3449 if (length1
< length2
) {
3452 } else if (length1
> length2
) {
3479 * Compare two standard HFS catalog keys
3481 * Result: +n search key > trial key
3482 * 0 search key = trial key
3483 * -n search key < trial key
3486 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
3488 cnid_t searchParentID
, trialParentID
;
3491 searchParentID
= searchKey
->parentID
;
3492 trialParentID
= trialKey
->parentID
;
3494 if (searchParentID
> trialParentID
)
3496 else if (searchParentID
< trialParentID
)
3498 else /* parent dirID's are equal, compare names */
3499 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
3506 * Compare two HFS+ catalog keys
3508 * Result: +n search key > trial key
3509 * 0 search key = trial key
3510 * -n search key < trial key
3513 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
3515 cnid_t searchParentID
, trialParentID
;
3518 searchParentID
= searchKey
->parentID
;
3519 trialParentID
= trialKey
->parentID
;
3521 if (searchParentID
> trialParentID
) {
3524 else if (searchParentID
< trialParentID
) {
3527 /* parent node ID's are equal, compare names */
3528 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
3529 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
3531 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
3532 searchKey
->nodeName
.length
,
3533 &trialKey
->nodeName
.unicode
[0],
3534 trialKey
->nodeName
.length
);
3542 * buildkey - build a Catalog b-tree key from a cnode descriptor
3545 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
3546 HFSPlusCatalogKey
*key
, int retry
)
3548 int utf8_flags
= UTF_ESCAPE_ILLEGAL
;
3550 size_t unicodeBytes
= 0;
3552 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
3553 return (EINVAL
); /* invalid name */
3555 key
->parentID
= descp
->cd_parentcnid
;
3556 key
->nodeName
.length
= 0;
3558 * Convert filename from UTF-8 into Unicode
3561 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
3562 utf8_flags
|= UTF_DECOMPOSED
;
3563 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
3564 key
->nodeName
.unicode
, &unicodeBytes
,
3565 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
3566 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
3567 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
3569 if (result
!= ENAMETOOLONG
)
3570 result
= EINVAL
; /* name has invalid characters */
3575 * For HFS volumes convert to an HFS compatible key
3577 * XXX need to save the encoding that succeeded
3579 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
3580 HFSCatalogKey hfskey
;
3582 bzero(&hfskey
, sizeof(hfskey
));
3583 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
3584 hfskey
.parentID
= key
->parentID
;
3585 hfskey
.nodeName
[0] = 0;
3586 if (key
->nodeName
.length
> 0) {
3588 if ((res
= unicode_to_hfs(HFSTOVCB(hfsmp
),
3589 key
->nodeName
.length
* 2,
3590 key
->nodeName
.unicode
,
3591 &hfskey
.nodeName
[0], retry
)) != 0) {
3592 if (res
!= ENAMETOOLONG
)
3597 hfskey
.keyLength
+= hfskey
.nodeName
[0];
3599 bcopy(&hfskey
, key
, sizeof(hfskey
));
3606 * Resolve hard link reference to obtain the inode record.
3609 cat_resolvelink(struct hfsmount
*hfsmp
, u_int32_t linkref
, int isdirlink
, struct HFSPlusCatalogFile
*recp
)
3611 FSBufferDescriptor btdata
;
3612 struct BTreeIterator
*iterator
;
3613 struct cat_desc idesc
;
3618 BDINIT(btdata
, recp
);
3621 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3622 parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
3624 MAKE_INODE_NAME(inodename
, sizeof(inodename
), (unsigned int)linkref
);
3625 parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
3628 /* Get space for iterator */
3629 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3630 bzero(iterator
, sizeof(*iterator
));
3632 /* Build a descriptor for private dir. */
3633 idesc
.cd_parentcnid
= parentcnid
;
3634 idesc
.cd_nameptr
= (const u_int8_t
*)inodename
;
3635 idesc
.cd_namelen
= strlen(inodename
);
3638 idesc
.cd_encoding
= 0;
3639 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
3641 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3642 &btdata
, NULL
, NULL
);
3645 /* Make sure there's a reference */
3646 if (recp
->hl_linkCount
== 0)
3647 recp
->hl_linkCount
= 2;
3649 printf("hfs: cat_resolvelink: can't find %s\n", inodename
);
3652 FREE(iterator
, M_TEMP
);
3654 return (result
? ENOENT
: 0);
3658 * Resolve hard link reference to obtain the inode number.
3661 resolvelinkid(struct hfsmount
*hfsmp
, u_int32_t linkref
, ino_t
*ino
)
3663 struct HFSPlusCatalogFile record
;
3667 * Since we know resolvelinkid is only called from
3668 * cat_getdirentries, we can assume that only file
3669 * hardlinks need to be resolved (cat_getdirentries
3670 * can resolve directory hardlinks in place).
3672 error
= cat_resolvelink(hfsmp
, linkref
, 0, &record
);
3674 if (record
.fileID
== 0)
3677 *ino
= record
.fileID
;
3683 * getkey - get a key from id by doing a thread lookup
3686 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
3688 struct BTreeIterator
* iterator
;
3689 FSBufferDescriptor btdata
;
3692 CatalogRecord
* recp
;
3696 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
3698 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3699 bzero(iterator
, sizeof(*iterator
));
3700 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
3702 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
3703 BDINIT(btdata
, recp
);
3705 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
3706 &btdata
, &datasize
, iterator
);
3710 /* Turn thread record into a cnode key (in place) */
3711 switch (recp
->recordType
) {
3712 case kHFSFileThreadRecord
:
3713 case kHFSFolderThreadRecord
:
3714 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
3715 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
3716 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
3719 case kHFSPlusFileThreadRecord
:
3720 case kHFSPlusFolderThreadRecord
:
3721 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
3722 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
3723 (keyp
->hfsPlus
.nodeName
.length
* 2);
3724 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
3733 FREE(iterator
, M_TEMP
);
3736 return MacToVFSError(result
);
3740 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
3741 * null arguments to cat_idlookup instead, but we save around 10% by not building the
3742 * cat_desc here). Both key and attrp must point to real structures.
3744 * The key's parent id is the only part of the key expected to be used by the caller.
3745 * The name portion of the key may not always be valid (ie in the case of a hard link).
3748 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
3752 result
= getkey(hfsmp
, cnid
, key
);
3755 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, 0, NULL
, attrp
, NULL
, NULL
);
3758 * Check for a raw file hardlink inode.
3759 * Fix up the parent id in the key if necessary.
3760 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
3762 if ((result
== 0) &&
3763 (key
->hfsPlus
.parentID
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3764 (attrp
->ca_recflags
& kHFSHasLinkChainMask
)) {
3765 cnid_t nextlinkid
= 0;
3766 cnid_t prevlinkid
= 0;
3767 struct cat_desc linkdesc
;
3770 * Pick up the first link in the chain and get a descriptor for it.
3771 * This allows blind bulk access checks to work for hardlinks.
3773 if ((cat_lookup_siblinglinks(hfsmp
, cnid
, &prevlinkid
, &nextlinkid
) == 0) &&
3774 (nextlinkid
!= 0)) {
3775 if (cat_findname(hfsmp
, nextlinkid
, &linkdesc
) == 0) {
3776 key
->hfsPlus
.parentID
= linkdesc
.cd_parentcnid
;
3777 cat_releasedesc(&linkdesc
);
3781 return MacToVFSError(result
);
3786 * buildrecord - build a default catalog directory or file record
3789 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
3790 CatalogRecord
*crp
, u_int32_t
*recordSize
)
3792 int type
= attrp
->ca_mode
& S_IFMT
;
3793 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
3796 createtime
= UTCToLocal(createtime
);
3797 if (type
== S_IFDIR
) {
3798 bzero(crp
, sizeof(HFSCatalogFolder
));
3799 crp
->recordType
= kHFSFolderRecord
;
3800 crp
->hfsFolder
.folderID
= cnid
;
3801 crp
->hfsFolder
.createDate
= createtime
;
3802 crp
->hfsFolder
.modifyDate
= createtime
;
3803 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
3804 *recordSize
= sizeof(HFSCatalogFolder
);
3806 bzero(crp
, sizeof(HFSCatalogFile
));
3807 crp
->recordType
= kHFSFileRecord
;
3808 crp
->hfsFile
.fileID
= cnid
;
3809 crp
->hfsFile
.createDate
= createtime
;
3810 crp
->hfsFile
.modifyDate
= createtime
;
3811 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
3812 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
3813 *recordSize
= sizeof(HFSCatalogFile
);
3816 struct HFSPlusBSDInfo
* bsdp
= NULL
;
3818 if (type
== S_IFDIR
) {
3819 crp
->recordType
= kHFSPlusFolderRecord
;
3820 crp
->hfsPlusFolder
.flags
= attrp
->ca_recflags
;
3821 crp
->hfsPlusFolder
.valence
= 0;
3822 crp
->hfsPlusFolder
.folderID
= cnid
;
3823 crp
->hfsPlusFolder
.createDate
= createtime
;
3824 crp
->hfsPlusFolder
.contentModDate
= createtime
;
3825 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
3826 crp
->hfsPlusFolder
.accessDate
= createtime
;
3827 crp
->hfsPlusFolder
.backupDate
= 0;
3828 crp
->hfsPlusFolder
.textEncoding
= encoding
;
3829 crp
->hfsPlusFolder
.folderCount
= 0;
3830 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
3831 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
3832 bsdp
->special
.linkCount
= 1;
3833 *recordSize
= sizeof(HFSPlusCatalogFolder
);
3835 crp
->recordType
= kHFSPlusFileRecord
;
3836 crp
->hfsPlusFile
.flags
= attrp
->ca_recflags
;
3837 crp
->hfsPlusFile
.reserved1
= 0;
3838 crp
->hfsPlusFile
.fileID
= cnid
;
3839 crp
->hfsPlusFile
.createDate
= createtime
;
3840 crp
->hfsPlusFile
.contentModDate
= createtime
;
3841 crp
->hfsPlusFile
.accessDate
= createtime
;
3842 crp
->hfsPlusFile
.attributeModDate
= createtime
;
3843 crp
->hfsPlusFile
.backupDate
= 0;
3844 crp
->hfsPlusFile
.textEncoding
= encoding
;
3845 crp
->hfsPlusFile
.reserved2
= 0;
3846 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
3847 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
3848 /* BLK/CHR need to save the device info */
3849 if (type
== S_IFBLK
|| type
== S_IFCHR
) {
3850 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
3852 bsdp
->special
.linkCount
= 1;
3854 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
3855 *recordSize
= sizeof(HFSPlusCatalogFile
);
3857 bsdp
->ownerID
= attrp
->ca_uid
;
3858 bsdp
->groupID
= attrp
->ca_gid
;
3859 bsdp
->fileMode
= attrp
->ca_mode
;
3860 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
3861 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
3867 * builddesc - build a cnode descriptor from an HFS+ key
3870 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_int32_t hint
, u_int32_t encoding
,
3871 int isdir
, struct cat_desc
*descp
)
3874 unsigned char * nameptr
;
3877 unsigned char tmpbuff
[128];
3879 /* guess a size... */
3880 bufsize
= (3 * key
->nodeName
.length
) + 1;
3881 if (bufsize
>= sizeof(tmpbuff
) - 1) {
3882 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3884 nameptr
= &tmpbuff
[0];
3887 result
= utf8_encodestr(key
->nodeName
.unicode
,
3888 key
->nodeName
.length
* sizeof(UniChar
),
3889 nameptr
, (size_t *)&utf8len
,
3892 if (result
== ENAMETOOLONG
) {
3893 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
3894 key
->nodeName
.length
* sizeof(UniChar
),
3896 FREE(nameptr
, M_TEMP
);
3897 MALLOC(nameptr
, unsigned char *, bufsize
, M_TEMP
, M_WAITOK
);
3899 result
= utf8_encodestr(key
->nodeName
.unicode
,
3900 key
->nodeName
.length
* sizeof(UniChar
),
3901 nameptr
, (size_t *)&utf8len
,
3904 descp
->cd_parentcnid
= key
->parentID
;
3905 descp
->cd_nameptr
= (const u_int8_t
*)vfs_addname((char *)nameptr
, utf8len
, 0, 0);
3906 descp
->cd_namelen
= utf8len
;
3907 descp
->cd_cnid
= cnid
;
3908 descp
->cd_hint
= hint
;
3909 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
3911 descp
->cd_flags
|= CD_ISDIR
;
3912 descp
->cd_encoding
= encoding
;
3913 if (nameptr
!= &tmpbuff
[0]) {
3914 FREE(nameptr
, M_TEMP
);
3921 * getbsdattr - get attributes in bsd format
3925 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
3927 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
3928 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
3930 attrp
->ca_recflags
= crp
->flags
;
3931 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
3932 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
3933 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
3934 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
3935 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
3936 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
3938 if ((bsd
->fileMode
& S_IFMT
) == 0) {
3939 attrp
->ca_flags
= 0;
3940 attrp
->ca_uid
= hfsmp
->hfs_uid
;
3941 attrp
->ca_gid
= hfsmp
->hfs_gid
;
3943 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
3945 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
3947 attrp
->ca_linkcount
= 1;
3950 attrp
->ca_linkcount
= 1; /* may be overridden below */
3952 attrp
->ca_uid
= bsd
->ownerID
;
3953 attrp
->ca_gid
= bsd
->groupID
;
3954 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
3955 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
3956 switch (attrp
->ca_mode
& S_IFMT
) {
3957 case S_IFCHR
: /* fall through */
3959 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
3962 case S_IFDIR
: /* fall through */
3964 /* Pick up the hard link count */
3965 if (bsd
->special
.linkCount
> 0)
3966 attrp
->ca_linkcount
= bsd
->special
.linkCount
;
3971 * Override the permissions as determined by the mount auguments
3972 * in ALMOST the same way unset permissions are treated but keep
3973 * track of whether or not the file or folder is hfs locked
3974 * by leaving the h_pflags field unchanged from what was unpacked
3975 * out of the catalog.
3978 * This code was used to do UID translation with MNT_IGNORE_OWNERS
3979 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
3980 * at the VFS layer, so there is no need to do it here now; this also
3981 * allows VFS to let root see the real UIDs.
3983 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
3984 * attrp->ca_uid = hfsmp->hfs_uid;
3985 * attrp->ca_gid = hfsmp->hfs_gid;
3991 if (!S_ISDIR(attrp
->ca_mode
)) {
3992 attrp
->ca_mode
&= ~S_IFMT
;
3993 attrp
->ca_mode
|= S_IFDIR
;
3995 attrp
->ca_entries
= ((const HFSPlusCatalogFolder
*)crp
)->valence
;
3996 attrp
->ca_dircount
= ((hfsmp
->hfs_flags
& HFS_FOLDERCOUNT
) && (attrp
->ca_recflags
& kHFSHasFolderCountMask
)) ?
3997 ((const HFSPlusCatalogFolder
*)crp
)->folderCount
: 0;
3999 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4000 if (((const HFSPlusCatalogFolder
*)crp
)->userInfo
.frFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4001 attrp
->ca_flags
|= UF_HIDDEN
;
4003 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4004 if (crp
->flags
& kHFSFileLockedMask
) {
4005 /* The file's supposed to be locked:
4006 Make sure at least one of the IMMUTABLE bits is set: */
4007 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
4008 attrp
->ca_flags
|= UF_IMMUTABLE
;
4010 /* The file's supposed to be unlocked: */
4011 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
4013 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4014 if (crp
->userInfo
.fdFlags
& OSSwapHostToBigConstInt16(kFinderInvisibleMask
))
4015 attrp
->ca_flags
|= UF_HIDDEN
;
4016 /* get total blocks (both forks) */
4017 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
4019 /* On HFS+ the ThreadExists flag must always be set. */
4020 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
4021 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
4023 /* Pick up the hardlink first link, if any. */
4024 attrp
->ca_firstlink
= (attrp
->ca_recflags
& kHFSHasLinkChainMask
) ? crp
->hl_firstLinkID
: 0;
4027 attrp
->ca_fileid
= crp
->fileID
;
4029 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
4033 * promotekey - promote hfs key to hfs plus key
4037 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
4038 HFSPlusCatalogKey
*keyp
, u_int32_t
*encoding
)
4040 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
4044 *encoding
= hfsmp
->hfs_encoding
;
4046 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
4047 kHFSPlusMaxFileNameChars
, &uniCount
);
4049 * When an HFS name cannot be encoded with the current
4050 * encoding use MacRoman as a fallback.
4052 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
4054 (void) mac_roman_to_unicode(hfskey
->nodeName
,
4055 keyp
->nodeName
.unicode
,
4056 kHFSPlusMaxFileNameChars
,
4060 keyp
->nodeName
.length
= uniCount
;
4061 keyp
->parentID
= hfskey
->parentID
;
4065 * promotefork - promote hfs fork info to hfs plus
4069 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
4070 int resource
, struct cat_fork
* forkp
)
4072 struct HFSPlusExtentDescriptor
*xp
;
4073 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4075 bzero(forkp
, sizeof(*forkp
));
4076 xp
= &forkp
->cf_extents
[0];
4078 forkp
->cf_size
= filep
->rsrcLogicalSize
;
4079 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
4080 forkp
->cf_bytesread
= 0;
4081 forkp
->cf_vblocks
= 0;
4082 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
4083 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
4084 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
4085 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
4086 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
4087 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
4089 forkp
->cf_size
= filep
->dataLogicalSize
;
4090 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
4091 forkp
->cf_bytesread
= 0;
4092 forkp
->cf_vblocks
= 0;
4093 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
4094 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
4095 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
4096 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
4097 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
4098 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
4103 * promoteattr - promote standard hfs catalog attributes to hfs plus
4107 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
4109 u_int32_t blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
4111 if (dataPtr
->recordType
== kHFSFolderRecord
) {
4112 const struct HFSCatalogFolder
* folder
;
4114 folder
= (const struct HFSCatalogFolder
*) dataPtr
;
4115 crp
->recordType
= kHFSPlusFolderRecord
;
4116 crp
->flags
= folder
->flags
;
4117 crp
->fileID
= folder
->folderID
;
4118 crp
->createDate
= LocalToUTC(folder
->createDate
);
4119 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
4120 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
4121 crp
->reserved1
= folder
->valence
;
4123 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
4125 const struct HFSCatalogFile
* file
;
4127 file
= (const struct HFSCatalogFile
*) dataPtr
;
4128 crp
->recordType
= kHFSPlusFileRecord
;
4129 crp
->flags
= file
->flags
;
4130 crp
->fileID
= file
->fileID
;
4131 crp
->createDate
= LocalToUTC(file
->createDate
);
4132 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
4133 crp
->backupDate
= LocalToUTC(file
->backupDate
);
4136 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
4137 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
4138 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
4139 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
4141 crp
->textEncoding
= 0;
4142 crp
->attributeModDate
= crp
->contentModDate
;
4143 crp
->accessDate
= crp
->contentModDate
;
4144 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
4148 * Build a catalog node thread record from a catalog key
4149 * and return the size of the record.
4152 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
4157 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
4158 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
4160 size
= sizeof(HFSCatalogThread
);
4163 rec
->recordType
= kHFSFolderThreadRecord
;
4165 rec
->recordType
= kHFSFileThreadRecord
;
4166 rec
->parentID
= key
->parentID
;
4167 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
4170 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
4171 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
4173 size
= sizeof(HFSPlusCatalogThread
);
4175 rec
->recordType
= kHFSPlusFolderThreadRecord
;
4177 rec
->recordType
= kHFSPlusFileThreadRecord
;
4179 rec
->parentID
= key
->parentID
;
4180 bcopy(&key
->nodeName
, &rec
->nodeName
,
4181 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
4183 /* HFS Plus has varaible sized thread records */
4184 size
-= (sizeof(rec
->nodeName
.unicode
) -
4185 (rec
->nodeName
.length
* sizeof(UniChar
)));
4192 * Build a catalog node thread key.
4195 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
4198 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
4199 key
->hfs
.reserved
= 0;
4200 key
->hfs
.parentID
= parentID
;
4201 key
->hfs
.nodeName
[0] = 0;
4203 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
4204 key
->hfsPlus
.parentID
= parentID
;
4205 key
->hfsPlus
.nodeName
.length
= 0;
4210 * Extract the text encoding from a catalog node record.
4213 getencoding(const CatalogRecord
*crp
)
4217 if (crp
->recordType
== kHFSPlusFolderRecord
)
4218 encoding
= crp
->hfsPlusFolder
.textEncoding
;
4219 else if (crp
->recordType
== kHFSPlusFileRecord
)
4220 encoding
= crp
->hfsPlusFile
.textEncoding
;
4228 * Extract the CNID from a catalog node record.
4231 getcnid(const CatalogRecord
*crp
)
4235 switch (crp
->recordType
) {
4236 case kHFSFolderRecord
:
4237 cnid
= crp
->hfsFolder
.folderID
;
4239 case kHFSFileRecord
:
4240 cnid
= crp
->hfsFile
.fileID
;
4242 case kHFSPlusFolderRecord
:
4243 cnid
= crp
->hfsPlusFolder
.folderID
;
4245 case kHFSPlusFileRecord
:
4246 cnid
= crp
->hfsPlusFile
.fileID
;
4249 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp
);
4257 * Extract the parent ID from a catalog node record.
4260 getparentcnid(const CatalogRecord
*recp
)
4264 switch (recp
->recordType
) {
4265 case kHFSFileThreadRecord
:
4266 case kHFSFolderThreadRecord
:
4267 cnid
= recp
->hfsThread
.parentID
;
4270 case kHFSPlusFileThreadRecord
:
4271 case kHFSPlusFolderThreadRecord
:
4272 cnid
= recp
->hfsPlusThread
.parentID
;
4275 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp
);
4283 * Determine if a catalog node record is a directory.
4286 isadir(const CatalogRecord
*crp
)
4288 return (crp
->recordType
== kHFSFolderRecord
||
4289 crp
->recordType
== kHFSPlusFolderRecord
);
4293 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4294 * (not inode) using catalog record id. Note that this function does
4295 * NOT resolve directory hard link to its directory inode and return
4298 * Note: The caller is responsible for releasing the output catalog
4299 * descriptor (when supplied outdescp is non-null).
4302 cat_lookup_dirlink(struct hfsmount
*hfsmp
, cnid_t dirlink_id
,
4303 u_int8_t forktype
, struct cat_desc
*outdescp
,
4304 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4306 struct BTreeIterator
*iterator
= NULL
;
4307 FSBufferDescriptor btdata
;
4310 CatalogRecord
*recp
= NULL
;
4313 /* No directory hard links on standard HFS */
4314 if (hfsmp
->vcbSigWord
== kHFSSigWord
) {
4318 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
4319 if (iterator
== NULL
) {
4322 bzero(iterator
, sizeof(*iterator
));
4323 buildthreadkey(dirlink_id
, 1, (CatalogKey
*)&iterator
->key
);
4325 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
4330 BDINIT(btdata
, recp
);
4332 error
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
4333 &btdata
, &datasize
, iterator
);
4337 /* Directory hard links are catalog file record */
4338 if (recp
->recordType
!= kHFSPlusFileThreadRecord
) {
4343 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
4344 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
4345 (keyp
->hfsPlus
.nodeName
.length
* 2);
4346 if (forktype
== kHFSResourceForkType
) {
4347 /* Lookup resource fork for directory hard link */
4348 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, true, outdescp
, attrp
, forkp
, NULL
);
4350 /* Lookup data fork, if any, for directory hard link */
4351 error
= cat_lookupbykey(hfsmp
, keyp
, HFS_LOOKUP_HARDLINK
, 0, false, outdescp
, attrp
, forkp
, NULL
);
4354 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id
, error
);
4355 hfs_mark_volume_inconsistent(hfsmp
);
4358 /* Just for sanity, make sure that id in catalog record and thread record match */
4359 if ((outdescp
!= NULL
) && (dirlink_id
!= outdescp
->cd_cnid
)) {
4360 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id
, outdescp
->cd_cnid
);
4361 hfs_mark_volume_inconsistent(hfsmp
);
4369 FREE(iterator
, M_TEMP
);
4371 return MacToVFSError(error
);
4375 * cnode_update_dirlink - update the catalog node for directory hard link
4376 * described by descp using the data from attrp and forkp.
4379 cat_update_dirlink(struct hfsmount
*hfsmp
, u_int8_t forktype
,
4380 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
4382 if (forktype
== kHFSResourceForkType
) {
4383 return cat_update_internal(hfsmp
, true, descp
, attrp
, NULL
, forkp
);
4385 return cat_update_internal(hfsmp
, true, descp
, attrp
, forkp
, NULL
);