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