2 * Copyright (c) 2000-2005 Apple Computer, 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/HFSUnicodeWrappers.h"
51 * Initialization of an FSBufferDescriptor structure.
53 #define BDINIT(bd, addr) { \
54 (bd).bufferAddress = (addr); \
55 (bd).itemSize = sizeof(*(addr)); \
61 BTreeIterator iterator
;
62 HFSPlusCatalogKey key
;
67 struct cat_desc
* s_desc
;
68 struct cat_attr
* s_attr
;
69 struct cat_fork
* s_datafork
;
70 struct cat_fork
* s_rsrcfork
;
71 struct hfsmount
* s_hfsmp
;
74 struct position_state
{
79 struct hfsmount
*hfsmp
;
82 /* Map file mode type to directory entry types */
83 u_char modetodirtype
[16] = {
84 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
85 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
86 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
87 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
89 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
92 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
93 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
95 static int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
96 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
98 extern int mac_roman_to_unicode(const Str31 hfs_str
, UniChar
*uni_str
,
99 UInt32 maxCharLen
, UInt32
*unicodeChars
);
101 extern int unicode_to_hfs(ExtendedVCB
*vcb
, ByteCount srcLen
,
102 const u_int16_t
* srcStr
, Str31 dstStr
, int retry
);
105 /* Internal catalog support routines */
107 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
108 struct position_state
*state
);
110 static int resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
);
112 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
114 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
115 HFSPlusCatalogKey
*key
, int retry
);
117 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
119 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, int *recordSize
);
121 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
123 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
124 int isdir
, struct cat_desc
*descp
);
126 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
128 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_long
*encoding
);
129 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
130 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
132 static cnid_t
getcnid(const CatalogRecord
*crp
);
133 static u_long
getencoding(const CatalogRecord
*crp
);
134 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
136 static int isadir(const CatalogRecord
*crp
);
138 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
143 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, struct proc
*p
)
149 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
151 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
153 result
= BTReserveSpace(fcb
, ops
, (void*)cookie
);
155 hfs_systemfile_unlock(hfsmp
, lockflags
);
157 return MacToVFSError(result
);
162 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, struct proc
*p
)
167 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
169 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
171 (void) BTReleaseReserve(fcb
, (void*)cookie
);
173 hfs_systemfile_unlock(hfsmp
, lockflags
);
180 struct hfsmount
*hfsmp
,
181 CatalogRecord
* recp
,
182 struct cat_attr
*attrp
,
183 struct cat_fork
*datafp
,
184 struct cat_fork
*rsrcfp
)
186 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
189 struct HFSPlusCatalogFile cnoderec
;
191 promoteattr(hfsmp
, recp
, &cnoderec
);
192 getbsdattr(hfsmp
, &cnoderec
, attrp
);
194 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
198 bzero(datafp
, sizeof(*datafp
));
200 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
201 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
203 /* Convert the data fork. */
204 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
205 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
206 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
207 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
208 datafp
->cf_bytesread
=
209 recp
->hfsPlusFile
.dataFork
.clumpSize
*
210 HFSTOVCB(hfsmp
)->blockSize
;
212 datafp
->cf_bytesread
= 0;
214 datafp
->cf_vblocks
= 0;
215 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
216 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
218 /* Convert the resource fork. */
219 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
220 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
221 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
222 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
223 datafp
->cf_bytesread
=
224 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
225 HFSTOVCB(hfsmp
)->blockSize
;
227 datafp
->cf_bytesread
= 0;
229 rsrcfp
->cf_vblocks
= 0;
230 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
231 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
238 struct hfsmount
*hfsmp
,
240 CatalogRecord
* recp
,
241 struct cat_desc
*descp
)
243 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
244 HFSPlusCatalogKey
* pluskey
= NULL
;
248 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
249 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
252 pluskey
= (HFSPlusCatalogKey
*)key
;
253 encoding
= getencoding(recp
);
256 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
258 FREE(pluskey
, M_TEMP
);
269 cat_releasedesc(struct cat_desc
*descp
)
276 if ((descp
->cd_flags
& CD_HASBUF
) &&
277 (descp
->cd_nameptr
!= NULL
)) {
278 name
= descp
->cd_nameptr
;
279 descp
->cd_nameptr
= NULL
;
280 descp
->cd_namelen
= 0;
281 descp
->cd_flags
&= ~CD_HASBUF
;
282 vfs_removename(name
);
284 descp
->cd_nameptr
= NULL
;
285 descp
->cd_namelen
= 0;
289 * These Catalog functions allow access to the HFS Catalog (database).
290 * The catalog b-tree lock must be aquired before calling any of these routines.
294 * cat_lookup - lookup a catalog node using a cnode decriptor
298 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
299 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
300 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
306 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
308 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
310 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
314 result
= cat_lookupbykey(hfsmp
, keyp
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
316 if (result
== ENOENT
) {
318 struct cat_desc temp_desc
;
319 if (outdescp
== NULL
) {
320 bzero(&temp_desc
, sizeof(temp_desc
));
321 outdescp
= &temp_desc
;
323 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
325 *desc_cnid
= outdescp
->cd_cnid
;
327 if (outdescp
== &temp_desc
) {
328 /* Release the local copy of desc */
329 cat_releasedesc(outdescp
);
331 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
332 // make MacRoman key from utf-8
333 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
334 // update desc text encoding so that other catalog ops succeed
345 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
347 struct BTreeIterator
*iterator
;
348 struct FSBufferDescriptor file_data
;
349 struct HFSCatalogFile file_rec
;
354 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
357 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
359 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
360 bzero(&iterator
[0], 2* sizeof(*iterator
));
361 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
365 BDINIT(file_data
, &file_rec
);
366 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
370 if (file_rec
.recordType
!= kHFSFileRecord
) {
375 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
376 struct FSBufferDescriptor thread_data
;
377 struct HFSCatalogThread thread_rec
;
379 file_rec
.flags
|= kHFSThreadExistsMask
;
380 BDINIT(thread_data
, &thread_rec
);
381 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
382 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
384 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
388 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
389 (void) BTFlushPath(fcb
);
392 (void) BTFlushPath(fcb
);
393 FREE(iterator
, M_TEMP
);
395 return MacToVFSError(result
);
400 * cat_findname - obtain a descriptor from cnid
402 * Only a thread lookup is performed.
406 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
408 struct BTreeIterator
* iterator
;
409 FSBufferDescriptor btdata
;
411 CatalogRecord
* recp
;
417 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
419 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
420 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
421 iterator
->hint
.nodeNum
= 0;
423 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
424 BDINIT(btdata
, recp
);
426 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
430 /* Turn thread record into a cnode key (in place). */
431 switch (recp
->recordType
) {
432 case kHFSFolderThreadRecord
:
435 case kHFSFileThreadRecord
:
436 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
437 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
440 case kHFSPlusFolderThreadRecord
:
443 case kHFSPlusFileThreadRecord
:
444 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
445 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
446 (keyp
->hfsPlus
.nodeName
.length
* 2);
453 HFSPlusCatalogKey
* pluskey
= NULL
;
456 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
457 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
458 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
459 FREE(pluskey
, M_TEMP
);
462 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
466 FREE(iterator
, M_TEMP
);
468 return MacToVFSError(result
);
472 * cat_idlookup - lookup a catalog node using a cnode id
476 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
,
477 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
479 struct BTreeIterator
* iterator
;
480 FSBufferDescriptor btdata
;
483 CatalogRecord
* recp
;
487 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
489 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
490 bzero(iterator
, sizeof(*iterator
));
491 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
493 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
494 BDINIT(btdata
, recp
);
496 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
497 &btdata
, &datasize
, iterator
);
501 /* Turn thread record into a cnode key (in place) */
502 switch (recp
->recordType
) {
503 case kHFSFileThreadRecord
:
504 case kHFSFolderThreadRecord
:
505 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
506 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
509 case kHFSPlusFileThreadRecord
:
510 case kHFSPlusFolderThreadRecord
:
511 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
512 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
513 (keyp
->hfsPlus
.nodeName
.length
* 2);
521 result
= cat_lookupbykey(hfsmp
, keyp
, 0, 0, outdescp
, attrp
, forkp
, NULL
);
522 if (!result
&& outdescp
) {
523 cnid_t dcnid
= outdescp
->cd_cnid
;
525 * Just for sanity's sake, let's make sure that
526 * the key in the thread matches the key in the record.
529 printf("Requested cnid (%d / 0x%08lx) != dcnid (%d / 0x%08lx)\n", cnid
, cnid
, dcnid
, dcnid
);
536 FREE(iterator
, M_TEMP
);
538 return MacToVFSError(result
);
543 * cat_lookupmangled - lookup a catalog node using a mangled name
546 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
547 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
556 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
557 if (fileID
< kHFSFirstUserCatalogNodeID
)
560 result
= cat_idlookup(hfsmp
, fileID
, outdescp
, attrp
, forkp
);
564 /* It must be in the correct directory */
565 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
568 if ((outdescp
->cd_namelen
< prefixlen
) ||
569 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
575 cat_releasedesc(outdescp
);
581 * cat_lookupbykey - lookup a catalog node using a cnode key
584 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
585 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
587 struct BTreeIterator
* iterator
;
588 FSBufferDescriptor btdata
;
589 CatalogRecord
* recp
;
597 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
599 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
600 BDINIT(btdata
, recp
);
601 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
602 bzero(iterator
, sizeof(*iterator
));
603 iterator
->hint
.nodeNum
= hint
;
604 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
606 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
607 &btdata
, &datasize
, iterator
);
611 /* Save the cnid now in case there's a hard link */
612 cnid
= getcnid(recp
);
613 encoding
= getencoding(recp
);
614 hint
= iterator
->hint
.nodeNum
;
616 /* Hide the journal files (if any) */
617 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
618 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
619 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
626 * When a hardlink link is encountered, auto resolve it
630 && (recp
->recordType
== kHFSPlusFileRecord
)
631 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
632 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)
633 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)HFSTOVCB(hfsmp
)->vcbCrDate
) ||
634 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
636 ilink
= recp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
638 (void) resolvelink(hfsmp
, ilink
, (struct HFSPlusCatalogFile
*)recp
);
643 struct HFSPlusCatalogFile cnoderec
;
645 promoteattr(hfsmp
, recp
, &cnoderec
);
646 getbsdattr(hfsmp
, &cnoderec
, attrp
);
648 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
650 attrp
->ca_rdev
= ilink
;
655 bzero(forkp
, sizeof(*forkp
));
656 } else if (std_hfs
) {
657 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
658 } else if (wantrsrc
) {
659 /* Convert the resource fork. */
660 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
661 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
662 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
663 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
664 forkp
->cf_bytesread
=
665 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
666 HFSTOVCB(hfsmp
)->blockSize
;
668 forkp
->cf_bytesread
= 0;
670 forkp
->cf_vblocks
= 0;
671 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
672 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
677 /* Convert the data fork. */
678 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
679 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
680 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
681 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
682 forkp
->cf_bytesread
=
683 recp
->hfsPlusFile
.dataFork
.clumpSize
*
684 HFSTOVCB(hfsmp
)->blockSize
;
686 forkp
->cf_bytesread
= 0;
688 forkp
->cf_vblocks
= 0;
689 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
690 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
692 /* Validate the fork's resident extents. */
694 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
695 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
696 /* Suppress any bad extents so a remove can succeed. */
697 forkp
->cf_extents
[i
].startBlock
= 0;
698 forkp
->cf_extents
[i
].blockCount
= 0;
701 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
704 validblks
+= forkp
->cf_extents
[i
].blockCount
;
707 /* Adjust for any missing blocks. */
708 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
711 forkp
->cf_blocks
= validblks
;
713 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
715 psize
= (u_int64_t
)validblks
* (u_int64_t
)hfsmp
->blockSize
;
716 if (psize
< forkp
->cf_size
) {
717 forkp
->cf_size
= psize
;
724 HFSPlusCatalogKey
* pluskey
= NULL
;
727 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
728 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
731 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
733 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
735 FREE(pluskey
, M_TEMP
);
739 if (desc_cnid
!= NULL
) {
743 FREE(iterator
, M_TEMP
);
746 return MacToVFSError(result
);
751 * cat_create - create a node in the catalog
755 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
756 struct cat_desc
*out_descp
)
761 FSBufferDescriptor btdata
;
770 modeformat
= attrp
->ca_mode
& S_IFMT
;
772 vcb
= HFSTOVCB(hfsmp
);
773 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
774 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
777 * Atomically get the next CNID. If we have wrapped the CNIDs
778 * then keep the hfsmp lock held until we have found a CNID.
780 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
782 nextCNID
= hfsmp
->vcbNxtCNID
;
783 if (nextCNID
== 0xFFFFFFFF) {
787 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
788 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
793 hfsmp
->vcbFlags
|= 0xFF00;
794 /* OK to drop lock if CNIDs are not wrapping */
795 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
) == 0) {
796 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
799 return (result
); /* HFS only exit */
802 /* Get space for iterator, key and data */
803 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
804 bto
->iterator
.hint
.nodeNum
= 0;
806 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
811 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
812 bto
->key
.nodeName
.length
);
813 hfs_setencodingbits(hfsmp
, encoding
);
817 * Insert the thread record first
819 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
820 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
821 S_ISDIR(attrp
->ca_mode
));
822 btdata
.bufferAddress
= &bto
->data
;
823 btdata
.itemSize
= datalen
;
824 btdata
.itemCount
= 1;
827 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
829 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
830 if ((result
== btExists
) && !std_hfs
&& mntlock
) {
832 * Allow CNIDs on HFS Plus volumes to wrap around
834 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
835 nextCNID
= kHFSFirstUserCatalogNodeID
;
841 if (result
) goto exit
;
845 * CNID is now established. If we have wrapped then
846 * update the vcbNxtCNID and drop the vcb lock.
849 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
850 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
851 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
853 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
858 * Now insert the file/directory record
860 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
861 btdata
.bufferAddress
= &bto
->data
;
862 btdata
.itemSize
= datalen
;
863 btdata
.itemCount
= 1;
865 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
867 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
869 if (result
== btExists
)
872 /* Back out the thread record */
873 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
874 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
875 (void) BTDeleteRecord(fcb
, &bto
->iterator
);
881 * Insert was Successfull, update name, parent and volume
885 if (out_descp
!= NULL
) {
886 HFSPlusCatalogKey
* pluskey
= NULL
;
889 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
890 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
893 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
895 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
896 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
898 FREE(pluskey
, M_TEMP
);
901 attrp
->ca_fileid
= nextCNID
;
905 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
907 (void) BTFlushPath(fcb
);
910 return MacToVFSError(result
);
915 * cnode_rename - rename a catalog node
917 * Assumes that the target's directory exists.
919 * Order of B-tree operations:
920 * 1. BTSearchRecord(from_cnode, &data);
921 * 2. BTInsertRecord(to_cnode, &data);
922 * 3. BTDeleteRecord(from_cnode);
923 * 4. BTDeleteRecord(from_thread);
924 * 5. BTInsertRecord(to_thread);
929 struct hfsmount
* hfsmp
,
930 struct cat_desc
* from_cdp
,
931 struct cat_desc
* todir_cdp
,
932 struct cat_desc
* to_cdp
,
933 struct cat_desc
* out_cdp
)
935 struct BTreeIterator
* to_iterator
= NULL
;
936 struct BTreeIterator
* from_iterator
= NULL
;
937 FSBufferDescriptor btdata
;
938 CatalogRecord
* recp
= NULL
;
939 HFSPlusCatalogKey
* to_key
;
946 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
950 vcb
= HFSTOVCB(hfsmp
);
951 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
952 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
954 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
957 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
958 bzero(from_iterator
, sizeof(*from_iterator
));
959 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
962 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
963 bzero(to_iterator
, sizeof(*to_iterator
));
964 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
967 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
968 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
969 BDINIT(btdata
, recp
);
972 * When moving a directory, make sure its a valid move.
974 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
975 struct BTreeIterator iterator
;
976 cnid_t cnid
= from_cdp
->cd_cnid
;
977 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
979 /* First check the obvious ones */
980 if (cnid
== fsRtDirID
||
981 cnid
== to_cdp
->cd_parentcnid
||
986 bzero(&iterator
, sizeof(iterator
));
988 * Traverese destination path all the way back to the root
989 * making sure that source directory is not encountered.
992 while (pathcnid
> fsRtDirID
) {
993 buildthreadkey(pathcnid
, std_hfs
,
994 (CatalogKey
*)&iterator
.key
);
995 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
997 if (result
) goto exit
;
999 pathcnid
= getparentcnid(recp
);
1000 if (pathcnid
== cnid
) {
1008 * Step 1: Find cnode data at old location
1010 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
1011 &datasize
, from_iterator
);
1013 if (std_hfs
|| (result
!= btNotFound
))
1016 struct cat_desc temp_desc
;
1018 /* Probably the node has mangled name */
1019 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1023 /* The file has mangled name. Search the cnode data using full name */
1024 bzero(from_iterator
, sizeof(*from_iterator
));
1025 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1027 cat_releasedesc(&temp_desc
);
1031 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1033 cat_releasedesc(&temp_desc
);
1037 cat_releasedesc(&temp_desc
);
1040 /* Update the text encoding (on disk and in descriptor) */
1042 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
,
1043 to_key
->nodeName
.length
);
1044 hfs_setencodingbits(hfsmp
, encoding
);
1045 recp
->hfsPlusFile
.textEncoding
= encoding
;
1047 out_cdp
->cd_encoding
= encoding
;
1050 if (std_hfs
&& !directory
&&
1051 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1055 * If the keys are identical then there's nothing left to do!
1057 * update the hint and exit
1060 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1062 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1066 /* Step 2: Insert cnode at new location */
1067 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1068 if (result
== btExists
) {
1069 int fromtype
= recp
->recordType
;
1071 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1072 goto exit
; /* EEXIST */
1074 /* Find cnode data at new location */
1075 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1079 if ((fromtype
!= recp
->recordType
) ||
1080 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1082 goto exit
; /* EEXIST */
1084 /* The old name is a case variant and must be removed */
1085 result
= BTDeleteRecord(fcb
, from_iterator
);
1089 /* Insert cnode (now that case duplicate is gone) */
1090 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1092 /* Try and restore original before leaving */
1097 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1099 panic("cat_create: could not undo (BTInsert = %d)", err
);
1102 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1111 /* Step 3: Remove cnode from old location */
1113 result
= BTDeleteRecord(fcb
, from_iterator
);
1115 /* Try and delete new record before leaving */
1120 err
= BTDeleteRecord(fcb
, to_iterator
);
1122 panic("cat_create: could not undo (BTDelete = %d)", err
);
1125 (void) BTDeleteRecord(fcb
, to_iterator
);
1131 /* #### POINT OF NO RETURN #### */
1134 * Step 4: Remove cnode's old thread record
1136 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1137 (void) BTDeleteRecord(fcb
, from_iterator
);
1140 * Step 5: Insert cnode's new thread record
1141 * (optional for HFS files)
1144 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1145 btdata
.itemSize
= datasize
;
1146 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1147 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1151 HFSPlusCatalogKey
* pluskey
= NULL
;
1154 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1155 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1157 /* Save the real encoding hint in the Finder Info (field 4). */
1158 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1161 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1162 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1166 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1168 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1169 encoding
, directory
, out_cdp
);
1171 FREE(pluskey
, M_TEMP
);
1175 (void) BTFlushPath(fcb
);
1177 FREE(from_iterator
, M_TEMP
);
1179 FREE(to_iterator
, M_TEMP
);
1182 return MacToVFSError(result
);
1187 * cat_delete - delete a node from the catalog
1189 * Order of B-tree operations:
1190 * 1. BTDeleteRecord(cnode);
1191 * 2. BTDeleteRecord(thread);
1192 * 3. BTUpdateRecord(parent);
1196 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1200 BTreeIterator
*iterator
;
1205 vcb
= HFSTOVCB(hfsmp
);
1206 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1207 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1211 * The root directory cannot be deleted
1212 * A directory must be empty
1213 * A file must be zero length (no blocks)
1215 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1216 descp
->cd_parentcnid
== kHFSRootParentID
)
1219 /* XXX Preflight Missing */
1221 /* Get space for iterator */
1222 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1223 iterator
->hint
.nodeNum
= 0;
1226 * Derive a key from either the file ID (for a virtual inode)
1227 * or the descriptor.
1229 if (descp
->cd_namelen
== 0) {
1230 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1231 cnid
= attrp
->ca_fileid
;
1233 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1234 cnid
= descp
->cd_cnid
;
1240 result
= BTDeleteRecord(fcb
, iterator
);
1242 if (std_hfs
|| (result
!= btNotFound
))
1245 struct cat_desc temp_desc
;
1247 /* Probably the node has mangled name */
1248 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1252 /* The file has mangled name. Delete the file using full name */
1253 bzero(iterator
, sizeof(*iterator
));
1254 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1255 cnid
= temp_desc
.cd_cnid
;
1257 cat_releasedesc(&temp_desc
);
1261 result
= BTDeleteRecord(fcb
, iterator
);
1263 cat_releasedesc(&temp_desc
);
1267 cat_releasedesc(&temp_desc
);
1270 /* Delete thread record, ignore errors */
1271 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1272 (void) BTDeleteRecord(fcb
, iterator
);
1275 (void) BTFlushPath(fcb
);
1276 FREE(iterator
, M_TEMP
);
1278 return MacToVFSError(result
);
1283 * cnode_update - update the catalog node described by descp
1284 * using the data from attrp and forkp.
1288 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1289 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1293 BTreeIterator
* iterator
;
1294 struct update_state state
;
1298 vcb
= HFSTOVCB(hfsmp
);
1299 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1300 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1302 state
.s_desc
= descp
;
1303 state
.s_attr
= attrp
;
1304 state
.s_datafork
= dataforkp
;
1305 state
.s_rsrcfork
= rsrcforkp
;
1306 state
.s_hfsmp
= hfsmp
;
1308 /* Get space for iterator */
1309 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1312 * For open-deleted files we need to do a lookup by cnid
1313 * (using thread rec).
1315 * For hard links, the target of the update is the inode
1316 * itself (not the link record) so a lookup by fileid
1317 * (i.e. thread rec) is needed.
1319 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (descp
->cd_namelen
== 0))
1320 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1322 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1326 /* Pass a node hint */
1327 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1329 result
= BTUpdateRecord(fcb
, iterator
,
1330 (IterateCallBackProcPtr
)catrec_update
, &state
);
1334 /* Update the node hint. */
1335 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1338 (void) BTFlushPath(fcb
);
1339 FREE(iterator
, M_TEMP
);
1341 return MacToVFSError(result
);
1345 * catrec_update - Update the fields of a catalog record
1346 * This is called from within BTUpdateRecord.
1349 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1351 struct cat_desc
*descp
;
1352 struct cat_attr
*attrp
;
1353 struct cat_fork
*forkp
;
1354 struct hfsmount
*hfsmp
;
1358 descp
= state
->s_desc
;
1359 attrp
= state
->s_attr
;
1360 hfsmp
= state
->s_hfsmp
;
1361 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1363 switch (crp
->recordType
) {
1364 case kHFSFolderRecord
: {
1365 HFSCatalogFolder
*dir
;
1367 dir
= (struct HFSCatalogFolder
*)crp
;
1368 /* Do a quick sanity check */
1369 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1370 (dir
->folderID
!= descp
->cd_cnid
))
1371 return (btNotFound
);
1372 dir
->valence
= attrp
->ca_entries
;
1373 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1374 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1375 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1376 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1377 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1380 case kHFSFileRecord
: {
1381 HFSCatalogFile
*file
;
1383 file
= (struct HFSCatalogFile
*)crp
;
1384 /* Do a quick sanity check */
1385 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1386 (file
->fileID
!= attrp
->ca_fileid
))
1387 return (btNotFound
);
1388 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1389 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1390 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1391 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1392 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1393 if (state
->s_rsrcfork
) {
1394 forkp
= state
->s_rsrcfork
;
1395 file
->rsrcLogicalSize
= forkp
->cf_size
;
1396 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1397 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1398 file
->rsrcExtents
[i
].startBlock
=
1399 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1400 file
->rsrcExtents
[i
].blockCount
=
1401 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1404 if (state
->s_datafork
) {
1405 forkp
= state
->s_datafork
;
1406 file
->dataLogicalSize
= forkp
->cf_size
;
1407 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1408 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1409 file
->dataExtents
[i
].startBlock
=
1410 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1411 file
->dataExtents
[i
].blockCount
=
1412 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1417 case kHFSPlusFolderRecord
: {
1418 HFSPlusCatalogFolder
*dir
;
1420 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1421 /* Do a quick sanity check */
1422 if ((ckp
->hfsPlus
.parentID
!= descp
->cd_parentcnid
) ||
1423 (dir
->folderID
!= descp
->cd_cnid
))
1424 return (btNotFound
);
1425 dir
->flags
= attrp
->ca_recflags
;
1426 dir
->valence
= attrp
->ca_entries
;
1427 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1428 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1429 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1430 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1431 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1432 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1433 dir
->textEncoding
= descp
->cd_encoding
;
1434 dir
->attrBlocks
= attrp
->ca_attrblks
;
1435 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1437 * Update the BSD Info if it was already initialized on
1438 * disk or if the runtime values have been modified.
1440 * If the BSD info was already initialized, but
1441 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1442 * probably different than what was on disk. We don't want
1443 * to overwrite the on-disk values (so if we turn off
1444 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1445 * This way, we can still change fields like the mode or
1446 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1448 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1449 * won't change the uid or gid from their defaults. So, if
1450 * the BSD info wasn't set, and the runtime values are not
1451 * default, then what changed was the mode or flags. We
1452 * have to set the uid and gid to something, so use the
1453 * supplied values (which will be default), which has the
1454 * same effect as creating a new file while
1455 * MNT_UNKNOWNPERMISSIONS is set.
1457 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1458 (attrp
->ca_flags
!= 0) ||
1459 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1460 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1461 ((attrp
->ca_mode
& ALLPERMS
) !=
1462 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1463 if ((dir
->bsdInfo
.fileMode
== 0) ||
1464 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1465 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1466 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1468 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1469 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1470 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1474 case kHFSPlusFileRecord
: {
1475 HFSPlusCatalogFile
*file
;
1477 file
= (struct HFSPlusCatalogFile
*)crp
;
1478 /* Do a quick sanity check */
1479 if (file
->fileID
!= attrp
->ca_fileid
)
1480 return (btNotFound
);
1481 file
->flags
= attrp
->ca_recflags
;
1482 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1483 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1484 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1485 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1486 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1487 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1488 file
->textEncoding
= descp
->cd_encoding
;
1489 file
->attrBlocks
= attrp
->ca_attrblks
;
1490 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1492 * Update the BSD Info if it was already initialized on
1493 * disk or if the runtime values have been modified.
1495 * If the BSD info was already initialized, but
1496 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1497 * probably different than what was on disk. We don't want
1498 * to overwrite the on-disk values (so if we turn off
1499 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1500 * This way, we can still change fields like the mode or
1501 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1503 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1504 * won't change the uid or gid from their defaults. So, if
1505 * the BSD info wasn't set, and the runtime values are not
1506 * default, then what changed was the mode or flags. We
1507 * have to set the uid and gid to something, so use the
1508 * supplied values (which will be default), which has the
1509 * same effect as creating a new file while
1510 * MNT_UNKNOWNPERMISSIONS is set.
1512 if ((file
->bsdInfo
.fileMode
!= 0) ||
1513 (attrp
->ca_flags
!= 0) ||
1514 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1515 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1516 ((attrp
->ca_mode
& ALLPERMS
) !=
1517 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1518 if ((file
->bsdInfo
.fileMode
== 0) ||
1519 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1520 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1521 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1523 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1524 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1525 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1527 if (state
->s_rsrcfork
) {
1528 forkp
= state
->s_rsrcfork
;
1529 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1530 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1531 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1532 sizeof(HFSPlusExtentRecord
));
1533 /* Push blocks read to disk */
1534 file
->resourceFork
.clumpSize
=
1535 howmany(forkp
->cf_bytesread
, blksize
);
1537 if (state
->s_datafork
) {
1538 forkp
= state
->s_datafork
;
1539 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1540 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1541 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1542 sizeof(HFSPlusExtentRecord
));
1543 /* Push blocks read to disk */
1544 file
->dataFork
.clumpSize
=
1545 howmany(forkp
->cf_bytesread
, blksize
);
1548 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1549 (file
->resourceFork
.extents
[0].startBlock
==
1550 file
->dataFork
.extents
[0].startBlock
))
1551 panic("catrec_update: rsrc fork == data fork");
1553 /* Synchronize the lock state */
1554 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1555 file
->flags
|= kHFSFileLockedMask
;
1557 file
->flags
&= ~kHFSFileLockedMask
;
1559 /* Push out special field if necessary */
1560 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
))
1561 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1562 else if (descp
->cd_cnid
!= attrp
->ca_fileid
1563 || attrp
->ca_nlink
== 2)
1564 file
->bsdInfo
.special
.linkCount
= attrp
->ca_nlink
;
1568 return (btNotFound
);
1574 * Callback to collect directory entries.
1575 * Called with readattr_state for each item in a directory.
1577 struct readattr_state
{
1578 struct hfsmount
*hfsmp
;
1579 struct cat_entrylist
*list
;
1586 cat_readattr(const CatalogKey
*key
, const CatalogRecord
*rec
,
1587 struct readattr_state
*state
)
1589 struct cat_entrylist
*list
= state
->list
;
1590 struct hfsmount
*hfsmp
= state
->hfsmp
;
1591 struct cat_entry
*cep
;
1594 if (list
->realentries
>= list
->maxentries
)
1595 return (0); /* stop */
1597 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
1599 switch(rec
->recordType
) {
1600 case kHFSPlusFolderRecord
:
1601 case kHFSPlusFileRecord
:
1602 case kHFSFolderRecord
:
1603 case kHFSFileRecord
:
1604 if (parentcnid
!= state
->dir_cnid
) {
1605 state
->error
= ENOENT
;
1606 return (0); /* stop */
1610 state
->error
= ENOENT
;
1611 return (0); /* stop */
1614 /* Hide the private meta data directory and journal files */
1615 if (parentcnid
== kHFSRootFolderID
) {
1616 if ((rec
->recordType
== kHFSPlusFolderRecord
) &&
1617 (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1618 return (1); /* continue */
1620 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
1621 (rec
->recordType
== kHFSPlusFileRecord
) &&
1622 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
1623 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
1625 return (1); /* continue */
1629 cep
= &list
->entry
[list
->realentries
++];
1631 if (state
->stdhfs
) {
1632 struct HFSPlusCatalogFile cnoderec
;
1633 HFSPlusCatalogKey
* pluskey
;
1636 promoteattr(hfsmp
, rec
, &cnoderec
);
1637 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
1639 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1640 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
1641 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
1642 FREE(pluskey
, M_TEMP
);
1644 if (rec
->recordType
== kHFSFileRecord
) {
1645 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1647 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
1648 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
1649 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
1650 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
1653 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
1654 builddesc((HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
1655 isadir(rec
), &cep
->ce_desc
);
1657 if (rec
->recordType
== kHFSPlusFileRecord
) {
1658 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
1659 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
1660 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
1661 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
1663 /* Save link reference for later processing. */
1664 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
1665 && (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
))
1666 cep
->ce_attr
.ca_rdev
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1670 return (list
->realentries
< list
->maxentries
);
1674 * Pack a cat_entrylist buffer with attributes from the catalog
1676 * Note: index is zero relative
1680 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
1684 BTreeIterator
* iterator
;
1685 struct readattr_state state
;
1693 ce_list
->realentries
= 0;
1695 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
1696 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
1697 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
1699 state
.hfsmp
= hfsmp
;
1700 state
.list
= ce_list
;
1701 state
.dir_cnid
= parentcnid
;
1702 state
.stdhfs
= std_hfs
;
1705 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1706 bzero(iterator
, sizeof(*iterator
));
1707 key
= (CatalogKey
*)&iterator
->key
;
1709 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
1710 index
= dirhint
->dh_index
+ 1;
1713 * Attempt to build a key from cached filename
1715 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
1716 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
1722 * If the last entry wasn't cached then position the btree iterator
1724 if ((index
== 0) || !have_key
) {
1726 * Position the iterator at the directory's thread record.
1727 * (i.e. just before the first entry)
1729 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
1730 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1732 result
= MacToVFSError(result
);
1737 * Iterate until we reach the entry just
1738 * before the one we want to start with.
1741 struct position_state ps
;
1746 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
1749 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1750 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
1754 result
= MacToVFSError(result
);
1756 result
= MacToVFSError(result
);
1762 /* Fill list with entries starting at iterator->key. */
1763 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1764 (IterateCallBackProcPtr
)cat_readattr
, &state
);
1767 result
= state
.error
;
1768 else if (ce_list
->realentries
== 0)
1771 result
= MacToVFSError(result
);
1777 * Resolve any hard links.
1779 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
1780 struct FndrFileInfo
*fip
;
1781 struct cat_entry
*cep
;
1782 struct HFSPlusCatalogFile filerec
;
1784 cep
= &ce_list
->entry
[i
];
1785 if (!S_ISREG(cep
->ce_attr
.ca_mode
))
1788 /* Note: Finder info is still in Big Endian */
1789 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
1791 /* Check for hard link signature. */
1792 if ((cep
->ce_attr
.ca_rdev
!= 0)
1793 && (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
)
1794 && (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)
1795 && ((cep
->ce_attr
.ca_itime
== (time_t)HFSTOVCB(hfsmp
)->vcbCrDate
) ||
1796 (cep
->ce_attr
.ca_itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
1798 if (resolvelink(hfsmp
, cep
->ce_attr
.ca_rdev
, &filerec
) != 0)
1800 /* Repack entry from inode record. */
1801 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
1802 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
1803 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
1804 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
1805 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
1809 FREE(iterator
, M_TEMP
);
1811 return MacToVFSError(result
);
1814 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1817 * Callback to pack directory entries.
1818 * Called with packdirentry_state for each item in a directory.
1821 /* Hard link information collected during cat_getdirentries. */
1824 user_addr_t dirent_addr
;
1826 typedef struct linkinfo linkinfo_t
;
1828 /* State information for the cat_packdirentry callback function. */
1829 struct packdirentry_state
{
1831 u_int32_t cbs_parentID
;
1832 u_int32_t cbs_index
;
1834 ExtendedVCB
* cbs_hfsmp
;
1837 int32_t cbs_maxlinks
;
1838 linkinfo_t
* cbs_linkinfo
;
1839 struct cat_desc
* cbs_desc
;
1840 // struct dirent * cbs_stdentry;
1841 // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
1842 struct direntry
* cbs_direntry
;
1843 struct direntry
* cbs_prevdirentry
;
1844 u_int32_t cbs_previlinkref
;
1845 Boolean cbs_hasprevdirentry
;
1850 cat_packdirentry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
1851 struct packdirentry_state
*state
)
1853 struct hfsmount
*hfsmp
;
1857 struct dirent catent
;
1858 struct direntry
* entry
= NULL
;
1860 u_int32_t ilinkref
= 0;
1861 u_int32_t curlinkref
= 0;
1865 u_int8_t is_mangled
= 0;
1867 user_addr_t uiobase
;
1872 Boolean stop_after_pack
= false;
1874 hfsmp
= state
->cbs_hfsmp
;
1876 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
1877 curID
= ckp
->hfs
.parentID
;
1879 curID
= ckp
->hfsPlus
.parentID
;
1881 /* We're done when parent directory changes */
1882 if (state
->cbs_parentID
!= curID
) {
1883 if (state
->cbs_extended
) {
1884 if (state
->cbs_hasprevdirentry
) { /* the last record haven't been returned yet, so we want to stop after
1885 * packing the last item */
1886 stop_after_pack
= true;
1888 state
->cbs_result
= ENOENT
;
1889 return (0); /* stop */
1892 state
->cbs_result
= ENOENT
;
1893 return (0); /* stop */
1897 if (state
->cbs_extended
) {
1898 entry
= state
->cbs_direntry
;
1899 nameptr
= &entry
->d_name
[0];
1900 maxnamelen
= NAME_MAX
;
1902 nameptr
= &catent
.d_name
[0];
1903 maxnamelen
= NAME_MAX
;
1906 if (state
->cbs_extended
&& stop_after_pack
) {
1907 cnid
= INT_MAX
; /* the last item returns a non-zero invalid cookie */
1909 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
)) {
1910 switch(crp
->recordType
) {
1911 case kHFSPlusFolderRecord
:
1913 cnid
= crp
->hfsPlusFolder
.folderID
;
1914 /* Hide our private meta data directory */
1915 if ((curID
== kHFSRootFolderID
) &&
1916 (cnid
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1921 case kHFSPlusFileRecord
:
1922 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
1924 * When a hardlink link is encountered save its link ref.
1926 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
1927 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
1928 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
1929 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
1930 ilinkref
= crp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1932 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
1933 cnid
= crp
->hfsPlusFile
.fileID
;
1934 /* Hide the journal files */
1935 if ((curID
== kHFSRootFolderID
) &&
1936 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
1937 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
1938 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
1943 return (0); /* stop */
1946 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
1947 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
1948 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
1949 if (result
== ENAMETOOLONG
) {
1950 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
1951 cnp
->ustr
.unicode
, maxnamelen
+ 1,
1952 (ByteCount
*)&namelen
, nameptr
,
1957 switch(crp
->recordType
) {
1958 case kHFSFolderRecord
:
1960 cnid
= crp
->hfsFolder
.folderID
;
1962 case kHFSFileRecord
:
1964 cnid
= crp
->hfsFile
.fileID
;
1967 return (0); /* stop */
1970 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
1971 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1,
1972 (ByteCount
*)&namelen
, nameptr
);
1974 * When an HFS name cannot be encoded with the current
1975 * volume encoding we use MacRoman as a fallback.
1978 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1,
1979 (ByteCount
*)&namelen
, nameptr
);
1983 if (state
->cbs_extended
) {
1985 * The index is 1 relative and includes "." and ".."
1987 * Also stuff the cnid in the upper 32 bits of the cookie. The cookie is stored to the previous entry, which
1988 * will be packed and copied this time
1990 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
1991 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
1992 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
1994 catent
.d_type
= type
;
1995 catent
.d_namlen
= namelen
;
1996 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
1998 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
2000 catent
.d_fileno
= cnid
;
2001 uioaddr
= (caddr_t
) &catent
;
2004 /* Save current base address for post processing of hard-links. */
2005 uiobase
= uio_curriovbase(state
->cbs_uio
);
2007 /* If this entry won't fit then we're done */
2008 if ((uiosize
> uio_resid(state
->cbs_uio
)) ||
2009 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
2010 return (0); /* stop */
2013 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
2014 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
2015 if (state
->cbs_result
== 0) {
2018 /* Remember previous entry */
2019 state
->cbs_desc
->cd_cnid
= cnid
;
2020 if (type
== DT_DIR
) {
2021 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2023 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2025 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2026 vfs_removename(state
->cbs_desc
->cd_nameptr
);
2029 state
->cbs_desc
->cd_encoding
= xxxx
;
2032 state
->cbs_desc
->cd_namelen
= namelen
;
2033 state
->cbs_desc
->cd_nameptr
= vfs_addname(nameptr
, namelen
, 0, 0);
2035 /* Store unmangled name for the directory hint else it will
2036 * restart readdir at the last location again
2040 size_t tmp_namelen
= 0;
2042 cnp
= (CatalogName
*)&ckp
->hfsPlus
.nodeName
;
2043 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
2044 cnp
->ustr
.length
* sizeof(UniChar
),
2046 MALLOC(new_nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2047 result
= utf8_encodestr(cnp
->ustr
.unicode
,
2048 cnp
->ustr
.length
* sizeof(UniChar
),
2049 new_nameptr
, &tmp_namelen
,
2052 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2053 state
->cbs_desc
->cd_nameptr
= vfs_addname(new_nameptr
, tmp_namelen
, 0, 0);
2055 FREE(new_nameptr
, M_TEMP
);
2058 if (state
->cbs_hasprevdirentry
) {
2059 curlinkref
= ilinkref
; /* save current */
2060 ilinkref
= state
->cbs_previlinkref
; /* use previous */
2063 * Record any hard links for post processing.
2065 if ((ilinkref
!= 0) &&
2066 (state
->cbs_result
== 0) &&
2067 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
2068 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
2069 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
2070 state
->cbs_nlinks
++;
2072 if (state
->cbs_hasprevdirentry
) {
2073 ilinkref
= curlinkref
; /* restore current */
2077 if (state
->cbs_extended
) { /* fill the direntry to be used the next time */
2078 if (stop_after_pack
) {
2079 state
->cbs_eof
= true;
2080 return (0); /* stop */
2082 entry
->d_type
= type
;
2083 entry
->d_namlen
= namelen
;
2084 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
2086 entry
->d_fileno
= 0; /* file number = 0 means skip entry */
2088 entry
->d_fileno
= cnid
;
2089 /* swap the current and previous entry */
2090 struct direntry
* tmp
;
2091 tmp
= state
->cbs_direntry
;
2092 state
->cbs_direntry
= state
->cbs_prevdirentry
;
2093 state
->cbs_prevdirentry
= tmp
;
2094 state
->cbs_hasprevdirentry
= true;
2095 state
->cbs_previlinkref
= ilinkref
;
2098 /* Continue iteration if there's room */
2099 return (state
->cbs_result
== 0 &&
2100 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2105 * Pack a uio buffer with directory entries from the catalog
2109 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
2110 uio_t uio
, int extended
, int * items
, int * eofflag
)
2113 BTreeIterator
* iterator
;
2115 struct packdirentry_state state
;
2123 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
2126 * Get a buffer for link info array, btree iterator and a direntry:
2128 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
2129 bufsize
= (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
2131 bufsize
+= 2*sizeof(struct direntry
);
2133 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
2134 bzero(buffer
, bufsize
);
2136 state
.cbs_extended
= extended
;
2137 state
.cbs_hasprevdirentry
= false;
2138 state
.cbs_previlinkref
= 0;
2139 state
.cbs_nlinks
= 0;
2140 state
.cbs_maxlinks
= maxlinks
;
2141 state
.cbs_linkinfo
= (linkinfo_t
*) buffer
;
2143 iterator
= (BTreeIterator
*) ((char *)buffer
+ (maxlinks
* sizeof(linkinfo_t
)));
2144 key
= (CatalogKey
*)&iterator
->key
;
2146 index
= dirhint
->dh_index
+ 1;
2148 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
2149 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
2150 state
.cbs_eof
= false;
2153 * Attempt to build a key from cached filename
2155 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2156 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2162 * If the last entry wasn't cached then position the btree iterator
2164 if ((index
== 0) || !have_key
) {
2166 * Position the iterator at the directory's thread record.
2167 * (i.e. just before the first entry)
2169 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2170 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2172 result
= MacToVFSError(result
);
2177 * Iterate until we reach the entry just
2178 * before the one we want to start with.
2181 struct position_state ps
;
2186 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2189 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2190 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2194 result
= MacToVFSError(result
);
2196 result
= MacToVFSError(result
);
2202 state
.cbs_index
= index
;
2203 state
.cbs_hfsmp
= hfsmp
;
2204 state
.cbs_uio
= uio
;
2205 state
.cbs_desc
= &dirhint
->dh_desc
;
2206 state
.cbs_result
= 0;
2207 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2209 enum BTreeIterationOperations op
;
2210 if (extended
&& index
!= 0 && have_key
)
2211 op
= kBTreeCurrentRecord
;
2213 op
= kBTreeNextRecord
;
2216 * Process as many entries as possible starting at iterator->key.
2218 result
= BTIterateRecords(fcb
, op
, iterator
,
2219 (IterateCallBackProcPtr
)cat_packdirentry
, &state
);
2221 /* If readdir is called for NFS and BTIterateRecords reaches the end of the
2222 * Catalog BTree, call cat_packdirentry() with dummy values to copy previous
2223 * direntry stored in state to the user buffer.
2225 if (state
.cbs_extended
&& (result
== fsBTRecordNotFoundErr
)) {
2229 bzero(&ckp
, sizeof(ckp
));
2230 bzero(&crp
, sizeof(crp
));
2232 result
= cat_packdirentry(&ckp
, &crp
, &state
);
2235 /* Note that state.cbs_index is still valid on errors */
2236 *items
= state
.cbs_index
- index
;
2237 index
= state
.cbs_index
;
2239 if (state
.cbs_eof
) {
2243 /* Finish updating the catalog iterator. */
2244 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
2245 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
2246 dirhint
->dh_index
= index
- 1;
2249 * Post process any hard links to get the real file id.
2251 if (state
.cbs_nlinks
> 0) {
2252 u_int32_t fileid
= 0;
2253 user_addr_t address
;
2256 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
2257 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
2259 /* This assumes that d_ino is always first field. */
2260 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
2261 if (address
== (user_addr_t
)0)
2263 if (uio_isuserspace(uio
)) {
2264 (void) copyout(&fileid
, address
,
2265 extended
? sizeof(ino64_t
) : sizeof(ino_t
));
2266 } else /* system space */ {
2267 ino64_t
*inoptr
= (ino64_t
*)CAST_DOWN(caddr_t
, address
);
2273 if (state
.cbs_result
)
2274 result
= state
.cbs_result
;
2276 result
= MacToVFSError(result
);
2278 if (result
== ENOENT
) {
2283 FREE(buffer
, M_TEMP
);
2290 * Callback to establish directory position.
2291 * Called with position_state for each item in a directory.
2294 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2295 struct position_state
*state
)
2299 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
2300 curID
= ckp
->hfs
.parentID
;
2302 curID
= ckp
->hfsPlus
.parentID
;
2304 /* Make sure parent directory didn't change */
2305 if (state
->parentID
!= curID
) {
2306 state
->error
= EINVAL
;
2307 return (0); /* stop */
2310 /* Count this entry */
2311 switch(crp
->recordType
) {
2312 case kHFSPlusFolderRecord
:
2313 case kHFSPlusFileRecord
:
2314 case kHFSFolderRecord
:
2315 case kHFSFileRecord
:
2319 printf("cat_findposition: invalid record type %d in dir %d\n",
2320 crp
->recordType
, curID
);
2321 state
->error
= EINVAL
;
2322 return (0); /* stop */
2325 return (state
->count
< state
->index
);
2330 * cat_binarykeycompare - compare two HFS Plus catalog keys.
2332 * The name portion of the key is compared using a 16-bit binary comparison.
2333 * This is called from the b-tree code.
2337 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
2339 u_int32_t searchParentID
, trialParentID
;
2342 searchParentID
= searchKey
->parentID
;
2343 trialParentID
= trialKey
->parentID
;
2346 if (searchParentID
> trialParentID
) {
2348 } else if (searchParentID
< trialParentID
) {
2351 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
2352 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
2353 int length1
= searchKey
->nodeName
.length
;
2354 int length2
= trialKey
->nodeName
.length
;
2358 if (length1
< length2
) {
2361 } else if (length1
> length2
) {
2388 * Compare two standard HFS catalog keys
2390 * Result: +n search key > trial key
2391 * 0 search key = trial key
2392 * -n search key < trial key
2395 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
2397 cnid_t searchParentID
, trialParentID
;
2400 searchParentID
= searchKey
->parentID
;
2401 trialParentID
= trialKey
->parentID
;
2403 if (searchParentID
> trialParentID
)
2405 else if (searchParentID
< trialParentID
)
2407 else /* parent dirID's are equal, compare names */
2408 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
2415 * Compare two HFS+ catalog keys
2417 * Result: +n search key > trial key
2418 * 0 search key = trial key
2419 * -n search key < trial key
2422 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
2424 cnid_t searchParentID
, trialParentID
;
2427 searchParentID
= searchKey
->parentID
;
2428 trialParentID
= trialKey
->parentID
;
2430 if (searchParentID
> trialParentID
) {
2433 else if (searchParentID
< trialParentID
) {
2436 /* parent node ID's are equal, compare names */
2437 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
2438 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
2440 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
2441 searchKey
->nodeName
.length
,
2442 &trialKey
->nodeName
.unicode
[0],
2443 trialKey
->nodeName
.length
);
2451 * buildkey - build a Catalog b-tree key from a cnode descriptor
2454 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
2455 HFSPlusCatalogKey
*key
, int retry
)
2459 size_t unicodeBytes
= 0;
2461 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
2462 return (EINVAL
); /* invalid name */
2464 key
->parentID
= descp
->cd_parentcnid
;
2465 key
->nodeName
.length
= 0;
2467 * Convert filename from UTF-8 into Unicode
2470 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
2471 utf8_flags
|= UTF_DECOMPOSED
;
2472 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
2473 key
->nodeName
.unicode
, &unicodeBytes
,
2474 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
2475 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
2476 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
2478 if (result
!= ENAMETOOLONG
)
2479 result
= EINVAL
; /* name has invalid characters */
2484 * For HFS volumes convert to an HFS compatible key
2486 * XXX need to save the encoding that succeeded
2488 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
2489 HFSCatalogKey hfskey
;
2491 bzero(&hfskey
, sizeof(hfskey
));
2492 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
2493 hfskey
.parentID
= key
->parentID
;
2494 hfskey
.nodeName
[0] = 0;
2495 if (key
->nodeName
.length
> 0) {
2496 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
2497 key
->nodeName
.length
* 2,
2498 key
->nodeName
.unicode
,
2499 &hfskey
.nodeName
[0], retry
) != 0) {
2502 hfskey
.keyLength
+= hfskey
.nodeName
[0];
2504 bcopy(&hfskey
, key
, sizeof(hfskey
));
2511 * Resolve hard link reference to obtain the inode record.
2515 resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
)
2517 FSBufferDescriptor btdata
;
2518 struct BTreeIterator
*iterator
;
2519 struct cat_desc idesc
;
2523 BDINIT(btdata
, recp
);
2524 MAKE_INODE_NAME(inodename
, linkref
);
2526 /* Get space for iterator */
2527 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2528 bzero(iterator
, sizeof(*iterator
));
2530 /* Build a descriptor for private dir. */
2531 idesc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
2532 idesc
.cd_nameptr
= inodename
;
2533 idesc
.cd_namelen
= strlen(inodename
);
2536 idesc
.cd_encoding
= 0;
2537 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
2539 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2540 &btdata
, NULL
, NULL
);
2543 /* Make sure there's a reference */
2544 if (recp
->bsdInfo
.special
.linkCount
== 0)
2545 recp
->bsdInfo
.special
.linkCount
= 2;
2547 printf("HFS resolvelink: can't find %s\n", inodename
);
2550 FREE(iterator
, M_TEMP
);
2552 return (result
? ENOENT
: 0);
2556 * Resolve hard link reference to obtain the inode number.
2559 resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
)
2561 struct HFSPlusCatalogFile record
;
2564 error
= resolvelink(hfsmp
, linkref
, &record
);
2566 if (record
.fileID
== 0)
2569 *ino
= record
.fileID
;
2575 * getkey - get a key from id by doing a thread lookup
2578 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
2580 struct BTreeIterator
* iterator
;
2581 FSBufferDescriptor btdata
;
2584 CatalogRecord
* recp
;
2588 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2590 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2591 bzero(iterator
, sizeof(*iterator
));
2592 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
2594 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
2595 BDINIT(btdata
, recp
);
2597 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2598 &btdata
, &datasize
, iterator
);
2602 /* Turn thread record into a cnode key (in place) */
2603 switch (recp
->recordType
) {
2604 case kHFSFileThreadRecord
:
2605 case kHFSFolderThreadRecord
:
2606 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
2607 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
2608 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
2611 case kHFSPlusFileThreadRecord
:
2612 case kHFSPlusFolderThreadRecord
:
2613 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
2614 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
2615 (keyp
->hfsPlus
.nodeName
.length
* 2);
2616 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
2625 FREE(iterator
, M_TEMP
);
2628 return MacToVFSError(result
);
2632 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
2633 * null arguments to cat_idlookup instead, but we save around 10% by not building the
2634 * cat_desc here). Both key and attrp must point to real structures.
2638 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
2642 result
= getkey(hfsmp
, cnid
, key
);
2645 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, NULL
, attrp
, NULL
, NULL
);
2648 return MacToVFSError(result
);
2653 * buildrecord - build a default catalog directory or file record
2656 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
2657 CatalogRecord
*crp
, int *recordSize
)
2659 int type
= attrp
->ca_mode
& S_IFMT
;
2660 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
2663 createtime
= UTCToLocal(createtime
);
2664 if (type
== S_IFDIR
) {
2665 bzero(crp
, sizeof(HFSCatalogFolder
));
2666 crp
->recordType
= kHFSFolderRecord
;
2667 crp
->hfsFolder
.folderID
= cnid
;
2668 crp
->hfsFolder
.createDate
= createtime
;
2669 crp
->hfsFolder
.modifyDate
= createtime
;
2670 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
2671 *recordSize
= sizeof(HFSCatalogFolder
);
2673 bzero(crp
, sizeof(HFSCatalogFile
));
2674 crp
->recordType
= kHFSFileRecord
;
2675 crp
->hfsFile
.fileID
= cnid
;
2676 crp
->hfsFile
.createDate
= createtime
;
2677 crp
->hfsFile
.modifyDate
= createtime
;
2678 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
2679 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
2680 *recordSize
= sizeof(HFSCatalogFile
);
2683 struct HFSPlusBSDInfo
* bsdp
= NULL
;
2684 struct FndrFileInfo
* fip
= NULL
;
2686 if (type
== S_IFDIR
) {
2687 crp
->recordType
= kHFSPlusFolderRecord
;
2688 crp
->hfsPlusFolder
.flags
= 0;
2689 crp
->hfsPlusFolder
.valence
= 0;
2690 crp
->hfsPlusFolder
.folderID
= cnid
;
2691 crp
->hfsPlusFolder
.createDate
= createtime
;
2692 crp
->hfsPlusFolder
.contentModDate
= createtime
;
2693 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
2694 crp
->hfsPlusFolder
.accessDate
= createtime
;
2695 crp
->hfsPlusFolder
.backupDate
= 0;
2696 crp
->hfsPlusFolder
.textEncoding
= encoding
;
2697 crp
->hfsPlusFolder
.attrBlocks
= 0;
2698 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
2699 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
2700 bsdp
->special
.rawDevice
= 0;
2701 *recordSize
= sizeof(HFSPlusCatalogFolder
);
2703 crp
->recordType
= kHFSPlusFileRecord
;
2704 crp
->hfsPlusFile
.flags
= kHFSThreadExistsMask
;
2705 crp
->hfsPlusFile
.reserved1
= 0;
2706 crp
->hfsPlusFile
.fileID
= cnid
;
2707 crp
->hfsPlusFile
.createDate
= createtime
;
2708 crp
->hfsPlusFile
.contentModDate
= createtime
;
2709 crp
->hfsPlusFile
.accessDate
= createtime
;
2710 crp
->hfsPlusFile
.attributeModDate
= createtime
;
2711 crp
->hfsPlusFile
.backupDate
= 0;
2712 crp
->hfsPlusFile
.textEncoding
= encoding
;
2713 crp
->hfsPlusFile
.attrBlocks
= 0;
2714 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
2715 bsdp
->special
.rawDevice
= 0;
2719 /* BLK/CHR need to save the device info */
2720 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
2721 bzero(&crp
->hfsPlusFile
.userInfo
, 32);
2724 /* Hardlink links need to save the linkref */
2725 fip
= (FndrFileInfo
*)&attrp
->ca_finderinfo
;
2726 if ((SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2727 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2728 bsdp
->special
.iNodeNum
= attrp
->ca_rdev
;
2730 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2733 /* Symlinks also have a type and creator */
2734 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2737 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
2738 *recordSize
= sizeof(HFSPlusCatalogFile
);
2740 bsdp
->ownerID
= attrp
->ca_uid
;
2741 bsdp
->groupID
= attrp
->ca_gid
;
2742 bsdp
->fileMode
= attrp
->ca_mode
;
2743 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
2744 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
2750 * builddesc - build a cnode descriptor from an HFS+ key
2753 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
2754 int isdir
, struct cat_desc
*descp
)
2762 /* guess a size... */
2763 bufsize
= (3 * key
->nodeName
.length
) + 1;
2764 if (bufsize
>= sizeof(tmpbuff
) - 1) {
2765 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2767 nameptr
= &tmpbuff
[0];
2770 result
= utf8_encodestr(key
->nodeName
.unicode
,
2771 key
->nodeName
.length
* sizeof(UniChar
),
2772 nameptr
, (size_t *)&utf8len
,
2775 if (result
== ENAMETOOLONG
) {
2776 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
2777 key
->nodeName
.length
* sizeof(UniChar
),
2779 FREE(nameptr
, M_TEMP
);
2780 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2782 result
= utf8_encodestr(key
->nodeName
.unicode
,
2783 key
->nodeName
.length
* sizeof(UniChar
),
2784 nameptr
, (size_t *)&utf8len
,
2787 descp
->cd_parentcnid
= key
->parentID
;
2788 descp
->cd_nameptr
= vfs_addname(nameptr
, utf8len
, 0, 0);
2789 descp
->cd_namelen
= utf8len
;
2790 descp
->cd_cnid
= cnid
;
2791 descp
->cd_hint
= hint
;
2792 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
2794 descp
->cd_flags
|= CD_ISDIR
;
2795 descp
->cd_encoding
= encoding
;
2796 if (nameptr
!= &tmpbuff
[0]) {
2797 FREE(nameptr
, M_TEMP
);
2804 * getbsdattr - get attributes in bsd format
2808 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
2810 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
2811 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
2813 attrp
->ca_recflags
= crp
->flags
;
2814 attrp
->ca_nlink
= 1;
2815 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
2816 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
2817 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
2818 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
2819 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
2820 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
2822 if ((bsd
->fileMode
& S_IFMT
) == 0) {
2823 attrp
->ca_flags
= 0;
2824 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2825 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2827 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
2829 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
2833 attrp
->ca_uid
= bsd
->ownerID
;
2834 attrp
->ca_gid
= bsd
->groupID
;
2835 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
2836 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
2837 switch (attrp
->ca_mode
& S_IFMT
) {
2838 case S_IFCHR
: /* fall through */
2840 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
2843 /* Pick up the hard link count */
2844 if (bsd
->special
.linkCount
> 0)
2845 attrp
->ca_nlink
= bsd
->special
.linkCount
;
2849 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) {
2851 * Override the permissions as determined by the mount auguments
2852 * in ALMOST the same way unset permissions are treated but keep
2853 * track of whether or not the file or folder is hfs locked
2854 * by leaving the h_pflags field unchanged from what was unpacked
2855 * out of the catalog.
2857 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2858 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2863 if (!S_ISDIR(attrp
->ca_mode
)) {
2864 attrp
->ca_mode
&= ~S_IFMT
;
2865 attrp
->ca_mode
|= S_IFDIR
;
2867 attrp
->ca_nlink
= 2 + ((HFSPlusCatalogFolder
*)crp
)->valence
;
2868 attrp
->ca_entries
= ((HFSPlusCatalogFolder
*)crp
)->valence
;
2869 attrp
->ca_attrblks
= ((HFSPlusCatalogFolder
*)crp
)->attrBlocks
;
2871 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2872 if (crp
->flags
& kHFSFileLockedMask
) {
2873 /* The file's supposed to be locked:
2874 Make sure at least one of the IMMUTABLE bits is set: */
2875 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
2876 attrp
->ca_flags
|= UF_IMMUTABLE
;
2878 /* The file's supposed to be unlocked: */
2879 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
2881 /* get total blocks (both forks) */
2882 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
2883 attrp
->ca_attrblks
= crp
->attrBlocks
;
2884 /* On HFS+ the ThreadExists flag must always be set. */
2885 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
2886 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
2889 attrp
->ca_fileid
= crp
->fileID
;
2891 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
2895 * promotekey - promote hfs key to hfs plus key
2899 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
2900 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
2902 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
2906 *encoding
= hfsmp
->hfs_encoding
;
2908 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
2909 kHFSPlusMaxFileNameChars
, &uniCount
);
2911 * When an HFS name cannot be encoded with the current
2912 * encoding use MacRoman as a fallback.
2914 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
2916 (void) mac_roman_to_unicode(hfskey
->nodeName
,
2917 keyp
->nodeName
.unicode
,
2918 kHFSPlusMaxFileNameChars
,
2922 keyp
->nodeName
.length
= uniCount
;
2923 keyp
->parentID
= hfskey
->parentID
;
2927 * promotefork - promote hfs fork info to hfs plus
2931 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
2932 int resource
, struct cat_fork
* forkp
)
2934 struct HFSPlusExtentDescriptor
*xp
;
2935 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2937 bzero(forkp
, sizeof(*forkp
));
2938 xp
= &forkp
->cf_extents
[0];
2940 forkp
->cf_size
= filep
->rsrcLogicalSize
;
2941 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
2942 forkp
->cf_bytesread
= 0;
2943 forkp
->cf_vblocks
= 0;
2944 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
2945 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
2946 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
2947 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
2948 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
2949 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
2951 forkp
->cf_size
= filep
->dataLogicalSize
;
2952 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
2953 forkp
->cf_bytesread
= 0;
2954 forkp
->cf_vblocks
= 0;
2955 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
2956 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
2957 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
2958 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
2959 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
2960 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
2965 * promoteattr - promote hfs catalog attributes to hfs plus
2969 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
2971 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2973 if (dataPtr
->recordType
== kHFSFolderRecord
) {
2974 struct HFSCatalogFolder
* folder
;
2976 folder
= (struct HFSCatalogFolder
*) dataPtr
;
2977 crp
->recordType
= kHFSPlusFolderRecord
;
2978 crp
->flags
= folder
->flags
;
2979 crp
->fileID
= folder
->folderID
;
2980 crp
->createDate
= LocalToUTC(folder
->createDate
);
2981 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
2982 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
2983 crp
->reserved1
= folder
->valence
;
2984 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
2986 struct HFSCatalogFile
* file
;
2988 file
= (struct HFSCatalogFile
*) dataPtr
;
2989 crp
->recordType
= kHFSPlusFileRecord
;
2990 crp
->flags
= file
->flags
;
2991 crp
->fileID
= file
->fileID
;
2992 crp
->createDate
= LocalToUTC(file
->createDate
);
2993 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
2994 crp
->backupDate
= LocalToUTC(file
->backupDate
);
2996 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
2997 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
2998 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
2999 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
3001 crp
->textEncoding
= 0;
3002 crp
->attributeModDate
= crp
->contentModDate
;
3003 crp
->accessDate
= crp
->contentModDate
;
3004 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
3005 crp
->attrBlocks
= 0;
3009 * Build a catalog node thread record from a catalog key
3010 * and return the size of the record.
3013 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
3018 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
3019 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
3021 size
= sizeof(HFSCatalogThread
);
3024 rec
->recordType
= kHFSFolderThreadRecord
;
3026 rec
->recordType
= kHFSFileThreadRecord
;
3027 rec
->parentID
= key
->parentID
;
3028 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
3031 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
3032 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
3034 size
= sizeof(HFSPlusCatalogThread
);
3036 rec
->recordType
= kHFSPlusFolderThreadRecord
;
3038 rec
->recordType
= kHFSPlusFileThreadRecord
;
3040 rec
->parentID
= key
->parentID
;
3041 bcopy(&key
->nodeName
, &rec
->nodeName
,
3042 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
3044 /* HFS Plus has varaible sized thread records */
3045 size
-= (sizeof(rec
->nodeName
.unicode
) -
3046 (rec
->nodeName
.length
* sizeof(UniChar
)));
3053 * Build a catalog node thread key.
3056 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
3059 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
3060 key
->hfs
.reserved
= 0;
3061 key
->hfs
.parentID
= parentID
;
3062 key
->hfs
.nodeName
[0] = 0;
3064 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
3065 key
->hfsPlus
.parentID
= parentID
;
3066 key
->hfsPlus
.nodeName
.length
= 0;
3071 * Extract the text encoding from a catalog node record.
3074 getencoding(const CatalogRecord
*crp
)
3078 if (crp
->recordType
== kHFSPlusFolderRecord
)
3079 encoding
= crp
->hfsPlusFolder
.textEncoding
;
3080 else if (crp
->recordType
== kHFSPlusFileRecord
)
3081 encoding
= crp
->hfsPlusFile
.textEncoding
;
3089 * Extract the CNID from a catalog node record.
3092 getcnid(const CatalogRecord
*crp
)
3096 switch (crp
->recordType
) {
3097 case kHFSFolderRecord
:
3098 cnid
= crp
->hfsFolder
.folderID
;
3100 case kHFSFileRecord
:
3101 cnid
= crp
->hfsFile
.fileID
;
3103 case kHFSPlusFolderRecord
:
3104 cnid
= crp
->hfsPlusFolder
.folderID
;
3106 case kHFSPlusFileRecord
:
3107 cnid
= crp
->hfsPlusFile
.fileID
;
3110 printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp
);
3118 * Extract the parent ID from a catalog node record.
3121 getparentcnid(const CatalogRecord
*recp
)
3125 switch (recp
->recordType
) {
3126 case kHFSFileThreadRecord
:
3127 case kHFSFolderThreadRecord
:
3128 cnid
= recp
->hfsThread
.parentID
;
3131 case kHFSPlusFileThreadRecord
:
3132 case kHFSPlusFolderThreadRecord
:
3133 cnid
= recp
->hfsPlusThread
.parentID
;
3136 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp
);
3144 * Determine if a catalog node record is a directory.
3147 isadir(const CatalogRecord
*crp
)
3149 return (crp
->recordType
== kHFSFolderRecord
||
3150 crp
->recordType
== kHFSPlusFolderRecord
);