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