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