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