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