2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/systm.h>
25 #include <sys/kernel.h>
26 #include <sys/malloc.h>
28 #include <sys/mount.h>
29 #include <sys/vnode.h>
30 #include <sys/dirent.h>
31 #include <vfs/vfs_support.h>
32 #include <libkern/libkern.h>
34 #include <sys/utfconv.h>
37 #include "hfs_catalog.h"
38 #include "hfs_format.h"
39 #include "hfs_endian.h"
41 #include "hfscommon/headers/BTreesInternal.h"
42 #include "hfscommon/headers/HFSUnicodeWrappers.h"
46 * Initialization of an FSBufferDescriptor structure.
48 #define BDINIT(bd, addr) { \
49 (bd).bufferAddress = (addr); \
50 (bd).itemSize = sizeof(*(addr)); \
56 BTreeIterator iterator
;
57 HFSPlusCatalogKey key
;
62 struct cat_desc
* s_desc
;
63 struct cat_attr
* s_attr
;
64 struct cat_fork
* s_datafork
;
65 struct cat_fork
* s_rsrcfork
;
66 struct hfsmount
* s_hfsmp
;
69 struct position_state
{
74 struct hfsmount
*hfsmp
;
77 /* Map file mode type to directory entry types */
78 u_char modetodirtype
[16] = {
79 DT_REG
, DT_FIFO
, DT_CHR
, DT_UNKNOWN
,
80 DT_DIR
, DT_UNKNOWN
, DT_BLK
, DT_UNKNOWN
,
81 DT_REG
, DT_UNKNOWN
, DT_LNK
, DT_UNKNOWN
,
82 DT_SOCK
, DT_UNKNOWN
, DT_WHT
, DT_UNKNOWN
84 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
87 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
88 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
);
90 static int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
91 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
93 extern int mac_roman_to_unicode(const Str31 hfs_str
, UniChar
*uni_str
,
94 UInt32 maxCharLen
, UInt32
*unicodeChars
);
96 extern int unicode_to_hfs(ExtendedVCB
*vcb
, ByteCount srcLen
,
97 const u_int16_t
* srcStr
, Str31 dstStr
, int retry
);
100 /* Internal catalog support routines */
102 static int cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
103 struct position_state
*state
);
105 static int resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
);
107 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
109 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
110 HFSPlusCatalogKey
*key
, int retry
);
112 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
114 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, int *recordSize
);
116 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
);
118 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
119 int isdir
, struct cat_desc
*descp
);
121 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
123 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_long
*encoding
);
124 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
125 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
127 static cnid_t
getcnid(const CatalogRecord
*crp
);
128 static u_long
getencoding(const CatalogRecord
*crp
);
129 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
131 static int isadir(const CatalogRecord
*crp
);
133 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
138 cat_preflight(struct hfsmount
*hfsmp
, catops_t ops
, cat_cookie_t
*cookie
, struct proc
*p
)
144 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
146 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
148 result
= BTReserveSpace(fcb
, ops
, (void*)cookie
);
150 hfs_systemfile_unlock(hfsmp
, lockflags
);
152 return MacToVFSError(result
);
157 cat_postflight(struct hfsmount
*hfsmp
, cat_cookie_t
*cookie
, struct proc
*p
)
162 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
164 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
166 (void) BTReleaseReserve(fcb
, (void*)cookie
);
168 hfs_systemfile_unlock(hfsmp
, lockflags
);
175 struct hfsmount
*hfsmp
,
176 CatalogRecord
* recp
,
177 struct cat_attr
*attrp
,
178 struct cat_fork
*datafp
,
179 struct cat_fork
*rsrcfp
)
181 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
184 struct HFSPlusCatalogFile cnoderec
;
186 promoteattr(hfsmp
, recp
, &cnoderec
);
187 getbsdattr(hfsmp
, &cnoderec
, attrp
);
189 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
193 bzero(datafp
, sizeof(*datafp
));
195 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
196 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
198 /* Convert the data fork. */
199 datafp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
200 datafp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
201 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
202 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
203 datafp
->cf_bytesread
=
204 recp
->hfsPlusFile
.dataFork
.clumpSize
*
205 HFSTOVCB(hfsmp
)->blockSize
;
207 datafp
->cf_bytesread
= 0;
209 datafp
->cf_vblocks
= 0;
210 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
211 &datafp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
213 /* Convert the resource fork. */
214 rsrcfp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
215 rsrcfp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
216 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
217 (attrp
->ca_atime
>= hfsmp
->hfc_timebase
)) {
218 datafp
->cf_bytesread
=
219 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
220 HFSTOVCB(hfsmp
)->blockSize
;
222 datafp
->cf_bytesread
= 0;
224 rsrcfp
->cf_vblocks
= 0;
225 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
226 &rsrcfp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
233 struct hfsmount
*hfsmp
,
235 CatalogRecord
* recp
,
236 struct cat_desc
*descp
)
238 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
239 HFSPlusCatalogKey
* pluskey
= NULL
;
243 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
244 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
247 pluskey
= (HFSPlusCatalogKey
*)key
;
248 encoding
= getencoding(recp
);
251 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
253 FREE(pluskey
, M_TEMP
);
264 cat_releasedesc(struct cat_desc
*descp
)
271 if ((descp
->cd_flags
& CD_HASBUF
) &&
272 (descp
->cd_nameptr
!= NULL
)) {
273 name
= descp
->cd_nameptr
;
274 descp
->cd_nameptr
= NULL
;
275 descp
->cd_namelen
= 0;
276 descp
->cd_flags
&= ~CD_HASBUF
;
277 vfs_removename(name
);
279 descp
->cd_nameptr
= NULL
;
280 descp
->cd_namelen
= 0;
284 * These Catalog functions allow access to the HFS Catalog (database).
285 * The catalog b-tree lock must be aquired before calling any of these routines.
289 * cat_lookup - lookup a catalog node using a cnode decriptor
293 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
294 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
295 struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
301 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
303 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
305 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
309 result
= cat_lookupbykey(hfsmp
, keyp
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
, desc_cnid
);
311 if (result
== ENOENT
) {
313 struct cat_desc temp_desc
;
314 if (outdescp
== NULL
) {
315 bzero(&temp_desc
, sizeof(temp_desc
));
316 outdescp
= &temp_desc
;
318 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
320 *desc_cnid
= outdescp
->cd_cnid
;
322 if (outdescp
== &temp_desc
) {
323 /* Release the local copy of desc */
324 cat_releasedesc(outdescp
);
326 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
327 // make MacRoman key from utf-8
328 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
329 // update desc text encoding so that other catalog ops succeed
340 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
342 struct BTreeIterator
*iterator
;
343 struct FSBufferDescriptor file_data
;
344 struct HFSCatalogFile file_rec
;
349 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
352 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
354 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
355 bzero(&iterator
[0], 2* sizeof(*iterator
));
356 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
360 BDINIT(file_data
, &file_rec
);
361 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
365 if (file_rec
.recordType
!= kHFSFileRecord
) {
370 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
371 struct FSBufferDescriptor thread_data
;
372 struct HFSCatalogThread thread_rec
;
374 file_rec
.flags
|= kHFSThreadExistsMask
;
375 BDINIT(thread_data
, &thread_rec
);
376 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
377 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
379 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
383 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
384 (void) BTFlushPath(fcb
);
387 (void) BTFlushPath(fcb
);
388 FREE(iterator
, M_TEMP
);
390 return MacToVFSError(result
);
395 * cat_findname - obtain a descriptor from cnid
397 * Only a thread lookup is performed.
401 cat_findname(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
)
403 struct BTreeIterator
* iterator
;
404 FSBufferDescriptor btdata
;
406 CatalogRecord
* recp
;
412 std_hfs
= (hfsmp
->hfs_flags
& HFS_STANDARD
);
414 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
415 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
416 iterator
->hint
.nodeNum
= 0;
418 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
419 BDINIT(btdata
, recp
);
421 result
= BTSearchRecord(VTOF(hfsmp
->hfs_catalog_vp
), iterator
, &btdata
, NULL
, NULL
);
425 /* Turn thread record into a cnode key (in place). */
426 switch (recp
->recordType
) {
427 case kHFSFolderThreadRecord
:
430 case kHFSFileThreadRecord
:
431 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
432 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
435 case kHFSPlusFolderThreadRecord
:
438 case kHFSPlusFileThreadRecord
:
439 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
440 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
441 (keyp
->hfsPlus
.nodeName
.length
* 2);
448 HFSPlusCatalogKey
* pluskey
= NULL
;
451 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
452 promotekey(hfsmp
, &keyp
->hfs
, pluskey
, &encoding
);
453 builddesc(pluskey
, cnid
, 0, encoding
, isdir
, outdescp
);
454 FREE(pluskey
, M_TEMP
);
457 builddesc((HFSPlusCatalogKey
*)keyp
, cnid
, 0, 0, isdir
, outdescp
);
461 FREE(iterator
, M_TEMP
);
463 return MacToVFSError(result
);
467 * cat_idlookup - lookup a catalog node using a cnode id
471 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
,
472 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
474 struct BTreeIterator
* iterator
;
475 FSBufferDescriptor btdata
;
478 CatalogRecord
* recp
;
482 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
484 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
485 bzero(iterator
, sizeof(*iterator
));
486 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
488 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
489 BDINIT(btdata
, recp
);
491 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
492 &btdata
, &datasize
, iterator
);
496 /* Turn thread record into a cnode key (in place) */
497 switch (recp
->recordType
) {
498 case kHFSFileThreadRecord
:
499 case kHFSFolderThreadRecord
:
500 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
501 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
504 case kHFSPlusFileThreadRecord
:
505 case kHFSPlusFolderThreadRecord
:
506 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
507 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
508 (keyp
->hfsPlus
.nodeName
.length
* 2);
516 result
= cat_lookupbykey(hfsmp
, keyp
, 0, 0, outdescp
, attrp
, forkp
, NULL
);
519 FREE(iterator
, M_TEMP
);
521 return MacToVFSError(result
);
526 * cat_lookupmangled - lookup a catalog node using a mangled name
529 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
530 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
539 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
540 if (fileID
< kHFSFirstUserCatalogNodeID
)
543 result
= cat_idlookup(hfsmp
, fileID
, outdescp
, attrp
, forkp
);
547 /* It must be in the correct directory */
548 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
551 if ((outdescp
->cd_namelen
< prefixlen
) ||
552 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
558 cat_releasedesc(outdescp
);
564 * cat_lookupbykey - lookup a catalog node using a cnode key
567 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
568 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
, cnid_t
*desc_cnid
)
570 struct BTreeIterator
* iterator
;
571 FSBufferDescriptor btdata
;
572 CatalogRecord
* recp
;
580 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
582 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
583 BDINIT(btdata
, recp
);
584 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
585 bzero(iterator
, sizeof(*iterator
));
586 iterator
->hint
.nodeNum
= hint
;
587 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
589 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
590 &btdata
, &datasize
, iterator
);
594 /* Save the cnid now in case there's a hard link */
595 cnid
= getcnid(recp
);
596 encoding
= getencoding(recp
);
597 hint
= iterator
->hint
.nodeNum
;
599 /* Hide the journal files (if any) */
601 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
602 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
609 * When a hardlink link is encountered, auto resolve it
613 && (recp
->recordType
== kHFSPlusFileRecord
)
614 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
615 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)
616 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)HFSTOVCB(hfsmp
)->vcbCrDate
) ||
617 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == (time_t)hfsmp
->hfs_metadata_createdate
))) {
619 ilink
= recp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
621 (void) resolvelink(hfsmp
, ilink
, (struct HFSPlusCatalogFile
*)recp
);
626 struct HFSPlusCatalogFile cnoderec
;
628 promoteattr(hfsmp
, recp
, &cnoderec
);
629 getbsdattr(hfsmp
, &cnoderec
, attrp
);
631 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
633 attrp
->ca_rdev
= ilink
;
638 bzero(forkp
, sizeof(*forkp
));
639 } else if (std_hfs
) {
640 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
641 } else if (wantrsrc
) {
642 /* Convert the resource fork. */
643 forkp
->cf_size
= recp
->hfsPlusFile
.resourceFork
.logicalSize
;
644 forkp
->cf_blocks
= recp
->hfsPlusFile
.resourceFork
.totalBlocks
;
645 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
646 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
647 forkp
->cf_bytesread
=
648 recp
->hfsPlusFile
.resourceFork
.clumpSize
*
649 HFSTOVCB(hfsmp
)->blockSize
;
651 forkp
->cf_bytesread
= 0;
653 forkp
->cf_vblocks
= 0;
654 bcopy(&recp
->hfsPlusFile
.resourceFork
.extents
[0],
655 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
657 /* Convert the data fork. */
658 forkp
->cf_size
= recp
->hfsPlusFile
.dataFork
.logicalSize
;
659 forkp
->cf_blocks
= recp
->hfsPlusFile
.dataFork
.totalBlocks
;
660 if ((hfsmp
->hfc_stage
== HFC_RECORDING
) &&
661 (to_bsd_time(recp
->hfsPlusFile
.accessDate
) >= hfsmp
->hfc_timebase
)) {
662 forkp
->cf_bytesread
=
663 recp
->hfsPlusFile
.dataFork
.clumpSize
*
664 HFSTOVCB(hfsmp
)->blockSize
;
666 forkp
->cf_bytesread
= 0;
668 forkp
->cf_vblocks
= 0;
669 bcopy(&recp
->hfsPlusFile
.dataFork
.extents
[0],
670 &forkp
->cf_extents
[0], sizeof(HFSPlusExtentRecord
));
674 HFSPlusCatalogKey
* pluskey
= NULL
;
677 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
678 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
681 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
683 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
685 FREE(pluskey
, M_TEMP
);
689 if (desc_cnid
!= NULL
) {
693 FREE(iterator
, M_TEMP
);
696 return MacToVFSError(result
);
701 * cat_create - create a node in the catalog
705 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
706 struct cat_desc
*out_descp
)
711 FSBufferDescriptor btdata
;
720 modeformat
= attrp
->ca_mode
& S_IFMT
;
722 vcb
= HFSTOVCB(hfsmp
);
723 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
724 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
727 * Atomically get the next CNID. If we have wrapped the CNIDs
728 * then keep the hfsmp lock held until we have found a CNID.
730 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
732 nextCNID
= hfsmp
->vcbNxtCNID
;
733 if (nextCNID
== 0xFFFFFFFF) {
737 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
738 hfsmp
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
743 hfsmp
->vcbFlags
|= 0xFF00;
744 /* OK to drop lock if CNIDs are not wrapping */
745 if ((hfsmp
->vcbAtrb
& kHFSCatalogNodeIDsReusedMask
) == 0) {
746 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
749 return (result
); /* HFS only exit */
752 /* Get space for iterator, key and data */
753 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
754 bto
->iterator
.hint
.nodeNum
= 0;
756 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
761 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
762 bto
->key
.nodeName
.length
);
763 hfs_setencodingbits(hfsmp
, encoding
);
767 * Insert the thread record first
769 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
770 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
771 S_ISDIR(attrp
->ca_mode
));
772 btdata
.bufferAddress
= &bto
->data
;
773 btdata
.itemSize
= datalen
;
774 btdata
.itemCount
= 1;
777 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
779 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
780 if ((result
== btExists
) && !std_hfs
&& mntlock
) {
782 * Allow CNIDs on HFS Plus volumes to wrap around
784 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
785 nextCNID
= kHFSFirstUserCatalogNodeID
;
791 if (result
) goto exit
;
795 * CNID is now established. If we have wrapped then
796 * update the vcbNxtCNID and drop the vcb lock.
799 hfsmp
->vcbNxtCNID
= nextCNID
+ 1;
800 if (hfsmp
->vcbNxtCNID
< kHFSFirstUserCatalogNodeID
) {
801 hfsmp
->vcbNxtCNID
= kHFSFirstUserCatalogNodeID
;
803 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
808 * Now insert the file/directory record
810 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
811 btdata
.bufferAddress
= &bto
->data
;
812 btdata
.itemSize
= datalen
;
813 btdata
.itemCount
= 1;
815 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
817 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
819 if (result
== btExists
)
822 /* Back out the thread record */
823 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
824 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
825 (void) BTDeleteRecord(fcb
, &bto
->iterator
);
831 * Insert was Successfull, update name, parent and volume
835 if (out_descp
!= NULL
) {
836 HFSPlusCatalogKey
* pluskey
= NULL
;
839 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
840 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
843 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
845 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
846 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
848 FREE(pluskey
, M_TEMP
);
851 attrp
->ca_fileid
= nextCNID
;
855 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
857 (void) BTFlushPath(fcb
);
860 return MacToVFSError(result
);
865 * cnode_rename - rename a catalog node
867 * Assumes that the target's directory exists.
869 * Order of B-tree operations:
870 * 1. BTSearchRecord(from_cnode, &data);
871 * 2. BTInsertRecord(to_cnode, &data);
872 * 3. BTDeleteRecord(from_cnode);
873 * 4. BTDeleteRecord(from_thread);
874 * 5. BTInsertRecord(to_thread);
879 struct hfsmount
* hfsmp
,
880 struct cat_desc
* from_cdp
,
881 struct cat_desc
* todir_cdp
,
882 struct cat_desc
* to_cdp
,
883 struct cat_desc
* out_cdp
)
885 struct BTreeIterator
* to_iterator
= NULL
;
886 struct BTreeIterator
* from_iterator
= NULL
;
887 FSBufferDescriptor btdata
;
888 CatalogRecord
* recp
= NULL
;
889 HFSPlusCatalogKey
* to_key
;
896 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
900 vcb
= HFSTOVCB(hfsmp
);
901 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
902 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
904 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
907 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
908 bzero(from_iterator
, sizeof(*from_iterator
));
909 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
912 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
913 bzero(to_iterator
, sizeof(*to_iterator
));
914 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
917 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
918 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
919 BDINIT(btdata
, recp
);
922 * When moving a directory, make sure its a valid move.
924 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
925 struct BTreeIterator iterator
;
926 cnid_t cnid
= from_cdp
->cd_cnid
;
927 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
929 /* First check the obvious ones */
930 if (cnid
== fsRtDirID
||
931 cnid
== to_cdp
->cd_parentcnid
||
936 bzero(&iterator
, sizeof(iterator
));
938 * Traverese destination path all the way back to the root
939 * making sure that source directory is not encountered.
942 while (pathcnid
> fsRtDirID
) {
943 buildthreadkey(pathcnid
, std_hfs
,
944 (CatalogKey
*)&iterator
.key
);
945 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
947 if (result
) goto exit
;
949 pathcnid
= getparentcnid(recp
);
950 if (pathcnid
== cnid
) {
958 * Step 1: Find cnode data at old location
960 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
961 &datasize
, from_iterator
);
963 if (std_hfs
|| (result
!= btNotFound
))
966 struct cat_desc temp_desc
;
968 /* Probably the node has mangled name */
969 result
= cat_lookupmangled(hfsmp
, from_cdp
, 0, &temp_desc
, NULL
, NULL
);
973 /* The file has mangled name. Search the cnode data using full name */
974 bzero(from_iterator
, sizeof(*from_iterator
));
975 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0);
977 cat_releasedesc(&temp_desc
);
981 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
, &datasize
, from_iterator
);
983 cat_releasedesc(&temp_desc
);
987 cat_releasedesc(&temp_desc
);
990 /* Update the text encoding (on disk and in descriptor) */
992 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
,
993 to_key
->nodeName
.length
);
994 hfs_setencodingbits(hfsmp
, encoding
);
995 recp
->hfsPlusFile
.textEncoding
= encoding
;
997 out_cdp
->cd_encoding
= encoding
;
1000 if (std_hfs
&& !directory
&&
1001 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
1005 * If the keys are identical then there's nothing left to do!
1007 * update the hint and exit
1010 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
1012 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
1016 /* Step 2: Insert cnode at new location */
1017 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1018 if (result
== btExists
) {
1019 int fromtype
= recp
->recordType
;
1021 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
1022 goto exit
; /* EEXIST */
1024 /* Find cnode data at new location */
1025 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
1027 if ((fromtype
!= recp
->recordType
) ||
1028 (from_cdp
->cd_cnid
!= getcnid(recp
)))
1029 goto exit
; /* EEXIST */
1031 /* The old name is a case variant and must be removed */
1032 result
= BTDeleteRecord(fcb
, from_iterator
);
1036 /* Insert cnode (now that case duplicate is gone) */
1037 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
1039 /* Try and restore original before leaving */
1044 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1046 panic("cat_create: could not undo (BTInsert = %d)", err
);
1049 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1058 /* Step 3: Remove cnode from old location */
1060 result
= BTDeleteRecord(fcb
, from_iterator
);
1062 /* Try and delete new record before leaving */
1067 err
= BTDeleteRecord(fcb
, to_iterator
);
1069 panic("cat_create: could not undo (BTDelete = %d)", err
);
1072 (void) BTDeleteRecord(fcb
, to_iterator
);
1078 /* #### POINT OF NO RETURN #### */
1081 * Step 4: Remove cnode's old thread record
1083 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1084 (void) BTDeleteRecord(fcb
, from_iterator
);
1087 * Step 5: Insert cnode's new thread record
1088 * (optional for HFS files)
1091 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
1092 btdata
.itemSize
= datasize
;
1093 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
1094 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
1098 HFSPlusCatalogKey
* pluskey
= NULL
;
1101 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1102 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
1104 /* Save the real encoding hint in the Finder Info (field 4). */
1105 if (directory
&& from_cdp
->cd_cnid
== kHFSRootFolderID
) {
1108 realhint
= hfs_pickencoding(pluskey
->nodeName
.unicode
, pluskey
->nodeName
.length
);
1109 vcb
->vcbFndrInfo
[4] = SET_HFS_TEXT_ENCODING(realhint
);
1113 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
1115 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
1116 encoding
, directory
, out_cdp
);
1118 FREE(pluskey
, M_TEMP
);
1122 (void) BTFlushPath(fcb
);
1124 FREE(from_iterator
, M_TEMP
);
1126 FREE(to_iterator
, M_TEMP
);
1129 return MacToVFSError(result
);
1134 * cat_delete - delete a node from the catalog
1136 * Order of B-tree operations:
1137 * 1. BTDeleteRecord(cnode);
1138 * 2. BTDeleteRecord(thread);
1139 * 3. BTUpdateRecord(parent);
1143 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
1147 BTreeIterator
*iterator
;
1152 vcb
= HFSTOVCB(hfsmp
);
1153 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1154 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1158 * The root directory cannot be deleted
1159 * A directory must be empty
1160 * A file must be zero length (no blocks)
1162 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
1163 descp
->cd_parentcnid
== kHFSRootParentID
)
1166 /* XXX Preflight Missing */
1168 /* Get space for iterator */
1169 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1170 iterator
->hint
.nodeNum
= 0;
1173 * Derive a key from either the file ID (for a virtual inode)
1174 * or the descriptor.
1176 if (descp
->cd_namelen
== 0) {
1177 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1178 cnid
= attrp
->ca_fileid
;
1180 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1181 cnid
= descp
->cd_cnid
;
1187 result
= BTDeleteRecord(fcb
, iterator
);
1189 if (std_hfs
|| (result
!= btNotFound
))
1192 struct cat_desc temp_desc
;
1194 /* Probably the node has mangled name */
1195 result
= cat_lookupmangled(hfsmp
, descp
, 0, &temp_desc
, attrp
, NULL
);
1199 /* The file has mangled name. Delete the file using full name */
1200 bzero(iterator
, sizeof(*iterator
));
1201 result
= buildkey(hfsmp
, &temp_desc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1202 cnid
= temp_desc
.cd_cnid
;
1204 cat_releasedesc(&temp_desc
);
1208 result
= BTDeleteRecord(fcb
, iterator
);
1210 cat_releasedesc(&temp_desc
);
1214 cat_releasedesc(&temp_desc
);
1217 /* Delete thread record, ignore errors */
1218 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1219 (void) BTDeleteRecord(fcb
, iterator
);
1222 (void) BTFlushPath(fcb
);
1223 FREE(iterator
, M_TEMP
);
1225 return MacToVFSError(result
);
1230 * cnode_update - update the catalog node described by descp
1231 * using the data from attrp and forkp.
1235 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
1236 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
1240 BTreeIterator
* iterator
;
1241 struct update_state state
;
1245 vcb
= HFSTOVCB(hfsmp
);
1246 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
1247 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
1249 state
.s_desc
= descp
;
1250 state
.s_attr
= attrp
;
1251 state
.s_datafork
= dataforkp
;
1252 state
.s_rsrcfork
= rsrcforkp
;
1253 state
.s_hfsmp
= hfsmp
;
1255 /* Get space for iterator */
1256 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1259 * For open-deleted files we need to do a lookup by cnid
1260 * (using thread rec).
1262 * For hard links, the target of the update is the inode
1263 * itself (not the link record) so a lookup by fileid
1264 * (i.e. thread rec) is needed.
1266 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (descp
->cd_namelen
== 0))
1267 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1269 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1273 /* Pass a node hint */
1274 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1276 result
= BTUpdateRecord(fcb
, iterator
,
1277 (IterateCallBackProcPtr
)catrec_update
, &state
);
1281 /* Update the node hint. */
1282 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1285 (void) BTFlushPath(fcb
);
1286 FREE(iterator
, M_TEMP
);
1288 return MacToVFSError(result
);
1292 * catrec_update - Update the fields of a catalog record
1293 * This is called from within BTUpdateRecord.
1296 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, struct update_state
*state
)
1298 struct cat_desc
*descp
;
1299 struct cat_attr
*attrp
;
1300 struct cat_fork
*forkp
;
1301 struct hfsmount
*hfsmp
;
1305 descp
= state
->s_desc
;
1306 attrp
= state
->s_attr
;
1307 hfsmp
= state
->s_hfsmp
;
1308 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1310 switch (crp
->recordType
) {
1311 case kHFSFolderRecord
: {
1312 HFSCatalogFolder
*dir
;
1314 dir
= (struct HFSCatalogFolder
*)crp
;
1315 /* Do a quick sanity check */
1316 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1317 (dir
->folderID
!= descp
->cd_cnid
))
1318 return (btNotFound
);
1319 dir
->valence
= attrp
->ca_entries
;
1320 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1321 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1322 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1323 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1324 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1327 case kHFSFileRecord
: {
1328 HFSCatalogFile
*file
;
1330 file
= (struct HFSCatalogFile
*)crp
;
1331 /* Do a quick sanity check */
1332 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1333 (file
->fileID
!= attrp
->ca_fileid
))
1334 return (btNotFound
);
1335 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1336 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1337 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1338 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1339 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1340 if (state
->s_rsrcfork
) {
1341 forkp
= state
->s_rsrcfork
;
1342 file
->rsrcLogicalSize
= forkp
->cf_size
;
1343 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1344 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1345 file
->rsrcExtents
[i
].startBlock
=
1346 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1347 file
->rsrcExtents
[i
].blockCount
=
1348 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1351 if (state
->s_datafork
) {
1352 forkp
= state
->s_datafork
;
1353 file
->dataLogicalSize
= forkp
->cf_size
;
1354 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1355 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1356 file
->dataExtents
[i
].startBlock
=
1357 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1358 file
->dataExtents
[i
].blockCount
=
1359 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1364 case kHFSPlusFolderRecord
: {
1365 HFSPlusCatalogFolder
*dir
;
1367 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1368 /* Do a quick sanity check */
1369 if ((ckp
->hfsPlus
.parentID
!= descp
->cd_parentcnid
) ||
1370 (dir
->folderID
!= descp
->cd_cnid
))
1371 return (btNotFound
);
1372 dir
->flags
= attrp
->ca_recflags
;
1373 dir
->valence
= attrp
->ca_entries
;
1374 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1375 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1376 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1377 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1378 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1379 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1380 dir
->textEncoding
= descp
->cd_encoding
;
1381 dir
->attrBlocks
= attrp
->ca_attrblks
;
1382 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1384 * Update the BSD Info if it was already initialized on
1385 * disk or if the runtime values have been modified.
1387 * If the BSD info was already initialized, but
1388 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1389 * probably different than what was on disk. We don't want
1390 * to overwrite the on-disk values (so if we turn off
1391 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1392 * This way, we can still change fields like the mode or
1393 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1395 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1396 * won't change the uid or gid from their defaults. So, if
1397 * the BSD info wasn't set, and the runtime values are not
1398 * default, then what changed was the mode or flags. We
1399 * have to set the uid and gid to something, so use the
1400 * supplied values (which will be default), which has the
1401 * same effect as creating a new file while
1402 * MNT_UNKNOWNPERMISSIONS is set.
1404 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1405 (attrp
->ca_flags
!= 0) ||
1406 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1407 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1408 ((attrp
->ca_mode
& ALLPERMS
) !=
1409 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1410 if ((dir
->bsdInfo
.fileMode
== 0) ||
1411 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1412 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1413 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1415 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1416 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1417 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1421 case kHFSPlusFileRecord
: {
1422 HFSPlusCatalogFile
*file
;
1424 file
= (struct HFSPlusCatalogFile
*)crp
;
1425 /* Do a quick sanity check */
1426 if (file
->fileID
!= attrp
->ca_fileid
)
1427 return (btNotFound
);
1428 file
->flags
= attrp
->ca_recflags
;
1429 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1430 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1431 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1432 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1433 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
1434 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1435 file
->textEncoding
= descp
->cd_encoding
;
1436 file
->attrBlocks
= attrp
->ca_attrblks
;
1437 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1439 * Update the BSD Info if it was already initialized on
1440 * disk or if the runtime values have been modified.
1442 * If the BSD info was already initialized, but
1443 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1444 * probably different than what was on disk. We don't want
1445 * to overwrite the on-disk values (so if we turn off
1446 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1447 * This way, we can still change fields like the mode or
1448 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1450 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1451 * won't change the uid or gid from their defaults. So, if
1452 * the BSD info wasn't set, and the runtime values are not
1453 * default, then what changed was the mode or flags. We
1454 * have to set the uid and gid to something, so use the
1455 * supplied values (which will be default), which has the
1456 * same effect as creating a new file while
1457 * MNT_UNKNOWNPERMISSIONS is set.
1459 if ((file
->bsdInfo
.fileMode
!= 0) ||
1460 (attrp
->ca_flags
!= 0) ||
1461 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1462 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1463 ((attrp
->ca_mode
& ALLPERMS
) !=
1464 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1465 if ((file
->bsdInfo
.fileMode
== 0) ||
1466 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) == 0) {
1467 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1468 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1470 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1471 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1472 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1474 if (state
->s_rsrcfork
) {
1475 forkp
= state
->s_rsrcfork
;
1476 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1477 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1478 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1479 sizeof(HFSPlusExtentRecord
));
1480 /* Push blocks read to disk */
1481 file
->resourceFork
.clumpSize
=
1482 howmany(forkp
->cf_bytesread
, blksize
);
1484 if (state
->s_datafork
) {
1485 forkp
= state
->s_datafork
;
1486 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1487 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1488 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1489 sizeof(HFSPlusExtentRecord
));
1490 /* Push blocks read to disk */
1491 file
->dataFork
.clumpSize
=
1492 howmany(forkp
->cf_bytesread
, blksize
);
1495 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1496 (file
->resourceFork
.extents
[0].startBlock
==
1497 file
->dataFork
.extents
[0].startBlock
))
1498 panic("catrec_update: rsrc fork == data fork");
1500 /* Synchronize the lock state */
1501 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1502 file
->flags
|= kHFSFileLockedMask
;
1504 file
->flags
&= ~kHFSFileLockedMask
;
1506 /* Push out special field if necessary */
1507 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
))
1508 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1509 else if (descp
->cd_cnid
!= attrp
->ca_fileid
1510 || attrp
->ca_nlink
== 2)
1511 file
->bsdInfo
.special
.linkCount
= attrp
->ca_nlink
;
1515 return (btNotFound
);
1521 * Callback to collect directory entries.
1522 * Called with readattr_state for each item in a directory.
1524 struct readattr_state
{
1525 struct hfsmount
*hfsmp
;
1526 struct cat_entrylist
*list
;
1533 cat_readattr(const CatalogKey
*key
, const CatalogRecord
*rec
,
1534 struct readattr_state
*state
)
1536 struct cat_entrylist
*list
= state
->list
;
1537 struct hfsmount
*hfsmp
= state
->hfsmp
;
1538 struct cat_entry
*cep
;
1541 if (list
->realentries
>= list
->maxentries
)
1542 return (0); /* stop */
1544 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
1546 switch(rec
->recordType
) {
1547 case kHFSPlusFolderRecord
:
1548 case kHFSPlusFileRecord
:
1549 case kHFSFolderRecord
:
1550 case kHFSFileRecord
:
1551 if (parentcnid
!= state
->dir_cnid
) {
1552 state
->error
= ENOENT
;
1553 return (0); /* stop */
1557 state
->error
= ENOENT
;
1558 return (0); /* stop */
1561 /* Hide the private meta data directory and journal files */
1562 if (parentcnid
== kHFSRootFolderID
) {
1563 if ((rec
->recordType
== kHFSPlusFolderRecord
) &&
1564 (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1565 return (1); /* continue */
1568 (rec
->recordType
== kHFSPlusFileRecord
) &&
1569 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
1570 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
1572 return (1); /* continue */
1576 cep
= &list
->entry
[list
->realentries
++];
1578 if (state
->stdhfs
) {
1579 struct HFSPlusCatalogFile cnoderec
;
1580 HFSPlusCatalogKey
* pluskey
;
1583 promoteattr(hfsmp
, rec
, &cnoderec
);
1584 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
1586 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1587 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
1588 builddesc(pluskey
, getcnid(rec
), 0, encoding
, isadir(rec
), &cep
->ce_desc
);
1589 FREE(pluskey
, M_TEMP
);
1591 if (rec
->recordType
== kHFSFileRecord
) {
1592 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1594 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
1595 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
1596 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
1597 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
1600 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
1601 builddesc((HFSPlusCatalogKey
*)key
, getcnid(rec
), 0, getencoding(rec
),
1602 isadir(rec
), &cep
->ce_desc
);
1604 if (rec
->recordType
== kHFSPlusFileRecord
) {
1605 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
1606 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
1607 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
1608 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
1610 /* Save link reference for later processing. */
1611 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
1612 && (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
))
1613 cep
->ce_attr
.ca_rdev
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1617 return (list
->realentries
< list
->maxentries
);
1621 * Pack a cat_entrylist buffer with attributes from the catalog
1623 * Note: index is zero relative
1627 cat_getentriesattr(struct hfsmount
*hfsmp
, directoryhint_t
*dirhint
, struct cat_entrylist
*ce_list
)
1631 BTreeIterator
* iterator
;
1632 struct readattr_state state
;
1640 ce_list
->realentries
= 0;
1642 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
1643 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
1644 parentcnid
= dirhint
->dh_desc
.cd_parentcnid
;
1646 state
.hfsmp
= hfsmp
;
1647 state
.list
= ce_list
;
1648 state
.dir_cnid
= parentcnid
;
1649 state
.stdhfs
= std_hfs
;
1652 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1653 bzero(iterator
, sizeof(*iterator
));
1654 key
= (CatalogKey
*)&iterator
->key
;
1656 iterator
->hint
.nodeNum
= dirhint
->dh_desc
.cd_hint
;
1657 index
= dirhint
->dh_index
+ 1;
1660 * Attempt to build a key from cached filename
1662 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
1663 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
1669 * If the last entry wasn't cached then position the btree iterator
1671 if ((index
== 0) || !have_key
) {
1673 * Position the iterator at the directory's thread record.
1674 * (i.e. just before the first entry)
1676 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
1677 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1679 result
= MacToVFSError(result
);
1684 * Iterate until we reach the entry just
1685 * before the one we want to start with.
1688 struct position_state ps
;
1693 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
1696 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1697 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
1701 result
= MacToVFSError(result
);
1703 result
= MacToVFSError(result
);
1709 /* Fill list with entries starting at iterator->key. */
1710 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1711 (IterateCallBackProcPtr
)cat_readattr
, &state
);
1714 result
= state
.error
;
1715 else if (ce_list
->realentries
== 0)
1718 result
= MacToVFSError(result
);
1724 * Resolve any hard links.
1726 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
1727 struct FndrFileInfo
*fip
;
1728 struct cat_entry
*cep
;
1729 struct HFSPlusCatalogFile filerec
;
1731 cep
= &ce_list
->entry
[i
];
1732 if (!S_ISREG(cep
->ce_attr
.ca_mode
))
1735 /* Note: Finder info is still in Big Endian */
1736 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
1738 /* Check for hard link signature. */
1739 if ((cep
->ce_attr
.ca_rdev
!= 0)
1740 && (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
)
1741 && (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)
1742 && ((cep
->ce_attr
.ca_itime
== (time_t)HFSTOVCB(hfsmp
)->vcbCrDate
) ||
1743 (cep
->ce_attr
.ca_itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
1745 if (resolvelink(hfsmp
, cep
->ce_attr
.ca_rdev
, &filerec
) != 0)
1747 /* Repack entry from inode record. */
1748 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
1749 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
1750 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
1751 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
1752 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
1756 FREE(iterator
, M_TEMP
);
1758 return MacToVFSError(result
);
1761 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1764 * Callback to pack directory entries.
1765 * Called with packdirentry_state for each item in a directory.
1768 /* Hard link information collected during cat_getdirentries. */
1771 user_addr_t dirent_addr
;
1773 typedef struct linkinfo linkinfo_t
;
1775 /* State information for the cat_packdirentry callback function. */
1776 struct packdirentry_state
{
1778 u_int32_t cbs_parentID
;
1779 u_int32_t cbs_index
;
1781 ExtendedVCB
* cbs_hfsmp
;
1784 int32_t cbs_maxlinks
;
1785 linkinfo_t
* cbs_linkinfo
;
1786 struct cat_desc
* cbs_desc
;
1787 // struct dirent * cbs_stdentry;
1788 // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
1789 struct direntry
* cbs_direntry
;
1790 struct direntry
* cbs_prevdirentry
;
1791 u_int32_t cbs_previlinkref
;
1792 Boolean cbs_hasprevdirentry
;
1797 cat_packdirentry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
1798 struct packdirentry_state
*state
)
1800 struct hfsmount
*hfsmp
;
1804 struct dirent catent
;
1805 struct direntry
* entry
= NULL
;
1807 u_int32_t ilinkref
= 0;
1808 u_int32_t curlinkref
= 0;
1812 u_int8_t is_mangled
= 0;
1814 user_addr_t uiobase
;
1819 Boolean stop_after_pack
= false;
1821 hfsmp
= state
->cbs_hfsmp
;
1823 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
1824 curID
= ckp
->hfs
.parentID
;
1826 curID
= ckp
->hfsPlus
.parentID
;
1828 /* We're done when parent directory changes */
1829 if (state
->cbs_parentID
!= curID
) {
1830 if (state
->cbs_extended
) {
1831 if (state
->cbs_hasprevdirentry
) { /* the last record haven't been returned yet, so we want to stop after
1832 * packing the last item */
1833 stop_after_pack
= true;
1835 state
->cbs_result
= ENOENT
;
1836 return (0); /* stop */
1839 state
->cbs_result
= ENOENT
;
1840 return (0); /* stop */
1844 if (state
->cbs_extended
) {
1845 entry
= state
->cbs_direntry
;
1846 nameptr
= &entry
->d_name
[0];
1847 maxnamelen
= NAME_MAX
;
1849 nameptr
= &catent
.d_name
[0];
1850 maxnamelen
= NAME_MAX
;
1853 if (state
->cbs_extended
&& stop_after_pack
) {
1854 cnid
= INT_MAX
; /* the last item returns a non-zero invalid cookie */
1856 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
)) {
1857 switch(crp
->recordType
) {
1858 case kHFSPlusFolderRecord
:
1860 cnid
= crp
->hfsPlusFolder
.folderID
;
1861 /* Hide our private meta data directory */
1862 if ((curID
== kHFSRootFolderID
) &&
1863 (cnid
== hfsmp
->hfs_privdir_desc
.cd_cnid
)) {
1868 case kHFSPlusFileRecord
:
1869 itime
= to_bsd_time(crp
->hfsPlusFile
.createDate
);
1871 * When a hardlink link is encountered save its link ref.
1873 if ((SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
) &&
1874 (SWAP_BE32(crp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
) &&
1875 ((itime
== (time_t)hfsmp
->hfs_itime
) ||
1876 (itime
== (time_t)hfsmp
->hfs_metadata_createdate
))) {
1877 ilinkref
= crp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1879 type
= MODE_TO_DT(crp
->hfsPlusFile
.bsdInfo
.fileMode
);
1880 cnid
= crp
->hfsPlusFile
.fileID
;
1881 /* Hide the journal files */
1882 if ((curID
== kHFSRootFolderID
) &&
1884 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
1885 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
1890 return (0); /* stop */
1893 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
1894 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
1895 nameptr
, &namelen
, maxnamelen
+ 1, ':', 0);
1896 if (result
== ENAMETOOLONG
) {
1897 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
1898 cnp
->ustr
.unicode
, maxnamelen
+ 1,
1899 (ByteCount
*)&namelen
, nameptr
,
1904 switch(crp
->recordType
) {
1905 case kHFSFolderRecord
:
1907 cnid
= crp
->hfsFolder
.folderID
;
1909 case kHFSFileRecord
:
1911 cnid
= crp
->hfsFile
.fileID
;
1914 return (0); /* stop */
1917 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
1918 result
= hfs_to_utf8(hfsmp
, cnp
->pstr
, maxnamelen
+ 1,
1919 (ByteCount
*)&namelen
, nameptr
);
1921 * When an HFS name cannot be encoded with the current
1922 * volume encoding we use MacRoman as a fallback.
1925 result
= mac_roman_to_utf8(cnp
->pstr
, maxnamelen
+ 1,
1926 (ByteCount
*)&namelen
, nameptr
);
1930 if (state
->cbs_extended
) {
1932 * The index is 1 relative and includes "." and ".."
1934 * Also stuff the cnid in the upper 32 bits of the cookie. The cookie is stored to the previous entry, which
1935 * will be packed and copied this time
1937 state
->cbs_prevdirentry
->d_seekoff
= (state
->cbs_index
+ 3) | ((u_int64_t
)cnid
<< 32);
1938 uiosize
= state
->cbs_prevdirentry
->d_reclen
;
1939 uioaddr
= (caddr_t
) state
->cbs_prevdirentry
;
1941 catent
.d_type
= type
;
1942 catent
.d_namlen
= namelen
;
1943 catent
.d_reclen
= uiosize
= STD_DIRENT_LEN(namelen
);
1945 catent
.d_fileno
= 0; /* file number = 0 means skip entry */
1947 catent
.d_fileno
= cnid
;
1948 uioaddr
= (caddr_t
) &catent
;
1951 /* Save current base address for post processing of hard-links. */
1952 uiobase
= uio_curriovbase(state
->cbs_uio
);
1954 /* If this entry won't fit then we're done */
1955 if ((uiosize
> uio_resid(state
->cbs_uio
)) ||
1956 (ilinkref
!= 0 && state
->cbs_nlinks
== state
->cbs_maxlinks
)) {
1957 return (0); /* stop */
1960 if (!state
->cbs_extended
|| state
->cbs_hasprevdirentry
) {
1961 state
->cbs_result
= uiomove(uioaddr
, uiosize
, state
->cbs_uio
);
1962 if (state
->cbs_result
== 0) {
1965 /* Remember previous entry */
1966 state
->cbs_desc
->cd_cnid
= cnid
;
1967 if (type
== DT_DIR
) {
1968 state
->cbs_desc
->cd_flags
|= CD_ISDIR
;
1970 state
->cbs_desc
->cd_flags
&= ~CD_ISDIR
;
1972 if (state
->cbs_desc
->cd_nameptr
!= NULL
) {
1973 vfs_removename(state
->cbs_desc
->cd_nameptr
);
1976 state
->cbs_desc
->cd_encoding
= xxxx
;
1979 state
->cbs_desc
->cd_namelen
= namelen
;
1980 state
->cbs_desc
->cd_nameptr
= vfs_addname(nameptr
, namelen
, 0, 0);
1982 /* Store unmangled name for the directory hint else it will
1983 * restart readdir at the last location again
1987 size_t tmp_namelen
= 0;
1989 cnp
= (CatalogName
*)&ckp
->hfsPlus
.nodeName
;
1990 bufsize
= 1 + utf8_encodelen(cnp
->ustr
.unicode
,
1991 cnp
->ustr
.length
* sizeof(UniChar
),
1993 MALLOC(new_nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
1994 result
= utf8_encodestr(cnp
->ustr
.unicode
,
1995 cnp
->ustr
.length
* sizeof(UniChar
),
1996 new_nameptr
, &tmp_namelen
,
1999 state
->cbs_desc
->cd_namelen
= tmp_namelen
;
2000 state
->cbs_desc
->cd_nameptr
= vfs_addname(new_nameptr
, tmp_namelen
, 0, 0);
2002 FREE(new_nameptr
, M_TEMP
);
2005 if (state
->cbs_hasprevdirentry
) {
2006 curlinkref
= ilinkref
; /* save current */
2007 ilinkref
= state
->cbs_previlinkref
; /* use previous */
2010 * Record any hard links for post processing.
2012 if ((ilinkref
!= 0) &&
2013 (state
->cbs_result
== 0) &&
2014 (state
->cbs_nlinks
< state
->cbs_maxlinks
)) {
2015 state
->cbs_linkinfo
[state
->cbs_nlinks
].dirent_addr
= uiobase
;
2016 state
->cbs_linkinfo
[state
->cbs_nlinks
].link_ref
= ilinkref
;
2017 state
->cbs_nlinks
++;
2019 if (state
->cbs_hasprevdirentry
) {
2020 ilinkref
= curlinkref
; /* restore current */
2024 if (state
->cbs_extended
) { /* fill the direntry to be used the next time */
2025 if (stop_after_pack
) {
2026 state
->cbs_eof
= true;
2027 return (0); /* stop */
2029 entry
->d_type
= type
;
2030 entry
->d_namlen
= namelen
;
2031 entry
->d_reclen
= EXT_DIRENT_LEN(namelen
);
2033 entry
->d_fileno
= 0; /* file number = 0 means skip entry */
2035 entry
->d_fileno
= cnid
;
2036 /* swap the current and previous entry */
2037 struct direntry
* tmp
;
2038 tmp
= state
->cbs_direntry
;
2039 state
->cbs_direntry
= state
->cbs_prevdirentry
;
2040 state
->cbs_prevdirentry
= tmp
;
2041 state
->cbs_hasprevdirentry
= true;
2042 state
->cbs_previlinkref
= ilinkref
;
2045 /* Continue iteration if there's room */
2046 return (state
->cbs_result
== 0 &&
2047 uio_resid(state
->cbs_uio
) >= SMALL_DIRENTRY_SIZE
);
2052 * Pack a uio buffer with directory entries from the catalog
2056 cat_getdirentries(struct hfsmount
*hfsmp
, int entrycnt
, directoryhint_t
*dirhint
,
2057 uio_t uio
, int extended
, int * items
, int * eofflag
)
2060 BTreeIterator
* iterator
;
2062 struct packdirentry_state state
;
2070 fcb
= GetFileControlBlock(hfsmp
->hfs_catalog_vp
);
2073 * Get a buffer for link info array, btree iterator and a direntry:
2075 maxlinks
= MIN(entrycnt
, uio_resid(uio
) / SMALL_DIRENTRY_SIZE
);
2076 bufsize
= (maxlinks
* sizeof(linkinfo_t
)) + sizeof(*iterator
);
2078 bufsize
+= 2*sizeof(struct direntry
);
2080 MALLOC(buffer
, void *, bufsize
, M_TEMP
, M_WAITOK
);
2081 bzero(buffer
, bufsize
);
2083 state
.cbs_extended
= extended
;
2084 state
.cbs_hasprevdirentry
= false;
2085 state
.cbs_previlinkref
= 0;
2086 state
.cbs_nlinks
= 0;
2087 state
.cbs_maxlinks
= maxlinks
;
2088 state
.cbs_linkinfo
= (linkinfo_t
*) buffer
;
2090 iterator
= (BTreeIterator
*) ((char *)buffer
+ (maxlinks
* sizeof(linkinfo_t
)));
2091 key
= (CatalogKey
*)&iterator
->key
;
2093 index
= dirhint
->dh_index
+ 1;
2095 state
.cbs_direntry
= (struct direntry
*)((char *)iterator
+ sizeof(BTreeIterator
));
2096 state
.cbs_prevdirentry
= state
.cbs_direntry
+ 1;
2097 state
.cbs_eof
= false;
2100 * Attempt to build a key from cached filename
2102 if (dirhint
->dh_desc
.cd_namelen
!= 0) {
2103 if (buildkey(hfsmp
, &dirhint
->dh_desc
, (HFSPlusCatalogKey
*)key
, 0) == 0) {
2109 * If the last entry wasn't cached then position the btree iterator
2111 if ((index
== 0) || !have_key
) {
2113 * Position the iterator at the directory's thread record.
2114 * (i.e. just before the first entry)
2116 buildthreadkey(dirhint
->dh_desc
.cd_parentcnid
, (hfsmp
->hfs_flags
& HFS_STANDARD
), key
);
2117 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
2119 result
= MacToVFSError(result
);
2124 * Iterate until we reach the entry just
2125 * before the one we want to start with.
2128 struct position_state ps
;
2133 ps
.parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2136 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
2137 (IterateCallBackProcPtr
)cat_findposition
, &ps
);
2141 result
= MacToVFSError(result
);
2143 result
= MacToVFSError(result
);
2149 state
.cbs_index
= index
;
2150 state
.cbs_hfsmp
= hfsmp
;
2151 state
.cbs_uio
= uio
;
2152 state
.cbs_desc
= &dirhint
->dh_desc
;
2153 state
.cbs_result
= 0;
2154 state
.cbs_parentID
= dirhint
->dh_desc
.cd_parentcnid
;
2156 enum BTreeIterationOperations op
;
2157 if (extended
&& index
!= 0 && have_key
)
2158 op
= kBTreeCurrentRecord
;
2160 op
= kBTreeNextRecord
;
2163 * Process as many entries as possible starting at iterator->key.
2165 result
= BTIterateRecords(fcb
, op
, iterator
,
2166 (IterateCallBackProcPtr
)cat_packdirentry
, &state
);
2168 /* Note that state.cbs_index is still valid on errors */
2169 *items
= state
.cbs_index
- index
;
2170 index
= state
.cbs_index
;
2172 if (state
.cbs_eof
) {
2176 /* Finish updating the catalog iterator. */
2177 dirhint
->dh_desc
.cd_hint
= iterator
->hint
.nodeNum
;
2178 dirhint
->dh_desc
.cd_flags
|= CD_DECOMPOSED
;
2179 dirhint
->dh_index
= index
- 1;
2182 * Post process any hard links to get the real file id.
2184 if (state
.cbs_nlinks
> 0) {
2185 u_int32_t fileid
= 0;
2186 user_addr_t address
;
2189 for (i
= 0; i
< state
.cbs_nlinks
; ++i
) {
2190 if (resolvelinkid(hfsmp
, state
.cbs_linkinfo
[i
].link_ref
, &fileid
) != 0)
2192 /* This assumes that d_ino is always first field. */
2193 address
= state
.cbs_linkinfo
[i
].dirent_addr
;
2194 if (address
== (user_addr_t
)0)
2196 if (uio_isuserspace(uio
)) {
2197 (void) copyout(&fileid
, address
,
2198 extended
? sizeof(ino64_t
) : sizeof(ino_t
));
2199 } else /* system space */ {
2200 ino64_t
*inoptr
= (ino64_t
*)CAST_DOWN(caddr_t
, address
);
2206 if (state
.cbs_result
)
2207 result
= state
.cbs_result
;
2209 result
= MacToVFSError(result
);
2211 if (result
== ENOENT
) {
2216 FREE(buffer
, M_TEMP
);
2223 * Callback to establish directory position.
2224 * Called with position_state for each item in a directory.
2227 cat_findposition(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
2228 struct position_state
*state
)
2232 if (state
->hfsmp
->hfs_flags
& HFS_STANDARD
)
2233 curID
= ckp
->hfs
.parentID
;
2235 curID
= ckp
->hfsPlus
.parentID
;
2237 /* Make sure parent directory didn't change */
2238 if (state
->parentID
!= curID
) {
2239 state
->error
= EINVAL
;
2240 return (0); /* stop */
2243 /* Count this entry */
2244 switch(crp
->recordType
) {
2245 case kHFSPlusFolderRecord
:
2246 case kHFSPlusFileRecord
:
2247 case kHFSFolderRecord
:
2248 case kHFSFileRecord
:
2252 printf("cat_findposition: invalid record type %d in dir %d\n",
2253 crp
->recordType
, curID
);
2254 state
->error
= EINVAL
;
2255 return (0); /* stop */
2258 return (state
->count
< state
->index
);
2263 * cat_binarykeycompare - compare two HFS Plus catalog keys.
2265 * The name portion of the key is compared using a 16-bit binary comparison.
2266 * This is called from the b-tree code.
2270 cat_binarykeycompare(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
2272 u_int32_t searchParentID
, trialParentID
;
2275 searchParentID
= searchKey
->parentID
;
2276 trialParentID
= trialKey
->parentID
;
2279 if (searchParentID
> trialParentID
) {
2281 } else if (searchParentID
< trialParentID
) {
2284 u_int16_t
* str1
= &searchKey
->nodeName
.unicode
[0];
2285 u_int16_t
* str2
= &trialKey
->nodeName
.unicode
[0];
2286 int length1
= searchKey
->nodeName
.length
;
2287 int length2
= trialKey
->nodeName
.length
;
2291 if (length1
< length2
) {
2294 } else if (length1
> length2
) {
2321 * Compare two standard HFS catalog keys
2323 * Result: +n search key > trial key
2324 * 0 search key = trial key
2325 * -n search key < trial key
2328 CompareCatalogKeys(HFSCatalogKey
*searchKey
, HFSCatalogKey
*trialKey
)
2330 cnid_t searchParentID
, trialParentID
;
2333 searchParentID
= searchKey
->parentID
;
2334 trialParentID
= trialKey
->parentID
;
2336 if (searchParentID
> trialParentID
)
2338 else if (searchParentID
< trialParentID
)
2340 else /* parent dirID's are equal, compare names */
2341 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
2348 * Compare two HFS+ catalog keys
2350 * Result: +n search key > trial key
2351 * 0 search key = trial key
2352 * -n search key < trial key
2355 CompareExtendedCatalogKeys(HFSPlusCatalogKey
*searchKey
, HFSPlusCatalogKey
*trialKey
)
2357 cnid_t searchParentID
, trialParentID
;
2360 searchParentID
= searchKey
->parentID
;
2361 trialParentID
= trialKey
->parentID
;
2363 if (searchParentID
> trialParentID
) {
2366 else if (searchParentID
< trialParentID
) {
2369 /* parent node ID's are equal, compare names */
2370 if ( searchKey
->nodeName
.length
== 0 || trialKey
->nodeName
.length
== 0 )
2371 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
2373 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
2374 searchKey
->nodeName
.length
,
2375 &trialKey
->nodeName
.unicode
[0],
2376 trialKey
->nodeName
.length
);
2384 * buildkey - build a Catalog b-tree key from a cnode descriptor
2387 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
2388 HFSPlusCatalogKey
*key
, int retry
)
2392 size_t unicodeBytes
= 0;
2394 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
2395 return (EINVAL
); /* invalid name */
2397 key
->parentID
= descp
->cd_parentcnid
;
2398 key
->nodeName
.length
= 0;
2400 * Convert filename from UTF-8 into Unicode
2403 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
2404 utf8_flags
|= UTF_DECOMPOSED
;
2405 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
2406 key
->nodeName
.unicode
, &unicodeBytes
,
2407 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
2408 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
2409 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
2411 if (result
!= ENAMETOOLONG
)
2412 result
= EINVAL
; /* name has invalid characters */
2417 * For HFS volumes convert to an HFS compatible key
2419 * XXX need to save the encoding that succeeded
2421 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
2422 HFSCatalogKey hfskey
;
2424 bzero(&hfskey
, sizeof(hfskey
));
2425 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
2426 hfskey
.parentID
= key
->parentID
;
2427 hfskey
.nodeName
[0] = 0;
2428 if (key
->nodeName
.length
> 0) {
2429 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
2430 key
->nodeName
.length
* 2,
2431 key
->nodeName
.unicode
,
2432 &hfskey
.nodeName
[0], retry
) != 0) {
2435 hfskey
.keyLength
+= hfskey
.nodeName
[0];
2437 bcopy(&hfskey
, key
, sizeof(hfskey
));
2444 * Resolve hard link reference to obtain the inode record.
2448 resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
)
2450 FSBufferDescriptor btdata
;
2451 struct BTreeIterator
*iterator
;
2452 struct cat_desc idesc
;
2456 BDINIT(btdata
, recp
);
2457 MAKE_INODE_NAME(inodename
, linkref
);
2459 /* Get space for iterator */
2460 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2461 bzero(iterator
, sizeof(*iterator
));
2463 /* Build a descriptor for private dir. */
2464 idesc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
2465 idesc
.cd_nameptr
= inodename
;
2466 idesc
.cd_namelen
= strlen(inodename
);
2469 idesc
.cd_encoding
= 0;
2470 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
2472 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2473 &btdata
, NULL
, NULL
);
2476 /* Make sure there's a reference */
2477 if (recp
->bsdInfo
.special
.linkCount
== 0)
2478 recp
->bsdInfo
.special
.linkCount
= 2;
2480 printf("HFS resolvelink: can't find %s\n", inodename
);
2483 FREE(iterator
, M_TEMP
);
2485 return (result
? ENOENT
: 0);
2489 * Resolve hard link reference to obtain the inode number.
2492 resolvelinkid(struct hfsmount
*hfsmp
, u_long linkref
, ino_t
*ino
)
2494 struct HFSPlusCatalogFile record
;
2497 error
= resolvelink(hfsmp
, linkref
, &record
);
2499 if (record
.fileID
== 0)
2502 *ino
= record
.fileID
;
2508 * getkey - get a key from id by doing a thread lookup
2511 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
2513 struct BTreeIterator
* iterator
;
2514 FSBufferDescriptor btdata
;
2517 CatalogRecord
* recp
;
2521 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
2523 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2524 bzero(iterator
, sizeof(*iterator
));
2525 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
2527 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
2528 BDINIT(btdata
, recp
);
2530 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
2531 &btdata
, &datasize
, iterator
);
2535 /* Turn thread record into a cnode key (in place) */
2536 switch (recp
->recordType
) {
2537 case kHFSFileThreadRecord
:
2538 case kHFSFolderThreadRecord
:
2539 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
2540 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
2541 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
2544 case kHFSPlusFileThreadRecord
:
2545 case kHFSPlusFolderThreadRecord
:
2546 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
2547 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
2548 (keyp
->hfsPlus
.nodeName
.length
* 2);
2549 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
2558 FREE(iterator
, M_TEMP
);
2561 return MacToVFSError(result
);
2565 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
2566 * null arguments to cat_idlookup instead, but we save around 10% by not building the
2567 * cat_desc here). Both key and attrp must point to real structures.
2571 cat_getkeyplusattr(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
, struct cat_attr
*attrp
)
2575 result
= getkey(hfsmp
, cnid
, key
);
2578 result
= cat_lookupbykey(hfsmp
, key
, 0, 0, NULL
, attrp
, NULL
, NULL
);
2581 return MacToVFSError(result
);
2586 * buildrecord - build a default catalog directory or file record
2589 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
2590 CatalogRecord
*crp
, int *recordSize
)
2592 int type
= attrp
->ca_mode
& S_IFMT
;
2593 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
2596 createtime
= UTCToLocal(createtime
);
2597 if (type
== S_IFDIR
) {
2598 bzero(crp
, sizeof(HFSCatalogFolder
));
2599 crp
->recordType
= kHFSFolderRecord
;
2600 crp
->hfsFolder
.folderID
= cnid
;
2601 crp
->hfsFolder
.createDate
= createtime
;
2602 crp
->hfsFolder
.modifyDate
= createtime
;
2603 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
2604 *recordSize
= sizeof(HFSCatalogFolder
);
2606 bzero(crp
, sizeof(HFSCatalogFile
));
2607 crp
->recordType
= kHFSFileRecord
;
2608 crp
->hfsFile
.fileID
= cnid
;
2609 crp
->hfsFile
.createDate
= createtime
;
2610 crp
->hfsFile
.modifyDate
= createtime
;
2611 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
2612 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
2613 *recordSize
= sizeof(HFSCatalogFile
);
2616 struct HFSPlusBSDInfo
* bsdp
= NULL
;
2617 struct FndrFileInfo
* fip
= NULL
;
2619 if (type
== S_IFDIR
) {
2620 crp
->recordType
= kHFSPlusFolderRecord
;
2621 crp
->hfsPlusFolder
.flags
= 0;
2622 crp
->hfsPlusFolder
.valence
= 0;
2623 crp
->hfsPlusFolder
.folderID
= cnid
;
2624 crp
->hfsPlusFolder
.createDate
= createtime
;
2625 crp
->hfsPlusFolder
.contentModDate
= createtime
;
2626 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
2627 crp
->hfsPlusFolder
.accessDate
= createtime
;
2628 crp
->hfsPlusFolder
.backupDate
= 0;
2629 crp
->hfsPlusFolder
.textEncoding
= encoding
;
2630 crp
->hfsPlusFolder
.attrBlocks
= 0;
2631 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
2632 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
2633 bsdp
->special
.rawDevice
= 0;
2634 *recordSize
= sizeof(HFSPlusCatalogFolder
);
2636 crp
->recordType
= kHFSPlusFileRecord
;
2637 crp
->hfsPlusFile
.flags
= kHFSThreadExistsMask
;
2638 crp
->hfsPlusFile
.reserved1
= 0;
2639 crp
->hfsPlusFile
.fileID
= cnid
;
2640 crp
->hfsPlusFile
.createDate
= createtime
;
2641 crp
->hfsPlusFile
.contentModDate
= createtime
;
2642 crp
->hfsPlusFile
.accessDate
= createtime
;
2643 crp
->hfsPlusFile
.attributeModDate
= createtime
;
2644 crp
->hfsPlusFile
.backupDate
= 0;
2645 crp
->hfsPlusFile
.textEncoding
= encoding
;
2646 crp
->hfsPlusFile
.attrBlocks
= 0;
2647 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
2648 bsdp
->special
.rawDevice
= 0;
2652 /* BLK/CHR need to save the device info */
2653 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
2654 bzero(&crp
->hfsPlusFile
.userInfo
, 32);
2657 /* Hardlink links need to save the linkref */
2658 fip
= (FndrFileInfo
*)&attrp
->ca_finderinfo
;
2659 if ((SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
2660 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
2661 bsdp
->special
.iNodeNum
= attrp
->ca_rdev
;
2663 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2666 /* Symlinks also have a type and creator */
2667 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
2670 bzero(&crp
->hfsPlusFile
.dataFork
, 2*sizeof(HFSPlusForkData
));
2671 *recordSize
= sizeof(HFSPlusCatalogFile
);
2673 bsdp
->ownerID
= attrp
->ca_uid
;
2674 bsdp
->groupID
= attrp
->ca_gid
;
2675 bsdp
->fileMode
= attrp
->ca_mode
;
2676 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
2677 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
2683 * builddesc - build a cnode descriptor from an HFS+ key
2686 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
2687 int isdir
, struct cat_desc
*descp
)
2695 /* guess a size... */
2696 bufsize
= (3 * key
->nodeName
.length
) + 1;
2697 if (bufsize
>= sizeof(tmpbuff
) - 1) {
2698 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2700 nameptr
= &tmpbuff
[0];
2703 result
= utf8_encodestr(key
->nodeName
.unicode
,
2704 key
->nodeName
.length
* sizeof(UniChar
),
2705 nameptr
, (size_t *)&utf8len
,
2708 if (result
== ENAMETOOLONG
) {
2709 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
2710 key
->nodeName
.length
* sizeof(UniChar
),
2712 FREE(nameptr
, M_TEMP
);
2713 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
2715 result
= utf8_encodestr(key
->nodeName
.unicode
,
2716 key
->nodeName
.length
* sizeof(UniChar
),
2717 nameptr
, (size_t *)&utf8len
,
2720 descp
->cd_parentcnid
= key
->parentID
;
2721 descp
->cd_nameptr
= vfs_addname(nameptr
, utf8len
, 0, 0);
2722 descp
->cd_namelen
= utf8len
;
2723 descp
->cd_cnid
= cnid
;
2724 descp
->cd_hint
= hint
;
2725 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
2727 descp
->cd_flags
|= CD_ISDIR
;
2728 descp
->cd_encoding
= encoding
;
2729 if (nameptr
!= &tmpbuff
[0]) {
2730 FREE(nameptr
, M_TEMP
);
2737 * getbsdattr - get attributes in bsd format
2741 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
2743 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
2744 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
2746 attrp
->ca_recflags
= crp
->flags
;
2747 attrp
->ca_nlink
= 1;
2748 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
2749 attrp
->ca_atimeondisk
= attrp
->ca_atime
;
2750 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
2751 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
2752 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
2753 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
2755 if ((bsd
->fileMode
& S_IFMT
) == 0) {
2756 attrp
->ca_flags
= 0;
2757 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2758 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2760 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
2762 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
2766 attrp
->ca_uid
= bsd
->ownerID
;
2767 attrp
->ca_gid
= bsd
->groupID
;
2768 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
2769 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
2770 switch (attrp
->ca_mode
& S_IFMT
) {
2771 case S_IFCHR
: /* fall through */
2773 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
2776 /* Pick up the hard link count */
2777 if (bsd
->special
.linkCount
> 0)
2778 attrp
->ca_nlink
= bsd
->special
.linkCount
;
2782 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
) {
2784 * Override the permissions as determined by the mount auguments
2785 * in ALMOST the same way unset permissions are treated but keep
2786 * track of whether or not the file or folder is hfs locked
2787 * by leaving the h_pflags field unchanged from what was unpacked
2788 * out of the catalog.
2790 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2791 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2796 if (!S_ISDIR(attrp
->ca_mode
)) {
2797 attrp
->ca_mode
&= ~S_IFMT
;
2798 attrp
->ca_mode
|= S_IFDIR
;
2800 attrp
->ca_nlink
= 2 + ((HFSPlusCatalogFolder
*)crp
)->valence
;
2801 attrp
->ca_entries
= ((HFSPlusCatalogFolder
*)crp
)->valence
;
2802 attrp
->ca_attrblks
= ((HFSPlusCatalogFolder
*)crp
)->attrBlocks
;
2804 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2805 if (crp
->flags
& kHFSFileLockedMask
) {
2806 /* The file's supposed to be locked:
2807 Make sure at least one of the IMMUTABLE bits is set: */
2808 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
2809 attrp
->ca_flags
|= UF_IMMUTABLE
;
2811 /* The file's supposed to be unlocked: */
2812 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
2814 /* get total blocks (both forks) */
2815 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
2816 attrp
->ca_attrblks
= crp
->attrBlocks
;
2817 /* On HFS+ the ThreadExists flag must always be set. */
2818 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
2819 attrp
->ca_recflags
|= kHFSThreadExistsMask
;
2822 attrp
->ca_fileid
= crp
->fileID
;
2824 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
2828 * promotekey - promote hfs key to hfs plus key
2832 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
2833 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
2835 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
2839 *encoding
= hfsmp
->hfs_encoding
;
2841 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
2842 kHFSPlusMaxFileNameChars
, &uniCount
);
2844 * When an HFS name cannot be encoded with the current
2845 * encoding use MacRoman as a fallback.
2847 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
2849 (void) mac_roman_to_unicode(hfskey
->nodeName
,
2850 keyp
->nodeName
.unicode
,
2851 kHFSPlusMaxFileNameChars
,
2855 keyp
->nodeName
.length
= uniCount
;
2856 keyp
->parentID
= hfskey
->parentID
;
2860 * promotefork - promote hfs fork info to hfs plus
2864 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
2865 int resource
, struct cat_fork
* forkp
)
2867 struct HFSPlusExtentDescriptor
*xp
;
2868 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2870 bzero(forkp
, sizeof(*forkp
));
2871 xp
= &forkp
->cf_extents
[0];
2873 forkp
->cf_size
= filep
->rsrcLogicalSize
;
2874 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
2875 forkp
->cf_bytesread
= 0;
2876 forkp
->cf_vblocks
= 0;
2877 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
2878 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
2879 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
2880 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
2881 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
2882 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
2884 forkp
->cf_size
= filep
->dataLogicalSize
;
2885 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
2886 forkp
->cf_bytesread
= 0;
2887 forkp
->cf_vblocks
= 0;
2888 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
2889 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
2890 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
2891 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
2892 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
2893 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
2898 * promoteattr - promote hfs catalog attributes to hfs plus
2902 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
2904 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2906 if (dataPtr
->recordType
== kHFSFolderRecord
) {
2907 struct HFSCatalogFolder
* folder
;
2909 folder
= (struct HFSCatalogFolder
*) dataPtr
;
2910 crp
->recordType
= kHFSPlusFolderRecord
;
2911 crp
->flags
= folder
->flags
;
2912 crp
->fileID
= folder
->folderID
;
2913 crp
->createDate
= LocalToUTC(folder
->createDate
);
2914 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
2915 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
2916 crp
->reserved1
= folder
->valence
;
2917 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
2919 struct HFSCatalogFile
* file
;
2921 file
= (struct HFSCatalogFile
*) dataPtr
;
2922 crp
->recordType
= kHFSPlusFileRecord
;
2923 crp
->flags
= file
->flags
;
2924 crp
->fileID
= file
->fileID
;
2925 crp
->createDate
= LocalToUTC(file
->createDate
);
2926 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
2927 crp
->backupDate
= LocalToUTC(file
->backupDate
);
2929 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
2930 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
2931 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
2932 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
2934 crp
->textEncoding
= 0;
2935 crp
->attributeModDate
= crp
->contentModDate
;
2936 crp
->accessDate
= crp
->contentModDate
;
2937 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
2938 crp
->attrBlocks
= 0;
2942 * Build a catalog node thread record from a catalog key
2943 * and return the size of the record.
2946 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
2951 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
2952 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
2954 size
= sizeof(HFSCatalogThread
);
2957 rec
->recordType
= kHFSFolderThreadRecord
;
2959 rec
->recordType
= kHFSFileThreadRecord
;
2960 rec
->parentID
= key
->parentID
;
2961 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
2964 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
2965 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
2967 size
= sizeof(HFSPlusCatalogThread
);
2969 rec
->recordType
= kHFSPlusFolderThreadRecord
;
2971 rec
->recordType
= kHFSPlusFileThreadRecord
;
2973 rec
->parentID
= key
->parentID
;
2974 bcopy(&key
->nodeName
, &rec
->nodeName
,
2975 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
2977 /* HFS Plus has varaible sized thread records */
2978 size
-= (sizeof(rec
->nodeName
.unicode
) -
2979 (rec
->nodeName
.length
* sizeof(UniChar
)));
2986 * Build a catalog node thread key.
2989 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
2992 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
2993 key
->hfs
.reserved
= 0;
2994 key
->hfs
.parentID
= parentID
;
2995 key
->hfs
.nodeName
[0] = 0;
2997 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
2998 key
->hfsPlus
.parentID
= parentID
;
2999 key
->hfsPlus
.nodeName
.length
= 0;
3004 * Extract the text encoding from a catalog node record.
3007 getencoding(const CatalogRecord
*crp
)
3011 if (crp
->recordType
== kHFSPlusFolderRecord
)
3012 encoding
= crp
->hfsPlusFolder
.textEncoding
;
3013 else if (crp
->recordType
== kHFSPlusFileRecord
)
3014 encoding
= crp
->hfsPlusFile
.textEncoding
;
3022 * Extract the CNID from a catalog node record.
3025 getcnid(const CatalogRecord
*crp
)
3029 switch (crp
->recordType
) {
3030 case kHFSFolderRecord
:
3031 cnid
= crp
->hfsFolder
.folderID
;
3033 case kHFSFileRecord
:
3034 cnid
= crp
->hfsFile
.fileID
;
3036 case kHFSPlusFolderRecord
:
3037 cnid
= crp
->hfsPlusFolder
.folderID
;
3039 case kHFSPlusFileRecord
:
3040 cnid
= crp
->hfsPlusFile
.fileID
;
3043 printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp
);
3051 * Extract the parent ID from a catalog node record.
3054 getparentcnid(const CatalogRecord
*recp
)
3058 switch (recp
->recordType
) {
3059 case kHFSFileThreadRecord
:
3060 case kHFSFolderThreadRecord
:
3061 cnid
= recp
->hfsThread
.parentID
;
3064 case kHFSPlusFileThreadRecord
:
3065 case kHFSPlusFolderThreadRecord
:
3066 cnid
= recp
->hfsPlusThread
.parentID
;
3069 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp
);
3077 * Determine if a catalog node record is a directory.
3080 isadir(const CatalogRecord
*crp
)
3082 return (crp
->recordType
== kHFSFolderRecord
||
3083 crp
->recordType
== kHFSPlusFolderRecord
);