]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_catalog.c
038144ec85aa7ff62fc26c9776bff0a9eb4d1ea3
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/stat.h>
35 #include <sys/mount.h>
36 #include <sys/vnode.h>
37 #include <sys/dirent.h>
38 #include <vfs/vfs_support.h>
39 #include <libkern/libkern.h>
40
41 #include <sys/utfconv.h>
42
43 #include "hfs.h"
44 #include "hfs_catalog.h"
45 #include "hfs_format.h"
46 #include "hfs_endian.h"
47
48 #include "hfscommon/headers/BTreesInternal.h"
49 #include "hfscommon/headers/HFSUnicodeWrappers.h"
50
51
52 /*
53 * Initialization of an FSBufferDescriptor structure.
54 */
55 #define BDINIT(bd, addr) { \
56 (bd).bufferAddress = (addr); \
57 (bd).itemSize = sizeof(*(addr)); \
58 (bd).itemCount = 1; \
59 }
60
61
62 struct btobj {
63 BTreeIterator iterator;
64 HFSPlusCatalogKey key;
65 CatalogRecord data;
66 };
67
68 struct update_state {
69 struct cat_desc * s_desc;
70 struct cat_attr * s_attr;
71 struct cat_fork * s_datafork;
72 struct cat_fork * s_rsrcfork;
73 struct hfsmount * s_hfsmp;
74 };
75
76 struct position_state {
77 int error;
78 u_int32_t count;
79 u_int32_t index;
80 u_int32_t parentID;
81 struct hfsmount *hfsmp;
82 };
83
84 /* Map file mode type to directory entry types */
85 u_char modetodirtype[16] = {
86 DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
87 DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
88 DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
89 DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
90 };
91 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
92
93
94 static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
95 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
96
97 static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
98 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
99
100 extern int mac_roman_to_unicode(const Str31 hfs_str, UniChar *uni_str,
101 UInt32 maxCharLen, UInt32 *unicodeChars);
102
103 extern int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen,
104 const u_int16_t* srcStr, Str31 dstStr, int retry);
105
106
107 /* Internal catalog support routines */
108
109 static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
110 struct position_state *state);
111
112 static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino);
113
114 static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
115
116 static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
117 HFSPlusCatalogKey *key, int retry);
118
119 static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key);
120
121 static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, int *recordSize);
122
123 static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
124
125 static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
126 int isdir, struct cat_desc *descp);
127
128 static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
129
130 static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
131 static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
132 static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
133
134 static cnid_t getcnid(const CatalogRecord *crp);
135 static u_long getencoding(const CatalogRecord *crp);
136 static cnid_t getparentcnid(const CatalogRecord *recp);
137
138 static int isadir(const CatalogRecord *crp);
139
140 static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
141
142
143 __private_extern__
144 int
145 cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, struct proc *p)
146 {
147 FCB *fcb;
148 int lockflags;
149 int result;
150
151 fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
152
153 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
154
155 result = BTReserveSpace(fcb, ops, (void*)cookie);
156
157 hfs_systemfile_unlock(hfsmp, lockflags);
158
159 return MacToVFSError(result);
160 }
161
162 __private_extern__
163 void
164 cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, struct proc *p)
165 {
166 FCB *fcb;
167 int lockflags;
168
169 fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
170
171 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
172
173 (void) BTReleaseReserve(fcb, (void*)cookie);
174
175 hfs_systemfile_unlock(hfsmp, lockflags);
176 }
177
178
179 __private_extern__
180 void
181 cat_convertattr(
182 struct hfsmount *hfsmp,
183 CatalogRecord * recp,
184 struct cat_attr *attrp,
185 struct cat_fork *datafp,
186 struct cat_fork *rsrcfp)
187 {
188 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
189
190 if (std_hfs) {
191 struct HFSPlusCatalogFile cnoderec;
192
193 promoteattr(hfsmp, recp, &cnoderec);
194 getbsdattr(hfsmp, &cnoderec, attrp);
195 } else {
196 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
197 }
198
199 if (isadir(recp))
200 bzero(datafp, sizeof(*datafp));
201 else if (std_hfs) {
202 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
203 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
204 } else {
205 /* Convert the data fork. */
206 datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
207 datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
208 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
209 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
210 datafp->cf_bytesread =
211 recp->hfsPlusFile.dataFork.clumpSize *
212 HFSTOVCB(hfsmp)->blockSize;
213 } else {
214 datafp->cf_bytesread = 0;
215 }
216 datafp->cf_vblocks = 0;
217 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
218 &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
219
220 /* Convert the resource fork. */
221 rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
222 rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
223 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
224 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
225 datafp->cf_bytesread =
226 recp->hfsPlusFile.resourceFork.clumpSize *
227 HFSTOVCB(hfsmp)->blockSize;
228 } else {
229 datafp->cf_bytesread = 0;
230 }
231 rsrcfp->cf_vblocks = 0;
232 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
233 &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
234 }
235 }
236
237 __private_extern__
238 int
239 cat_convertkey(
240 struct hfsmount *hfsmp,
241 CatalogKey *key,
242 CatalogRecord * recp,
243 struct cat_desc *descp)
244 {
245 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
246 HFSPlusCatalogKey * pluskey = NULL;
247 u_long encoding;
248
249 if (std_hfs) {
250 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
251 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
252
253 } else {
254 pluskey = (HFSPlusCatalogKey *)key;
255 encoding = getencoding(recp);
256 }
257
258 builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp);
259 if (std_hfs) {
260 FREE(pluskey, M_TEMP);
261 }
262 return (0);
263 }
264
265
266 /*
267 * cat_releasedesc
268 */
269 __private_extern__
270 void
271 cat_releasedesc(struct cat_desc *descp)
272 {
273 char * name;
274
275 if (descp == NULL)
276 return;
277
278 if ((descp->cd_flags & CD_HASBUF) &&
279 (descp->cd_nameptr != NULL)) {
280 name = descp->cd_nameptr;
281 descp->cd_nameptr = NULL;
282 descp->cd_namelen = 0;
283 descp->cd_flags &= ~CD_HASBUF;
284 vfs_removename(name);
285 }
286 descp->cd_nameptr = NULL;
287 descp->cd_namelen = 0;
288 }
289
290 /*
291 * These Catalog functions allow access to the HFS Catalog (database).
292 * The catalog b-tree lock must be aquired before calling any of these routines.
293 */
294
295 /*
296 * cat_lookup - lookup a catalog node using a cnode decriptor
297 */
298 __private_extern__
299 int
300 cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
301 struct cat_desc *outdescp, struct cat_attr *attrp,
302 struct cat_fork *forkp, cnid_t *desc_cnid)
303 {
304 CatalogKey * keyp;
305 int std_hfs;
306 int result;
307
308 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
309
310 MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
311
312 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1);
313 if (result)
314 goto exit;
315
316 result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
317
318 if (result == ENOENT) {
319 if (!std_hfs) {
320 struct cat_desc temp_desc;
321 if (outdescp == NULL) {
322 bzero(&temp_desc, sizeof(temp_desc));
323 outdescp = &temp_desc;
324 }
325 result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
326 if (desc_cnid) {
327 *desc_cnid = outdescp->cd_cnid;
328 }
329 if (outdescp == &temp_desc) {
330 /* Release the local copy of desc */
331 cat_releasedesc(outdescp);
332 }
333 } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
334 // make MacRoman key from utf-8
335 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
336 // update desc text encoding so that other catalog ops succeed
337 }
338 }
339 exit:
340 FREE(keyp, M_TEMP);
341
342 return (result);
343 }
344
345 __private_extern__
346 int
347 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
348 {
349 struct BTreeIterator *iterator;
350 struct FSBufferDescriptor file_data;
351 struct HFSCatalogFile file_rec;
352 UInt16 datasize;
353 FCB *fcb;
354 int result;
355
356 if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord)
357 return (EINVAL);
358
359 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
360
361 MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
362 bzero(&iterator[0], 2* sizeof(*iterator));
363 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0);
364 if (result)
365 goto exit;
366
367 BDINIT(file_data, &file_rec);
368 result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
369 if (result)
370 goto exit;
371
372 if (file_rec.recordType != kHFSFileRecord) {
373 result = EISDIR;
374 goto exit;
375 }
376
377 if ((file_rec.flags & kHFSThreadExistsMask) == 0) {
378 struct FSBufferDescriptor thread_data;
379 struct HFSCatalogThread thread_rec;
380
381 file_rec.flags |= kHFSThreadExistsMask;
382 BDINIT(thread_data, &thread_rec);
383 thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0);
384 buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key);
385
386 result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize);
387 if (result)
388 goto exit;
389
390 (void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize);
391 (void) BTFlushPath(fcb);
392 }
393 exit:
394 (void) BTFlushPath(fcb);
395 FREE(iterator, M_TEMP);
396
397 return MacToVFSError(result);
398 }
399
400
401 /*
402 * cat_findname - obtain a descriptor from cnid
403 *
404 * Only a thread lookup is performed.
405 */
406 __private_extern__
407 int
408 cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
409 {
410 struct BTreeIterator * iterator;
411 FSBufferDescriptor btdata;
412 CatalogKey * keyp;
413 CatalogRecord * recp;
414 int isdir;
415 int result;
416 int std_hfs;
417
418 isdir = 0;
419 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
420
421 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
422 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
423 iterator->hint.nodeNum = 0;
424
425 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
426 BDINIT(btdata, recp);
427
428 result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
429 if (result)
430 goto exit;
431
432 /* Turn thread record into a cnode key (in place). */
433 switch (recp->recordType) {
434 case kHFSFolderThreadRecord:
435 isdir = 1;
436 /* fall through */
437 case kHFSFileThreadRecord:
438 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
439 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
440 break;
441
442 case kHFSPlusFolderThreadRecord:
443 isdir = 1;
444 /* fall through */
445 case kHFSPlusFileThreadRecord:
446 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
447 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
448 (keyp->hfsPlus.nodeName.length * 2);
449 break;
450 default:
451 result = ENOENT;
452 goto exit;
453 }
454 if (std_hfs) {
455 HFSPlusCatalogKey * pluskey = NULL;
456 u_long encoding;
457
458 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
459 promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
460 builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
461 FREE(pluskey, M_TEMP);
462
463 } else {
464 builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
465 }
466 exit:
467 FREE(recp, M_TEMP);
468 FREE(iterator, M_TEMP);
469
470 return MacToVFSError(result);
471 }
472
473 /*
474 * cat_idlookup - lookup a catalog node using a cnode id
475 */
476 __private_extern__
477 int
478 cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
479 struct cat_attr *attrp, struct cat_fork *forkp)
480 {
481 struct BTreeIterator * iterator;
482 FSBufferDescriptor btdata;
483 UInt16 datasize;
484 CatalogKey * keyp;
485 CatalogRecord * recp;
486 int result;
487 int std_hfs;
488
489 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
490
491 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
492 bzero(iterator, sizeof(*iterator));
493 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
494
495 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
496 BDINIT(btdata, recp);
497
498 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
499 &btdata, &datasize, iterator);
500 if (result)
501 goto exit;
502
503 /* Turn thread record into a cnode key (in place) */
504 switch (recp->recordType) {
505 case kHFSFileThreadRecord:
506 case kHFSFolderThreadRecord:
507 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
508 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
509 break;
510
511 case kHFSPlusFileThreadRecord:
512 case kHFSPlusFolderThreadRecord:
513 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
514 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
515 (keyp->hfsPlus.nodeName.length * 2);
516 break;
517
518 default:
519 result = ENOENT;
520 goto exit;
521 }
522
523 result = cat_lookupbykey(hfsmp, keyp, 0, 0, outdescp, attrp, forkp, NULL);
524 exit:
525 FREE(recp, M_TEMP);
526 FREE(iterator, M_TEMP);
527
528 return MacToVFSError(result);
529 }
530
531
532 /*
533 * cat_lookupmangled - lookup a catalog node using a mangled name
534 */
535 static int
536 cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
537 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
538 {
539 cnid_t fileID;
540 int prefixlen;
541 int result;
542
543 if (wantrsrc)
544 return (ENOENT);
545
546 fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
547 if (fileID < kHFSFirstUserCatalogNodeID)
548 return (ENOENT);
549
550 result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
551 if (result)
552 return (ENOENT);
553
554 /* It must be in the correct directory */
555 if (descp->cd_parentcnid != outdescp->cd_parentcnid)
556 goto falsematch;
557
558 if ((outdescp->cd_namelen < prefixlen) ||
559 bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
560 goto falsematch;
561
562 return (0);
563
564 falsematch:
565 cat_releasedesc(outdescp);
566 return (ENOENT);
567 }
568
569
570 /*
571 * cat_lookupbykey - lookup a catalog node using a cnode key
572 */
573 static int
574 cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
575 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
576 {
577 struct BTreeIterator * iterator;
578 FSBufferDescriptor btdata;
579 CatalogRecord * recp;
580 UInt16 datasize;
581 int result;
582 int std_hfs;
583 u_long ilink = 0;
584 cnid_t cnid = 0;
585 u_long encoding = 0;
586
587 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
588
589 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
590 BDINIT(btdata, recp);
591 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
592 bzero(iterator, sizeof(*iterator));
593 iterator->hint.nodeNum = hint;
594 bcopy(keyp, &iterator->key, sizeof(CatalogKey));
595
596 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
597 &btdata, &datasize, iterator);
598 if (result)
599 goto exit;
600
601 /* Save the cnid now in case there's a hard link */
602 cnid = getcnid(recp);
603 encoding = getencoding(recp);
604 hint = iterator->hint.nodeNum;
605
606 /* Hide the journal files (if any) */
607 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
608 ((cnid == hfsmp->hfs_jnlfileid) ||
609 (cnid == hfsmp->hfs_jnlinfoblkid))) {
610
611 result = ENOENT;
612 goto exit;
613 }
614
615 /*
616 * When a hardlink link is encountered, auto resolve it
617 */
618 if (!std_hfs
619 && (attrp || forkp)
620 && (recp->recordType == kHFSPlusFileRecord)
621 && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
622 && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
623 && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
624 (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
625
626 ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
627
628 (void) resolvelink(hfsmp, ilink, (struct HFSPlusCatalogFile *)recp);
629 }
630
631 if (attrp != NULL) {
632 if (std_hfs) {
633 struct HFSPlusCatalogFile cnoderec;
634
635 promoteattr(hfsmp, recp, &cnoderec);
636 getbsdattr(hfsmp, &cnoderec, attrp);
637 } else {
638 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
639 if (ilink)
640 attrp->ca_rdev = ilink;
641 }
642 }
643 if (forkp != NULL) {
644 if (isadir(recp)) {
645 bzero(forkp, sizeof(*forkp));
646 } else if (std_hfs) {
647 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
648 } else if (wantrsrc) {
649 /* Convert the resource fork. */
650 forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
651 forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
652 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
653 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
654 forkp->cf_bytesread =
655 recp->hfsPlusFile.resourceFork.clumpSize *
656 HFSTOVCB(hfsmp)->blockSize;
657 } else {
658 forkp->cf_bytesread = 0;
659 }
660 forkp->cf_vblocks = 0;
661 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
662 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
663 } else {
664 int i;
665 u_int32_t validblks;
666
667 /* Convert the data fork. */
668 forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
669 forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
670 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
671 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
672 forkp->cf_bytesread =
673 recp->hfsPlusFile.dataFork.clumpSize *
674 HFSTOVCB(hfsmp)->blockSize;
675 } else {
676 forkp->cf_bytesread = 0;
677 }
678 forkp->cf_vblocks = 0;
679 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
680 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
681
682 /* Validate the fork's resident extents. */
683 validblks = 0;
684 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
685 if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) {
686 /* Suppress any bad extents so a remove can succeed. */
687 forkp->cf_extents[i].startBlock = 0;
688 forkp->cf_extents[i].blockCount = 0;
689 /* Disable writes */
690 if (attrp != NULL) {
691 attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH;
692 }
693 } else {
694 validblks += forkp->cf_extents[i].blockCount;
695 }
696 }
697 /* Adjust for any missing blocks. */
698 if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
699 u_int64_t psize;
700
701 forkp->cf_blocks = validblks;
702 if (attrp != NULL) {
703 attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
704 }
705 psize = (u_int64_t)validblks * (u_int64_t)hfsmp->blockSize;
706 if (psize < forkp->cf_size) {
707 forkp->cf_size = psize;
708 }
709
710 }
711 }
712 }
713 if (descp != NULL) {
714 HFSPlusCatalogKey * pluskey = NULL;
715
716 if (std_hfs) {
717 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
718 promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
719
720 } else
721 pluskey = (HFSPlusCatalogKey *)&iterator->key;
722
723 builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
724 if (std_hfs) {
725 FREE(pluskey, M_TEMP);
726 }
727 }
728
729 if (desc_cnid != NULL) {
730 *desc_cnid = cnid;
731 }
732 exit:
733 FREE(iterator, M_TEMP);
734 FREE(recp, M_TEMP);
735
736 return MacToVFSError(result);
737 }
738
739
740 /*
741 * cat_create - create a node in the catalog
742 */
743 __private_extern__
744 int
745 cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
746 struct cat_desc *out_descp)
747 {
748 ExtendedVCB * vcb;
749 FCB * fcb;
750 struct btobj * bto;
751 FSBufferDescriptor btdata;
752 u_int32_t nextCNID;
753 u_int32_t datalen;
754 int std_hfs;
755 int result = 0;
756 u_long encoding;
757 int modeformat;
758 int mntlock = 0;
759
760 modeformat = attrp->ca_mode & S_IFMT;
761
762 vcb = HFSTOVCB(hfsmp);
763 fcb = GetFileControlBlock(vcb->catalogRefNum);
764 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
765
766 /*
767 * Atomically get the next CNID. If we have wrapped the CNIDs
768 * then keep the hfsmp lock held until we have found a CNID.
769 */
770 HFS_MOUNT_LOCK(hfsmp, TRUE);
771 mntlock = 1;
772 nextCNID = hfsmp->vcbNxtCNID;
773 if (nextCNID == 0xFFFFFFFF) {
774 if (std_hfs) {
775 result = ENOSPC;
776 } else {
777 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
778 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
779 }
780 } else {
781 hfsmp->vcbNxtCNID++;
782 }
783 hfsmp->vcbFlags |= 0xFF00;
784 /* OK to drop lock if CNIDs are not wrapping */
785 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask) == 0) {
786 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
787 mntlock = 0;
788 if (result)
789 return (result); /* HFS only exit */
790 }
791
792 /* Get space for iterator, key and data */
793 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
794 bto->iterator.hint.nodeNum = 0;
795
796 result = buildkey(hfsmp, descp, &bto->key, 0);
797 if (result)
798 goto exit;
799
800 if (!std_hfs) {
801 encoding = hfs_pickencoding(bto->key.nodeName.unicode,
802 bto->key.nodeName.length);
803 hfs_setencodingbits(hfsmp, encoding);
804 }
805
806 /*
807 * Insert the thread record first
808 */
809 if (!std_hfs || (modeformat == S_IFDIR)) {
810 datalen = buildthread((void*)&bto->key, &bto->data, std_hfs,
811 S_ISDIR(attrp->ca_mode));
812 btdata.bufferAddress = &bto->data;
813 btdata.itemSize = datalen;
814 btdata.itemCount = 1;
815
816 for (;;) {
817 buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
818
819 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
820 if ((result == btExists) && !std_hfs && mntlock) {
821 /*
822 * Allow CNIDs on HFS Plus volumes to wrap around
823 */
824 if (++nextCNID < kHFSFirstUserCatalogNodeID) {
825 nextCNID = kHFSFirstUserCatalogNodeID;
826 }
827 continue;
828 }
829 break;
830 }
831 if (result) goto exit;
832 }
833
834 /*
835 * CNID is now established. If we have wrapped then
836 * update the vcbNxtCNID and drop the vcb lock.
837 */
838 if (mntlock) {
839 hfsmp->vcbNxtCNID = nextCNID + 1;
840 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
841 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
842 }
843 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
844 mntlock = 0;
845 }
846
847 /*
848 * Now insert the file/directory record
849 */
850 buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen);
851 btdata.bufferAddress = &bto->data;
852 btdata.itemSize = datalen;
853 btdata.itemCount = 1;
854
855 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
856
857 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
858 if (result) {
859 if (result == btExists)
860 result = EEXIST;
861
862 /* Back out the thread record */
863 if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
864 buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key);
865 (void) BTDeleteRecord(fcb, &bto->iterator);
866 }
867 goto exit;
868 }
869
870 /*
871 * Insert was Successfull, update name, parent and volume
872 */
873
874
875 if (out_descp != NULL) {
876 HFSPlusCatalogKey * pluskey = NULL;
877
878 if (std_hfs) {
879 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
880 promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
881
882 } else
883 pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
884
885 builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum,
886 encoding, S_ISDIR(attrp->ca_mode), out_descp);
887 if (std_hfs) {
888 FREE(pluskey, M_TEMP);
889 }
890 }
891 attrp->ca_fileid = nextCNID;
892
893 exit:
894 if (mntlock)
895 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
896
897 (void) BTFlushPath(fcb);
898 FREE(bto, M_TEMP);
899
900 return MacToVFSError(result);
901 }
902
903
904 /*
905 * cnode_rename - rename a catalog node
906 *
907 * Assumes that the target's directory exists.
908 *
909 * Order of B-tree operations:
910 * 1. BTSearchRecord(from_cnode, &data);
911 * 2. BTInsertRecord(to_cnode, &data);
912 * 3. BTDeleteRecord(from_cnode);
913 * 4. BTDeleteRecord(from_thread);
914 * 5. BTInsertRecord(to_thread);
915 */
916 __private_extern__
917 int
918 cat_rename (
919 struct hfsmount * hfsmp,
920 struct cat_desc * from_cdp,
921 struct cat_desc * todir_cdp,
922 struct cat_desc * to_cdp,
923 struct cat_desc * out_cdp )
924 {
925 struct BTreeIterator * to_iterator = NULL;
926 struct BTreeIterator * from_iterator = NULL;
927 FSBufferDescriptor btdata;
928 CatalogRecord * recp = NULL;
929 HFSPlusCatalogKey * to_key;
930 ExtendedVCB * vcb;
931 FCB * fcb;
932 UInt16 datasize;
933 int result = 0;
934 int sourcegone = 0;
935 int skipthread = 0;
936 int directory = from_cdp->cd_flags & CD_ISDIR;
937 int std_hfs;
938 u_long encoding = 0;
939
940 vcb = HFSTOVCB(hfsmp);
941 fcb = GetFileControlBlock(vcb->catalogRefNum);
942 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
943
944 if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
945 return (EINVAL);
946
947 MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK);
948 bzero(from_iterator, sizeof(*from_iterator));
949 if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0)))
950 goto exit;
951
952 MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK);
953 bzero(to_iterator, sizeof(*to_iterator));
954 if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
955 goto exit;
956
957 to_key = (HFSPlusCatalogKey *)&to_iterator->key;
958 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
959 BDINIT(btdata, recp);
960
961 /*
962 * When moving a directory, make sure its a valid move.
963 */
964 if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
965 struct BTreeIterator iterator;
966 cnid_t cnid = from_cdp->cd_cnid;
967 cnid_t pathcnid = todir_cdp->cd_parentcnid;
968
969 /* First check the obvious ones */
970 if (cnid == fsRtDirID ||
971 cnid == to_cdp->cd_parentcnid ||
972 cnid == pathcnid) {
973 result = EINVAL;
974 goto exit;
975 }
976 bzero(&iterator, sizeof(iterator));
977 /*
978 * Traverese destination path all the way back to the root
979 * making sure that source directory is not encountered.
980 *
981 */
982 while (pathcnid > fsRtDirID) {
983 buildthreadkey(pathcnid, std_hfs,
984 (CatalogKey *)&iterator.key);
985 result = BTSearchRecord(fcb, &iterator, &btdata,
986 &datasize, NULL);
987 if (result) goto exit;
988
989 pathcnid = getparentcnid(recp);
990 if (pathcnid == cnid) {
991 result = EINVAL;
992 goto exit;
993 }
994 }
995 }
996
997 /*
998 * Step 1: Find cnode data at old location
999 */
1000 result = BTSearchRecord(fcb, from_iterator, &btdata,
1001 &datasize, from_iterator);
1002 if (result) {
1003 if (std_hfs || (result != btNotFound))
1004 goto exit;
1005
1006 struct cat_desc temp_desc;
1007
1008 /* Probably the node has mangled name */
1009 result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL);
1010 if (result)
1011 goto exit;
1012
1013 /* The file has mangled name. Search the cnode data using full name */
1014 bzero(from_iterator, sizeof(*from_iterator));
1015 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
1016 if (result) {
1017 cat_releasedesc(&temp_desc);
1018 goto exit;
1019 }
1020
1021 result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
1022 if (result) {
1023 cat_releasedesc(&temp_desc);
1024 goto exit;
1025 }
1026
1027 cat_releasedesc(&temp_desc);
1028 }
1029
1030 /* Update the text encoding (on disk and in descriptor) */
1031 if (!std_hfs) {
1032 encoding = hfs_pickencoding(to_key->nodeName.unicode,
1033 to_key->nodeName.length);
1034 hfs_setencodingbits(hfsmp, encoding);
1035 recp->hfsPlusFile.textEncoding = encoding;
1036 if (out_cdp)
1037 out_cdp->cd_encoding = encoding;
1038 }
1039
1040 if (std_hfs && !directory &&
1041 !(recp->hfsFile.flags & kHFSThreadExistsMask))
1042 skipthread = 1;
1043 #if 0
1044 /*
1045 * If the keys are identical then there's nothing left to do!
1046 *
1047 * update the hint and exit
1048 *
1049 */
1050 if (std_hfs && hfskeycompare(to_key, iter->key) == 0)
1051 goto exit;
1052 if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0)
1053 goto exit;
1054 #endif
1055
1056 /* Step 2: Insert cnode at new location */
1057 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1058 if (result == btExists) {
1059 int fromtype = recp->recordType;
1060
1061 if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
1062 goto exit; /* EEXIST */
1063
1064 /* Find cnode data at new location */
1065 result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
1066 if (result)
1067 goto exit;
1068
1069 if ((fromtype != recp->recordType) ||
1070 (from_cdp->cd_cnid != getcnid(recp))) {
1071 result = EEXIST;
1072 goto exit; /* EEXIST */
1073 }
1074 /* The old name is a case variant and must be removed */
1075 result = BTDeleteRecord(fcb, from_iterator);
1076 if (result)
1077 goto exit;
1078
1079 /* Insert cnode (now that case duplicate is gone) */
1080 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1081 if (result) {
1082 /* Try and restore original before leaving */
1083 // XXXdbg
1084 #if 1
1085 {
1086 int err;
1087 err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1088 if (err)
1089 panic("cat_create: could not undo (BTInsert = %d)", err);
1090 }
1091 #else
1092 (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1093 #endif
1094 goto exit;
1095 }
1096 sourcegone = 1;
1097 }
1098 if (result)
1099 goto exit;
1100
1101 /* Step 3: Remove cnode from old location */
1102 if (!sourcegone) {
1103 result = BTDeleteRecord(fcb, from_iterator);
1104 if (result) {
1105 /* Try and delete new record before leaving */
1106 // XXXdbg
1107 #if 1
1108 {
1109 int err;
1110 err = BTDeleteRecord(fcb, to_iterator);
1111 if (err)
1112 panic("cat_create: could not undo (BTDelete = %d)", err);
1113 }
1114 #else
1115 (void) BTDeleteRecord(fcb, to_iterator);
1116 #endif
1117 goto exit;
1118 }
1119 }
1120
1121 /* #### POINT OF NO RETURN #### */
1122
1123 /*
1124 * Step 4: Remove cnode's old thread record
1125 */
1126 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1127 (void) BTDeleteRecord(fcb, from_iterator);
1128
1129 /*
1130 * Step 5: Insert cnode's new thread record
1131 * (optional for HFS files)
1132 */
1133 if (!skipthread) {
1134 datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
1135 btdata.itemSize = datasize;
1136 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1137 result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1138 }
1139
1140 if (out_cdp) {
1141 HFSPlusCatalogKey * pluskey = NULL;
1142
1143 if (std_hfs) {
1144 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1145 promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
1146
1147 /* Save the real encoding hint in the Finder Info (field 4). */
1148 if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
1149 u_long realhint;
1150
1151 realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
1152 vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
1153 }
1154
1155 } else
1156 pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
1157
1158 builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
1159 encoding, directory, out_cdp);
1160 if (std_hfs) {
1161 FREE(pluskey, M_TEMP);
1162 }
1163 }
1164 exit:
1165 (void) BTFlushPath(fcb);
1166 if (from_iterator)
1167 FREE(from_iterator, M_TEMP);
1168 if (to_iterator)
1169 FREE(to_iterator, M_TEMP);
1170 if (recp)
1171 FREE(recp, M_TEMP);
1172 return MacToVFSError(result);
1173 }
1174
1175
1176 /*
1177 * cat_delete - delete a node from the catalog
1178 *
1179 * Order of B-tree operations:
1180 * 1. BTDeleteRecord(cnode);
1181 * 2. BTDeleteRecord(thread);
1182 * 3. BTUpdateRecord(parent);
1183 */
1184 __private_extern__
1185 int
1186 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
1187 {
1188 ExtendedVCB * vcb;
1189 FCB * fcb;
1190 BTreeIterator *iterator;
1191 cnid_t cnid;
1192 int std_hfs;
1193 int result;
1194
1195 vcb = HFSTOVCB(hfsmp);
1196 fcb = GetFileControlBlock(vcb->catalogRefNum);
1197 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1198
1199 /* Preflight check:
1200 *
1201 * The root directory cannot be deleted
1202 * A directory must be empty
1203 * A file must be zero length (no blocks)
1204 */
1205 if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
1206 descp->cd_parentcnid == kHFSRootParentID)
1207 return (EINVAL);
1208
1209 /* XXX Preflight Missing */
1210
1211 /* Get space for iterator */
1212 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1213 iterator->hint.nodeNum = 0;
1214
1215 /*
1216 * Derive a key from either the file ID (for a virtual inode)
1217 * or the descriptor.
1218 */
1219 if (descp->cd_namelen == 0) {
1220 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1221 cnid = attrp->ca_fileid;
1222 } else {
1223 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1224 cnid = descp->cd_cnid;
1225 }
1226 if (result)
1227 goto exit;
1228
1229 /* Delete record */
1230 result = BTDeleteRecord(fcb, iterator);
1231 if (result) {
1232 if (std_hfs || (result != btNotFound))
1233 goto exit;
1234
1235 struct cat_desc temp_desc;
1236
1237 /* Probably the node has mangled name */
1238 result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL);
1239 if (result)
1240 goto exit;
1241
1242 /* The file has mangled name. Delete the file using full name */
1243 bzero(iterator, sizeof(*iterator));
1244 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
1245 cnid = temp_desc.cd_cnid;
1246 if (result) {
1247 cat_releasedesc(&temp_desc);
1248 goto exit;
1249 }
1250
1251 result = BTDeleteRecord(fcb, iterator);
1252 if (result) {
1253 cat_releasedesc(&temp_desc);
1254 goto exit;
1255 }
1256
1257 cat_releasedesc(&temp_desc);
1258 }
1259
1260 /* Delete thread record, ignore errors */
1261 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
1262 (void) BTDeleteRecord(fcb, iterator);
1263
1264 exit:
1265 (void) BTFlushPath(fcb);
1266 FREE(iterator, M_TEMP);
1267
1268 return MacToVFSError(result);
1269 }
1270
1271
1272 /*
1273 * cnode_update - update the catalog node described by descp
1274 * using the data from attrp and forkp.
1275 */
1276 __private_extern__
1277 int
1278 cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
1279 struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
1280 {
1281 ExtendedVCB * vcb;
1282 FCB * fcb;
1283 BTreeIterator * iterator;
1284 struct update_state state;
1285 int std_hfs;
1286 int result;
1287
1288 vcb = HFSTOVCB(hfsmp);
1289 fcb = GetFileControlBlock(vcb->catalogRefNum);
1290 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1291
1292 state.s_desc = descp;
1293 state.s_attr = attrp;
1294 state.s_datafork = dataforkp;
1295 state.s_rsrcfork = rsrcforkp;
1296 state.s_hfsmp = hfsmp;
1297
1298 /* Get space for iterator */
1299 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1300
1301 /*
1302 * For open-deleted files we need to do a lookup by cnid
1303 * (using thread rec).
1304 *
1305 * For hard links, the target of the update is the inode
1306 * itself (not the link record) so a lookup by fileid
1307 * (i.e. thread rec) is needed.
1308 */
1309 if ((descp->cd_cnid != attrp->ca_fileid) || (descp->cd_namelen == 0))
1310 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1311 else
1312 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1313 if (result)
1314 goto exit;
1315
1316 /* Pass a node hint */
1317 iterator->hint.nodeNum = descp->cd_hint;
1318
1319 result = BTUpdateRecord(fcb, iterator,
1320 (IterateCallBackProcPtr)catrec_update, &state);
1321 if (result)
1322 goto exit;
1323
1324 /* Update the node hint. */
1325 descp->cd_hint = iterator->hint.nodeNum;
1326
1327 exit:
1328 (void) BTFlushPath(fcb);
1329 FREE(iterator, M_TEMP);
1330
1331 return MacToVFSError(result);
1332 }
1333
1334 /*
1335 * catrec_update - Update the fields of a catalog record
1336 * This is called from within BTUpdateRecord.
1337 */
1338 static int
1339 catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
1340 {
1341 struct cat_desc *descp;
1342 struct cat_attr *attrp;
1343 struct cat_fork *forkp;
1344 struct hfsmount *hfsmp;
1345 long blksize;
1346 int i;
1347
1348 descp = state->s_desc;
1349 attrp = state->s_attr;
1350 hfsmp = state->s_hfsmp;
1351 blksize = HFSTOVCB(hfsmp)->blockSize;
1352
1353 switch (crp->recordType) {
1354 case kHFSFolderRecord: {
1355 HFSCatalogFolder *dir;
1356
1357 dir = (struct HFSCatalogFolder *)crp;
1358 /* Do a quick sanity check */
1359 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1360 (dir->folderID != descp->cd_cnid))
1361 return (btNotFound);
1362 dir->valence = attrp->ca_entries;
1363 dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1364 dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1365 dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1366 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16);
1367 bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16);
1368 break;
1369 }
1370 case kHFSFileRecord: {
1371 HFSCatalogFile *file;
1372
1373 file = (struct HFSCatalogFile *)crp;
1374 /* Do a quick sanity check */
1375 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1376 (file->fileID != attrp->ca_fileid))
1377 return (btNotFound);
1378 file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1379 file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1380 file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1381 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16);
1382 bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16);
1383 if (state->s_rsrcfork) {
1384 forkp = state->s_rsrcfork;
1385 file->rsrcLogicalSize = forkp->cf_size;
1386 file->rsrcPhysicalSize = forkp->cf_blocks * blksize;
1387 for (i = 0; i < kHFSExtentDensity; ++i) {
1388 file->rsrcExtents[i].startBlock =
1389 (u_int16_t)forkp->cf_extents[i].startBlock;
1390 file->rsrcExtents[i].blockCount =
1391 (u_int16_t)forkp->cf_extents[i].blockCount;
1392 }
1393 }
1394 if (state->s_datafork) {
1395 forkp = state->s_datafork;
1396 file->dataLogicalSize = forkp->cf_size;
1397 file->dataPhysicalSize = forkp->cf_blocks * blksize;
1398 for (i = 0; i < kHFSExtentDensity; ++i) {
1399 file->dataExtents[i].startBlock =
1400 (u_int16_t)forkp->cf_extents[i].startBlock;
1401 file->dataExtents[i].blockCount =
1402 (u_int16_t)forkp->cf_extents[i].blockCount;
1403 }
1404 }
1405 break;
1406 }
1407 case kHFSPlusFolderRecord: {
1408 HFSPlusCatalogFolder *dir;
1409
1410 dir = (struct HFSPlusCatalogFolder *)crp;
1411 /* Do a quick sanity check */
1412 if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
1413 (dir->folderID != descp->cd_cnid))
1414 return (btNotFound);
1415 dir->flags = attrp->ca_recflags;
1416 dir->valence = attrp->ca_entries;
1417 dir->createDate = to_hfs_time(attrp->ca_itime);
1418 dir->contentModDate = to_hfs_time(attrp->ca_mtime);
1419 dir->backupDate = to_hfs_time(attrp->ca_btime);
1420 dir->accessDate = to_hfs_time(attrp->ca_atime);
1421 attrp->ca_atimeondisk = attrp->ca_atime;
1422 dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
1423 dir->textEncoding = descp->cd_encoding;
1424 dir->attrBlocks = attrp->ca_attrblks;
1425 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
1426 /*
1427 * Update the BSD Info if it was already initialized on
1428 * disk or if the runtime values have been modified.
1429 *
1430 * If the BSD info was already initialized, but
1431 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1432 * probably different than what was on disk. We don't want
1433 * to overwrite the on-disk values (so if we turn off
1434 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1435 * This way, we can still change fields like the mode or
1436 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1437 *
1438 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1439 * won't change the uid or gid from their defaults. So, if
1440 * the BSD info wasn't set, and the runtime values are not
1441 * default, then what changed was the mode or flags. We
1442 * have to set the uid and gid to something, so use the
1443 * supplied values (which will be default), which has the
1444 * same effect as creating a new file while
1445 * MNT_UNKNOWNPERMISSIONS is set.
1446 */
1447 if ((dir->bsdInfo.fileMode != 0) ||
1448 (attrp->ca_flags != 0) ||
1449 (attrp->ca_uid != hfsmp->hfs_uid) ||
1450 (attrp->ca_gid != hfsmp->hfs_gid) ||
1451 ((attrp->ca_mode & ALLPERMS) !=
1452 (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
1453 if ((dir->bsdInfo.fileMode == 0) ||
1454 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1455 dir->bsdInfo.ownerID = attrp->ca_uid;
1456 dir->bsdInfo.groupID = attrp->ca_gid;
1457 }
1458 dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1459 dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1460 dir->bsdInfo.fileMode = attrp->ca_mode;
1461 }
1462 break;
1463 }
1464 case kHFSPlusFileRecord: {
1465 HFSPlusCatalogFile *file;
1466
1467 file = (struct HFSPlusCatalogFile *)crp;
1468 /* Do a quick sanity check */
1469 if (file->fileID != attrp->ca_fileid)
1470 return (btNotFound);
1471 file->flags = attrp->ca_recflags;
1472 file->createDate = to_hfs_time(attrp->ca_itime);
1473 file->contentModDate = to_hfs_time(attrp->ca_mtime);
1474 file->backupDate = to_hfs_time(attrp->ca_btime);
1475 file->accessDate = to_hfs_time(attrp->ca_atime);
1476 attrp->ca_atimeondisk = attrp->ca_atime;
1477 file->attributeModDate = to_hfs_time(attrp->ca_ctime);
1478 file->textEncoding = descp->cd_encoding;
1479 file->attrBlocks = attrp->ca_attrblks;
1480 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
1481 /*
1482 * Update the BSD Info if it was already initialized on
1483 * disk or if the runtime values have been modified.
1484 *
1485 * If the BSD info was already initialized, but
1486 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1487 * probably different than what was on disk. We don't want
1488 * to overwrite the on-disk values (so if we turn off
1489 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1490 * This way, we can still change fields like the mode or
1491 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1492 *
1493 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1494 * won't change the uid or gid from their defaults. So, if
1495 * the BSD info wasn't set, and the runtime values are not
1496 * default, then what changed was the mode or flags. We
1497 * have to set the uid and gid to something, so use the
1498 * supplied values (which will be default), which has the
1499 * same effect as creating a new file while
1500 * MNT_UNKNOWNPERMISSIONS is set.
1501 */
1502 if ((file->bsdInfo.fileMode != 0) ||
1503 (attrp->ca_flags != 0) ||
1504 (attrp->ca_uid != hfsmp->hfs_uid) ||
1505 (attrp->ca_gid != hfsmp->hfs_gid) ||
1506 ((attrp->ca_mode & ALLPERMS) !=
1507 (hfsmp->hfs_file_mask & ACCESSPERMS))) {
1508 if ((file->bsdInfo.fileMode == 0) ||
1509 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1510 file->bsdInfo.ownerID = attrp->ca_uid;
1511 file->bsdInfo.groupID = attrp->ca_gid;
1512 }
1513 file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1514 file->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1515 file->bsdInfo.fileMode = attrp->ca_mode;
1516 }
1517 if (state->s_rsrcfork) {
1518 forkp = state->s_rsrcfork;
1519 file->resourceFork.logicalSize = forkp->cf_size;
1520 file->resourceFork.totalBlocks = forkp->cf_blocks;
1521 bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
1522 sizeof(HFSPlusExtentRecord));
1523 /* Push blocks read to disk */
1524 file->resourceFork.clumpSize =
1525 howmany(forkp->cf_bytesread, blksize);
1526 }
1527 if (state->s_datafork) {
1528 forkp = state->s_datafork;
1529 file->dataFork.logicalSize = forkp->cf_size;
1530 file->dataFork.totalBlocks = forkp->cf_blocks;
1531 bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
1532 sizeof(HFSPlusExtentRecord));
1533 /* Push blocks read to disk */
1534 file->dataFork.clumpSize =
1535 howmany(forkp->cf_bytesread, blksize);
1536 }
1537
1538 if ((file->resourceFork.extents[0].startBlock != 0) &&
1539 (file->resourceFork.extents[0].startBlock ==
1540 file->dataFork.extents[0].startBlock))
1541 panic("catrec_update: rsrc fork == data fork");
1542
1543 /* Synchronize the lock state */
1544 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
1545 file->flags |= kHFSFileLockedMask;
1546 else
1547 file->flags &= ~kHFSFileLockedMask;
1548
1549 /* Push out special field if necessary */
1550 if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode))
1551 file->bsdInfo.special.rawDevice = attrp->ca_rdev;
1552 else if (descp->cd_cnid != attrp->ca_fileid
1553 || attrp->ca_nlink == 2)
1554 file->bsdInfo.special.linkCount = attrp->ca_nlink;
1555 break;
1556 }
1557 default:
1558 return (btNotFound);
1559 }
1560 return (0);
1561 }
1562
1563 /*
1564 * Callback to collect directory entries.
1565 * Called with readattr_state for each item in a directory.
1566 */
1567 struct readattr_state {
1568 struct hfsmount *hfsmp;
1569 struct cat_entrylist *list;
1570 cnid_t dir_cnid;
1571 int stdhfs;
1572 int error;
1573 };
1574
1575 static int
1576 cat_readattr(const CatalogKey *key, const CatalogRecord *rec,
1577 struct readattr_state *state)
1578 {
1579 struct cat_entrylist *list = state->list;
1580 struct hfsmount *hfsmp = state->hfsmp;
1581 struct cat_entry *cep;
1582 cnid_t parentcnid;
1583
1584 if (list->realentries >= list->maxentries)
1585 return (0); /* stop */
1586
1587 parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
1588
1589 switch(rec->recordType) {
1590 case kHFSPlusFolderRecord:
1591 case kHFSPlusFileRecord:
1592 case kHFSFolderRecord:
1593 case kHFSFileRecord:
1594 if (parentcnid != state->dir_cnid) {
1595 state->error = ENOENT;
1596 return (0); /* stop */
1597 }
1598 break;
1599 default:
1600 state->error = ENOENT;
1601 return (0); /* stop */
1602 }
1603
1604 /* Hide the private meta data directory and journal files */
1605 if (parentcnid == kHFSRootFolderID) {
1606 if ((rec->recordType == kHFSPlusFolderRecord) &&
1607 (rec->hfsPlusFolder.folderID == hfsmp->hfs_privdir_desc.cd_cnid)) {
1608 return (1); /* continue */
1609 }
1610 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
1611 (rec->recordType == kHFSPlusFileRecord) &&
1612 ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
1613 (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
1614
1615 return (1); /* continue */
1616 }
1617 }
1618
1619 cep = &list->entry[list->realentries++];
1620
1621 if (state->stdhfs) {
1622 struct HFSPlusCatalogFile cnoderec;
1623 HFSPlusCatalogKey * pluskey;
1624 long encoding;
1625
1626 promoteattr(hfsmp, rec, &cnoderec);
1627 getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
1628
1629 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1630 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
1631 builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
1632 FREE(pluskey, M_TEMP);
1633
1634 if (rec->recordType == kHFSFileRecord) {
1635 int blksize = HFSTOVCB(hfsmp)->blockSize;
1636
1637 cep->ce_datasize = rec->hfsFile.dataLogicalSize;
1638 cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
1639 cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
1640 cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
1641 }
1642 } else {
1643 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
1644 builddesc((HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
1645 isadir(rec), &cep->ce_desc);
1646
1647 if (rec->recordType == kHFSPlusFileRecord) {
1648 cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
1649 cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
1650 cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
1651 cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
1652
1653 /* Save link reference for later processing. */
1654 if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
1655 && (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator))
1656 cep->ce_attr.ca_rdev = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
1657 }
1658 }
1659
1660 return (list->realentries < list->maxentries);
1661 }
1662
1663 /*
1664 * Pack a cat_entrylist buffer with attributes from the catalog
1665 *
1666 * Note: index is zero relative
1667 */
1668 __private_extern__
1669 int
1670 cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
1671 {
1672 FCB* fcb;
1673 CatalogKey * key;
1674 BTreeIterator * iterator;
1675 struct readattr_state state;
1676 cnid_t parentcnid;
1677 int i;
1678 int std_hfs;
1679 int index;
1680 int have_key;
1681 int result = 0;
1682
1683 ce_list->realentries = 0;
1684
1685 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
1686 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
1687 parentcnid = dirhint->dh_desc.cd_parentcnid;
1688
1689 state.hfsmp = hfsmp;
1690 state.list = ce_list;
1691 state.dir_cnid = parentcnid;
1692 state.stdhfs = std_hfs;
1693 state.error = 0;
1694
1695 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1696 bzero(iterator, sizeof(*iterator));
1697 key = (CatalogKey *)&iterator->key;
1698 have_key = 0;
1699 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
1700 index = dirhint->dh_index + 1;
1701
1702 /*
1703 * Attempt to build a key from cached filename
1704 */
1705 if (dirhint->dh_desc.cd_namelen != 0) {
1706 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
1707 have_key = 1;
1708 }
1709 }
1710
1711 /*
1712 * If the last entry wasn't cached then position the btree iterator
1713 */
1714 if ((index == 0) || !have_key) {
1715 /*
1716 * Position the iterator at the directory's thread record.
1717 * (i.e. just before the first entry)
1718 */
1719 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
1720 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
1721 if (result) {
1722 result = MacToVFSError(result);
1723 goto exit;
1724 }
1725
1726 /*
1727 * Iterate until we reach the entry just
1728 * before the one we want to start with.
1729 */
1730 if (index > 0) {
1731 struct position_state ps;
1732
1733 ps.error = 0;
1734 ps.count = 0;
1735 ps.index = index;
1736 ps.parentID = dirhint->dh_desc.cd_parentcnid;
1737 ps.hfsmp = hfsmp;
1738
1739 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
1740 (IterateCallBackProcPtr)cat_findposition, &ps);
1741 if (ps.error)
1742 result = ps.error;
1743 else
1744 result = MacToVFSError(result);
1745 if (result) {
1746 result = MacToVFSError(result);
1747 goto exit;
1748 }
1749 }
1750 }
1751
1752 /* Fill list with entries starting at iterator->key. */
1753 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
1754 (IterateCallBackProcPtr)cat_readattr, &state);
1755
1756 if (state.error)
1757 result = state.error;
1758 else if (ce_list->realentries == 0)
1759 result = ENOENT;
1760 else
1761 result = MacToVFSError(result);
1762
1763 if (std_hfs)
1764 goto exit;
1765
1766 /*
1767 * Resolve any hard links.
1768 */
1769 for (i = 0; i < (int)ce_list->realentries; ++i) {
1770 struct FndrFileInfo *fip;
1771 struct cat_entry *cep;
1772 struct HFSPlusCatalogFile filerec;
1773
1774 cep = &ce_list->entry[i];
1775 if (!S_ISREG(cep->ce_attr.ca_mode))
1776 continue;
1777
1778 /* Note: Finder info is still in Big Endian */
1779 fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
1780
1781 /* Check for hard link signature. */
1782 if ((cep->ce_attr.ca_rdev != 0)
1783 && (SWAP_BE32(fip->fdType) == kHardLinkFileType)
1784 && (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
1785 && ((cep->ce_attr.ca_itime == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
1786 (cep->ce_attr.ca_itime == (time_t)hfsmp->hfs_metadata_createdate))) {
1787
1788 if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &filerec) != 0)
1789 continue;
1790 /* Repack entry from inode record. */
1791 getbsdattr(hfsmp, &filerec, &cep->ce_attr);
1792 cep->ce_datasize = filerec.dataFork.logicalSize;
1793 cep->ce_datablks = filerec.dataFork.totalBlocks;
1794 cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
1795 cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
1796 }
1797 }
1798 exit:
1799 FREE(iterator, M_TEMP);
1800
1801 return MacToVFSError(result);
1802 }
1803
1804 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1805
1806 /*
1807 * Callback to pack directory entries.
1808 * Called with packdirentry_state for each item in a directory.
1809 */
1810
1811 /* Hard link information collected during cat_getdirentries. */
1812 struct linkinfo {
1813 u_long link_ref;
1814 user_addr_t dirent_addr;
1815 };
1816 typedef struct linkinfo linkinfo_t;
1817
1818 /* State information for the cat_packdirentry callback function. */
1819 struct packdirentry_state {
1820 int cbs_extended;
1821 u_int32_t cbs_parentID;
1822 u_int32_t cbs_index;
1823 uio_t cbs_uio;
1824 ExtendedVCB * cbs_hfsmp;
1825 int cbs_result;
1826 int32_t cbs_nlinks;
1827 int32_t cbs_maxlinks;
1828 linkinfo_t * cbs_linkinfo;
1829 struct cat_desc * cbs_desc;
1830 // struct dirent * cbs_stdentry;
1831 // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
1832 struct direntry * cbs_direntry;
1833 struct direntry * cbs_prevdirentry;
1834 u_int32_t cbs_previlinkref;
1835 Boolean cbs_hasprevdirentry;
1836 Boolean cbs_eof;
1837 };
1838
1839 static int
1840 cat_packdirentry(const CatalogKey *ckp, const CatalogRecord *crp,
1841 struct packdirentry_state *state)
1842 {
1843 struct hfsmount *hfsmp;
1844 CatalogName *cnp;
1845 cnid_t curID;
1846 OSErr result;
1847 struct dirent catent;
1848 struct direntry * entry = NULL;
1849 time_t itime;
1850 u_int32_t ilinkref = 0;
1851 u_int32_t curlinkref = 0;
1852 cnid_t cnid;
1853 int hide = 0;
1854 u_int8_t type;
1855 u_int8_t is_mangled = 0;
1856 char *nameptr;
1857 user_addr_t uiobase;
1858 size_t namelen = 0;
1859 size_t maxnamelen;
1860 size_t uiosize = 0;
1861 caddr_t uioaddr;
1862 Boolean stop_after_pack = false;
1863
1864 hfsmp = state->cbs_hfsmp;
1865
1866 if (hfsmp->hfs_flags & HFS_STANDARD)
1867 curID = ckp->hfs.parentID;
1868 else
1869 curID = ckp->hfsPlus.parentID;
1870
1871 /* We're done when parent directory changes */
1872 if (state->cbs_parentID != curID) {
1873 if (state->cbs_extended) {
1874 if (state->cbs_hasprevdirentry) { /* the last record haven't been returned yet, so we want to stop after
1875 * packing the last item */
1876 stop_after_pack = true;
1877 } else {
1878 state->cbs_result = ENOENT;
1879 return (0); /* stop */
1880 }
1881 } else {
1882 state->cbs_result = ENOENT;
1883 return (0); /* stop */
1884 }
1885 }
1886
1887 if (state->cbs_extended) {
1888 entry = state->cbs_direntry;
1889 nameptr = &entry->d_name[0];
1890 maxnamelen = NAME_MAX;
1891 } else {
1892 nameptr = &catent.d_name[0];
1893 maxnamelen = NAME_MAX;
1894 }
1895
1896 if (state->cbs_extended && stop_after_pack) {
1897 cnid = INT_MAX; /* the last item returns a non-zero invalid cookie */
1898 } else {
1899 if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
1900 switch(crp->recordType) {
1901 case kHFSPlusFolderRecord:
1902 type = DT_DIR;
1903 cnid = crp->hfsPlusFolder.folderID;
1904 /* Hide our private meta data directory */
1905 if ((curID == kHFSRootFolderID) &&
1906 (cnid == hfsmp->hfs_privdir_desc.cd_cnid)) {
1907 hide = 1;
1908 }
1909
1910 break;
1911 case kHFSPlusFileRecord:
1912 itime = to_bsd_time(crp->hfsPlusFile.createDate);
1913 /*
1914 * When a hardlink link is encountered save its link ref.
1915 */
1916 if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
1917 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
1918 ((itime == (time_t)hfsmp->hfs_itime) ||
1919 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
1920 ilinkref = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
1921 }
1922 type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
1923 cnid = crp->hfsPlusFile.fileID;
1924 /* Hide the journal files */
1925 if ((curID == kHFSRootFolderID) &&
1926 ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
1927 ((cnid == hfsmp->hfs_jnlfileid) ||
1928 (cnid == hfsmp->hfs_jnlinfoblkid))) {
1929 hide = 1;
1930 }
1931 break;
1932 default:
1933 return (0); /* stop */
1934 };
1935
1936 cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
1937 result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
1938 nameptr, &namelen, maxnamelen + 1, ':', 0);
1939 if (result == ENAMETOOLONG) {
1940 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
1941 cnp->ustr.unicode, maxnamelen + 1,
1942 (ByteCount*)&namelen, nameptr,
1943 cnid);
1944 is_mangled = 1;
1945 }
1946 } else { /* hfs */
1947 switch(crp->recordType) {
1948 case kHFSFolderRecord:
1949 type = DT_DIR;
1950 cnid = crp->hfsFolder.folderID;
1951 break;
1952 case kHFSFileRecord:
1953 type = DT_REG;
1954 cnid = crp->hfsFile.fileID;
1955 break;
1956 default:
1957 return (0); /* stop */
1958 };
1959
1960 cnp = (CatalogName*) ckp->hfs.nodeName;
1961 result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1,
1962 (ByteCount *)&namelen, nameptr);
1963 /*
1964 * When an HFS name cannot be encoded with the current
1965 * volume encoding we use MacRoman as a fallback.
1966 */
1967 if (result)
1968 result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1,
1969 (ByteCount *)&namelen, nameptr);
1970 }
1971 }
1972
1973 if (state->cbs_extended) {
1974 /*
1975 * The index is 1 relative and includes "." and ".."
1976 *
1977 * Also stuff the cnid in the upper 32 bits of the cookie. The cookie is stored to the previous entry, which
1978 * will be packed and copied this time
1979 */
1980 state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
1981 uiosize = state->cbs_prevdirentry->d_reclen;
1982 uioaddr = (caddr_t) state->cbs_prevdirentry;
1983 } else {
1984 catent.d_type = type;
1985 catent.d_namlen = namelen;
1986 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
1987 if (hide)
1988 catent.d_fileno = 0; /* file number = 0 means skip entry */
1989 else
1990 catent.d_fileno = cnid;
1991 uioaddr = (caddr_t) &catent;
1992 }
1993
1994 /* Save current base address for post processing of hard-links. */
1995 uiobase = uio_curriovbase(state->cbs_uio);
1996
1997 /* If this entry won't fit then we're done */
1998 if ((uiosize > uio_resid(state->cbs_uio)) ||
1999 (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
2000 return (0); /* stop */
2001 }
2002
2003 if (!state->cbs_extended || state->cbs_hasprevdirentry) {
2004 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
2005 if (state->cbs_result == 0) {
2006 ++state->cbs_index;
2007
2008 /* Remember previous entry */
2009 state->cbs_desc->cd_cnid = cnid;
2010 if (type == DT_DIR) {
2011 state->cbs_desc->cd_flags |= CD_ISDIR;
2012 } else {
2013 state->cbs_desc->cd_flags &= ~CD_ISDIR;
2014 }
2015 if (state->cbs_desc->cd_nameptr != NULL) {
2016 vfs_removename(state->cbs_desc->cd_nameptr);
2017 }
2018 #if 0
2019 state->cbs_desc->cd_encoding = xxxx;
2020 #endif
2021 if (!is_mangled) {
2022 state->cbs_desc->cd_namelen = namelen;
2023 state->cbs_desc->cd_nameptr = vfs_addname(nameptr, namelen, 0, 0);
2024 } else {
2025 /* Store unmangled name for the directory hint else it will
2026 * restart readdir at the last location again
2027 */
2028 char *new_nameptr;
2029 size_t bufsize;
2030 size_t tmp_namelen = 0;
2031
2032 cnp = (CatalogName *)&ckp->hfsPlus.nodeName;
2033 bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
2034 cnp->ustr.length * sizeof(UniChar),
2035 ':', 0);
2036 MALLOC(new_nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2037 result = utf8_encodestr(cnp->ustr.unicode,
2038 cnp->ustr.length * sizeof(UniChar),
2039 new_nameptr, &tmp_namelen,
2040 bufsize, ':', 0);
2041
2042 state->cbs_desc->cd_namelen = tmp_namelen;
2043 state->cbs_desc->cd_nameptr = vfs_addname(new_nameptr, tmp_namelen, 0, 0);
2044
2045 FREE(new_nameptr, M_TEMP);
2046 }
2047 }
2048 if (state->cbs_hasprevdirentry) {
2049 curlinkref = ilinkref; /* save current */
2050 ilinkref = state->cbs_previlinkref; /* use previous */
2051 }
2052 /*
2053 * Record any hard links for post processing.
2054 */
2055 if ((ilinkref != 0) &&
2056 (state->cbs_result == 0) &&
2057 (state->cbs_nlinks < state->cbs_maxlinks)) {
2058 state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
2059 state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
2060 state->cbs_nlinks++;
2061 }
2062 if (state->cbs_hasprevdirentry) {
2063 ilinkref = curlinkref; /* restore current */
2064 }
2065 }
2066
2067 if (state->cbs_extended) { /* fill the direntry to be used the next time */
2068 if (stop_after_pack) {
2069 state->cbs_eof = true;
2070 return (0); /* stop */
2071 }
2072 entry->d_type = type;
2073 entry->d_namlen = namelen;
2074 entry->d_reclen = EXT_DIRENT_LEN(namelen);
2075 if (hide)
2076 entry->d_fileno = 0; /* file number = 0 means skip entry */
2077 else
2078 entry->d_fileno = cnid;
2079 /* swap the current and previous entry */
2080 struct direntry * tmp;
2081 tmp = state->cbs_direntry;
2082 state->cbs_direntry = state->cbs_prevdirentry;
2083 state->cbs_prevdirentry = tmp;
2084 state->cbs_hasprevdirentry = true;
2085 state->cbs_previlinkref = ilinkref;
2086 }
2087
2088 /* Continue iteration if there's room */
2089 return (state->cbs_result == 0 &&
2090 uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
2091 }
2092
2093
2094 /*
2095 * Pack a uio buffer with directory entries from the catalog
2096 */
2097 __private_extern__
2098 int
2099 cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
2100 uio_t uio, int extended, int * items, int * eofflag)
2101 {
2102 FCB* fcb;
2103 BTreeIterator * iterator;
2104 CatalogKey * key;
2105 struct packdirentry_state state;
2106 void * buffer;
2107 int bufsize;
2108 int maxlinks;
2109 int result;
2110 int index;
2111 int have_key;
2112
2113 fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
2114
2115 /*
2116 * Get a buffer for link info array, btree iterator and a direntry:
2117 */
2118 maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
2119 bufsize = (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
2120 if (extended) {
2121 bufsize += 2*sizeof(struct direntry);
2122 }
2123 MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
2124 bzero(buffer, bufsize);
2125
2126 state.cbs_extended = extended;
2127 state.cbs_hasprevdirentry = false;
2128 state.cbs_previlinkref = 0;
2129 state.cbs_nlinks = 0;
2130 state.cbs_maxlinks = maxlinks;
2131 state.cbs_linkinfo = (linkinfo_t *) buffer;
2132
2133 iterator = (BTreeIterator *) ((char *)buffer + (maxlinks * sizeof(linkinfo_t)));
2134 key = (CatalogKey *)&iterator->key;
2135 have_key = 0;
2136 index = dirhint->dh_index + 1;
2137 if (extended) {
2138 state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
2139 state.cbs_prevdirentry = state.cbs_direntry + 1;
2140 state.cbs_eof = false;
2141 }
2142 /*
2143 * Attempt to build a key from cached filename
2144 */
2145 if (dirhint->dh_desc.cd_namelen != 0) {
2146 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2147 have_key = 1;
2148 }
2149 }
2150
2151 /*
2152 * If the last entry wasn't cached then position the btree iterator
2153 */
2154 if ((index == 0) || !have_key) {
2155 /*
2156 * Position the iterator at the directory's thread record.
2157 * (i.e. just before the first entry)
2158 */
2159 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
2160 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
2161 if (result) {
2162 result = MacToVFSError(result);
2163 goto cleanup;
2164 }
2165
2166 /*
2167 * Iterate until we reach the entry just
2168 * before the one we want to start with.
2169 */
2170 if (index > 0) {
2171 struct position_state ps;
2172
2173 ps.error = 0;
2174 ps.count = 0;
2175 ps.index = index;
2176 ps.parentID = dirhint->dh_desc.cd_parentcnid;
2177 ps.hfsmp = hfsmp;
2178
2179 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2180 (IterateCallBackProcPtr)cat_findposition, &ps);
2181 if (ps.error)
2182 result = ps.error;
2183 else
2184 result = MacToVFSError(result);
2185 if (result) {
2186 result = MacToVFSError(result);
2187 goto cleanup;
2188 }
2189 }
2190 }
2191
2192 state.cbs_index = index;
2193 state.cbs_hfsmp = hfsmp;
2194 state.cbs_uio = uio;
2195 state.cbs_desc = &dirhint->dh_desc;
2196 state.cbs_result = 0;
2197 state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
2198
2199 enum BTreeIterationOperations op;
2200 if (extended && index != 0 && have_key)
2201 op = kBTreeCurrentRecord;
2202 else
2203 op = kBTreeNextRecord;
2204
2205 /*
2206 * Process as many entries as possible starting at iterator->key.
2207 */
2208 result = BTIterateRecords(fcb, op, iterator,
2209 (IterateCallBackProcPtr)cat_packdirentry, &state);
2210
2211 /* Note that state.cbs_index is still valid on errors */
2212 *items = state.cbs_index - index;
2213 index = state.cbs_index;
2214
2215 if (state.cbs_eof) {
2216 *eofflag = 1;
2217 }
2218
2219 /* Finish updating the catalog iterator. */
2220 dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
2221 dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
2222 dirhint->dh_index = index - 1;
2223
2224 /*
2225 * Post process any hard links to get the real file id.
2226 */
2227 if (state.cbs_nlinks > 0) {
2228 u_int32_t fileid = 0;
2229 user_addr_t address;
2230 int i;
2231
2232 for (i = 0; i < state.cbs_nlinks; ++i) {
2233 if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
2234 continue;
2235 /* This assumes that d_ino is always first field. */
2236 address = state.cbs_linkinfo[i].dirent_addr;
2237 if (address == (user_addr_t)0)
2238 continue;
2239 if (uio_isuserspace(uio)) {
2240 (void) copyout(&fileid, address,
2241 extended ? sizeof(ino64_t) : sizeof(ino_t));
2242 } else /* system space */ {
2243 ino64_t *inoptr = (ino64_t *)CAST_DOWN(caddr_t, address);
2244 *inoptr = fileid;
2245 }
2246 }
2247 }
2248
2249 if (state.cbs_result)
2250 result = state.cbs_result;
2251 else
2252 result = MacToVFSError(result);
2253
2254 if (result == ENOENT) {
2255 result = 0;
2256 }
2257
2258 cleanup:
2259 FREE(buffer, M_TEMP);
2260
2261 return (result);
2262 }
2263
2264
2265 /*
2266 * Callback to establish directory position.
2267 * Called with position_state for each item in a directory.
2268 */
2269 static int
2270 cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
2271 struct position_state *state)
2272 {
2273 cnid_t curID;
2274
2275 if (state->hfsmp->hfs_flags & HFS_STANDARD)
2276 curID = ckp->hfs.parentID;
2277 else
2278 curID = ckp->hfsPlus.parentID;
2279
2280 /* Make sure parent directory didn't change */
2281 if (state->parentID != curID) {
2282 state->error = EINVAL;
2283 return (0); /* stop */
2284 }
2285
2286 /* Count this entry */
2287 switch(crp->recordType) {
2288 case kHFSPlusFolderRecord:
2289 case kHFSPlusFileRecord:
2290 case kHFSFolderRecord:
2291 case kHFSFileRecord:
2292 ++state->count;
2293 break;
2294 default:
2295 printf("cat_findposition: invalid record type %d in dir %d\n",
2296 crp->recordType, curID);
2297 state->error = EINVAL;
2298 return (0); /* stop */
2299 };
2300
2301 return (state->count < state->index);
2302 }
2303
2304
2305 /*
2306 * cat_binarykeycompare - compare two HFS Plus catalog keys.
2307
2308 * The name portion of the key is compared using a 16-bit binary comparison.
2309 * This is called from the b-tree code.
2310 */
2311 __private_extern__
2312 int
2313 cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
2314 {
2315 u_int32_t searchParentID, trialParentID;
2316 int result;
2317
2318 searchParentID = searchKey->parentID;
2319 trialParentID = trialKey->parentID;
2320 result = 0;
2321
2322 if (searchParentID > trialParentID) {
2323 ++result;
2324 } else if (searchParentID < trialParentID) {
2325 --result;
2326 } else {
2327 u_int16_t * str1 = &searchKey->nodeName.unicode[0];
2328 u_int16_t * str2 = &trialKey->nodeName.unicode[0];
2329 int length1 = searchKey->nodeName.length;
2330 int length2 = trialKey->nodeName.length;
2331 u_int16_t c1, c2;
2332 int length;
2333
2334 if (length1 < length2) {
2335 length = length1;
2336 --result;
2337 } else if (length1 > length2) {
2338 length = length2;
2339 ++result;
2340 } else {
2341 length = length1;
2342 }
2343
2344 while (length--) {
2345 c1 = *(str1++);
2346 c2 = *(str2++);
2347
2348 if (c1 > c2) {
2349 result = 1;
2350 break;
2351 }
2352 if (c1 < c2) {
2353 result = -1;
2354 break;
2355 }
2356 }
2357 }
2358
2359 return result;
2360 }
2361
2362
2363 /*
2364 * Compare two standard HFS catalog keys
2365 *
2366 * Result: +n search key > trial key
2367 * 0 search key = trial key
2368 * -n search key < trial key
2369 */
2370 int
2371 CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
2372 {
2373 cnid_t searchParentID, trialParentID;
2374 int result;
2375
2376 searchParentID = searchKey->parentID;
2377 trialParentID = trialKey->parentID;
2378
2379 if (searchParentID > trialParentID)
2380 result = 1;
2381 else if (searchParentID < trialParentID)
2382 result = -1;
2383 else /* parent dirID's are equal, compare names */
2384 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
2385
2386 return result;
2387 }
2388
2389
2390 /*
2391 * Compare two HFS+ catalog keys
2392 *
2393 * Result: +n search key > trial key
2394 * 0 search key = trial key
2395 * -n search key < trial key
2396 */
2397 int
2398 CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
2399 {
2400 cnid_t searchParentID, trialParentID;
2401 int result;
2402
2403 searchParentID = searchKey->parentID;
2404 trialParentID = trialKey->parentID;
2405
2406 if (searchParentID > trialParentID) {
2407 result = 1;
2408 }
2409 else if (searchParentID < trialParentID) {
2410 result = -1;
2411 } else {
2412 /* parent node ID's are equal, compare names */
2413 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
2414 result = searchKey->nodeName.length - trialKey->nodeName.length;
2415 else
2416 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
2417 searchKey->nodeName.length,
2418 &trialKey->nodeName.unicode[0],
2419 trialKey->nodeName.length);
2420 }
2421
2422 return result;
2423 }
2424
2425
2426 /*
2427 * buildkey - build a Catalog b-tree key from a cnode descriptor
2428 */
2429 static int
2430 buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
2431 HFSPlusCatalogKey *key, int retry)
2432 {
2433 int utf8_flags = 0;
2434 int result = 0;
2435 size_t unicodeBytes = 0;
2436
2437 if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
2438 return (EINVAL); /* invalid name */
2439
2440 key->parentID = descp->cd_parentcnid;
2441 key->nodeName.length = 0;
2442 /*
2443 * Convert filename from UTF-8 into Unicode
2444 */
2445
2446 if ((descp->cd_flags & CD_DECOMPOSED) == 0)
2447 utf8_flags |= UTF_DECOMPOSED;
2448 result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
2449 key->nodeName.unicode, &unicodeBytes,
2450 sizeof(key->nodeName.unicode), ':', utf8_flags);
2451 key->nodeName.length = unicodeBytes / sizeof(UniChar);
2452 key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
2453 if (result) {
2454 if (result != ENAMETOOLONG)
2455 result = EINVAL; /* name has invalid characters */
2456 return (result);
2457 }
2458
2459 /*
2460 * For HFS volumes convert to an HFS compatible key
2461 *
2462 * XXX need to save the encoding that succeeded
2463 */
2464 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
2465 HFSCatalogKey hfskey;
2466
2467 bzero(&hfskey, sizeof(hfskey));
2468 hfskey.keyLength = kHFSCatalogKeyMinimumLength;
2469 hfskey.parentID = key->parentID;
2470 hfskey.nodeName[0] = 0;
2471 if (key->nodeName.length > 0) {
2472 if (unicode_to_hfs(HFSTOVCB(hfsmp),
2473 key->nodeName.length * 2,
2474 key->nodeName.unicode,
2475 &hfskey.nodeName[0], retry) != 0) {
2476 return (EINVAL);
2477 }
2478 hfskey.keyLength += hfskey.nodeName[0];
2479 }
2480 bcopy(&hfskey, key, sizeof(hfskey));
2481 }
2482 return (0);
2483 }
2484
2485
2486 /*
2487 * Resolve hard link reference to obtain the inode record.
2488 */
2489 __private_extern__
2490 int
2491 resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp)
2492 {
2493 FSBufferDescriptor btdata;
2494 struct BTreeIterator *iterator;
2495 struct cat_desc idesc;
2496 char inodename[32];
2497 int result = 0;
2498
2499 BDINIT(btdata, recp);
2500 MAKE_INODE_NAME(inodename, linkref);
2501
2502 /* Get space for iterator */
2503 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2504 bzero(iterator, sizeof(*iterator));
2505
2506 /* Build a descriptor for private dir. */
2507 idesc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
2508 idesc.cd_nameptr = inodename;
2509 idesc.cd_namelen = strlen(inodename);
2510 idesc.cd_flags = 0;
2511 idesc.cd_hint = 0;
2512 idesc.cd_encoding = 0;
2513 (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
2514
2515 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
2516 &btdata, NULL, NULL);
2517
2518 if (result == 0) {
2519 /* Make sure there's a reference */
2520 if (recp->bsdInfo.special.linkCount == 0)
2521 recp->bsdInfo.special.linkCount = 2;
2522 } else {
2523 printf("HFS resolvelink: can't find %s\n", inodename);
2524 }
2525
2526 FREE(iterator, M_TEMP);
2527
2528 return (result ? ENOENT : 0);
2529 }
2530
2531 /*
2532 * Resolve hard link reference to obtain the inode number.
2533 */
2534 static int
2535 resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
2536 {
2537 struct HFSPlusCatalogFile record;
2538 int error;
2539
2540 error = resolvelink(hfsmp, linkref, &record);
2541 if (error == 0) {
2542 if (record.fileID == 0)
2543 error = ENOENT;
2544 else
2545 *ino = record.fileID;
2546 }
2547 return (error);
2548 }
2549
2550 /*
2551 * getkey - get a key from id by doing a thread lookup
2552 */
2553 static int
2554 getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
2555 {
2556 struct BTreeIterator * iterator;
2557 FSBufferDescriptor btdata;
2558 UInt16 datasize;
2559 CatalogKey * keyp;
2560 CatalogRecord * recp;
2561 int result;
2562 int std_hfs;
2563
2564 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
2565
2566 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2567 bzero(iterator, sizeof(*iterator));
2568 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
2569
2570 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
2571 BDINIT(btdata, recp);
2572
2573 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
2574 &btdata, &datasize, iterator);
2575 if (result)
2576 goto exit;
2577
2578 /* Turn thread record into a cnode key (in place) */
2579 switch (recp->recordType) {
2580 case kHFSFileThreadRecord:
2581 case kHFSFolderThreadRecord:
2582 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
2583 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
2584 bcopy(keyp, key, keyp->hfs.keyLength + 1);
2585 break;
2586
2587 case kHFSPlusFileThreadRecord:
2588 case kHFSPlusFolderThreadRecord:
2589 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
2590 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
2591 (keyp->hfsPlus.nodeName.length * 2);
2592 bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
2593 break;
2594
2595 default:
2596 result = ENOENT;
2597 break;
2598 }
2599
2600 exit:
2601 FREE(iterator, M_TEMP);
2602 FREE(recp, M_TEMP);
2603
2604 return MacToVFSError(result);
2605 }
2606
2607 /*
2608 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
2609 * null arguments to cat_idlookup instead, but we save around 10% by not building the
2610 * cat_desc here). Both key and attrp must point to real structures.
2611 */
2612 __private_extern__
2613 int
2614 cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
2615 {
2616 int result;
2617
2618 result = getkey(hfsmp, cnid, key);
2619
2620 if (result == 0) {
2621 result = cat_lookupbykey(hfsmp, key, 0, 0, NULL, attrp, NULL, NULL);
2622 }
2623
2624 return MacToVFSError(result);
2625 }
2626
2627
2628 /*
2629 * buildrecord - build a default catalog directory or file record
2630 */
2631 static void
2632 buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
2633 CatalogRecord *crp, int *recordSize)
2634 {
2635 int type = attrp->ca_mode & S_IFMT;
2636 u_int32_t createtime = to_hfs_time(attrp->ca_itime);
2637
2638 if (std_hfs) {
2639 createtime = UTCToLocal(createtime);
2640 if (type == S_IFDIR) {
2641 bzero(crp, sizeof(HFSCatalogFolder));
2642 crp->recordType = kHFSFolderRecord;
2643 crp->hfsFolder.folderID = cnid;
2644 crp->hfsFolder.createDate = createtime;
2645 crp->hfsFolder.modifyDate = createtime;
2646 bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
2647 *recordSize = sizeof(HFSCatalogFolder);
2648 } else {
2649 bzero(crp, sizeof(HFSCatalogFile));
2650 crp->recordType = kHFSFileRecord;
2651 crp->hfsFile.fileID = cnid;
2652 crp->hfsFile.createDate = createtime;
2653 crp->hfsFile.modifyDate = createtime;
2654 bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
2655 bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
2656 *recordSize = sizeof(HFSCatalogFile);
2657 }
2658 } else {
2659 struct HFSPlusBSDInfo * bsdp = NULL;
2660 struct FndrFileInfo * fip = NULL;
2661
2662 if (type == S_IFDIR) {
2663 crp->recordType = kHFSPlusFolderRecord;
2664 crp->hfsPlusFolder.flags = 0;
2665 crp->hfsPlusFolder.valence = 0;
2666 crp->hfsPlusFolder.folderID = cnid;
2667 crp->hfsPlusFolder.createDate = createtime;
2668 crp->hfsPlusFolder.contentModDate = createtime;
2669 crp->hfsPlusFolder.attributeModDate = createtime;
2670 crp->hfsPlusFolder.accessDate = createtime;
2671 crp->hfsPlusFolder.backupDate = 0;
2672 crp->hfsPlusFolder.textEncoding = encoding;
2673 crp->hfsPlusFolder.attrBlocks = 0;
2674 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
2675 bsdp = &crp->hfsPlusFolder.bsdInfo;
2676 bsdp->special.rawDevice = 0;
2677 *recordSize = sizeof(HFSPlusCatalogFolder);
2678 } else {
2679 crp->recordType = kHFSPlusFileRecord;
2680 crp->hfsPlusFile.flags = kHFSThreadExistsMask;
2681 crp->hfsPlusFile.reserved1 = 0;
2682 crp->hfsPlusFile.fileID = cnid;
2683 crp->hfsPlusFile.createDate = createtime;
2684 crp->hfsPlusFile.contentModDate = createtime;
2685 crp->hfsPlusFile.accessDate = createtime;
2686 crp->hfsPlusFile.attributeModDate = createtime;
2687 crp->hfsPlusFile.backupDate = 0;
2688 crp->hfsPlusFile.textEncoding = encoding;
2689 crp->hfsPlusFile.attrBlocks = 0;
2690 bsdp = &crp->hfsPlusFile.bsdInfo;
2691 bsdp->special.rawDevice = 0;
2692 switch(type) {
2693 case S_IFBLK:
2694 case S_IFCHR:
2695 /* BLK/CHR need to save the device info */
2696 bsdp->special.rawDevice = attrp->ca_rdev;
2697 bzero(&crp->hfsPlusFile.userInfo, 32);
2698 break;
2699 case S_IFREG:
2700 /* Hardlink links need to save the linkref */
2701 fip = (FndrFileInfo *)&attrp->ca_finderinfo;
2702 if ((SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
2703 (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
2704 bsdp->special.iNodeNum = attrp->ca_rdev;
2705 }
2706 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
2707 break;
2708 case S_IFLNK:
2709 /* Symlinks also have a type and creator */
2710 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
2711 break;
2712 }
2713 bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
2714 *recordSize = sizeof(HFSPlusCatalogFile);
2715 }
2716 bsdp->ownerID = attrp->ca_uid;
2717 bsdp->groupID = attrp->ca_gid;
2718 bsdp->fileMode = attrp->ca_mode;
2719 bsdp->adminFlags = attrp->ca_flags >> 16;
2720 bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
2721 }
2722 }
2723
2724
2725 /*
2726 * builddesc - build a cnode descriptor from an HFS+ key
2727 */
2728 static int
2729 builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
2730 int isdir, struct cat_desc *descp)
2731 {
2732 int result = 0;
2733 char * nameptr;
2734 size_t bufsize;
2735 size_t utf8len;
2736 char tmpbuff[128];
2737
2738 /* guess a size... */
2739 bufsize = (3 * key->nodeName.length) + 1;
2740 if (bufsize >= sizeof(tmpbuff) - 1) {
2741 MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2742 } else {
2743 nameptr = &tmpbuff[0];
2744 }
2745
2746 result = utf8_encodestr(key->nodeName.unicode,
2747 key->nodeName.length * sizeof(UniChar),
2748 nameptr, (size_t *)&utf8len,
2749 bufsize, ':', 0);
2750
2751 if (result == ENAMETOOLONG) {
2752 bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
2753 key->nodeName.length * sizeof(UniChar),
2754 ':', 0);
2755 FREE(nameptr, M_TEMP);
2756 MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2757
2758 result = utf8_encodestr(key->nodeName.unicode,
2759 key->nodeName.length * sizeof(UniChar),
2760 nameptr, (size_t *)&utf8len,
2761 bufsize, ':', 0);
2762 }
2763 descp->cd_parentcnid = key->parentID;
2764 descp->cd_nameptr = vfs_addname(nameptr, utf8len, 0, 0);
2765 descp->cd_namelen = utf8len;
2766 descp->cd_cnid = cnid;
2767 descp->cd_hint = hint;
2768 descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
2769 if (isdir)
2770 descp->cd_flags |= CD_ISDIR;
2771 descp->cd_encoding = encoding;
2772 if (nameptr != &tmpbuff[0]) {
2773 FREE(nameptr, M_TEMP);
2774 }
2775 return result;
2776 }
2777
2778
2779 /*
2780 * getbsdattr - get attributes in bsd format
2781 *
2782 */
2783 static void
2784 getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
2785 {
2786 int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
2787 const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
2788
2789 attrp->ca_recflags = crp->flags;
2790 attrp->ca_nlink = 1;
2791 attrp->ca_atime = to_bsd_time(crp->accessDate);
2792 attrp->ca_atimeondisk = attrp->ca_atime;
2793 attrp->ca_mtime = to_bsd_time(crp->contentModDate);
2794 attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
2795 attrp->ca_itime = to_bsd_time(crp->createDate);
2796 attrp->ca_btime = to_bsd_time(crp->backupDate);
2797
2798 if ((bsd->fileMode & S_IFMT) == 0) {
2799 attrp->ca_flags = 0;
2800 attrp->ca_uid = hfsmp->hfs_uid;
2801 attrp->ca_gid = hfsmp->hfs_gid;
2802 if (isDirectory)
2803 attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
2804 else
2805 attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
2806 attrp->ca_rdev = 0;
2807 } else {
2808 attrp->ca_rdev = 0;
2809 attrp->ca_uid = bsd->ownerID;
2810 attrp->ca_gid = bsd->groupID;
2811 attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
2812 attrp->ca_mode = (mode_t)bsd->fileMode;
2813 switch (attrp->ca_mode & S_IFMT) {
2814 case S_IFCHR: /* fall through */
2815 case S_IFBLK:
2816 attrp->ca_rdev = bsd->special.rawDevice;
2817 break;
2818 case S_IFREG:
2819 /* Pick up the hard link count */
2820 if (bsd->special.linkCount > 0)
2821 attrp->ca_nlink = bsd->special.linkCount;
2822 break;
2823 }
2824
2825 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
2826 /*
2827 * Override the permissions as determined by the mount auguments
2828 * in ALMOST the same way unset permissions are treated but keep
2829 * track of whether or not the file or folder is hfs locked
2830 * by leaving the h_pflags field unchanged from what was unpacked
2831 * out of the catalog.
2832 */
2833 attrp->ca_uid = hfsmp->hfs_uid;
2834 attrp->ca_gid = hfsmp->hfs_gid;
2835 }
2836 }
2837
2838 if (isDirectory) {
2839 if (!S_ISDIR(attrp->ca_mode)) {
2840 attrp->ca_mode &= ~S_IFMT;
2841 attrp->ca_mode |= S_IFDIR;
2842 }
2843 attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
2844 attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
2845 attrp->ca_attrblks = ((HFSPlusCatalogFolder *)crp)->attrBlocks;
2846 } else {
2847 /* Keep IMMUTABLE bits in sync with HFS locked flag */
2848 if (crp->flags & kHFSFileLockedMask) {
2849 /* The file's supposed to be locked:
2850 Make sure at least one of the IMMUTABLE bits is set: */
2851 if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
2852 attrp->ca_flags |= UF_IMMUTABLE;
2853 } else {
2854 /* The file's supposed to be unlocked: */
2855 attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
2856 }
2857 /* get total blocks (both forks) */
2858 attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
2859 attrp->ca_attrblks = crp->attrBlocks;
2860 /* On HFS+ the ThreadExists flag must always be set. */
2861 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
2862 attrp->ca_recflags |= kHFSThreadExistsMask;
2863 }
2864
2865 attrp->ca_fileid = crp->fileID;
2866
2867 bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
2868 }
2869
2870 /*
2871 * promotekey - promote hfs key to hfs plus key
2872 *
2873 */
2874 static void
2875 promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
2876 HFSPlusCatalogKey *keyp, u_long *encoding)
2877 {
2878 hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
2879 UInt32 uniCount;
2880 int error;
2881
2882 *encoding = hfsmp->hfs_encoding;
2883
2884 error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
2885 kHFSPlusMaxFileNameChars, &uniCount);
2886 /*
2887 * When an HFS name cannot be encoded with the current
2888 * encoding use MacRoman as a fallback.
2889 */
2890 if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
2891 *encoding = 0;
2892 (void) mac_roman_to_unicode(hfskey->nodeName,
2893 keyp->nodeName.unicode,
2894 kHFSPlusMaxFileNameChars,
2895 &uniCount);
2896 }
2897
2898 keyp->nodeName.length = uniCount;
2899 keyp->parentID = hfskey->parentID;
2900 }
2901
2902 /*
2903 * promotefork - promote hfs fork info to hfs plus
2904 *
2905 */
2906 static void
2907 promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
2908 int resource, struct cat_fork * forkp)
2909 {
2910 struct HFSPlusExtentDescriptor *xp;
2911 u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
2912
2913 bzero(forkp, sizeof(*forkp));
2914 xp = &forkp->cf_extents[0];
2915 if (resource) {
2916 forkp->cf_size = filep->rsrcLogicalSize;
2917 forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
2918 forkp->cf_bytesread = 0;
2919 forkp->cf_vblocks = 0;
2920 xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
2921 xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
2922 xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
2923 xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
2924 xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
2925 xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
2926 } else {
2927 forkp->cf_size = filep->dataLogicalSize;
2928 forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
2929 forkp->cf_bytesread = 0;
2930 forkp->cf_vblocks = 0;
2931 xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
2932 xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
2933 xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
2934 xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
2935 xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
2936 xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
2937 }
2938 }
2939
2940 /*
2941 * promoteattr - promote hfs catalog attributes to hfs plus
2942 *
2943 */
2944 static void
2945 promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
2946 {
2947 u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
2948
2949 if (dataPtr->recordType == kHFSFolderRecord) {
2950 struct HFSCatalogFolder * folder;
2951
2952 folder = (struct HFSCatalogFolder *) dataPtr;
2953 crp->recordType = kHFSPlusFolderRecord;
2954 crp->flags = folder->flags;
2955 crp->fileID = folder->folderID;
2956 crp->createDate = LocalToUTC(folder->createDate);
2957 crp->contentModDate = LocalToUTC(folder->modifyDate);
2958 crp->backupDate = LocalToUTC(folder->backupDate);
2959 crp->reserved1 = folder->valence;
2960 bcopy(&folder->userInfo, &crp->userInfo, 32);
2961 } else /* file */ {
2962 struct HFSCatalogFile * file;
2963
2964 file = (struct HFSCatalogFile *) dataPtr;
2965 crp->recordType = kHFSPlusFileRecord;
2966 crp->flags = file->flags;
2967 crp->fileID = file->fileID;
2968 crp->createDate = LocalToUTC(file->createDate);
2969 crp->contentModDate = LocalToUTC(file->modifyDate);
2970 crp->backupDate = LocalToUTC(file->backupDate);
2971 crp->reserved1 = 0;
2972 bcopy(&file->userInfo, &crp->userInfo, 16);
2973 bcopy(&file->finderInfo, &crp->finderInfo, 16);
2974 crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
2975 crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
2976 }
2977 crp->textEncoding = 0;
2978 crp->attributeModDate = crp->contentModDate;
2979 crp->accessDate = crp->contentModDate;
2980 bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
2981 crp->attrBlocks = 0;
2982 }
2983
2984 /*
2985 * Build a catalog node thread record from a catalog key
2986 * and return the size of the record.
2987 */
2988 static int
2989 buildthread(void *keyp, void *recp, int std_hfs, int directory)
2990 {
2991 int size = 0;
2992
2993 if (std_hfs) {
2994 HFSCatalogKey *key = (HFSCatalogKey *)keyp;
2995 HFSCatalogThread *rec = (HFSCatalogThread *)recp;
2996
2997 size = sizeof(HFSCatalogThread);
2998 bzero(rec, size);
2999 if (directory)
3000 rec->recordType = kHFSFolderThreadRecord;
3001 else
3002 rec->recordType = kHFSFileThreadRecord;
3003 rec->parentID = key->parentID;
3004 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
3005
3006 } else /* HFS+ */ {
3007 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
3008 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
3009
3010 size = sizeof(HFSPlusCatalogThread);
3011 if (directory)
3012 rec->recordType = kHFSPlusFolderThreadRecord;
3013 else
3014 rec->recordType = kHFSPlusFileThreadRecord;
3015 rec->reserved = 0;
3016 rec->parentID = key->parentID;
3017 bcopy(&key->nodeName, &rec->nodeName,
3018 sizeof(UniChar) * (key->nodeName.length + 1));
3019
3020 /* HFS Plus has varaible sized thread records */
3021 size -= (sizeof(rec->nodeName.unicode) -
3022 (rec->nodeName.length * sizeof(UniChar)));
3023 }
3024
3025 return (size);
3026 }
3027
3028 /*
3029 * Build a catalog node thread key.
3030 */
3031 static void
3032 buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
3033 {
3034 if (std_hfs) {
3035 key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
3036 key->hfs.reserved = 0;
3037 key->hfs.parentID = parentID;
3038 key->hfs.nodeName[0] = 0;
3039 } else {
3040 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
3041 key->hfsPlus.parentID = parentID;
3042 key->hfsPlus.nodeName.length = 0;
3043 }
3044 }
3045
3046 /*
3047 * Extract the text encoding from a catalog node record.
3048 */
3049 static u_long
3050 getencoding(const CatalogRecord *crp)
3051 {
3052 u_long encoding;
3053
3054 if (crp->recordType == kHFSPlusFolderRecord)
3055 encoding = crp->hfsPlusFolder.textEncoding;
3056 else if (crp->recordType == kHFSPlusFileRecord)
3057 encoding = crp->hfsPlusFile.textEncoding;
3058 else
3059 encoding = 0;
3060
3061 return (encoding);
3062 }
3063
3064 /*
3065 * Extract the CNID from a catalog node record.
3066 */
3067 static cnid_t
3068 getcnid(const CatalogRecord *crp)
3069 {
3070 cnid_t cnid = 0;
3071
3072 switch (crp->recordType) {
3073 case kHFSFolderRecord:
3074 cnid = crp->hfsFolder.folderID;
3075 break;
3076 case kHFSFileRecord:
3077 cnid = crp->hfsFile.fileID;
3078 break;
3079 case kHFSPlusFolderRecord:
3080 cnid = crp->hfsPlusFolder.folderID;
3081 break;
3082 case kHFSPlusFileRecord:
3083 cnid = crp->hfsPlusFile.fileID;
3084 break;
3085 default:
3086 printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp);
3087 break;
3088 }
3089
3090 return (cnid);
3091 }
3092
3093 /*
3094 * Extract the parent ID from a catalog node record.
3095 */
3096 static cnid_t
3097 getparentcnid(const CatalogRecord *recp)
3098 {
3099 cnid_t cnid = 0;
3100
3101 switch (recp->recordType) {
3102 case kHFSFileThreadRecord:
3103 case kHFSFolderThreadRecord:
3104 cnid = recp->hfsThread.parentID;
3105 break;
3106
3107 case kHFSPlusFileThreadRecord:
3108 case kHFSPlusFolderThreadRecord:
3109 cnid = recp->hfsPlusThread.parentID;
3110 break;
3111 default:
3112 panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp);
3113 break;
3114 }
3115
3116 return (cnid);
3117 }
3118
3119 /*
3120 * Determine if a catalog node record is a directory.
3121 */
3122 static int
3123 isadir(const CatalogRecord *crp)
3124 {
3125 return (crp->recordType == kHFSFolderRecord ||
3126 crp->recordType == kHFSPlusFolderRecord);
3127 }
3128