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