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