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