2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/systm.h>
27 #include <sys/kernel.h>
28 #include <sys/malloc.h>
30 #include <sys/mount.h>
31 #include <sys/vnode.h>
32 #include <sys/namei.h>
33 #include <sys/dirent.h>
34 #include <vfs/vfs_support.h>
35 #include <libkern/libkern.h>
37 #include <sys/utfconv.h>
40 #include "hfs_catalog.h"
41 #include "hfs_format.h"
42 #include "hfs_endian.h"
44 #include "hfscommon/headers/BTreesInternal.h"
45 #include "hfscommon/headers/CatalogPrivate.h"
46 #include "hfscommon/headers/HFSUnicodeWrappers.h"
48 extern OSErr
PositionIterator(CatalogIterator
*cip
, UInt32 offset
, BTreeIterator
*bip
, UInt16
*op
);
51 * Initialization of an FSBufferDescriptor structure.
53 #define BDINIT(bd, addr) { \
54 (bd).bufferAddress = (addr); \
55 (bd).itemSize = sizeof(*(addr)); \
61 BTreeIterator iterator
;
62 HFSPlusCatalogKey key
;
67 struct cat_desc
* s_desc
;
68 struct cat_attr
* s_attr
;
69 struct cat_fork
* s_datafork
;
70 struct cat_fork
* s_rsrcfork
;
71 struct hfsmount
* s_hfsmp
;
75 static int cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
76 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
78 static int cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
79 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
);
81 extern int mac_roman_to_unicode(const Str31 hfs_str
, UniChar
*uni_str
,
82 UInt32 maxCharLen
, UInt32
*unicodeChars
);
84 extern int unicode_to_hfs(ExtendedVCB
*vcb
, ByteCount srcLen
,
85 const u_int16_t
* srcStr
, Str31 dstStr
, int retry
);
88 /* Internal catalog support routines */
90 int resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
);
92 static int getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
);
94 static int buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
95 HFSPlusCatalogKey
*key
, int retry
);
97 static void buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
);
99 static void buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
, CatalogRecord
*crp
, int *recordSize
);
101 static int catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, u_int16_t reclen
, struct update_state
*state
);
103 static int builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
104 int isdir
, struct cat_desc
*descp
);
106 static void getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
);
108 static void promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
, HFSPlusCatalogKey
*keyp
, u_long
*encoding
);
109 static void promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*file
, int resource
, struct cat_fork
* forkp
);
110 static void promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
);
112 static cnid_t
getcnid(const CatalogRecord
*crp
);
113 static u_long
getencoding(const CatalogRecord
*crp
);
114 static cnid_t
getparentcnid(const CatalogRecord
*recp
);
116 static int isadir(const CatalogRecord
*crp
);
118 static int buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
);
125 struct hfsmount
*hfsmp
,
126 CatalogRecord
* recp
,
127 struct cat_attr
*attrp
,
128 struct cat_fork
*datafp
,
129 struct cat_fork
*rsrcfp
)
131 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
134 struct HFSPlusCatalogFile cnoderec
;
136 promoteattr(hfsmp
, recp
, &cnoderec
);
137 getbsdattr(hfsmp
, &cnoderec
, attrp
);
139 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
143 bzero(datafp
, sizeof(*datafp
));
145 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 0, datafp
);
146 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, 1, rsrcfp
);
148 bcopy(&recp
->hfsPlusFile
.dataFork
, datafp
, sizeof(*datafp
));
149 bcopy(&recp
->hfsPlusFile
.resourceFork
, rsrcfp
, sizeof(*rsrcfp
));
155 struct hfsmount
*hfsmp
,
157 CatalogRecord
* recp
,
158 struct cat_desc
*descp
)
160 int std_hfs
= HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
;
161 HFSPlusCatalogKey
* pluskey
= NULL
;
165 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
166 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
169 pluskey
= (HFSPlusCatalogKey
*)key
;
170 encoding
= getencoding(recp
);
173 builddesc(pluskey
, getcnid(recp
), 0, encoding
, isadir(recp
), descp
);
175 FREE(pluskey
, M_TEMP
);
185 cat_releasedesc(struct cat_desc
*descp
)
192 if ((descp
->cd_flags
& CD_HASBUF
) &&
193 (descp
->cd_nameptr
!= NULL
)) {
194 name
= descp
->cd_nameptr
;
195 descp
->cd_nameptr
= NULL
;
196 descp
->cd_namelen
= 0;
197 descp
->cd_flags
&= ~CD_HASBUF
;
200 descp
->cd_nameptr
= NULL
;
201 descp
->cd_namelen
= 0;
205 * These Catalog functions allow access to the HFS Catalog (database).
206 * The catalog b-tree lock must be aquired before calling any of these routines.
210 * cat_lookup - lookup a catalog node using a cnode decriptor
213 cat_lookup(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
214 struct cat_desc
*outdescp
, struct cat_attr
*attrp
,
215 struct cat_fork
*forkp
)
221 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
223 MALLOC(keyp
, CatalogKey
*, sizeof(CatalogKey
), M_TEMP
, M_WAITOK
);
225 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)keyp
, 1);
229 result
= cat_lookupbykey(hfsmp
, keyp
, descp
->cd_hint
, wantrsrc
, outdescp
, attrp
, forkp
);
231 if (result
== ENOENT
) {
233 result
= cat_lookupmangled(hfsmp
, descp
, wantrsrc
, outdescp
, attrp
, forkp
);
234 } else if (hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
235 // make MacRoman key from utf-8
236 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
237 // update desc text encoding so that other catalog ops succeed
247 cat_insertfilethread(struct hfsmount
*hfsmp
, struct cat_desc
*descp
)
249 struct BTreeIterator
*iterator
;
250 struct FSBufferDescriptor file_data
;
251 struct HFSCatalogFile file_rec
;
256 if (HFSTOVCB(hfsmp
)->vcbSigWord
!= kHFSSigWord
)
259 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
261 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
262 bzero(&iterator
[0], 2* sizeof(*iterator
));
263 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
[0].key
, 0);
267 // XXXdbg - preflight all btree operations to make sure there's enough space
268 result
= BTCheckFreeSpace(fcb
);
272 BDINIT(file_data
, &file_rec
);
273 result
= BTSearchRecord(fcb
, &iterator
[0], &file_data
, &datasize
, &iterator
[0]);
277 if (file_rec
.recordType
!= kHFSFileRecord
) {
282 if ((file_rec
.flags
& kHFSThreadExistsMask
) == 0) {
283 struct FSBufferDescriptor thread_data
;
284 struct HFSCatalogThread thread_rec
;
286 file_rec
.flags
|= kHFSThreadExistsMask
;
287 BDINIT(thread_data
, &thread_rec
);
288 thread_data
.itemSize
= buildthread(&iterator
[0].key
, &thread_rec
, 1, 0);
289 buildthreadkey(file_rec
.fileID
, 1, (CatalogKey
*)&iterator
[1].key
);
291 result
= BTInsertRecord(fcb
, &iterator
[1], &thread_data
, thread_data
.itemSize
);
295 (void) BTReplaceRecord(fcb
, &iterator
[0], &file_data
, datasize
);
296 (void) BTFlushPath(fcb
);
299 (void) BTFlushPath(fcb
);
300 FREE(iterator
, M_TEMP
);
302 return MacToVFSError(result
);
307 * cat_idlookup - lookup a catalog node using a cnode id
310 cat_idlookup(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*outdescp
,
311 struct cat_attr
*attrp
, struct cat_fork
*forkp
)
313 struct BTreeIterator
* iterator
;
314 FSBufferDescriptor btdata
;
317 CatalogRecord
* recp
;
321 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
323 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
324 bzero(iterator
, sizeof(*iterator
));
325 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
327 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
328 BDINIT(btdata
, recp
);
330 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
331 &btdata
, &datasize
, iterator
);
335 /* Turn thread record into a cnode key (in place) */
336 switch (recp
->recordType
) {
337 case kHFSFileThreadRecord
:
338 case kHFSFolderThreadRecord
:
339 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
340 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
343 case kHFSPlusFileThreadRecord
:
344 case kHFSPlusFolderThreadRecord
:
345 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
346 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
347 (keyp
->hfsPlus
.nodeName
.length
* 2);
355 result
= cat_lookupbykey(hfsmp
, keyp
, 0, 0, outdescp
, attrp
, forkp
);
358 FREE(iterator
, M_TEMP
);
360 return MacToVFSError(result
);
365 * cat_lookupmangled - lookup a catalog node using a mangled name
368 cat_lookupmangled(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, int wantrsrc
,
369 struct cat_desc
*outdescp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
378 fileID
= GetEmbeddedFileID(descp
->cd_nameptr
, descp
->cd_namelen
, &prefixlen
);
379 if (fileID
< kHFSFirstUserCatalogNodeID
)
382 result
= cat_idlookup(hfsmp
, fileID
, outdescp
, attrp
, forkp
);
386 /* It must be in the correct directory */
387 if (descp
->cd_parentcnid
!= outdescp
->cd_parentcnid
)
390 if ((outdescp
->cd_namelen
< prefixlen
) ||
391 bcmp(outdescp
->cd_nameptr
, descp
->cd_nameptr
, prefixlen
-6) != 0)
397 cat_releasedesc(outdescp
);
403 * cat_lookupbykey - lookup a catalog node using a cnode key
406 cat_lookupbykey(struct hfsmount
*hfsmp
, CatalogKey
*keyp
, u_long hint
, int wantrsrc
,
407 struct cat_desc
*descp
, struct cat_attr
*attrp
, struct cat_fork
*forkp
)
409 struct BTreeIterator
* iterator
;
410 FSBufferDescriptor btdata
;
411 CatalogRecord
* recp
;
419 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
421 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
422 BDINIT(btdata
, recp
);
423 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
424 bzero(iterator
, sizeof(*iterator
));
425 iterator
->hint
.nodeNum
= hint
;
426 bcopy(keyp
, &iterator
->key
, sizeof(CatalogKey
));
428 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
429 &btdata
, &datasize
, iterator
);
433 /* Save the cnid now in case there's a hard link */
434 cnid
= getcnid(recp
);
435 encoding
= getencoding(recp
);
436 hint
= iterator
->hint
.nodeNum
;
438 /* Hide the journal files (if any) */
440 ((cnid
== hfsmp
->hfs_jnlfileid
) ||
441 (cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
448 * When a hardlink link is encountered, auto resolve it
452 && (recp
->recordType
== kHFSPlusFileRecord
)
453 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
454 && (SWAP_BE32(recp
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
)
455 && ((to_bsd_time(recp
->hfsPlusFile
.createDate
) == HFSTOVCB(hfsmp
)->vcbCrDate
) ||
456 (to_bsd_time(recp
->hfsPlusFile
.createDate
) == hfsmp
->hfs_metadata_createdate
))) {
458 ilink
= recp
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
460 (void) resolvelink(hfsmp
, ilink
, (struct HFSPlusCatalogFile
*)recp
);
465 struct HFSPlusCatalogFile cnoderec
;
467 promoteattr(hfsmp
, recp
, &cnoderec
);
468 getbsdattr(hfsmp
, &cnoderec
, attrp
);
470 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)recp
, attrp
);
472 attrp
->ca_rdev
= ilink
;
477 bzero(forkp
, sizeof(*forkp
));
479 promotefork(hfsmp
, (HFSCatalogFile
*)&recp
->hfsFile
, wantrsrc
, forkp
);
481 bcopy(&recp
->hfsPlusFile
.resourceFork
, forkp
, sizeof(*forkp
));
483 bcopy(&recp
->hfsPlusFile
.dataFork
, forkp
, sizeof(*forkp
));
486 HFSPlusCatalogKey
* pluskey
= NULL
;
489 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
490 promotekey(hfsmp
, (HFSCatalogKey
*)&iterator
->key
, pluskey
, &encoding
);
493 pluskey
= (HFSPlusCatalogKey
*)&iterator
->key
;
495 builddesc(pluskey
, cnid
, hint
, encoding
, isadir(recp
), descp
);
497 FREE(pluskey
, M_TEMP
);
501 FREE(iterator
, M_TEMP
);
504 return MacToVFSError(result
);
509 * cat_create - create a node in the catalog
512 cat_create(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
513 struct cat_desc
*out_descp
)
518 FSBufferDescriptor btdata
;
526 modeformat
= attrp
->ca_mode
& S_IFMT
;
528 vcb
= HFSTOVCB(hfsmp
);
529 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
530 nextCNID
= vcb
->vcbNxtCNID
;
531 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
533 if (std_hfs
&& nextCNID
== 0xFFFFFFFF)
536 /* Get space for iterator, key and data */
537 MALLOC(bto
, struct btobj
*, sizeof(struct btobj
), M_TEMP
, M_WAITOK
);
538 bzero(bto
, sizeof(struct btobj
));
540 result
= buildkey(hfsmp
, descp
, &bto
->key
, 0);
545 encoding
= hfs_pickencoding(bto
->key
.nodeName
.unicode
,
546 bto
->key
.nodeName
.length
);
547 hfs_setencodingbits(hfsmp
, encoding
);
550 // XXXdbg - preflight all btree operations to make sure there's enough space
551 result
= BTCheckFreeSpace(fcb
);
556 * Insert the thread record first
558 if (!std_hfs
|| (modeformat
== S_IFDIR
)) {
559 datalen
= buildthread((void*)&bto
->key
, &bto
->data
, std_hfs
,
560 S_ISDIR(attrp
->ca_mode
));
561 btdata
.bufferAddress
= &bto
->data
;
562 btdata
.itemSize
= datalen
;
563 btdata
.itemCount
= 1;
566 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*) &bto
->iterator
.key
);
568 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
569 if (result
== btExists
&& !std_hfs
) {
571 * Allow CNIDs on HFS Plus volumes to wrap around
574 if (nextCNID
< kHFSFirstUserCatalogNodeID
) {
575 vcb
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
576 vcb
->vcbFlags
|= 0xFF00;
577 nextCNID
= kHFSFirstUserCatalogNodeID
;
583 if (result
) goto exit
;
587 * Now insert the file/directory record
589 buildrecord(attrp
, nextCNID
, std_hfs
, encoding
, &bto
->data
, &datalen
);
590 btdata
.bufferAddress
= &bto
->data
;
591 btdata
.itemSize
= datalen
;
592 btdata
.itemCount
= 1;
594 bcopy(&bto
->key
, &bto
->iterator
.key
, sizeof(bto
->key
));
596 result
= BTInsertRecord(fcb
, &bto
->iterator
, &btdata
, datalen
);
598 if (result
== btExists
)
601 /* Back out the thread record */
602 if (!std_hfs
|| S_ISDIR(attrp
->ca_mode
)) {
603 buildthreadkey(nextCNID
, std_hfs
, (CatalogKey
*)&bto
->iterator
.key
);
604 (void) BTDeleteRecord(fcb
, &bto
->iterator
);
610 * Insert was Successfull, update name, parent and volume
614 if (out_descp
!= NULL
) {
615 HFSPlusCatalogKey
* pluskey
= NULL
;
618 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
619 promotekey(hfsmp
, (HFSCatalogKey
*)&bto
->iterator
.key
, pluskey
, &encoding
);
622 pluskey
= (HFSPlusCatalogKey
*)&bto
->iterator
.key
;
624 builddesc(pluskey
, nextCNID
, bto
->iterator
.hint
.nodeNum
,
625 encoding
, S_ISDIR(attrp
->ca_mode
), out_descp
);
627 FREE(pluskey
, M_TEMP
);
630 attrp
->ca_fileid
= nextCNID
;
632 /* Update parent stats */
633 TrashCatalogIterator(vcb
, descp
->cd_parentcnid
);
635 /* Update volume stats */
636 if (++nextCNID
< kHFSFirstUserCatalogNodeID
) {
637 vcb
->vcbAtrb
|= kHFSCatalogNodeIDsReusedMask
;
638 nextCNID
= kHFSFirstUserCatalogNodeID
;
640 vcb
->vcbNxtCNID
= nextCNID
;
641 vcb
->vcbFlags
|= 0xFF00;
644 (void) BTFlushPath(fcb
);
647 return MacToVFSError(result
);
652 * cnode_rename - rename a catalog node
654 * Assumes that the target's directory exists.
656 * Order of B-tree operations:
657 * 1. BTSearchRecord(from_cnode, &data);
658 * 2. BTInsertRecord(to_cnode, &data);
659 * 3. BTDeleteRecord(from_cnode);
660 * 4. BTDeleteRecord(from_thread);
661 * 5. BTInsertRecord(to_thread);
665 struct hfsmount
* hfsmp
,
666 struct cat_desc
* from_cdp
,
667 struct cat_desc
* todir_cdp
,
668 struct cat_desc
* to_cdp
,
669 struct cat_desc
* out_cdp
)
671 struct BTreeIterator
* to_iterator
= NULL
;
672 struct BTreeIterator
* from_iterator
= NULL
;
673 FSBufferDescriptor btdata
;
674 CatalogRecord
* recp
= NULL
;
675 HFSPlusCatalogKey
* to_key
;
682 int directory
= from_cdp
->cd_flags
& CD_ISDIR
;
686 vcb
= HFSTOVCB(hfsmp
);
687 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
688 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
690 if (from_cdp
->cd_namelen
== 0 || to_cdp
->cd_namelen
== 0)
693 MALLOC(from_iterator
, BTreeIterator
*, sizeof(*from_iterator
), M_TEMP
, M_WAITOK
);
694 bzero(from_iterator
, sizeof(*from_iterator
));
695 if ((result
= buildkey(hfsmp
, from_cdp
, (HFSPlusCatalogKey
*)&from_iterator
->key
, 0)))
698 MALLOC(to_iterator
, BTreeIterator
*, sizeof(*to_iterator
), M_TEMP
, M_WAITOK
);
699 bzero(to_iterator
, sizeof(*to_iterator
));
700 if ((result
= buildkey(hfsmp
, to_cdp
, (HFSPlusCatalogKey
*)&to_iterator
->key
, 0)))
703 // XXXdbg - preflight all btree operations to make sure there's enough space
704 result
= BTCheckFreeSpace(fcb
);
708 to_key
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
709 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
710 BDINIT(btdata
, recp
);
713 * When moving a directory, make sure its a valid move.
715 if (directory
&& (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)) {
716 struct BTreeIterator iterator
= {0};
717 cnid_t cnid
= from_cdp
->cd_cnid
;
718 cnid_t pathcnid
= todir_cdp
->cd_parentcnid
;
720 /* First check the obvious ones */
721 if (cnid
== fsRtDirID
||
722 cnid
== to_cdp
->cd_parentcnid
||
729 * Traverese destination path all the way back to the root
730 * making sure that source directory is not encountered.
733 while (pathcnid
> fsRtDirID
) {
734 buildthreadkey(pathcnid
, std_hfs
,
735 (CatalogKey
*)&iterator
.key
);
736 result
= BTSearchRecord(fcb
, &iterator
, &btdata
,
738 if (result
) goto exit
;
740 pathcnid
= getparentcnid(recp
);
741 if (pathcnid
== cnid
) {
749 * Step 1: Find cnode data at old location
751 result
= BTSearchRecord(fcb
, from_iterator
, &btdata
,
752 &datasize
, from_iterator
);
756 /* Update the text encoding (on disk and in descriptor */
758 encoding
= hfs_pickencoding(to_key
->nodeName
.unicode
,
759 to_key
->nodeName
.length
);
760 hfs_setencodingbits(hfsmp
, encoding
);
761 recp
->hfsPlusFile
.textEncoding
= encoding
;
763 out_cdp
->cd_encoding
= encoding
;
766 if (std_hfs
&& !directory
&&
767 !(recp
->hfsFile
.flags
& kHFSThreadExistsMask
))
771 * If the keys are identical then there's nothing left to do!
773 * update the hint and exit
776 if (std_hfs
&& hfskeycompare(to_key
, iter
->key
) == 0)
778 if (!std_hfs
&& hfspluskeycompare(to_key
, iter
->key
) == 0)
782 /* Trash the iterator caches */
783 TrashCatalogIterator(vcb
, from_cdp
->cd_parentcnid
);
784 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
785 TrashCatalogIterator(vcb
, to_cdp
->cd_parentcnid
);
787 /* Step 2: Insert cnode at new location */
788 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
789 if (result
== btExists
) {
790 int fromtype
= recp
->recordType
;
792 if (from_cdp
->cd_parentcnid
!= to_cdp
->cd_parentcnid
)
793 goto exit
; /* EEXIST */
795 /* Find cnode data at new location */
796 result
= BTSearchRecord(fcb
, to_iterator
, &btdata
, &datasize
, NULL
);
798 if ((fromtype
!= recp
->recordType
) ||
799 (from_cdp
->cd_cnid
!= getcnid(recp
)))
800 goto exit
; /* EEXIST */
802 /* The old name is a case variant and must be removed */
803 result
= BTDeleteRecord(fcb
, from_iterator
);
807 /* Insert cnode (now that case duplicate is gone) */
808 result
= BTInsertRecord(fcb
, to_iterator
, &btdata
, datasize
);
810 /* Try and restore original before leaving */
815 err
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
817 panic("cat_create: could not undo (BTInsert = %d)", err
);
820 (void) BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
829 /* Step 3: Remove cnode from old location */
831 result
= BTDeleteRecord(fcb
, from_iterator
);
833 /* Try and delete new record before leaving */
838 err
= BTDeleteRecord(fcb
, to_iterator
);
840 panic("cat_create: could not undo (BTDelete = %d)", err
);
843 (void) BTDeleteRecord(fcb
, to_iterator
);
849 /* #### POINT OF NO RETURN #### */
852 * Step 4: Remove cnode's old thread record
854 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
855 (void) BTDeleteRecord(fcb
, from_iterator
);
858 * Step 5: Insert cnode's new thread record
859 * (optional for HFS files)
862 datasize
= buildthread(&to_iterator
->key
, recp
, std_hfs
, directory
);
863 btdata
.itemSize
= datasize
;
864 buildthreadkey(from_cdp
->cd_cnid
, std_hfs
, (CatalogKey
*)&from_iterator
->key
);
865 result
= BTInsertRecord(fcb
, from_iterator
, &btdata
, datasize
);
869 HFSPlusCatalogKey
* pluskey
= NULL
;
872 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
873 promotekey(hfsmp
, (HFSCatalogKey
*)&to_iterator
->key
, pluskey
, &encoding
);
876 pluskey
= (HFSPlusCatalogKey
*)&to_iterator
->key
;
878 builddesc(pluskey
, from_cdp
->cd_cnid
, to_iterator
->hint
.nodeNum
,
879 encoding
, directory
, out_cdp
);
881 FREE(pluskey
, M_TEMP
);
885 (void) BTFlushPath(fcb
);
887 FREE(from_iterator
, M_TEMP
);
889 FREE(to_iterator
, M_TEMP
);
892 return MacToVFSError(result
);
897 * cat_delete - delete a node from the catalog
899 * Order of B-tree operations:
900 * 1. BTDeleteRecord(cnode);
901 * 2. BTDeleteRecord(thread);
902 * 3. BTUpdateRecord(parent);
905 cat_delete(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
)
909 BTreeIterator
*iterator
;
914 vcb
= HFSTOVCB(hfsmp
);
915 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
916 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
920 * The root directory cannot be deleted
921 * A directory must be empty
922 * A file must be zero length (no blocks)
924 if (descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
||
925 descp
->cd_parentcnid
== kRootParID
)
928 /* XXX Preflight Missing */
930 /* Get space for iterator */
931 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
932 bzero(iterator
, sizeof(*iterator
));
935 * Derive a key from either the file ID (for a virtual inode)
938 if (descp
->cd_namelen
== 0) {
939 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
940 cnid
= attrp
->ca_fileid
;
942 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
943 cnid
= descp
->cd_cnid
;
948 // XXXdbg - preflight all btree operations to make sure there's enough space
949 result
= BTCheckFreeSpace(fcb
);
954 result
= BTDeleteRecord(fcb
, iterator
);
958 /* Delete thread record, ignore errors */
959 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
960 (void) BTDeleteRecord(fcb
, iterator
);
962 TrashCatalogIterator(vcb
, descp
->cd_parentcnid
);
965 (void) BTFlushPath(fcb
);
966 FREE(iterator
, M_TEMP
);
968 return MacToVFSError(result
);
973 * cnode_update - update the catalog node described by descp
974 * using the data from attrp and forkp.
977 cat_update(struct hfsmount
*hfsmp
, struct cat_desc
*descp
, struct cat_attr
*attrp
,
978 struct cat_fork
*dataforkp
, struct cat_fork
*rsrcforkp
)
982 BTreeIterator
* iterator
;
983 struct update_state state
;
987 vcb
= HFSTOVCB(hfsmp
);
988 fcb
= GetFileControlBlock(vcb
->catalogRefNum
);
989 std_hfs
= (vcb
->vcbSigWord
== kHFSSigWord
);
991 state
.s_desc
= descp
;
992 state
.s_attr
= attrp
;
993 state
.s_datafork
= dataforkp
;
994 state
.s_rsrcfork
= rsrcforkp
;
995 state
.s_hfsmp
= hfsmp
;
997 /* Get space for iterator */
998 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
999 bzero(iterator
, sizeof(*iterator
));
1002 * For open-deleted files we need to do a lookup by cnid
1003 * (using thread rec).
1005 * For hard links, the target of the update is the inode
1006 * itself (not the link record) so a lookup by fileid
1007 * (i.e. thread rec) is needed.
1009 if ((descp
->cd_cnid
!= attrp
->ca_fileid
) || (descp
->cd_namelen
== 0))
1010 result
= getkey(hfsmp
, attrp
->ca_fileid
, (CatalogKey
*)&iterator
->key
);
1012 result
= buildkey(hfsmp
, descp
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1016 /* Pass a node hint */
1017 iterator
->hint
.nodeNum
= descp
->cd_hint
;
1019 result
= BTUpdateRecord(fcb
, iterator
,
1020 (IterateCallBackProcPtr
)catrec_update
, &state
);
1024 /* Update the node hint. */
1025 descp
->cd_hint
= iterator
->hint
.nodeNum
;
1028 (void) BTFlushPath(fcb
);
1029 FREE(iterator
, M_TEMP
);
1031 return MacToVFSError(result
);
1035 * catrec_update - Update the fields of a catalog record
1036 * This is called from within BTUpdateRecord.
1039 catrec_update(const CatalogKey
*ckp
, CatalogRecord
*crp
, u_int16_t reclen
,
1040 struct update_state
*state
)
1042 struct cat_desc
*descp
;
1043 struct cat_attr
*attrp
;
1044 struct cat_fork
*forkp
;
1045 struct hfsmount
*hfsmp
;
1049 descp
= state
->s_desc
;
1050 attrp
= state
->s_attr
;
1051 hfsmp
= state
->s_hfsmp
;
1052 blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1054 switch (crp
->recordType
) {
1055 case kHFSFolderRecord
: {
1056 HFSCatalogFolder
*dir
;
1058 dir
= (struct HFSCatalogFolder
*)crp
;
1059 /* Do a quick sanity check */
1060 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1061 (dir
->folderID
!= descp
->cd_cnid
))
1062 return (btNotFound
);
1063 dir
->valence
= attrp
->ca_entries
;
1064 dir
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1065 dir
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1066 dir
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1067 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 16);
1068 bcopy(&attrp
->ca_finderinfo
[16], &dir
->finderInfo
, 16);
1071 case kHFSFileRecord
: {
1072 HFSCatalogFile
*file
;
1074 file
= (struct HFSCatalogFile
*)crp
;
1075 /* Do a quick sanity check */
1076 if ((ckp
->hfs
.parentID
!= descp
->cd_parentcnid
) ||
1077 (file
->fileID
!= attrp
->ca_fileid
))
1078 return (btNotFound
);
1079 file
->createDate
= UTCToLocal(to_hfs_time(attrp
->ca_itime
));
1080 file
->modifyDate
= UTCToLocal(to_hfs_time(attrp
->ca_mtime
));
1081 file
->backupDate
= UTCToLocal(to_hfs_time(attrp
->ca_btime
));
1082 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 16);
1083 bcopy(&attrp
->ca_finderinfo
[16], &file
->finderInfo
, 16);
1084 if (state
->s_rsrcfork
) {
1085 forkp
= state
->s_rsrcfork
;
1086 file
->rsrcLogicalSize
= forkp
->cf_size
;
1087 file
->rsrcPhysicalSize
= forkp
->cf_blocks
* blksize
;
1088 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1089 file
->rsrcExtents
[i
].startBlock
=
1090 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1091 file
->rsrcExtents
[i
].blockCount
=
1092 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1095 if (state
->s_datafork
) {
1096 forkp
= state
->s_datafork
;
1097 file
->dataLogicalSize
= forkp
->cf_size
;
1098 file
->dataPhysicalSize
= forkp
->cf_blocks
* blksize
;
1099 for (i
= 0; i
< kHFSExtentDensity
; ++i
) {
1100 file
->dataExtents
[i
].startBlock
=
1101 (u_int16_t
)forkp
->cf_extents
[i
].startBlock
;
1102 file
->dataExtents
[i
].blockCount
=
1103 (u_int16_t
)forkp
->cf_extents
[i
].blockCount
;
1108 case kHFSPlusFolderRecord
: {
1109 HFSPlusCatalogFolder
*dir
;
1111 dir
= (struct HFSPlusCatalogFolder
*)crp
;
1112 /* Do a quick sanity check */
1113 if ((ckp
->hfsPlus
.parentID
!= descp
->cd_parentcnid
) ||
1114 (dir
->folderID
!= descp
->cd_cnid
))
1115 return (btNotFound
);
1116 dir
->valence
= attrp
->ca_entries
;
1117 dir
->createDate
= to_hfs_time(attrp
->ca_itime
);
1118 dir
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1119 dir
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1120 dir
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1121 dir
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1122 dir
->textEncoding
= descp
->cd_encoding
;
1123 bcopy(&attrp
->ca_finderinfo
[0], &dir
->userInfo
, 32);
1125 * Update the BSD Info if it was already initialized on
1126 * disk or if the runtime values have been modified.
1128 * If the BSD info was already initialized, but
1129 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1130 * probably different than what was on disk. We don't want
1131 * to overwrite the on-disk values (so if we turn off
1132 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1133 * This way, we can still change fields like the mode or
1134 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1136 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1137 * won't change the uid or gid from their defaults. So, if
1138 * the BSD info wasn't set, and the runtime values are not
1139 * default, then what changed was the mode or flags. We
1140 * have to set the uid and gid to something, so use the
1141 * supplied values (which will be default), which has the
1142 * same effect as creating a new file while
1143 * MNT_UNKNOWNPERMISSIONS is set.
1145 if ((dir
->bsdInfo
.fileMode
!= 0) ||
1146 (attrp
->ca_flags
!= 0) ||
1147 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1148 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1149 ((attrp
->ca_mode
& ALLPERMS
) !=
1150 (hfsmp
->hfs_dir_mask
& ACCESSPERMS
))) {
1151 if ((dir
->bsdInfo
.fileMode
== 0) ||
1152 (HFSTOVFS(hfsmp
)->mnt_flag
&
1153 MNT_UNKNOWNPERMISSIONS
) == 0) {
1154 dir
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1155 dir
->bsdInfo
.groupID
= attrp
->ca_gid
;
1157 dir
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1158 dir
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1159 dir
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1163 case kHFSPlusFileRecord
: {
1164 HFSPlusCatalogFile
*file
;
1166 file
= (struct HFSPlusCatalogFile
*)crp
;
1167 /* Do a quick sanity check */
1168 if (file
->fileID
!= attrp
->ca_fileid
)
1169 return (btNotFound
);
1170 file
->createDate
= to_hfs_time(attrp
->ca_itime
);
1171 file
->contentModDate
= to_hfs_time(attrp
->ca_mtime
);
1172 file
->backupDate
= to_hfs_time(attrp
->ca_btime
);
1173 file
->accessDate
= to_hfs_time(attrp
->ca_atime
);
1174 file
->attributeModDate
= to_hfs_time(attrp
->ca_ctime
);
1175 file
->textEncoding
= descp
->cd_encoding
;
1176 bcopy(&attrp
->ca_finderinfo
[0], &file
->userInfo
, 32);
1178 * Update the BSD Info if it was already initialized on
1179 * disk or if the runtime values have been modified.
1181 * If the BSD info was already initialized, but
1182 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1183 * probably different than what was on disk. We don't want
1184 * to overwrite the on-disk values (so if we turn off
1185 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1186 * This way, we can still change fields like the mode or
1187 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1189 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1190 * won't change the uid or gid from their defaults. So, if
1191 * the BSD info wasn't set, and the runtime values are not
1192 * default, then what changed was the mode or flags. We
1193 * have to set the uid and gid to something, so use the
1194 * supplied values (which will be default), which has the
1195 * same effect as creating a new file while
1196 * MNT_UNKNOWNPERMISSIONS is set.
1198 if ((file
->bsdInfo
.fileMode
!= 0) ||
1199 (attrp
->ca_flags
!= 0) ||
1200 (attrp
->ca_uid
!= hfsmp
->hfs_uid
) ||
1201 (attrp
->ca_gid
!= hfsmp
->hfs_gid
) ||
1202 ((attrp
->ca_mode
& ALLPERMS
) !=
1203 (hfsmp
->hfs_file_mask
& ACCESSPERMS
))) {
1204 if ((file
->bsdInfo
.fileMode
== 0) ||
1205 (HFSTOVFS(hfsmp
)->mnt_flag
&
1206 MNT_UNKNOWNPERMISSIONS
) == 0) {
1207 file
->bsdInfo
.ownerID
= attrp
->ca_uid
;
1208 file
->bsdInfo
.groupID
= attrp
->ca_gid
;
1210 file
->bsdInfo
.ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1211 file
->bsdInfo
.adminFlags
= attrp
->ca_flags
>> 16;
1212 file
->bsdInfo
.fileMode
= attrp
->ca_mode
;
1214 if (state
->s_rsrcfork
) {
1215 forkp
= state
->s_rsrcfork
;
1216 file
->resourceFork
.logicalSize
= forkp
->cf_size
;
1217 file
->resourceFork
.totalBlocks
= forkp
->cf_blocks
;
1218 bcopy(&forkp
->cf_extents
[0], &file
->resourceFork
.extents
,
1219 sizeof(HFSPlusExtentRecord
));
1221 if (state
->s_datafork
) {
1222 forkp
= state
->s_datafork
;
1223 file
->dataFork
.logicalSize
= forkp
->cf_size
;
1224 file
->dataFork
.totalBlocks
= forkp
->cf_blocks
;
1225 bcopy(&forkp
->cf_extents
[0], &file
->dataFork
.extents
,
1226 sizeof(HFSPlusExtentRecord
));
1229 if ((file
->resourceFork
.extents
[0].startBlock
!= 0) &&
1230 (file
->resourceFork
.extents
[0].startBlock
==
1231 file
->dataFork
.extents
[0].startBlock
))
1232 panic("catrec_update: rsrc fork == data fork");
1234 /* Synchronize the lock state */
1235 if (attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
))
1236 file
->flags
|= kHFSFileLockedMask
;
1238 file
->flags
&= ~kHFSFileLockedMask
;
1240 /* Push out special field if necessary */
1241 if (S_ISBLK(attrp
->ca_mode
) || S_ISCHR(attrp
->ca_mode
))
1242 file
->bsdInfo
.special
.rawDevice
= attrp
->ca_rdev
;
1243 else if (descp
->cd_cnid
!= attrp
->ca_fileid
1244 || attrp
->ca_nlink
== 2)
1245 file
->bsdInfo
.special
.linkCount
= attrp
->ca_nlink
;
1249 return (btNotFound
);
1256 * This is called from within BTIterateRecords.
1258 struct readattr_state
{
1259 struct hfsmount
*hfsmp
;
1260 struct cat_entrylist
*list
;
1267 catrec_readattr(const CatalogKey
*key
, const CatalogRecord
*rec
,
1268 u_long node
, struct readattr_state
*state
)
1270 struct cat_entrylist
*list
= state
->list
;
1271 struct hfsmount
*hfsmp
= state
->hfsmp
;
1272 struct cat_entry
*cep
;
1275 if (list
->realentries
>= list
->maxentries
)
1276 return (0); /* stop */
1278 parentcnid
= state
->stdhfs
? key
->hfs
.parentID
: key
->hfsPlus
.parentID
;
1280 switch(rec
->recordType
) {
1281 case kHFSPlusFolderRecord
:
1282 case kHFSPlusFileRecord
:
1283 case kHFSFolderRecord
:
1284 case kHFSFileRecord
:
1285 if (parentcnid
!= state
->dir_cnid
) {
1286 state
->error
= ENOENT
;
1287 return (0); /* stop */
1291 state
->error
= ENOENT
;
1292 return (0); /* stop */
1295 /* Hide the private meta data directory and journal files */
1296 if (parentcnid
== kRootDirID
) {
1297 if ((rec
->recordType
== kHFSPlusFolderRecord
) &&
1298 (rec
->hfsPlusFolder
.folderID
== hfsmp
->hfs_private_metadata_dir
)) {
1299 return (1); /* continue */
1302 (rec
->recordType
== kHFSPlusFileRecord
) &&
1303 ((rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlfileid
) ||
1304 (rec
->hfsPlusFile
.fileID
== hfsmp
->hfs_jnlinfoblkid
))) {
1306 return (1); /* continue */
1311 cep
= &list
->entry
[list
->realentries
++];
1313 if (state
->stdhfs
) {
1314 struct HFSPlusCatalogFile cnoderec
;
1315 HFSPlusCatalogKey
* pluskey
;
1318 promoteattr(hfsmp
, rec
, &cnoderec
);
1319 getbsdattr(hfsmp
, &cnoderec
, &cep
->ce_attr
);
1321 MALLOC(pluskey
, HFSPlusCatalogKey
*, sizeof(HFSPlusCatalogKey
), M_TEMP
, M_WAITOK
);
1322 promotekey(hfsmp
, (HFSCatalogKey
*)key
, pluskey
, &encoding
);
1323 builddesc(pluskey
, getcnid(rec
), node
, encoding
, isadir(rec
), &cep
->ce_desc
);
1324 FREE(pluskey
, M_TEMP
);
1326 if (rec
->recordType
== kHFSFileRecord
) {
1327 int blksize
= HFSTOVCB(hfsmp
)->blockSize
;
1329 cep
->ce_datasize
= rec
->hfsFile
.dataLogicalSize
;
1330 cep
->ce_datablks
= rec
->hfsFile
.dataPhysicalSize
/ blksize
;
1331 cep
->ce_rsrcsize
= rec
->hfsFile
.rsrcLogicalSize
;
1332 cep
->ce_rsrcblks
= rec
->hfsFile
.rsrcPhysicalSize
/ blksize
;
1335 getbsdattr(hfsmp
, (struct HFSPlusCatalogFile
*)rec
, &cep
->ce_attr
);
1336 builddesc((HFSPlusCatalogKey
*)key
, getcnid(rec
), node
, getencoding(rec
),
1337 isadir(rec
), &cep
->ce_desc
);
1339 if (rec
->recordType
== kHFSPlusFileRecord
) {
1340 cep
->ce_datasize
= rec
->hfsPlusFile
.dataFork
.logicalSize
;
1341 cep
->ce_datablks
= rec
->hfsPlusFile
.dataFork
.totalBlocks
;
1342 cep
->ce_rsrcsize
= rec
->hfsPlusFile
.resourceFork
.logicalSize
;
1343 cep
->ce_rsrcblks
= rec
->hfsPlusFile
.resourceFork
.totalBlocks
;
1345 /* Save link reference for later processing. */
1346 if ((SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdType
) == kHardLinkFileType
)
1347 && (SWAP_BE32(rec
->hfsPlusFile
.userInfo
.fdCreator
) == kHFSPlusCreator
))
1348 cep
->ce_attr
.ca_rdev
= rec
->hfsPlusFile
.bsdInfo
.special
.iNodeNum
;
1352 return (list
->realentries
< list
->maxentries
);
1356 * Note: index is zero relative
1359 cat_getentriesattr(struct hfsmount
*hfsmp
, struct cat_desc
*prevdesc
, int index
,
1360 struct cat_entrylist
*ce_list
)
1364 BTreeIterator
* iterator
;
1365 struct readattr_state state
;
1371 ce_list
->realentries
= 0;
1373 fcb
= GetFileControlBlock(HFSTOVCB(hfsmp
)->catalogRefNum
);
1374 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
1375 parentcnid
= prevdesc
->cd_parentcnid
;
1377 state
.hfsmp
= hfsmp
;
1378 state
.list
= ce_list
;
1379 state
.dir_cnid
= parentcnid
;
1380 state
.stdhfs
= std_hfs
;
1383 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1384 bzero(iterator
, sizeof(*iterator
));
1385 key
= (CatalogKey
*)&iterator
->key
;
1386 iterator
->hint
.nodeNum
= prevdesc
->cd_hint
;
1389 * If the last entry wasn't cached then establish the iterator
1392 (prevdesc
->cd_namelen
== 0) ||
1393 (buildkey(hfsmp
, prevdesc
, (HFSPlusCatalogKey
*)key
, 0) != 0)) {
1396 * Position the iterator at the directory thread.
1397 * (ie just before the first entry)
1399 buildthreadkey(parentcnid
, std_hfs
, key
);
1400 result
= BTSearchRecord(fcb
, iterator
, NULL
, NULL
, iterator
);
1402 goto exit
; /* bad news */
1404 * Iterate until we reach the entry just
1405 * before the one we want to start with.
1407 for (i
= 0; i
< index
; ++i
) {
1408 result
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, NULL
, NULL
);
1410 goto exit
; /* bad news */
1414 /* Fill list with entries. */
1415 result
= BTIterateRecords(fcb
, kBTreeNextRecord
, iterator
,
1416 (IterateCallBackProcPtr
)catrec_readattr
, &state
);
1419 result
= state
.error
;
1420 else if (ce_list
->realentries
== 0)
1423 result
= MacToVFSError(result
);
1429 * Resolve any hard links.
1431 for (i
= 0; i
< ce_list
->realentries
; ++i
) {
1432 struct FndrFileInfo
*fip
;
1433 struct cat_entry
*cep
;
1434 struct HFSPlusCatalogFile filerec
;
1436 cep
= &ce_list
->entry
[i
];
1437 if (!S_ISREG(cep
->ce_attr
.ca_mode
))
1440 /* Note: Finder info is still in Big Endian */
1441 fip
= (struct FndrFileInfo
*)&cep
->ce_attr
.ca_finderinfo
;
1443 /* Check for hard link signature. */
1444 if ((cep
->ce_attr
.ca_rdev
!= 0)
1445 && (SWAP_BE32(fip
->fdType
) == kHardLinkFileType
)
1446 && (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)
1447 && ((cep
->ce_attr
.ca_itime
== HFSTOVCB(hfsmp
)->vcbCrDate
) ||
1448 (cep
->ce_attr
.ca_itime
== hfsmp
->hfs_metadata_createdate
))) {
1450 if (resolvelink(hfsmp
, cep
->ce_attr
.ca_rdev
, &filerec
) != 0)
1452 /* Repack entry from inode record. */
1453 getbsdattr(hfsmp
, &filerec
, &cep
->ce_attr
);
1454 cep
->ce_datasize
= filerec
.dataFork
.logicalSize
;
1455 cep
->ce_datablks
= filerec
.dataFork
.totalBlocks
;
1456 cep
->ce_rsrcsize
= filerec
.resourceFork
.logicalSize
;
1457 cep
->ce_rsrcblks
= filerec
.resourceFork
.totalBlocks
;
1461 FREE(iterator
, M_TEMP
);
1463 return MacToVFSError(result
);
1468 u_int32_t cbs_parentID
;
1469 u_int32_t cbs_hiddenDirID
;
1470 u_int32_t cbs_hiddenJournalID
;
1471 u_int32_t cbs_hiddenInfoBlkID
;
1472 off_t cbs_lastoffset
;
1473 struct uio
* cbs_uio
;
1474 ExtendedVCB
* cbs_vcb
;
1475 int16_t cbs_hfsPlus
;
1481 catrec_read(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
1482 u_int16_t recordLen
, struct read_state
*state
)
1488 struct dirent catent
;
1490 if (state
->cbs_hfsPlus
)
1491 curID
= ckp
->hfsPlus
.parentID
;
1493 curID
= ckp
->hfs
.parentID
;
1495 /* We're done when parent directory changes */
1496 if (state
->cbs_parentID
!= curID
) {
1499 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
1500 * so remove padding for now...
1504 * Pad the end of list with an empty record.
1505 * This eliminates an extra call by readdir(3c).
1507 catent
.d_fileno
= 0;
1508 catent
.d_reclen
= 0;
1510 catent
.d_namlen
= 0;
1511 *(int32_t*)&catent
.d_name
[0] = 0;
1513 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1515 state
->cbs_result
= uiomove((caddr_t
) &catent
, 12, state
->cbs_uio
);
1516 if (state
->cbs_result
== 0)
1517 state
->cbs_result
= ENOENT
;
1519 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1520 state
->cbs_result
= ENOENT
;
1522 return (0); /* stop */
1525 if (state
->cbs_hfsPlus
) {
1526 switch(crp
->recordType
) {
1527 case kHFSPlusFolderRecord
:
1528 catent
.d_type
= DT_DIR
;
1529 catent
.d_fileno
= crp
->hfsPlusFolder
.folderID
;
1531 case kHFSPlusFileRecord
:
1532 catent
.d_type
= DT_REG
;
1533 catent
.d_fileno
= crp
->hfsPlusFile
.fileID
;
1536 return (0); /* stop */
1539 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
1540 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
1541 catent
.d_name
, &utf8chars
, kdirentMaxNameBytes
+ 1, ':', 0);
1542 if (result
== ENAMETOOLONG
) {
1543 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
1544 cnp
->ustr
.unicode
, kdirentMaxNameBytes
+ 1, (ByteCount
*)&utf8chars
, catent
.d_name
, catent
.d_fileno
);
1547 switch(crp
->recordType
) {
1548 case kHFSFolderRecord
:
1549 catent
.d_type
= DT_DIR
;
1550 catent
.d_fileno
= crp
->hfsFolder
.folderID
;
1552 case kHFSFileRecord
:
1553 catent
.d_type
= DT_REG
;
1554 catent
.d_fileno
= crp
->hfsFile
.fileID
;
1557 return (0); /* stop */
1560 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
1561 result
= hfs_to_utf8(state
->cbs_vcb
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
1562 (ByteCount
*)&utf8chars
, catent
.d_name
);
1564 * When an HFS name cannot be encoded with the current
1565 * volume encoding we use MacRoman as a fallback.
1568 result
= mac_roman_to_utf8(cnp
->pstr
, kdirentMaxNameBytes
+ 1,
1569 (ByteCount
*)&utf8chars
, catent
.d_name
);
1572 catent
.d_namlen
= utf8chars
;
1573 catent
.d_reclen
= DIRENTRY_SIZE(utf8chars
);
1575 /* hide our private meta data directory */
1576 if (curID
== kRootDirID
&&
1577 catent
.d_fileno
== state
->cbs_hiddenDirID
&&
1578 catent
.d_type
== DT_DIR
)
1581 /* Hide the journal files */
1582 if ((curID
== kRootDirID
) &&
1583 (catent
.d_type
== DT_REG
) &&
1584 ((catent
.d_fileno
== state
->cbs_hiddenJournalID
) ||
1585 (catent
.d_fileno
== state
->cbs_hiddenInfoBlkID
))) {
1587 return (1); /* skip and continue */
1590 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
1592 /* if this entry won't fit then we're done */
1593 if (catent
.d_reclen
> state
->cbs_uio
->uio_resid
)
1594 return (0); /* stop */
1596 state
->cbs_result
= uiomove((caddr_t
) &catent
, catent
.d_reclen
, state
->cbs_uio
);
1598 /* continue iteration if there's room */
1599 return (state
->cbs_result
== 0 &&
1600 state
->cbs_uio
->uio_resid
>= AVERAGE_HFSDIRENTRY_SIZE
);
1607 cat_getdirentries(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
1608 struct uio
*uio
, int *eofflag
)
1610 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
1611 BTreeIterator
* iterator
;
1612 CatalogIterator
*cip
;
1613 u_int32_t diroffset
;
1615 struct read_state state
;
1616 u_int32_t dirID
= descp
->cd_cnid
;
1619 diroffset
= uio
->uio_offset
;
1622 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1623 bzero(iterator
, sizeof(*iterator
));
1625 /* get an iterator and position it */
1626 cip
= GetCatalogIterator(vcb
, dirID
, diroffset
);
1628 result
= PositionIterator(cip
, diroffset
, iterator
, &op
);
1629 if (result
== cmNotFound
) {
1632 AgeCatalogIterator(cip
);
1634 } else if ((result
= MacToVFSError(result
)))
1637 state
.cbs_hiddenDirID
= hfsmp
->hfs_private_metadata_dir
;
1639 state
.cbs_hiddenJournalID
= hfsmp
->hfs_jnlfileid
;
1640 state
.cbs_hiddenInfoBlkID
= hfsmp
->hfs_jnlinfoblkid
;
1643 state
.cbs_lastoffset
= cip
->currentOffset
;
1644 state
.cbs_vcb
= vcb
;
1645 state
.cbs_uio
= uio
;
1646 state
.cbs_result
= 0;
1647 state
.cbs_parentID
= dirID
;
1649 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
1650 state
.cbs_hfsPlus
= 1;
1652 state
.cbs_hfsPlus
= 0;
1654 /* process as many entries as possible... */
1655 result
= BTIterateRecords(GetFileControlBlock(vcb
->catalogRefNum
), op
,
1656 iterator
, (IterateCallBackProcPtr
)catrec_read
, &state
);
1658 if (state
.cbs_result
)
1659 result
= state
.cbs_result
;
1661 result
= MacToVFSError(result
);
1663 if (result
== ENOENT
) {
1669 cip
->currentOffset
= state
.cbs_lastoffset
;
1670 cip
->nextOffset
= uio
->uio_offset
;
1671 UpdateCatalogIterator(iterator
, cip
);
1678 AgeCatalogIterator(cip
);
1681 (void) ReleaseCatalogIterator(cip
);
1682 FREE(iterator
, M_TEMP
);
1689 * buildkey - build a Catalog b-tree key from a cnode descriptor
1692 buildkey(struct hfsmount
*hfsmp
, struct cat_desc
*descp
,
1693 HFSPlusCatalogKey
*key
, int retry
)
1697 size_t unicodeBytes
= 0;
1699 if (descp
->cd_namelen
== 0 || descp
->cd_nameptr
[0] == '\0')
1700 return (EINVAL
); /* invalid name */
1702 key
->parentID
= descp
->cd_parentcnid
;
1703 key
->nodeName
.length
= 0;
1705 * Convert filename from UTF-8 into Unicode
1708 if ((descp
->cd_flags
& CD_DECOMPOSED
) == 0)
1709 utf8_flags
|= UTF_DECOMPOSED
;
1710 result
= utf8_decodestr(descp
->cd_nameptr
, descp
->cd_namelen
,
1711 key
->nodeName
.unicode
, &unicodeBytes
,
1712 sizeof(key
->nodeName
.unicode
), ':', utf8_flags
);
1713 key
->nodeName
.length
= unicodeBytes
/ sizeof(UniChar
);
1714 key
->keyLength
= kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
;
1716 if (result
!= ENAMETOOLONG
)
1717 result
= EINVAL
; /* name has invalid characters */
1722 * For HFS volumes convert to an HFS compatible key
1724 * XXX need to save the encoding that succeeded
1726 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
1727 HFSCatalogKey hfskey
;
1729 bzero(&hfskey
, sizeof(hfskey
));
1730 hfskey
.keyLength
= kHFSCatalogKeyMinimumLength
;
1731 hfskey
.parentID
= key
->parentID
;
1732 hfskey
.nodeName
[0] = 0;
1733 if (key
->nodeName
.length
> 0) {
1734 if (unicode_to_hfs(HFSTOVCB(hfsmp
),
1735 key
->nodeName
.length
* 2,
1736 key
->nodeName
.unicode
,
1737 &hfskey
.nodeName
[0], retry
) != 0) {
1740 hfskey
.keyLength
+= hfskey
.nodeName
[0];
1742 bcopy(&hfskey
, key
, sizeof(hfskey
));
1749 * Resolve hard link reference to obtain the inode record.
1753 resolvelink(struct hfsmount
*hfsmp
, u_long linkref
, struct HFSPlusCatalogFile
*recp
)
1755 FSBufferDescriptor btdata
;
1756 struct BTreeIterator
*iterator
;
1757 struct cat_desc idesc
;
1761 BDINIT(btdata
, recp
);
1762 MAKE_INODE_NAME(inodename
, linkref
);
1764 /* Get space for iterator */
1765 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1766 bzero(iterator
, sizeof(*iterator
));
1768 /* Build a descriptor for private dir. */
1769 idesc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
1770 idesc
.cd_nameptr
= inodename
;
1771 idesc
.cd_namelen
= strlen(inodename
);
1774 idesc
.cd_encoding
= 0;
1775 (void) buildkey(hfsmp
, &idesc
, (HFSPlusCatalogKey
*)&iterator
->key
, 0);
1777 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
1778 &btdata
, NULL
, NULL
);
1781 /* Make sure there's a reference */
1782 if (recp
->bsdInfo
.special
.linkCount
== 0)
1783 recp
->bsdInfo
.special
.linkCount
= 2;
1785 printf("HFS resolvelink: can't find %s\n", inodename
);
1788 FREE(iterator
, M_TEMP
);
1790 return (result
? ENOENT
: 0);
1794 * getkey - get a key from id by doing a thread lookup
1797 getkey(struct hfsmount
*hfsmp
, cnid_t cnid
, CatalogKey
* key
)
1799 struct BTreeIterator
* iterator
;
1800 FSBufferDescriptor btdata
;
1803 CatalogRecord
* recp
;
1807 std_hfs
= (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
);
1809 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1810 bzero(iterator
, sizeof(*iterator
));
1811 buildthreadkey(cnid
, std_hfs
, (CatalogKey
*)&iterator
->key
);
1813 MALLOC(recp
, CatalogRecord
*, sizeof(CatalogRecord
), M_TEMP
, M_WAITOK
);
1814 BDINIT(btdata
, recp
);
1816 result
= BTSearchRecord(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), iterator
,
1817 &btdata
, &datasize
, iterator
);
1821 /* Turn thread record into a cnode key (in place) */
1822 switch (recp
->recordType
) {
1823 case kHFSFileThreadRecord
:
1824 case kHFSFolderThreadRecord
:
1825 keyp
= (CatalogKey
*)((char *)&recp
->hfsThread
.reserved
+ 6);
1826 keyp
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
+ keyp
->hfs
.nodeName
[0];
1827 bcopy(keyp
, key
, keyp
->hfs
.keyLength
+ 1);
1830 case kHFSPlusFileThreadRecord
:
1831 case kHFSPlusFolderThreadRecord
:
1832 keyp
= (CatalogKey
*)&recp
->hfsPlusThread
.reserved
;
1833 keyp
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
+
1834 (keyp
->hfsPlus
.nodeName
.length
* 2);
1835 bcopy(keyp
, key
, keyp
->hfsPlus
.keyLength
+ 2);
1844 FREE(iterator
, M_TEMP
);
1847 return MacToVFSError(result
);
1852 * buildrecord - build a default catalog directory or file record
1855 buildrecord(struct cat_attr
*attrp
, cnid_t cnid
, int std_hfs
, u_int32_t encoding
,
1856 CatalogRecord
*crp
, int *recordSize
)
1858 int type
= attrp
->ca_mode
& S_IFMT
;
1859 u_int32_t createtime
= to_hfs_time(attrp
->ca_itime
);
1862 createtime
= UTCToLocal(createtime
);
1863 if (type
== S_IFDIR
) {
1864 bzero(crp
, sizeof(HFSCatalogFolder
));
1865 crp
->recordType
= kHFSFolderRecord
;
1866 crp
->hfsFolder
.folderID
= cnid
;
1867 crp
->hfsFolder
.createDate
= createtime
;
1868 crp
->hfsFolder
.modifyDate
= createtime
;
1869 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFolder
.userInfo
, 32);
1870 *recordSize
= sizeof(HFSCatalogFolder
);
1872 bzero(crp
, sizeof(HFSCatalogFile
));
1873 crp
->recordType
= kHFSFileRecord
;
1874 crp
->hfsFile
.fileID
= cnid
;
1875 crp
->hfsFile
.createDate
= createtime
;
1876 crp
->hfsFile
.modifyDate
= createtime
;
1877 bcopy(attrp
->ca_finderinfo
, &crp
->hfsFile
.userInfo
, 16);
1878 bcopy(&attrp
->ca_finderinfo
[16], &crp
->hfsFile
.finderInfo
, 16);
1879 *recordSize
= sizeof(HFSCatalogFile
);
1882 struct HFSPlusBSDInfo
* bsdp
= NULL
;
1883 struct FndrFileInfo
* fip
= NULL
;
1885 if (type
== S_IFDIR
) {
1886 bzero(crp
, sizeof(HFSPlusCatalogFolder
));
1887 crp
->recordType
= kHFSPlusFolderRecord
;
1888 crp
->hfsPlusFolder
.folderID
= cnid
;
1889 crp
->hfsPlusFolder
.createDate
= createtime
;
1890 crp
->hfsPlusFolder
.contentModDate
= createtime
;
1891 crp
->hfsPlusFolder
.accessDate
= createtime
;
1892 crp
->hfsPlusFolder
.attributeModDate
= createtime
;
1893 crp
->hfsPlusFolder
.textEncoding
= encoding
;
1894 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFolder
.userInfo
, 32);
1895 bsdp
= &crp
->hfsPlusFolder
.bsdInfo
;
1896 *recordSize
= sizeof(HFSPlusCatalogFolder
);
1898 bzero(crp
, sizeof(HFSPlusCatalogFile
));
1899 crp
->recordType
= kHFSPlusFileRecord
;
1900 crp
->hfsPlusFile
.fileID
= cnid
;
1901 crp
->hfsPlusFile
.createDate
= createtime
;
1902 crp
->hfsPlusFile
.contentModDate
= createtime
;
1903 crp
->hfsPlusFile
.accessDate
= createtime
;
1904 crp
->hfsPlusFile
.attributeModDate
= createtime
;
1905 crp
->hfsPlusFile
.flags
|= kHFSThreadExistsMask
;
1906 crp
->hfsPlusFile
.textEncoding
= encoding
;
1907 bsdp
= &crp
->hfsPlusFile
.bsdInfo
;
1911 /* BLK/CHR need to save the device info */
1912 bsdp
->special
.rawDevice
= attrp
->ca_rdev
;
1915 /* Hardlink links need to save the linkref */
1916 fip
= (FndrFileInfo
*)&attrp
->ca_finderinfo
;
1917 if ((SWAP_BE32(fip
->fdType
) == kHardLinkFileType
) &&
1918 (SWAP_BE32(fip
->fdCreator
) == kHFSPlusCreator
)) {
1919 bsdp
->special
.iNodeNum
= attrp
->ca_rdev
;
1921 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
1924 /* Symlinks also have a type and creator */
1925 bcopy(attrp
->ca_finderinfo
, &crp
->hfsPlusFile
.userInfo
, 32);
1928 *recordSize
= sizeof(HFSPlusCatalogFile
);
1930 bsdp
->ownerID
= attrp
->ca_uid
;
1931 bsdp
->groupID
= attrp
->ca_gid
;
1932 bsdp
->fileMode
= attrp
->ca_mode
;
1933 bsdp
->adminFlags
= attrp
->ca_flags
>> 16;
1934 bsdp
->ownerFlags
= attrp
->ca_flags
& 0x000000FF;
1940 * builddesc - build a cnode descriptor from an HFS+ key
1943 builddesc(const HFSPlusCatalogKey
*key
, cnid_t cnid
, u_long hint
, u_long encoding
,
1944 int isdir
, struct cat_desc
*descp
)
1951 /* guess a size... */
1952 bufsize
= (3 * key
->nodeName
.length
) + 1;
1953 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
1955 result
= utf8_encodestr(key
->nodeName
.unicode
,
1956 key
->nodeName
.length
* sizeof(UniChar
),
1957 nameptr
, (size_t *)&utf8len
,
1960 if (result
== ENAMETOOLONG
) {
1961 bufsize
= 1 + utf8_encodelen(key
->nodeName
.unicode
,
1962 key
->nodeName
.length
* sizeof(UniChar
),
1964 FREE(nameptr
, M_TEMP
);
1965 MALLOC(nameptr
, char *, bufsize
, M_TEMP
, M_WAITOK
);
1967 result
= utf8_encodestr(key
->nodeName
.unicode
,
1968 key
->nodeName
.length
* sizeof(UniChar
),
1969 nameptr
, (size_t *)&utf8len
,
1972 descp
->cd_parentcnid
= key
->parentID
;
1973 descp
->cd_nameptr
= nameptr
;
1974 descp
->cd_namelen
= utf8len
;
1975 descp
->cd_cnid
= cnid
;
1976 descp
->cd_hint
= hint
;
1977 descp
->cd_flags
= CD_DECOMPOSED
| CD_HASBUF
;
1979 descp
->cd_flags
|= CD_ISDIR
;
1980 descp
->cd_encoding
= encoding
;
1986 * getbsdattr - get attributes in bsd format
1990 getbsdattr(struct hfsmount
*hfsmp
, const struct HFSPlusCatalogFile
*crp
, struct cat_attr
* attrp
)
1992 int isDirectory
= (crp
->recordType
== kHFSPlusFolderRecord
);
1993 const struct HFSPlusBSDInfo
*bsd
= &crp
->bsdInfo
;
1995 attrp
->ca_nlink
= 1;
1996 attrp
->ca_atime
= to_bsd_time(crp
->accessDate
);
1997 attrp
->ca_mtime
= to_bsd_time(crp
->contentModDate
);
1998 attrp
->ca_mtime_nsec
= 0;
1999 attrp
->ca_ctime
= to_bsd_time(crp
->attributeModDate
);
2000 attrp
->ca_itime
= to_bsd_time(crp
->createDate
);
2001 attrp
->ca_btime
= to_bsd_time(crp
->backupDate
);
2003 if ((bsd
->fileMode
& S_IFMT
) == 0) {
2004 attrp
->ca_flags
= 0;
2005 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2006 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2008 attrp
->ca_mode
= S_IFDIR
| (hfsmp
->hfs_dir_mask
& ACCESSPERMS
);
2010 attrp
->ca_mode
= S_IFREG
| (hfsmp
->hfs_file_mask
& ACCESSPERMS
);
2014 attrp
->ca_uid
= bsd
->ownerID
;
2015 attrp
->ca_gid
= bsd
->groupID
;
2016 attrp
->ca_flags
= bsd
->ownerFlags
| (bsd
->adminFlags
<< 16);
2017 attrp
->ca_mode
= (mode_t
)bsd
->fileMode
;
2018 switch (attrp
->ca_mode
& S_IFMT
) {
2019 case S_IFCHR
: /* fall through */
2021 attrp
->ca_rdev
= bsd
->special
.rawDevice
;
2024 /* Pick up the hard link count */
2025 if (bsd
->special
.linkCount
> 0)
2026 attrp
->ca_nlink
= bsd
->special
.linkCount
;
2030 if (HFSTOVFS(hfsmp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
2032 * Override the permissions as determined by the mount auguments
2033 * in ALMOST the same way unset permissions are treated but keep
2034 * track of whether or not the file or folder is hfs locked
2035 * by leaving the h_pflags field unchanged from what was unpacked
2036 * out of the catalog.
2038 attrp
->ca_uid
= hfsmp
->hfs_uid
;
2039 attrp
->ca_gid
= hfsmp
->hfs_gid
;
2044 if (!S_ISDIR(attrp
->ca_mode
)) {
2045 attrp
->ca_mode
&= ~S_IFMT
;
2046 attrp
->ca_mode
|= S_IFDIR
;
2048 attrp
->ca_nlink
= 2 + ((HFSPlusCatalogFolder
*)crp
)->valence
;
2049 attrp
->ca_entries
= ((HFSPlusCatalogFolder
*)crp
)->valence
;
2051 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2052 if (crp
->flags
& kHFSFileLockedMask
) {
2053 /* The file's supposed to be locked:
2054 Make sure at least one of the IMMUTABLE bits is set: */
2055 if ((attrp
->ca_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) == 0)
2056 attrp
->ca_flags
|= UF_IMMUTABLE
;
2058 /* The file's supposed to be unlocked: */
2059 attrp
->ca_flags
&= ~(SF_IMMUTABLE
| UF_IMMUTABLE
);
2061 /* get total blocks (both forks) */
2062 attrp
->ca_blocks
= crp
->dataFork
.totalBlocks
+ crp
->resourceFork
.totalBlocks
;
2065 attrp
->ca_fileid
= crp
->fileID
;
2067 bcopy(&crp
->userInfo
, attrp
->ca_finderinfo
, 32);
2071 * promotekey - promote hfs key to hfs plus key
2075 promotekey(struct hfsmount
*hfsmp
, const HFSCatalogKey
*hfskey
,
2076 HFSPlusCatalogKey
*keyp
, u_long
*encoding
)
2078 hfs_to_unicode_func_t hfs_get_unicode
= hfsmp
->hfs_get_unicode
;
2082 *encoding
= hfsmp
->hfs_encoding
;
2084 error
= hfs_get_unicode(hfskey
->nodeName
, keyp
->nodeName
.unicode
,
2085 kHFSPlusMaxFileNameChars
, &uniCount
);
2087 * When an HFS name cannot be encoded with the current
2088 * encoding use MacRoman as a fallback.
2090 if (error
&& hfsmp
->hfs_encoding
!= kTextEncodingMacRoman
) {
2092 (void) mac_roman_to_unicode(hfskey
->nodeName
,
2093 keyp
->nodeName
.unicode
,
2094 kHFSPlusMaxFileNameChars
,
2098 keyp
->nodeName
.length
= uniCount
;
2099 keyp
->parentID
= hfskey
->parentID
;
2103 * promotefork - promote hfs fork info to hfs plus
2107 promotefork(struct hfsmount
*hfsmp
, const struct HFSCatalogFile
*filep
,
2108 int resource
, struct cat_fork
* forkp
)
2110 struct HFSPlusExtentDescriptor
*xp
;
2111 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2113 bzero(forkp
, sizeof(*forkp
));
2114 xp
= &forkp
->cf_extents
[0];
2116 forkp
->cf_size
= filep
->rsrcLogicalSize
;
2117 forkp
->cf_blocks
= filep
->rsrcPhysicalSize
/ blocksize
;
2118 xp
[0].startBlock
= (u_int32_t
)filep
->rsrcExtents
[0].startBlock
;
2119 xp
[0].blockCount
= (u_int32_t
)filep
->rsrcExtents
[0].blockCount
;
2120 xp
[1].startBlock
= (u_int32_t
)filep
->rsrcExtents
[1].startBlock
;
2121 xp
[1].blockCount
= (u_int32_t
)filep
->rsrcExtents
[1].blockCount
;
2122 xp
[2].startBlock
= (u_int32_t
)filep
->rsrcExtents
[2].startBlock
;
2123 xp
[2].blockCount
= (u_int32_t
)filep
->rsrcExtents
[2].blockCount
;
2125 forkp
->cf_size
= filep
->dataLogicalSize
;
2126 forkp
->cf_blocks
= filep
->dataPhysicalSize
/ blocksize
;
2127 xp
[0].startBlock
= (u_int32_t
)filep
->dataExtents
[0].startBlock
;
2128 xp
[0].blockCount
= (u_int32_t
)filep
->dataExtents
[0].blockCount
;
2129 xp
[1].startBlock
= (u_int32_t
)filep
->dataExtents
[1].startBlock
;
2130 xp
[1].blockCount
= (u_int32_t
)filep
->dataExtents
[1].blockCount
;
2131 xp
[2].startBlock
= (u_int32_t
)filep
->dataExtents
[2].startBlock
;
2132 xp
[2].blockCount
= (u_int32_t
)filep
->dataExtents
[2].blockCount
;
2137 * promoteattr - promote hfs catalog attributes to hfs plus
2141 promoteattr(struct hfsmount
*hfsmp
, const CatalogRecord
*dataPtr
, struct HFSPlusCatalogFile
*crp
)
2143 u_long blocksize
= HFSTOVCB(hfsmp
)->blockSize
;
2145 if (dataPtr
->recordType
== kHFSFolderRecord
) {
2146 struct HFSCatalogFolder
* folder
;
2148 folder
= (struct HFSCatalogFolder
*) dataPtr
;
2149 crp
->recordType
= kHFSPlusFolderRecord
;
2150 crp
->flags
= folder
->flags
;
2151 crp
->fileID
= folder
->folderID
;
2152 crp
->createDate
= LocalToUTC(folder
->createDate
);
2153 crp
->contentModDate
= LocalToUTC(folder
->modifyDate
);
2154 crp
->backupDate
= LocalToUTC(folder
->backupDate
);
2155 crp
->reserved1
= folder
->valence
;
2156 bcopy(&folder
->userInfo
, &crp
->userInfo
, 32);
2158 struct HFSCatalogFile
* file
;
2160 file
= (struct HFSCatalogFile
*) dataPtr
;
2161 crp
->recordType
= kHFSPlusFileRecord
;
2162 crp
->flags
= file
->flags
;
2163 crp
->fileID
= file
->fileID
;
2164 crp
->createDate
= LocalToUTC(file
->createDate
);
2165 crp
->contentModDate
= LocalToUTC(file
->modifyDate
);
2166 crp
->backupDate
= LocalToUTC(file
->backupDate
);
2168 bcopy(&file
->userInfo
, &crp
->userInfo
, 16);
2169 bcopy(&file
->finderInfo
, &crp
->finderInfo
, 16);
2170 crp
->dataFork
.totalBlocks
= file
->dataPhysicalSize
/ blocksize
;
2171 crp
->resourceFork
.totalBlocks
= file
->rsrcPhysicalSize
/ blocksize
;
2173 crp
->textEncoding
= 0;
2174 crp
->attributeModDate
= crp
->contentModDate
;
2175 crp
->accessDate
= crp
->contentModDate
;
2176 bzero(&crp
->bsdInfo
, sizeof(HFSPlusBSDInfo
));
2181 * Build a catalog node thread record from a catalog key
2182 * and return the size of the record.
2185 buildthread(void *keyp
, void *recp
, int std_hfs
, int directory
)
2190 HFSCatalogKey
*key
= (HFSCatalogKey
*)keyp
;
2191 HFSCatalogThread
*rec
= (HFSCatalogThread
*)recp
;
2193 size
= sizeof(HFSCatalogThread
);
2196 rec
->recordType
= kHFSFolderThreadRecord
;
2198 rec
->recordType
= kHFSFileThreadRecord
;
2199 rec
->parentID
= key
->parentID
;
2200 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
2203 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)keyp
;
2204 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)recp
;
2206 size
= sizeof(HFSPlusCatalogThread
);
2208 rec
->recordType
= kHFSPlusFolderThreadRecord
;
2210 rec
->recordType
= kHFSPlusFileThreadRecord
;
2212 rec
->parentID
= key
->parentID
;
2213 bcopy(&key
->nodeName
, &rec
->nodeName
,
2214 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
2216 /* HFS Plus has varaible sized thread records */
2217 size
-= (sizeof(rec
->nodeName
.unicode
) -
2218 (rec
->nodeName
.length
* sizeof(UniChar
)));
2225 * Build a catalog node thread key.
2228 buildthreadkey(HFSCatalogNodeID parentID
, int std_hfs
, CatalogKey
*key
)
2231 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
2232 key
->hfs
.reserved
= 0;
2233 key
->hfs
.parentID
= parentID
;
2234 key
->hfs
.nodeName
[0] = 0;
2236 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
2237 key
->hfsPlus
.parentID
= parentID
;
2238 key
->hfsPlus
.nodeName
.length
= 0;
2243 * Extract the text encoding from a catalog node record.
2246 getencoding(const CatalogRecord
*crp
)
2250 if (crp
->recordType
== kHFSPlusFolderRecord
)
2251 encoding
= crp
->hfsPlusFolder
.textEncoding
;
2252 else if (crp
->recordType
== kHFSPlusFileRecord
)
2253 encoding
= crp
->hfsPlusFile
.textEncoding
;
2261 * Extract the CNID from a catalog node record.
2264 getcnid(const CatalogRecord
*crp
)
2268 switch (crp
->recordType
) {
2269 case kHFSFolderRecord
:
2270 cnid
= crp
->hfsFolder
.folderID
;
2272 case kHFSFileRecord
:
2273 cnid
= crp
->hfsFile
.fileID
;
2275 case kHFSPlusFolderRecord
:
2276 cnid
= crp
->hfsPlusFolder
.folderID
;
2278 case kHFSPlusFileRecord
:
2279 cnid
= crp
->hfsPlusFile
.fileID
;
2282 panic("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp
);
2290 * Extract the parent ID from a catalog node record.
2293 getparentcnid(const CatalogRecord
*recp
)
2297 switch (recp
->recordType
) {
2298 case kHFSFileThreadRecord
:
2299 case kHFSFolderThreadRecord
:
2300 cnid
= recp
->hfsThread
.parentID
;
2303 case kHFSPlusFileThreadRecord
:
2304 case kHFSPlusFolderThreadRecord
:
2305 cnid
= recp
->hfsPlusThread
.parentID
;
2308 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp
);
2316 * Determine if a catalog node record is a directory.
2319 isadir(const CatalogRecord
*crp
)
2321 return (crp
->recordType
== kHFSFolderRecord
||
2322 crp
->recordType
== kHFSPlusFolderRecord
);