2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
27 #include <sys/mount.h>
28 #include <sys/vnode.h>
29 #include <sys/dirent.h>
30 #include <vfs/vfs_support.h>
31 #include <libkern/libkern.h>
33 #include <sys/utfconv.h>
36 #include "hfs_catalog.h"
37 #include "hfs_format.h"
38 #include "hfs_endian.h"
40 #include "hfscommon/headers/BTreesInternal.h"
41 #include "hfscommon/headers/HFSUnicodeWrappers.h"
45 * Initialization of an FSBufferDescriptor structure.
47 #define BDINIT(bd, addr) { \
48 (bd).bufferAddress = (addr); \
49 (bd).itemSize = sizeof(*(addr)); \
55 BTreeIterator iterator
;
56 HFSPlusCatalogKey key
;
61 struct cat_desc
* s_desc
;
62 struct cat_attr
* s_attr
;
63 struct cat_fork
* s_datafork
;
64 struct cat_fork
* s_rsrcfork
;
65 struct hfsmount
* s_hfsmp
;
68 struct position_state
{
73 struct hfsmount
*hfsmp
;
76 /* Map file mode type to directory entry types */
77 u_char modetodirtype
[16] = {
78 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
79 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
80 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
81 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
83 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
86 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
87 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
89 static int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
90 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
92 extern int mac_roman_to_unicode(const Str31 hfs_str
, UniChar
*uni_str
,
93 UInt32 maxCharLen
, UInt32
*unicodeChars
);
95 extern int unicode_to_hfs(ExtendedVCB
*vcb
, ByteCount srcLen
,
96 const u_int16_t
* srcStr
, Str31 dstStr
, int retry
);
99 /* Internal catalog support routines */
101 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
102 struct position_state
*state
);
104 static int resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
);
106 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
108 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
109 HFSPlusCatalogKey
*key
, int retry
);
111 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
113 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, int *recordSize
);
115 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
117 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
118 int isdir
, struct cat_desc
*descp
);
120 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
122 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_long
*encoding
);
123 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
124 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
126 static cnid_t
getcnid(const CatalogRecord
*crp
);
127 static u_long
getencoding(const CatalogRecord
*crp
);
128 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
130 static int isadir(const CatalogRecord
*crp
);
132 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
137 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, struct proc
*p
)
143 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
145 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
147 result
= BTReserveSpace(fcb
, ops
, (void*)cookie
);
149 hfs_systemfile_unlock(hfsmp
, lockflags
);
151 return MacToVFSError(result
);
156 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, struct proc
*p
)
161 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
163 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
165 (void) BTReleaseReserve(fcb
, (void*)cookie
);
167 hfs_systemfile_unlock(hfsmp
, lockflags
);
174 struct hfsmount
*hfsmp
,
175 CatalogRecord
* recp
,
176 struct cat_attr
*attrp
,
177 struct cat_fork
*datafp
,
178 struct cat_fork
*rsrcfp
)
180 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
183 struct HFSPlusCatalogFile cnoderec
;
185 promoteattr(hfsmp
, recp
, &cnoderec
);
186 getbsdattr(hfsmp
, &cnoderec
, attrp
);
188 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
192 bzero(datafp
, sizeof(*datafp
));
194 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
195 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
197 /* Convert the data fork. */
198 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
199 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
200 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
201 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
202 datafp
->cf_bytesread
=
203 recp
->hfsPlusFile
.dataFork
.clumpSize
*
204 HFSTOVCB(hfsmp
)->blockSize
;
206 datafp
->cf_bytesread
= 0;
208 datafp
->cf_vblocks
= 0;
209 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
210 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
212 /* Convert the resource fork. */
213 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
214 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
215 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
216 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
217 datafp
->cf_bytesread
=
218 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
219 HFSTOVCB(hfsmp
)->blockSize
;
221 datafp
->cf_bytesread
= 0;
223 rsrcfp
->cf_vblocks
= 0;
224 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
225 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
232 struct hfsmount
*hfsmp
,
234 CatalogRecord
* recp
,
235 struct cat_desc
*descp
)
237 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
238 HFSPlusCatalogKey
* pluskey
= NULL
;
242 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
243 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
246 pluskey
= (HFSPlusCatalogKey
*)key
;
247 encoding
= getencoding(recp
);
250 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
252 FREE(pluskey
, M_TEMP
);
263 cat_releasedesc(struct cat_desc
*descp
)
270 if ((descp
->cd_flags
& CD_HASBUF
) &&
271 (descp
->cd_nameptr
!= NULL
)) {
272 name
= descp
->cd_nameptr
;
273 descp
->cd_nameptr
= NULL
;
274 descp
->cd_namelen
= 0;
275 descp
->cd_flags
&= ~CD_HASBUF
;
276 vfs_removename(name
);
278 descp
->cd_nameptr
= NULL
;
279 descp
->cd_namelen
= 0;
283 * These Catalog functions allow access to the HFS Catalog (database).
284 * The catalog b-tree lock must be aquired before calling any of these routines.
288 * cat_lookup - lookup a catalog node using a cnode decriptor
292 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
293 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
294 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
300 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
302 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
304 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
308 result
= cat_lookupbykey(hfsmp
, keyp
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
310 if (result
== ENOENT
) {
312 struct cat_desc temp_desc
;
313 if (outdescp
== NULL
) {
314 bzero(&temp_desc
, sizeof(temp_desc
));
315 outdescp
= &temp_desc
;
317 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
319 *desc_cnid
= outdescp
->cd_cnid
;
321 if (outdescp
== &temp_desc
) {
322 /* Release the local copy of desc */
323 cat_releasedesc(outdescp
);
325 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
326 // make MacRoman key from utf-8
327 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
328 // update desc text encoding so that other catalog ops succeed
339 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
341 struct BTreeIterator
*iterator
;
342 struct FSBufferDescriptor file_data
;
343 struct HFSCatalogFile file_rec
;
348 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
351 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
353 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
354 bzero(&iterator
[0], 2* sizeof(*iterator
));
355 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
359 BDINIT(file_data
, &file_rec
);
360 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
364 if (file_rec
.recordType
!= kHFSFileRecord
) {
369 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
370 struct FSBufferDescriptor thread_data
;
371 struct HFSCatalogThread thread_rec
;
373 file_rec
.flags
|= kHFSThreadExistsMask
;
374 BDINIT(thread_data
, &thread_rec
);
375 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
376 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
378 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
382 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
383 (void) BTFlushPath(fcb
);
386 (void) BTFlushPath(fcb
);
387 FREE(iterator
, M_TEMP
);
389 return MacToVFSError(result
);
394 * cat_findname - obtain a descriptor from cnid
396 * Only a thread lookup is performed.
400 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
402 struct BTreeIterator
* iterator
;
403 FSBufferDescriptor btdata
;
405 CatalogRecord
* recp
;
411 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
413 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
414 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
415 iterator
->hint
.nodeNum
= 0;
417 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
418 BDINIT(btdata
, recp
);
420 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
424 /* Turn thread record into a cnode key (in place). */
425 switch (recp
->recordType
) {
426 case kHFSFolderThreadRecord
:
429 case kHFSFileThreadRecord
:
430 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
431 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
434 case kHFSPlusFolderThreadRecord
:
437 case kHFSPlusFileThreadRecord
:
438 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
439 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
440 (keyp
->hfsPlus
.nodeName
.length
* 2);
447 HFSPlusCatalogKey
* pluskey
= NULL
;
450 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
451 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
452 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
453 FREE(pluskey
, M_TEMP
);
456 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
460 FREE(iterator
, M_TEMP
);
462 return MacToVFSError(result
);
466 * cat_idlookup - lookup a catalog node using a cnode id
470 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
,
471 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
473 struct BTreeIterator
* iterator
;
474 FSBufferDescriptor btdata
;
477 CatalogRecord
* recp
;
481 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
483 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
484 bzero(iterator
, sizeof(*iterator
));
485 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
487 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
488 BDINIT(btdata
, recp
);
490 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
491 &btdata
, &datasize
, iterator
);
495 /* Turn thread record into a cnode key (in place) */
496 switch (recp
->recordType
) {
497 case kHFSFileThreadRecord
:
498 case kHFSFolderThreadRecord
:
499 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
500 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
503 case kHFSPlusFileThreadRecord
:
504 case kHFSPlusFolderThreadRecord
:
505 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
506 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
507 (keyp
->hfsPlus
.nodeName
.length
* 2);
515 result
= cat_lookupbykey(hfsmp
, keyp
, 0, 0, outdescp
, attrp
, forkp
, NULL
);
518 FREE(iterator
, M_TEMP
);
520 return MacToVFSError(result
);
525 * cat_lookupmangled - lookup a catalog node using a mangled name
528 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
529 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
538 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
539 if (fileID
< kHFSFirstUserCatalogNodeID
)
542 result
= cat_idlookup(hfsmp
, fileID
, outdescp
, attrp
, forkp
);
546 /* It must be in the correct directory */
547 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
550 if ((outdescp
->cd_namelen
< prefixlen
) ||
551 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
557 cat_releasedesc(outdescp
);
563 * cat_lookupbykey - lookup a catalog node using a cnode key
566 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
567 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
569 struct BTreeIterator
* iterator
;
570 FSBufferDescriptor btdata
;
571 CatalogRecord
* recp
;
579 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
581 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
582 BDINIT(btdata
, recp
);
583 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
584 bzero(iterator
, sizeof(*iterator
));
585 iterator
->hint
.nodeNum
= hint
;
586 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
588 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
589 &btdata
, &datasize
, iterator
);
593 /* Save the cnid now in case there's a hard link */
594 cnid
= getcnid(recp
);
595 encoding
= getencoding(recp
);
596 hint
= iterator
->hint
.nodeNum
;
598 /* Hide the journal files (if any) */
599 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
600 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
601 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
608 * When a hardlink link is encountered, auto resolve it
612 && (recp
->recordType
== kHFSPlusFileRecord
)
613 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
614 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)
615 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)HFSTOVCB(hfsmp
)->vcbCrDate
) ||
616 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
618 ilink
= recp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
620 (void) resolvelink(hfsmp
, ilink
, (struct HFSPlusCatalogFile
*)recp
);
625 struct HFSPlusCatalogFile cnoderec
;
627 promoteattr(hfsmp
, recp
, &cnoderec
);
628 getbsdattr(hfsmp
, &cnoderec
, attrp
);
630 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
632 attrp
->ca_rdev
= ilink
;
637 bzero(forkp
, sizeof(*forkp
));
638 } else if (std_hfs
) {
639 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
640 } else if (wantrsrc
) {
641 /* Convert the resource fork. */
642 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
643 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
644 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
645 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
646 forkp
->cf_bytesread
=
647 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
648 HFSTOVCB(hfsmp
)->blockSize
;
650 forkp
->cf_bytesread
= 0;
652 forkp
->cf_vblocks
= 0;
653 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
654 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
659 /* Convert the data fork. */
660 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
661 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.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
.dataFork
.clumpSize
*
666 HFSTOVCB(hfsmp
)->blockSize
;
668 forkp
->cf_bytesread
= 0;
670 forkp
->cf_vblocks
= 0;
671 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
672 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
674 /* Validate the fork's resident extents. */
676 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
677 if (forkp
->cf_extents
[i
].startBlock
+ forkp
->cf_extents
[i
].blockCount
>= hfsmp
->totalBlocks
) {
678 /* Suppress any bad extents so a remove can succeed. */
679 forkp
->cf_extents
[i
].startBlock
= 0;
680 forkp
->cf_extents
[i
].blockCount
= 0;
683 attrp
->ca_mode
&= S_IFMT
| S_IRUSR
| S_IRGRP
| S_IROTH
;
686 validblks
+= forkp
->cf_extents
[i
].blockCount
;
689 /* Adjust for any missing blocks. */
690 if ((validblks
< forkp
->cf_blocks
) && (forkp
->cf_extents
[7].blockCount
== 0)) {
693 forkp
->cf_blocks
= validblks
;
695 attrp
->ca_blocks
= validblks
+ recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
697 psize
= (u_int64_t
)validblks
* (u_int64_t
)hfsmp
->blockSize
;
698 if (psize
< forkp
->cf_size
) {
699 forkp
->cf_size
= psize
;
706 HFSPlusCatalogKey
* pluskey
= NULL
;
709 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
710 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
713 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
715 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
717 FREE(pluskey
, M_TEMP
);
721 if (desc_cnid
!= NULL
) {
725 FREE(iterator
, M_TEMP
);
728 return MacToVFSError(result
);
733 * cat_create - create a node in the catalog
737 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
738 struct cat_desc
*out_descp
)
743 FSBufferDescriptor btdata
;
752 modeformat
= attrp
->ca_mode
& S_IFMT
;
754 vcb
= HFSTOVCB(hfsmp
);
755 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
756 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
759 * Atomically get the next CNID. If we have wrapped the CNIDs
760 * then keep the hfsmp lock held until we have found a CNID.
762 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
764 nextCNID
= hfsmp
->vcbNxtCNID
;
765 if (nextCNID
== 0xFFFFFFFF) {
769 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
770 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
775 hfsmp
->vcbFlags
|= 0xFF00;
776 /* OK to drop lock if CNIDs are not wrapping */
777 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
) == 0) {
778 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
781 return (result
); /* HFS only exit */
784 /* Get space for iterator, key and data */
785 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
786 bto
->iterator
.hint
.nodeNum
= 0;
788 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
793 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
794 bto
->key
.nodeName
.length
);
795 hfs_setencodingbits(hfsmp
, encoding
);
799 * Insert the thread record first
801 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
802 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
803 S_ISDIR(attrp
->ca_mode
));
804 btdata
.bufferAddress
= &bto
->data
;
805 btdata
.itemSize
= datalen
;
806 btdata
.itemCount
= 1;
809 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
811 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
812 if ((result
== btExists
) && !std_hfs
&& mntlock
) {
814 * Allow CNIDs on HFS Plus volumes to wrap around
816 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
817 nextCNID
= kHFSFirstUserCatalogNodeID
;
823 if (result
) goto exit
;
827 * CNID is now established. If we have wrapped then
828 * update the vcbNxtCNID and drop the vcb lock.
831 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
832 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
833 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
835 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
840 * Now insert the file/directory record
842 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
843 btdata
.bufferAddress
= &bto
->data
;
844 btdata
.itemSize
= datalen
;
845 btdata
.itemCount
= 1;
847 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
849 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
851 if (result
== btExists
)
854 /* Back out the thread record */
855 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
856 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
857 (void) BTDeleteRecord(fcb
, &bto
->iterator
);
863 * Insert was Successfull, update name, parent and volume
867 if (out_descp
!= NULL
) {
868 HFSPlusCatalogKey
* pluskey
= NULL
;
871 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
872 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
875 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
877 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
878 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
880 FREE(pluskey
, M_TEMP
);
883 attrp
->ca_fileid
= nextCNID
;
887 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
889 (void) BTFlushPath(fcb
);
892 return MacToVFSError(result
);
897 * cnode_rename - rename a catalog node
899 * Assumes that the target's directory exists.
901 * Order of B-tree operations:
902 * 1. BTSearchRecord(from_cnode, &data);
903 * 2. BTInsertRecord(to_cnode, &data);
904 * 3. BTDeleteRecord(from_cnode);
905 * 4. BTDeleteRecord(from_thread);
906 * 5. BTInsertRecord(to_thread);
911 struct hfsmount
* hfsmp
,
912 struct cat_desc
* from_cdp
,
913 struct cat_desc
* todir_cdp
,
914 struct cat_desc
* to_cdp
,
915 struct cat_desc
* out_cdp
)
917 struct BTreeIterator
* to_iterator
= NULL
;
918 struct BTreeIterator
* from_iterator
= NULL
;
919 FSBufferDescriptor btdata
;
920 CatalogRecord
* recp
= NULL
;
921 HFSPlusCatalogKey
* to_key
;
928 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
932 vcb
= HFSTOVCB(hfsmp
);
933 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
934 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
936 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
939 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
940 bzero(from_iterator
, sizeof(*from_iterator
));
941 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
944 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
945 bzero(to_iterator
, sizeof(*to_iterator
));
946 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
949 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
950 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
951 BDINIT(btdata
, recp
);
954 * When moving a directory, make sure its a valid move.
956 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
957 struct BTreeIterator iterator
;
958 cnid_t cnid
= from_cdp
->cd_cnid
;
959 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
961 /* First check the obvious ones */
962 if (cnid
== fsRtDirID
||
963 cnid
== to_cdp
->cd_parentcnid
||
968 bzero(&iterator
, sizeof(iterator
));
970 * Traverese destination path all the way back to the root
971 * making sure that source directory is not encountered.
974 while (pathcnid
> fsRtDirID
) {
975 buildthreadkey(pathcnid
, std_hfs
,
976 (CatalogKey
*)&iterator
.key
);
977 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
979 if (result
) goto exit
;
981 pathcnid
= getparentcnid(recp
);
982 if (pathcnid
== cnid
) {
990 * Step 1: Find cnode data at old location
992 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
993 &datasize
, from_iterator
);
995 if (std_hfs
|| (result
!= btNotFound
))
998 struct cat_desc temp_desc
;
1000 /* Probably the node has mangled name */
1001 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
1005 /* The file has mangled name. Search the cnode data using full name */
1006 bzero(from_iterator
, sizeof(*from_iterator
));
1007 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
1009 cat_releasedesc(&temp_desc
);
1013 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
1015 cat_releasedesc(&temp_desc
);
1019 cat_releasedesc(&temp_desc
);
1022 /* Update the text encoding (on disk and in descriptor) */
1024 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
,
1025 to_key
->nodeName
.length
);
1026 hfs_setencodingbits(hfsmp
, encoding
);
1027 recp
->hfsPlusFile
.textEncoding
= encoding
;
1029 out_cdp
->cd_encoding
= encoding
;
1032 if (std_hfs
&& !directory
&&
1033 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1037 * If the keys are identical then there's nothing left to do!
1039 * update the hint and exit
1042 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1044 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1048 /* Step 2: Insert cnode at new location */
1049 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1050 if (result
== btExists
) {
1051 int fromtype
= recp
->recordType
;
1053 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1054 goto exit
; /* EEXIST */
1056 /* Find cnode data at new location */
1057 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1061 if ((fromtype
!= recp
->recordType
) ||
1062 (from_cdp
->cd_cnid
!= getcnid(recp
))) {
1064 goto exit
; /* EEXIST */
1066 /* The old name is a case variant and must be removed */
1067 result
= BTDeleteRecord(fcb
, from_iterator
);
1071 /* Insert cnode (now that case duplicate is gone) */
1072 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1074 /* Try and restore original before leaving */
1079 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1081 panic("cat_create: could not undo (BTInsert = %d)", err
);
1084 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1093 /* Step 3: Remove cnode from old location */
1095 result
= BTDeleteRecord(fcb
, from_iterator
);
1097 /* Try and delete new record before leaving */
1102 err
= BTDeleteRecord(fcb
, to_iterator
);
1104 panic("cat_create: could not undo (BTDelete = %d)", err
);
1107 (void) BTDeleteRecord(fcb
, to_iterator
);
1113 /* #### POINT OF NO RETURN #### */
1116 * Step 4: Remove cnode's old thread record
1118 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1119 (void) BTDeleteRecord(fcb
, from_iterator
);
1122 * Step 5: Insert cnode's new thread record
1123 * (optional for HFS files)
1126 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1127 btdata
.itemSize
= datasize
;
1128 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1129 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1133 HFSPlusCatalogKey
* pluskey
= NULL
;
1136 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1137 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1139 /* Save the real encoding hint in the Finder Info (field 4). */
1140 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1143 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1144 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1148 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1150 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1151 encoding
, directory
, out_cdp
);
1153 FREE(pluskey
, M_TEMP
);
1157 (void) BTFlushPath(fcb
);
1159 FREE(from_iterator
, M_TEMP
);
1161 FREE(to_iterator
, M_TEMP
);
1164 return MacToVFSError(result
);
1169 * cat_delete - delete a node from the catalog
1171 * Order of B-tree operations:
1172 * 1. BTDeleteRecord(cnode);
1173 * 2. BTDeleteRecord(thread);
1174 * 3. BTUpdateRecord(parent);
1178 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1182 BTreeIterator
*iterator
;
1187 vcb
= HFSTOVCB(hfsmp
);
1188 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1189 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1193 * The root directory cannot be deleted
1194 * A directory must be empty
1195 * A file must be zero length (no blocks)
1197 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1198 descp
->cd_parentcnid
== kHFSRootParentID
)
1201 /* XXX Preflight Missing */
1203 /* Get space for iterator */
1204 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1205 iterator
->hint
.nodeNum
= 0;
1208 * Derive a key from either the file ID (for a virtual inode)
1209 * or the descriptor.
1211 if (descp
->cd_namelen
== 0) {
1212 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1213 cnid
= attrp
->ca_fileid
;
1215 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1216 cnid
= descp
->cd_cnid
;
1222 result
= BTDeleteRecord(fcb
, iterator
);
1224 if (std_hfs
|| (result
!= btNotFound
))
1227 struct cat_desc temp_desc
;
1229 /* Probably the node has mangled name */
1230 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1234 /* The file has mangled name. Delete the file using full name */
1235 bzero(iterator
, sizeof(*iterator
));
1236 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1237 cnid
= temp_desc
.cd_cnid
;
1239 cat_releasedesc(&temp_desc
);
1243 result
= BTDeleteRecord(fcb
, iterator
);
1245 cat_releasedesc(&temp_desc
);
1249 cat_releasedesc(&temp_desc
);
1252 /* Delete thread record, ignore errors */
1253 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1254 (void) BTDeleteRecord(fcb
, iterator
);
1257 (void) BTFlushPath(fcb
);
1258 FREE(iterator
, M_TEMP
);
1260 return MacToVFSError(result
);
1265 * cnode_update - update the catalog node described by descp
1266 * using the data from attrp and forkp.
1270 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1271 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1275 BTreeIterator
* iterator
;
1276 struct update_state state
;
1280 vcb
= HFSTOVCB(hfsmp
);
1281 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1282 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1284 state
.s_desc
= descp
;
1285 state
.s_attr
= attrp
;
1286 state
.s_datafork
= dataforkp
;
1287 state
.s_rsrcfork
= rsrcforkp
;
1288 state
.s_hfsmp
= hfsmp
;
1290 /* Get space for iterator */
1291 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1294 * For open-deleted files we need to do a lookup by cnid
1295 * (using thread rec).
1297 * For hard links, the target of the update is the inode
1298 * itself (not the link record) so a lookup by fileid
1299 * (i.e. thread rec) is needed.
1301 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (descp
->cd_namelen
== 0))
1302 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1304 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1308 /* Pass a node hint */
1309 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1311 result
= BTUpdateRecord(fcb
, iterator
,
1312 (IterateCallBackProcPtr
)catrec_update
, &state
);
1316 /* Update the node hint. */
1317 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1320 (void) BTFlushPath(fcb
);
1321 FREE(iterator
, M_TEMP
);
1323 return MacToVFSError(result
);
1327 * catrec_update - Update the fields of a catalog record
1328 * This is called from within BTUpdateRecord.
1331 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1333 struct cat_desc
*descp
;
1334 struct cat_attr
*attrp
;
1335 struct cat_fork
*forkp
;
1336 struct hfsmount
*hfsmp
;
1340 descp
= state
->s_desc
;
1341 attrp
= state
->s_attr
;
1342 hfsmp
= state
->s_hfsmp
;
1343 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1345 switch (crp
->recordType
) {
1346 case kHFSFolderRecord
: {
1347 HFSCatalogFolder
*dir
;
1349 dir
= (struct HFSCatalogFolder
*)crp
;
1350 /* Do a quick sanity check */
1351 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1352 (dir
->folderID
!= descp
->cd_cnid
))
1353 return (btNotFound
);
1354 dir
->valence
= attrp
->ca_entries
;
1355 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1356 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1357 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1358 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1359 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1362 case kHFSFileRecord
: {
1363 HFSCatalogFile
*file
;
1365 file
= (struct HFSCatalogFile
*)crp
;
1366 /* Do a quick sanity check */
1367 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1368 (file
->fileID
!= attrp
->ca_fileid
))
1369 return (btNotFound
);
1370 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1371 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1372 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1373 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1374 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1375 if (state
->s_rsrcfork
) {
1376 forkp
= state
->s_rsrcfork
;
1377 file
->rsrcLogicalSize
= forkp
->cf_size
;
1378 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1379 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1380 file
->rsrcExtents
[i
].startBlock
=
1381 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1382 file
->rsrcExtents
[i
].blockCount
=
1383 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1386 if (state
->s_datafork
) {
1387 forkp
= state
->s_datafork
;
1388 file
->dataLogicalSize
= forkp
->cf_size
;
1389 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1390 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1391 file
->dataExtents
[i
].startBlock
=
1392 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1393 file
->dataExtents
[i
].blockCount
=
1394 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1399 case kHFSPlusFolderRecord
: {
1400 HFSPlusCatalogFolder
*dir
;
1402 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1403 /* Do a quick sanity check */
1404 if ((ckp
->hfsPlus
.parentID
!= descp
->cd_parentcnid
) ||
1405 (dir
->folderID
!= descp
->cd_cnid
))
1406 return (btNotFound
);
1407 dir
->flags
= attrp
->ca_recflags
;
1408 dir
->valence
= attrp
->ca_entries
;
1409 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1410 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1411 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1412 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1413 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1414 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1415 dir
->textEncoding
= descp
->cd_encoding
;
1416 dir
->attrBlocks
= attrp
->ca_attrblks
;
1417 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1419 * Update the BSD Info if it was already initialized on
1420 * disk or if the runtime values have been modified.
1422 * If the BSD info was already initialized, but
1423 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1424 * probably different than what was on disk. We don't want
1425 * to overwrite the on-disk values (so if we turn off
1426 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1427 * This way, we can still change fields like the mode or
1428 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1430 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1431 * won't change the uid or gid from their defaults. So, if
1432 * the BSD info wasn't set, and the runtime values are not
1433 * default, then what changed was the mode or flags. We
1434 * have to set the uid and gid to something, so use the
1435 * supplied values (which will be default), which has the
1436 * same effect as creating a new file while
1437 * MNT_UNKNOWNPERMISSIONS is set.
1439 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1440 (attrp
->ca_flags
!= 0) ||
1441 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1442 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1443 ((attrp
->ca_mode
& ALLPERMS
) !=
1444 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1445 if ((dir
->bsdInfo
.fileMode
== 0) ||
1446 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1447 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1448 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1450 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1451 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1452 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1456 case kHFSPlusFileRecord
: {
1457 HFSPlusCatalogFile
*file
;
1459 file
= (struct HFSPlusCatalogFile
*)crp
;
1460 /* Do a quick sanity check */
1461 if (file
->fileID
!= attrp
->ca_fileid
)
1462 return (btNotFound
);
1463 file
->flags
= attrp
->ca_recflags
;
1464 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1465 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1466 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1467 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1468 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1469 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1470 file
->textEncoding
= descp
->cd_encoding
;
1471 file
->attrBlocks
= attrp
->ca_attrblks
;
1472 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1474 * Update the BSD Info if it was already initialized on
1475 * disk or if the runtime values have been modified.
1477 * If the BSD info was already initialized, but
1478 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1479 * probably different than what was on disk. We don't want
1480 * to overwrite the on-disk values (so if we turn off
1481 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1482 * This way, we can still change fields like the mode or
1483 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1485 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1486 * won't change the uid or gid from their defaults. So, if
1487 * the BSD info wasn't set, and the runtime values are not
1488 * default, then what changed was the mode or flags. We
1489 * have to set the uid and gid to something, so use the
1490 * supplied values (which will be default), which has the
1491 * same effect as creating a new file while
1492 * MNT_UNKNOWNPERMISSIONS is set.
1494 if ((file
->bsdInfo
.fileMode
!= 0) ||
1495 (attrp
->ca_flags
!= 0) ||
1496 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1497 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1498 ((attrp
->ca_mode
& ALLPERMS
) !=
1499 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1500 if ((file
->bsdInfo
.fileMode
== 0) ||
1501 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1502 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1503 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1505 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1506 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1507 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1509 if (state
->s_rsrcfork
) {
1510 forkp
= state
->s_rsrcfork
;
1511 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1512 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1513 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1514 sizeof(HFSPlusExtentRecord
));
1515 /* Push blocks read to disk */
1516 file
->resourceFork
.clumpSize
=
1517 howmany(forkp
->cf_bytesread
, blksize
);
1519 if (state
->s_datafork
) {
1520 forkp
= state
->s_datafork
;
1521 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1522 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1523 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1524 sizeof(HFSPlusExtentRecord
));
1525 /* Push blocks read to disk */
1526 file
->dataFork
.clumpSize
=
1527 howmany(forkp
->cf_bytesread
, blksize
);
1530 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1531 (file
->resourceFork
.extents
[0].startBlock
==
1532 file
->dataFork
.extents
[0].startBlock
))
1533 panic("catrec_update: rsrc fork == data fork");
1535 /* Synchronize the lock state */
1536 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1537 file
->flags
|= kHFSFileLockedMask
;
1539 file
->flags
&= ~kHFSFileLockedMask
;
1541 /* Push out special field if necessary */
1542 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
))
1543 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1544 else if (descp
->cd_cnid
!= attrp
->ca_fileid
1545 || attrp
->ca_nlink
== 2)
1546 file
->bsdInfo
.special
.linkCount
= attrp
->ca_nlink
;
1550 return (btNotFound
);
1556 * Callback to collect directory entries.
1557 * Called with readattr_state for each item in a directory.
1559 struct readattr_state
{
1560 struct hfsmount
*hfsmp
;
1561 struct cat_entrylist
*list
;
1568 cat_readattr(const CatalogKey
*key
, const CatalogRecord
*rec
,
1569 struct readattr_state
*state
)
1571 struct cat_entrylist
*list
= state
->list
;
1572 struct hfsmount
*hfsmp
= state
->hfsmp
;
1573 struct cat_entry
*cep
;
1576 if (list
->realentries
>= list
->maxentries
)
1577 return (0); /* stop */
1579 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
1581 switch(rec
->recordType
) {
1582 case kHFSPlusFolderRecord
:
1583 case kHFSPlusFileRecord
:
1584 case kHFSFolderRecord
:
1585 case kHFSFileRecord
:
1586 if (parentcnid
!= state
->dir_cnid
) {
1587 state
->error
= ENOENT
;
1588 return (0); /* stop */
1592 state
->error
= ENOENT
;
1593 return (0); /* stop */
1596 /* Hide the private meta data directory and journal files */
1597 if (parentcnid
== kHFSRootFolderID
) {
1598 if ((rec
->recordType
== kHFSPlusFolderRecord
) &&
1599 (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1600 return (1); /* continue */
1602 if ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
))) &&
1603 (rec
->recordType
== kHFSPlusFileRecord
) &&
1604 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
1605 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
1607 return (1); /* continue */
1611 cep
= &list
->entry
[list
->realentries
++];
1613 if (state
->stdhfs
) {
1614 struct HFSPlusCatalogFile cnoderec
;
1615 HFSPlusCatalogKey
* pluskey
;
1618 promoteattr(hfsmp
, rec
, &cnoderec
);
1619 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
1621 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1622 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
1623 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
1624 FREE(pluskey
, M_TEMP
);
1626 if (rec
->recordType
== kHFSFileRecord
) {
1627 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1629 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
1630 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
1631 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
1632 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
1635 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
1636 builddesc((HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
1637 isadir(rec
), &cep
->ce_desc
);
1639 if (rec
->recordType
== kHFSPlusFileRecord
) {
1640 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
1641 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
1642 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
1643 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
1645 /* Save link reference for later processing. */
1646 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
1647 && (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
))
1648 cep
->ce_attr
.ca_rdev
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1652 return (list
->realentries
< list
->maxentries
);
1656 * Pack a cat_entrylist buffer with attributes from the catalog
1658 * Note: index is zero relative
1662 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
1666 BTreeIterator
* iterator
;
1667 struct readattr_state state
;
1675 ce_list
->realentries
= 0;
1677 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
1678 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
1679 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
1681 state
.hfsmp
= hfsmp
;
1682 state
.list
= ce_list
;
1683 state
.dir_cnid
= parentcnid
;
1684 state
.stdhfs
= std_hfs
;
1687 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1688 bzero(iterator
, sizeof(*iterator
));
1689 key
= (CatalogKey
*)&iterator
->key
;
1691 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
1692 index
= dirhint
->dh_index
+ 1;
1695 * Attempt to build a key from cached filename
1697 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
1698 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
1704 * If the last entry wasn't cached then position the btree iterator
1706 if ((index
== 0) || !have_key
) {
1708 * Position the iterator at the directory's thread record.
1709 * (i.e. just before the first entry)
1711 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
1712 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1714 result
= MacToVFSError(result
);
1719 * Iterate until we reach the entry just
1720 * before the one we want to start with.
1723 struct position_state ps
;
1728 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
1731 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1732 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
1736 result
= MacToVFSError(result
);
1738 result
= MacToVFSError(result
);
1744 /* Fill list with entries starting at iterator->key. */
1745 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1746 (IterateCallBackProcPtr
)cat_readattr
, &state
);
1749 result
= state
.error
;
1750 else if (ce_list
->realentries
== 0)
1753 result
= MacToVFSError(result
);
1759 * Resolve any hard links.
1761 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
1762 struct FndrFileInfo
*fip
;
1763 struct cat_entry
*cep
;
1764 struct HFSPlusCatalogFile filerec
;
1766 cep
= &ce_list
->entry
[i
];
1767 if (!S_ISREG(cep
->ce_attr
.ca_mode
))
1770 /* Note: Finder info is still in Big Endian */
1771 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
1773 /* Check for hard link signature. */
1774 if ((cep
->ce_attr
.ca_rdev
!= 0)
1775 && (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
)
1776 && (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)
1777 && ((cep
->ce_attr
.ca_itime
== (time_t)HFSTOVCB(hfsmp
)->vcbCrDate
) ||
1778 (cep
->ce_attr
.ca_itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
1780 if (resolvelink(hfsmp
, cep
->ce_attr
.ca_rdev
, &filerec
) != 0)
1782 /* Repack entry from inode record. */
1783 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
1784 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
1785 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
1786 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
1787 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
1791 FREE(iterator
, M_TEMP
);
1793 return MacToVFSError(result
);
1796 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1799 * Callback to pack directory entries.
1800 * Called with packdirentry_state for each item in a directory.
1803 /* Hard link information collected during cat_getdirentries. */
1806 user_addr_t dirent_addr
;
1808 typedef struct linkinfo linkinfo_t
;
1810 /* State information for the cat_packdirentry callback function. */
1811 struct packdirentry_state
{
1813 u_int32_t cbs_parentID
;
1814 u_int32_t cbs_index
;
1816 ExtendedVCB
* cbs_hfsmp
;
1819 int32_t cbs_maxlinks
;
1820 linkinfo_t
* cbs_linkinfo
;
1821 struct cat_desc
* cbs_desc
;
1822 // struct dirent * cbs_stdentry;
1823 // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
1824 struct direntry
* cbs_direntry
;
1825 struct direntry
* cbs_prevdirentry
;
1826 u_int32_t cbs_previlinkref
;
1827 Boolean cbs_hasprevdirentry
;
1832 cat_packdirentry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
1833 struct packdirentry_state
*state
)
1835 struct hfsmount
*hfsmp
;
1839 struct dirent catent
;
1840 struct direntry
* entry
= NULL
;
1842 u_int32_t ilinkref
= 0;
1843 u_int32_t curlinkref
= 0;
1847 u_int8_t is_mangled
= 0;
1849 user_addr_t uiobase
;
1854 Boolean stop_after_pack
= false;
1856 hfsmp
= state
->cbs_hfsmp
;
1858 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
1859 curID
= ckp
->hfs
.parentID
;
1861 curID
= ckp
->hfsPlus
.parentID
;
1863 /* We're done when parent directory changes */
1864 if (state
->cbs_parentID
!= curID
) {
1865 if (state
->cbs_extended
) {
1866 if (state
->cbs_hasprevdirentry
) { /* the last record haven't been returned yet, so we want to stop after
1867 * packing the last item */
1868 stop_after_pack
= true;
1870 state
->cbs_result
= ENOENT
;
1871 return (0); /* stop */
1874 state
->cbs_result
= ENOENT
;
1875 return (0); /* stop */
1879 if (state
->cbs_extended
) {
1880 entry
= state
->cbs_direntry
;
1881 nameptr
= &entry
->d_name
[0];
1882 maxnamelen
= NAME_MAX
;
1884 nameptr
= &catent
.d_name
[0];
1885 maxnamelen
= NAME_MAX
;
1888 if (state
->cbs_extended
&& stop_after_pack
) {
1889 cnid
= INT_MAX
; /* the last item returns a non-zero invalid cookie */
1891 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
)) {
1892 switch(crp
->recordType
) {
1893 case kHFSPlusFolderRecord
:
1895 cnid
= crp
->hfsPlusFolder
.folderID
;
1896 /* Hide our private meta data directory */
1897 if ((curID
== kHFSRootFolderID
) &&
1898 (cnid
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1903 case kHFSPlusFileRecord
:
1904 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
1906 * When a hardlink link is encountered save its link ref.
1908 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
1909 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
1910 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
1911 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
1912 ilinkref
= crp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1914 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
1915 cnid
= crp
->hfsPlusFile
.fileID
;
1916 /* Hide the journal files */
1917 if ((curID
== kHFSRootFolderID
) &&
1918 ((hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))) &&
1919 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
1920 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
1925 return (0); /* stop */
1928 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
1929 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
1930 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
1931 if (result
== ENAMETOOLONG
) {
1932 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
1933 cnp
->ustr
.unicode
, maxnamelen
+ 1,
1934 (ByteCount
*)&namelen
, nameptr
,
1939 switch(crp
->recordType
) {
1940 case kHFSFolderRecord
:
1942 cnid
= crp
->hfsFolder
.folderID
;
1944 case kHFSFileRecord
:
1946 cnid
= crp
->hfsFile
.fileID
;
1949 return (0); /* stop */
1952 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
1953 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1,
1954 (ByteCount
*)&namelen
, nameptr
);
1956 * When an HFS name cannot be encoded with the current
1957 * volume encoding we use MacRoman as a fallback.
1960 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1,
1961 (ByteCount
*)&namelen
, nameptr
);
1965 if (state
->cbs_extended
) {
1967 * The index is 1 relative and includes "." and ".."
1969 * Also stuff the cnid in the upper 32 bits of the cookie. The cookie is stored to the previous entry, which
1970 * will be packed and copied this time
1972 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
1973 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
1974 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
1976 catent
.d_type
= type
;
1977 catent
.d_namlen
= namelen
;
1978 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
1980 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
1982 catent
.d_fileno
= cnid
;
1983 uioaddr
= (caddr_t
) &catent
;
1986 /* Save current base address for post processing of hard-links. */
1987 uiobase
= uio_curriovbase(state
->cbs_uio
);
1989 /* If this entry won't fit then we're done */
1990 if ((uiosize
> uio_resid(state
->cbs_uio
)) ||
1991 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
1992 return (0); /* stop */
1995 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
1996 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
1997 if (state
->cbs_result
== 0) {
2000 /* Remember previous entry */
2001 state
->cbs_desc
->cd_cnid
= cnid
;
2002 if (type
== DT_DIR
) {
2003 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
2005 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
2007 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
2008 vfs_removename(state
->cbs_desc
->cd_nameptr
);
2011 state
->cbs_desc
->cd_encoding
= xxxx
;
2014 state
->cbs_desc
->cd_namelen
= namelen
;
2015 state
->cbs_desc
->cd_nameptr
= vfs_addname(nameptr
, namelen
, 0, 0);
2017 /* Store unmangled name for the directory hint else it will
2018 * restart readdir at the last location again
2022 size_t tmp_namelen
= 0;
2024 cnp
= (CatalogName
*)&ckp
->hfsPlus
.nodeName
;
2025 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
2026 cnp
->ustr
.length
* sizeof(UniChar
),
2028 MALLOC(new_nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2029 result
= utf8_encodestr(cnp
->ustr
.unicode
,
2030 cnp
->ustr
.length
* sizeof(UniChar
),
2031 new_nameptr
, &tmp_namelen
,
2034 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2035 state
->cbs_desc
->cd_nameptr
= vfs_addname(new_nameptr
, tmp_namelen
, 0, 0);
2037 FREE(new_nameptr
, M_TEMP
);
2040 if (state
->cbs_hasprevdirentry
) {
2041 curlinkref
= ilinkref
; /* save current */
2042 ilinkref
= state
->cbs_previlinkref
; /* use previous */
2045 * Record any hard links for post processing.
2047 if ((ilinkref
!= 0) &&
2048 (state
->cbs_result
== 0) &&
2049 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
2050 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
2051 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
2052 state
->cbs_nlinks
++;
2054 if (state
->cbs_hasprevdirentry
) {
2055 ilinkref
= curlinkref
; /* restore current */
2059 if (state
->cbs_extended
) { /* fill the direntry to be used the next time */
2060 if (stop_after_pack
) {
2061 state
->cbs_eof
= true;
2062 return (0); /* stop */
2064 entry
->d_type
= type
;
2065 entry
->d_namlen
= namelen
;
2066 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
2068 entry
->d_fileno
= 0; /* file number = 0 means skip entry */
2070 entry
->d_fileno
= cnid
;
2071 /* swap the current and previous entry */
2072 struct direntry
* tmp
;
2073 tmp
= state
->cbs_direntry
;
2074 state
->cbs_direntry
= state
->cbs_prevdirentry
;
2075 state
->cbs_prevdirentry
= tmp
;
2076 state
->cbs_hasprevdirentry
= true;
2077 state
->cbs_previlinkref
= ilinkref
;
2080 /* Continue iteration if there's room */
2081 return (state
->cbs_result
== 0 &&
2082 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2087 * Pack a uio buffer with directory entries from the catalog
2091 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
2092 uio_t uio
, int extended
, int * items
, int * eofflag
)
2095 BTreeIterator
* iterator
;
2097 struct packdirentry_state state
;
2105 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
2108 * Get a buffer for link info array, btree iterator and a direntry:
2110 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
2111 bufsize
= (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
2113 bufsize
+= 2*sizeof(struct direntry
);
2115 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
2116 bzero(buffer
, bufsize
);
2118 state
.cbs_extended
= extended
;
2119 state
.cbs_hasprevdirentry
= false;
2120 state
.cbs_previlinkref
= 0;
2121 state
.cbs_nlinks
= 0;
2122 state
.cbs_maxlinks
= maxlinks
;
2123 state
.cbs_linkinfo
= (linkinfo_t
*) buffer
;
2125 iterator
= (BTreeIterator
*) ((char *)buffer
+ (maxlinks
* sizeof(linkinfo_t
)));
2126 key
= (CatalogKey
*)&iterator
->key
;
2128 index
= dirhint
->dh_index
+ 1;
2130 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
2131 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
2132 state
.cbs_eof
= false;
2135 * Attempt to build a key from cached filename
2137 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2138 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2144 * If the last entry wasn't cached then position the btree iterator
2146 if ((index
== 0) || !have_key
) {
2148 * Position the iterator at the directory's thread record.
2149 * (i.e. just before the first entry)
2151 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2152 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2154 result
= MacToVFSError(result
);
2159 * Iterate until we reach the entry just
2160 * before the one we want to start with.
2163 struct position_state ps
;
2168 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2171 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2172 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2176 result
= MacToVFSError(result
);
2178 result
= MacToVFSError(result
);
2184 state
.cbs_index
= index
;
2185 state
.cbs_hfsmp
= hfsmp
;
2186 state
.cbs_uio
= uio
;
2187 state
.cbs_desc
= &dirhint
->dh_desc
;
2188 state
.cbs_result
= 0;
2189 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2191 enum BTreeIterationOperations op
;
2192 if (extended
&& index
!= 0 && have_key
)
2193 op
= kBTreeCurrentRecord
;
2195 op
= kBTreeNextRecord
;
2198 * Process as many entries as possible starting at iterator->key.
2200 result
= BTIterateRecords(fcb
, op
, iterator
,
2201 (IterateCallBackProcPtr
)cat_packdirentry
, &state
);
2203 /* Note that state.cbs_index is still valid on errors */
2204 *items
= state
.cbs_index
- index
;
2205 index
= state
.cbs_index
;
2207 if (state
.cbs_eof
) {
2211 /* Finish updating the catalog iterator. */
2212 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
2213 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
2214 dirhint
->dh_index
= index
- 1;
2217 * Post process any hard links to get the real file id.
2219 if (state
.cbs_nlinks
> 0) {
2220 u_int32_t fileid
= 0;
2221 user_addr_t address
;
2224 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
2225 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
2227 /* This assumes that d_ino is always first field. */
2228 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
2229 if (address
== (user_addr_t
)0)
2231 if (uio_isuserspace(uio
)) {
2232 (void) copyout(&fileid
, address
,
2233 extended
? sizeof(ino64_t
) : sizeof(ino_t
));
2234 } else /* system space */ {
2235 ino64_t
*inoptr
= (ino64_t
*)CAST_DOWN(caddr_t
, address
);
2241 if (state
.cbs_result
)
2242 result
= state
.cbs_result
;
2244 result
= MacToVFSError(result
);
2246 if (result
== ENOENT
) {
2251 FREE(buffer
, M_TEMP
);
2258 * Callback to establish directory position.
2259 * Called with position_state for each item in a directory.
2262 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2263 struct position_state
*state
)
2267 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
2268 curID
= ckp
->hfs
.parentID
;
2270 curID
= ckp
->hfsPlus
.parentID
;
2272 /* Make sure parent directory didn't change */
2273 if (state
->parentID
!= curID
) {
2274 state
->error
= EINVAL
;
2275 return (0); /* stop */
2278 /* Count this entry */
2279 switch(crp
->recordType
) {
2280 case kHFSPlusFolderRecord
:
2281 case kHFSPlusFileRecord
:
2282 case kHFSFolderRecord
:
2283 case kHFSFileRecord
:
2287 printf("cat_findposition: invalid record type %d in dir %d\n",
2288 crp
->recordType
, curID
);
2289 state
->error
= EINVAL
;
2290 return (0); /* stop */
2293 return (state
->count
< state
->index
);
2298 * cat_binarykeycompare - compare two HFS Plus catalog keys.
2300 * The name portion of the key is compared using a 16-bit binary comparison.
2301 * This is called from the b-tree code.
2305 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
2307 u_int32_t searchParentID
, trialParentID
;
2310 searchParentID
= searchKey
->parentID
;
2311 trialParentID
= trialKey
->parentID
;
2314 if (searchParentID
> trialParentID
) {
2316 } else if (searchParentID
< trialParentID
) {
2319 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
2320 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
2321 int length1
= searchKey
->nodeName
.length
;
2322 int length2
= trialKey
->nodeName
.length
;
2326 if (length1
< length2
) {
2329 } else if (length1
> length2
) {
2356 * Compare two standard HFS catalog keys
2358 * Result: +n search key > trial key
2359 * 0 search key = trial key
2360 * -n search key < trial key
2363 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
2365 cnid_t searchParentID
, trialParentID
;
2368 searchParentID
= searchKey
->parentID
;
2369 trialParentID
= trialKey
->parentID
;
2371 if (searchParentID
> trialParentID
)
2373 else if (searchParentID
< trialParentID
)
2375 else /* parent dirID's are equal, compare names */
2376 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
2383 * Compare two HFS+ catalog keys
2385 * Result: +n search key > trial key
2386 * 0 search key = trial key
2387 * -n search key < trial key
2390 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
2392 cnid_t searchParentID
, trialParentID
;
2395 searchParentID
= searchKey
->parentID
;
2396 trialParentID
= trialKey
->parentID
;
2398 if (searchParentID
> trialParentID
) {
2401 else if (searchParentID
< trialParentID
) {
2404 /* parent node ID's are equal, compare names */
2405 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
2406 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
2408 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
2409 searchKey
->nodeName
.length
,
2410 &trialKey
->nodeName
.unicode
[0],
2411 trialKey
->nodeName
.length
);
2419 * buildkey - build a Catalog b-tree key from a cnode descriptor
2422 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
2423 HFSPlusCatalogKey
*key
, int retry
)
2427 size_t unicodeBytes
= 0;
2429 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
2430 return (EINVAL
); /* invalid name */
2432 key
->parentID
= descp
->cd_parentcnid
;
2433 key
->nodeName
.length
= 0;
2435 * Convert filename from UTF-8 into Unicode
2438 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
2439 utf8_flags
|= UTF_DECOMPOSED
;
2440 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
2441 key
->nodeName
.unicode
, &unicodeBytes
,
2442 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
2443 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
2444 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
2446 if (result
!= ENAMETOOLONG
)
2447 result
= EINVAL
; /* name has invalid characters */
2452 * For HFS volumes convert to an HFS compatible key
2454 * XXX need to save the encoding that succeeded
2456 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
2457 HFSCatalogKey hfskey
;
2459 bzero(&hfskey
, sizeof(hfskey
));
2460 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
2461 hfskey
.parentID
= key
->parentID
;
2462 hfskey
.nodeName
[0] = 0;
2463 if (key
->nodeName
.length
> 0) {
2464 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
2465 key
->nodeName
.length
* 2,
2466 key
->nodeName
.unicode
,
2467 &hfskey
.nodeName
[0], retry
) != 0) {
2470 hfskey
.keyLength
+= hfskey
.nodeName
[0];
2472 bcopy(&hfskey
, key
, sizeof(hfskey
));
2479 * Resolve hard link reference to obtain the inode record.
2483 resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
)
2485 FSBufferDescriptor btdata
;
2486 struct BTreeIterator
*iterator
;
2487 struct cat_desc idesc
;
2491 BDINIT(btdata
, recp
);
2492 MAKE_INODE_NAME(inodename
, linkref
);
2494 /* Get space for iterator */
2495 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2496 bzero(iterator
, sizeof(*iterator
));
2498 /* Build a descriptor for private dir. */
2499 idesc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
2500 idesc
.cd_nameptr
= inodename
;
2501 idesc
.cd_namelen
= strlen(inodename
);
2504 idesc
.cd_encoding
= 0;
2505 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
2507 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2508 &btdata
, NULL
, NULL
);
2511 /* Make sure there's a reference */
2512 if (recp
->bsdInfo
.special
.linkCount
== 0)
2513 recp
->bsdInfo
.special
.linkCount
= 2;
2515 printf("HFS resolvelink: can't find %s\n", inodename
);
2518 FREE(iterator
, M_TEMP
);
2520 return (result
? ENOENT
: 0);
2524 * Resolve hard link reference to obtain the inode number.
2527 resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
)
2529 struct HFSPlusCatalogFile record
;
2532 error
= resolvelink(hfsmp
, linkref
, &record
);
2534 if (record
.fileID
== 0)
2537 *ino
= record
.fileID
;
2543 * getkey - get a key from id by doing a thread lookup
2546 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
2548 struct BTreeIterator
* iterator
;
2549 FSBufferDescriptor btdata
;
2552 CatalogRecord
* recp
;
2556 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2558 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2559 bzero(iterator
, sizeof(*iterator
));
2560 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
2562 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
2563 BDINIT(btdata
, recp
);
2565 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2566 &btdata
, &datasize
, iterator
);
2570 /* Turn thread record into a cnode key (in place) */
2571 switch (recp
->recordType
) {
2572 case kHFSFileThreadRecord
:
2573 case kHFSFolderThreadRecord
:
2574 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
2575 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
2576 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
2579 case kHFSPlusFileThreadRecord
:
2580 case kHFSPlusFolderThreadRecord
:
2581 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
2582 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
2583 (keyp
->hfsPlus
.nodeName
.length
* 2);
2584 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
2593 FREE(iterator
, M_TEMP
);
2596 return MacToVFSError(result
);
2600 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
2601 * null arguments to cat_idlookup instead, but we save around 10% by not building the
2602 * cat_desc here). Both key and attrp must point to real structures.
2606 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
2610 result
= getkey(hfsmp
, cnid
, key
);
2613 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, NULL
, attrp
, NULL
, NULL
);
2616 return MacToVFSError(result
);
2621 * buildrecord - build a default catalog directory or file record
2624 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
2625 CatalogRecord
*crp
, int *recordSize
)
2627 int type
= attrp
->ca_mode
& S_IFMT
;
2628 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
2631 createtime
= UTCToLocal(createtime
);
2632 if (type
== S_IFDIR
) {
2633 bzero(crp
, sizeof(HFSCatalogFolder
));
2634 crp
->recordType
= kHFSFolderRecord
;
2635 crp
->hfsFolder
.folderID
= cnid
;
2636 crp
->hfsFolder
.createDate
= createtime
;
2637 crp
->hfsFolder
.modifyDate
= createtime
;
2638 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
2639 *recordSize
= sizeof(HFSCatalogFolder
);
2641 bzero(crp
, sizeof(HFSCatalogFile
));
2642 crp
->recordType
= kHFSFileRecord
;
2643 crp
->hfsFile
.fileID
= cnid
;
2644 crp
->hfsFile
.createDate
= createtime
;
2645 crp
->hfsFile
.modifyDate
= createtime
;
2646 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
2647 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
2648 *recordSize
= sizeof(HFSCatalogFile
);
2651 struct HFSPlusBSDInfo
* bsdp
= NULL
;
2652 struct FndrFileInfo
* fip
= NULL
;
2654 if (type
== S_IFDIR
) {
2655 crp
->recordType
= kHFSPlusFolderRecord
;
2656 crp
->hfsPlusFolder
.flags
= 0;
2657 crp
->hfsPlusFolder
.valence
= 0;
2658 crp
->hfsPlusFolder
.folderID
= cnid
;
2659 crp
->hfsPlusFolder
.createDate
= createtime
;
2660 crp
->hfsPlusFolder
.contentModDate
= createtime
;
2661 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
2662 crp
->hfsPlusFolder
.accessDate
= createtime
;
2663 crp
->hfsPlusFolder
.backupDate
= 0;
2664 crp
->hfsPlusFolder
.textEncoding
= encoding
;
2665 crp
->hfsPlusFolder
.attrBlocks
= 0;
2666 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
2667 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
2668 bsdp
->special
.rawDevice
= 0;
2669 *recordSize
= sizeof(HFSPlusCatalogFolder
);
2671 crp
->recordType
= kHFSPlusFileRecord
;
2672 crp
->hfsPlusFile
.flags
= kHFSThreadExistsMask
;
2673 crp
->hfsPlusFile
.reserved1
= 0;
2674 crp
->hfsPlusFile
.fileID
= cnid
;
2675 crp
->hfsPlusFile
.createDate
= createtime
;
2676 crp
->hfsPlusFile
.contentModDate
= createtime
;
2677 crp
->hfsPlusFile
.accessDate
= createtime
;
2678 crp
->hfsPlusFile
.attributeModDate
= createtime
;
2679 crp
->hfsPlusFile
.backupDate
= 0;
2680 crp
->hfsPlusFile
.textEncoding
= encoding
;
2681 crp
->hfsPlusFile
.attrBlocks
= 0;
2682 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
2683 bsdp
->special
.rawDevice
= 0;
2687 /* BLK/CHR need to save the device info */
2688 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
2689 bzero(&crp
->hfsPlusFile
.userInfo
, 32);
2692 /* Hardlink links need to save the linkref */
2693 fip
= (FndrFileInfo
*)&attrp
->ca_finderinfo
;
2694 if ((SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2695 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2696 bsdp
->special
.iNodeNum
= attrp
->ca_rdev
;
2698 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2701 /* Symlinks also have a type and creator */
2702 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2705 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
2706 *recordSize
= sizeof(HFSPlusCatalogFile
);
2708 bsdp
->ownerID
= attrp
->ca_uid
;
2709 bsdp
->groupID
= attrp
->ca_gid
;
2710 bsdp
->fileMode
= attrp
->ca_mode
;
2711 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
2712 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
2718 * builddesc - build a cnode descriptor from an HFS+ key
2721 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
2722 int isdir
, struct cat_desc
*descp
)
2730 /* guess a size... */
2731 bufsize
= (3 * key
->nodeName
.length
) + 1;
2732 if (bufsize
>= sizeof(tmpbuff
) - 1) {
2733 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2735 nameptr
= &tmpbuff
[0];
2738 result
= utf8_encodestr(key
->nodeName
.unicode
,
2739 key
->nodeName
.length
* sizeof(UniChar
),
2740 nameptr
, (size_t *)&utf8len
,
2743 if (result
== ENAMETOOLONG
) {
2744 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
2745 key
->nodeName
.length
* sizeof(UniChar
),
2747 FREE(nameptr
, M_TEMP
);
2748 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2750 result
= utf8_encodestr(key
->nodeName
.unicode
,
2751 key
->nodeName
.length
* sizeof(UniChar
),
2752 nameptr
, (size_t *)&utf8len
,
2755 descp
->cd_parentcnid
= key
->parentID
;
2756 descp
->cd_nameptr
= vfs_addname(nameptr
, utf8len
, 0, 0);
2757 descp
->cd_namelen
= utf8len
;
2758 descp
->cd_cnid
= cnid
;
2759 descp
->cd_hint
= hint
;
2760 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
2762 descp
->cd_flags
|= CD_ISDIR
;
2763 descp
->cd_encoding
= encoding
;
2764 if (nameptr
!= &tmpbuff
[0]) {
2765 FREE(nameptr
, M_TEMP
);
2772 * getbsdattr - get attributes in bsd format
2776 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
2778 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
2779 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
2781 attrp
->ca_recflags
= crp
->flags
;
2782 attrp
->ca_nlink
= 1;
2783 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
2784 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
2785 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
2786 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
2787 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
2788 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
2790 if ((bsd
->fileMode
& S_IFMT
) == 0) {
2791 attrp
->ca_flags
= 0;
2792 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2793 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2795 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
2797 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
2801 attrp
->ca_uid
= bsd
->ownerID
;
2802 attrp
->ca_gid
= bsd
->groupID
;
2803 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
2804 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
2805 switch (attrp
->ca_mode
& S_IFMT
) {
2806 case S_IFCHR
: /* fall through */
2808 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
2811 /* Pick up the hard link count */
2812 if (bsd
->special
.linkCount
> 0)
2813 attrp
->ca_nlink
= bsd
->special
.linkCount
;
2817 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) {
2819 * Override the permissions as determined by the mount auguments
2820 * in ALMOST the same way unset permissions are treated but keep
2821 * track of whether or not the file or folder is hfs locked
2822 * by leaving the h_pflags field unchanged from what was unpacked
2823 * out of the catalog.
2825 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2826 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2831 if (!S_ISDIR(attrp
->ca_mode
)) {
2832 attrp
->ca_mode
&= ~S_IFMT
;
2833 attrp
->ca_mode
|= S_IFDIR
;
2835 attrp
->ca_nlink
= 2 + ((HFSPlusCatalogFolder
*)crp
)->valence
;
2836 attrp
->ca_entries
= ((HFSPlusCatalogFolder
*)crp
)->valence
;
2837 attrp
->ca_attrblks
= ((HFSPlusCatalogFolder
*)crp
)->attrBlocks
;
2839 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2840 if (crp
->flags
& kHFSFileLockedMask
) {
2841 /* The file's supposed to be locked:
2842 Make sure at least one of the IMMUTABLE bits is set: */
2843 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
2844 attrp
->ca_flags
|= UF_IMMUTABLE
;
2846 /* The file's supposed to be unlocked: */
2847 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
2849 /* get total blocks (both forks) */
2850 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
2851 attrp
->ca_attrblks
= crp
->attrBlocks
;
2852 /* On HFS+ the ThreadExists flag must always be set. */
2853 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
2854 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
2857 attrp
->ca_fileid
= crp
->fileID
;
2859 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
2863 * promotekey - promote hfs key to hfs plus key
2867 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
2868 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
2870 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
2874 *encoding
= hfsmp
->hfs_encoding
;
2876 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
2877 kHFSPlusMaxFileNameChars
, &uniCount
);
2879 * When an HFS name cannot be encoded with the current
2880 * encoding use MacRoman as a fallback.
2882 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
2884 (void) mac_roman_to_unicode(hfskey
->nodeName
,
2885 keyp
->nodeName
.unicode
,
2886 kHFSPlusMaxFileNameChars
,
2890 keyp
->nodeName
.length
= uniCount
;
2891 keyp
->parentID
= hfskey
->parentID
;
2895 * promotefork - promote hfs fork info to hfs plus
2899 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
2900 int resource
, struct cat_fork
* forkp
)
2902 struct HFSPlusExtentDescriptor
*xp
;
2903 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2905 bzero(forkp
, sizeof(*forkp
));
2906 xp
= &forkp
->cf_extents
[0];
2908 forkp
->cf_size
= filep
->rsrcLogicalSize
;
2909 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
2910 forkp
->cf_bytesread
= 0;
2911 forkp
->cf_vblocks
= 0;
2912 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
2913 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
2914 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
2915 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
2916 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
2917 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
2919 forkp
->cf_size
= filep
->dataLogicalSize
;
2920 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
2921 forkp
->cf_bytesread
= 0;
2922 forkp
->cf_vblocks
= 0;
2923 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
2924 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
2925 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
2926 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
2927 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
2928 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
2933 * promoteattr - promote hfs catalog attributes to hfs plus
2937 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
2939 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2941 if (dataPtr
->recordType
== kHFSFolderRecord
) {
2942 struct HFSCatalogFolder
* folder
;
2944 folder
= (struct HFSCatalogFolder
*) dataPtr
;
2945 crp
->recordType
= kHFSPlusFolderRecord
;
2946 crp
->flags
= folder
->flags
;
2947 crp
->fileID
= folder
->folderID
;
2948 crp
->createDate
= LocalToUTC(folder
->createDate
);
2949 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
2950 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
2951 crp
->reserved1
= folder
->valence
;
2952 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
2954 struct HFSCatalogFile
* file
;
2956 file
= (struct HFSCatalogFile
*) dataPtr
;
2957 crp
->recordType
= kHFSPlusFileRecord
;
2958 crp
->flags
= file
->flags
;
2959 crp
->fileID
= file
->fileID
;
2960 crp
->createDate
= LocalToUTC(file
->createDate
);
2961 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
2962 crp
->backupDate
= LocalToUTC(file
->backupDate
);
2964 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
2965 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
2966 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
2967 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
2969 crp
->textEncoding
= 0;
2970 crp
->attributeModDate
= crp
->contentModDate
;
2971 crp
->accessDate
= crp
->contentModDate
;
2972 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
2973 crp
->attrBlocks
= 0;
2977 * Build a catalog node thread record from a catalog key
2978 * and return the size of the record.
2981 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
2986 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
2987 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
2989 size
= sizeof(HFSCatalogThread
);
2992 rec
->recordType
= kHFSFolderThreadRecord
;
2994 rec
->recordType
= kHFSFileThreadRecord
;
2995 rec
->parentID
= key
->parentID
;
2996 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
2999 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
3000 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
3002 size
= sizeof(HFSPlusCatalogThread
);
3004 rec
->recordType
= kHFSPlusFolderThreadRecord
;
3006 rec
->recordType
= kHFSPlusFileThreadRecord
;
3008 rec
->parentID
= key
->parentID
;
3009 bcopy(&key
->nodeName
, &rec
->nodeName
,
3010 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
3012 /* HFS Plus has varaible sized thread records */
3013 size
-= (sizeof(rec
->nodeName
.unicode
) -
3014 (rec
->nodeName
.length
* sizeof(UniChar
)));
3021 * Build a catalog node thread key.
3024 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
3027 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
3028 key
->hfs
.reserved
= 0;
3029 key
->hfs
.parentID
= parentID
;
3030 key
->hfs
.nodeName
[0] = 0;
3032 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
3033 key
->hfsPlus
.parentID
= parentID
;
3034 key
->hfsPlus
.nodeName
.length
= 0;
3039 * Extract the text encoding from a catalog node record.
3042 getencoding(const CatalogRecord
*crp
)
3046 if (crp
->recordType
== kHFSPlusFolderRecord
)
3047 encoding
= crp
->hfsPlusFolder
.textEncoding
;
3048 else if (crp
->recordType
== kHFSPlusFileRecord
)
3049 encoding
= crp
->hfsPlusFile
.textEncoding
;
3057 * Extract the CNID from a catalog node record.
3060 getcnid(const CatalogRecord
*crp
)
3064 switch (crp
->recordType
) {
3065 case kHFSFolderRecord
:
3066 cnid
= crp
->hfsFolder
.folderID
;
3068 case kHFSFileRecord
:
3069 cnid
= crp
->hfsFile
.fileID
;
3071 case kHFSPlusFolderRecord
:
3072 cnid
= crp
->hfsPlusFolder
.folderID
;
3074 case kHFSPlusFileRecord
:
3075 cnid
= crp
->hfsPlusFile
.fileID
;
3078 printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp
);
3086 * Extract the parent ID from a catalog node record.
3089 getparentcnid(const CatalogRecord
*recp
)
3093 switch (recp
->recordType
) {
3094 case kHFSFileThreadRecord
:
3095 case kHFSFolderThreadRecord
:
3096 cnid
= recp
->hfsThread
.parentID
;
3099 case kHFSPlusFileThreadRecord
:
3100 case kHFSPlusFolderThreadRecord
:
3101 cnid
= recp
->hfsPlusThread
.parentID
;
3104 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp
);
3112 * Determine if a catalog node record is a directory.
3115 isadir(const CatalogRecord
*crp
)
3117 return (crp
->recordType
== kHFSFolderRecord
||
3118 crp
->recordType
== kHFSPlusFolderRecord
);