]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_catalog.c
5e7e13d99e4baad1fa2b396db06487084e8021fd
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
1 /*
2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
32 #include <sys/stat.h>
33 #include <sys/mount.h>
34 #include <sys/vnode.h>
35 #include <sys/dirent.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
38
39 #include <sys/utfconv.h>
40
41 #include "hfs.h"
42 #include "hfs_catalog.h"
43 #include "hfs_format.h"
44 #include "hfs_endian.h"
45
46 #include "hfscommon/headers/BTreesInternal.h"
47 #include "hfscommon/headers/BTreesPrivate.h"
48 #include "hfscommon/headers/HFSUnicodeWrappers.h"
49
50
51 /*
52 * Initialization of an FSBufferDescriptor structure.
53 */
54 #define BDINIT(bd, addr) { \
55 (bd).bufferAddress = (addr); \
56 (bd).itemSize = sizeof(*(addr)); \
57 (bd).itemCount = 1; \
58 }
59
60
61 struct btobj {
62 BTreeIterator iterator;
63 HFSPlusCatalogKey key;
64 CatalogRecord data;
65 };
66
67 struct update_state {
68 struct cat_desc * s_desc;
69 struct cat_attr * s_attr;
70 struct cat_fork * s_datafork;
71 struct cat_fork * s_rsrcfork;
72 struct hfsmount * s_hfsmp;
73 };
74
75 struct position_state {
76 int error;
77 u_int32_t count;
78 u_int32_t index;
79 u_int32_t parentID;
80 struct hfsmount *hfsmp;
81 };
82
83 /* Map file mode type to directory entry types */
84 u_char modetodirtype[16] = {
85 DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
86 DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
87 DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
88 DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
89 };
90 #define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
91
92
93 static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_int32_t hint, int wantrsrc,
94 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
95
96 static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
97 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
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_int32_t 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, u_int32_t *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_int32_t hint, u_int32_t 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_int32_t *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_int32_t 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 static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
135
136
137 __private_extern__
138 int
139 cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
140 {
141 int lockflags = 0;
142 int result;
143
144 if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
145 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
146
147 result = BTReserveSpace(hfsmp->hfs_catalog_cp->c_datafork, ops, (void*)cookie);
148
149 if (lockflags)
150 hfs_systemfile_unlock(hfsmp, lockflags);
151
152 return MacToVFSError(result);
153 }
154
155 __private_extern__
156 void
157 cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
158 {
159 int lockflags = 0;
160
161 if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
162 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
163
164 (void) BTReleaseReserve(hfsmp->hfs_catalog_cp->c_datafork, (void*)cookie);
165
166 if (lockflags)
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_new_size = 0;
200 datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
201 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
202 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
203 datafp->cf_bytesread =
204 recp->hfsPlusFile.dataFork.clumpSize *
205 HFSTOVCB(hfsmp)->blockSize;
206 } else {
207 datafp->cf_bytesread = 0;
208 }
209 datafp->cf_vblocks = 0;
210 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
211 &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
212
213 /* Convert the resource fork. */
214 rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
215 rsrcfp->cf_new_size = 0;
216 rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
217 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
218 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
219 datafp->cf_bytesread =
220 recp->hfsPlusFile.resourceFork.clumpSize *
221 HFSTOVCB(hfsmp)->blockSize;
222 } else {
223 datafp->cf_bytesread = 0;
224 }
225 rsrcfp->cf_vblocks = 0;
226 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
227 &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
228 }
229 }
230
231 /*
232 * Convert a raw catalog key and record into an in-core catalog descriptor.
233 *
234 * Note: The caller is responsible for releasing the catalog descriptor.
235 */
236 __private_extern__
237 int
238 cat_convertkey(
239 struct hfsmount *hfsmp,
240 CatalogKey *key,
241 CatalogRecord * recp,
242 struct cat_desc *descp)
243 {
244 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
245 HFSPlusCatalogKey * pluskey = NULL;
246 u_int32_t encoding;
247
248 if (std_hfs) {
249 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
250 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
251
252 } else {
253 pluskey = (HFSPlusCatalogKey *)key;
254 encoding = getencoding(recp);
255 }
256
257 builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp);
258 if (std_hfs) {
259 FREE(pluskey, M_TEMP);
260 }
261 return (0);
262 }
263
264
265 /*
266 * cat_releasedesc
267 */
268 __private_extern__
269 void
270 cat_releasedesc(struct cat_desc *descp)
271 {
272 const u_int8_t * name;
273
274 if (descp == NULL)
275 return;
276
277 if ((descp->cd_flags & CD_HASBUF) &&
278 (descp->cd_nameptr != NULL)) {
279 name = descp->cd_nameptr;
280 descp->cd_nameptr = NULL;
281 descp->cd_namelen = 0;
282 vfs_removename((const char *)name);
283 }
284 descp->cd_nameptr = NULL;
285 descp->cd_namelen = 0;
286 descp->cd_flags &= ~CD_HASBUF;
287 }
288
289 /*
290 * These Catalog functions allow access to the HFS Catalog (database).
291 * The catalog b-tree lock must be acquired before calling any of these routines.
292 */
293
294 /*
295 * cat_lookup - lookup a catalog node using a cnode descriptor
296 *
297 * Note: The caller is responsible for releasing the output
298 * catalog descriptor (when supplied outdescp is non-null).
299 */
300 __private_extern__
301 int
302 cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
303 struct cat_desc *outdescp, struct cat_attr *attrp,
304 struct cat_fork *forkp, cnid_t *desc_cnid)
305 {
306 CatalogKey * keyp;
307 int std_hfs;
308 int result;
309
310 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
311
312 MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
313
314 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1);
315 if (result)
316 goto exit;
317
318 result = cat_lookupbykey(hfsmp, keyp, 0, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
319
320 if (result == ENOENT) {
321 if (!std_hfs) {
322 struct cat_desc temp_desc;
323 if (outdescp == NULL) {
324 bzero(&temp_desc, sizeof(temp_desc));
325 outdescp = &temp_desc;
326 }
327 result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
328 if (desc_cnid) {
329 *desc_cnid = outdescp->cd_cnid;
330 }
331 if (outdescp == &temp_desc) {
332 /* Release the local copy of desc */
333 cat_releasedesc(outdescp);
334 }
335 } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
336 // make MacRoman key from utf-8
337 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
338 // update desc text encoding so that other catalog ops succeed
339 }
340 }
341 exit:
342 FREE(keyp, M_TEMP);
343
344 return (result);
345 }
346
347 __private_extern__
348 int
349 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
350 {
351 struct BTreeIterator *iterator;
352 struct FSBufferDescriptor file_data;
353 struct HFSCatalogFile file_rec;
354 u_int16_t datasize;
355 FCB *fcb;
356 int result;
357
358 if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord)
359 return (EINVAL);
360
361 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
362
363 MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
364 bzero(&iterator[0], 2* sizeof(*iterator));
365 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0);
366 if (result)
367 goto exit;
368
369 BDINIT(file_data, &file_rec);
370 result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
371 if (result)
372 goto exit;
373
374 if (file_rec.recordType != kHFSFileRecord) {
375 result = EISDIR;
376 goto exit;
377 }
378
379 if ((file_rec.flags & kHFSThreadExistsMask) == 0) {
380 struct FSBufferDescriptor thread_data;
381 struct HFSCatalogThread thread_rec;
382
383 file_rec.flags |= kHFSThreadExistsMask;
384 BDINIT(thread_data, &thread_rec);
385 thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0);
386 buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key);
387
388 result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize);
389 if (result)
390 goto exit;
391
392 (void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize);
393 (void) BTFlushPath(fcb);
394 }
395 exit:
396 (void) BTFlushPath(fcb);
397 FREE(iterator, M_TEMP);
398
399 return MacToVFSError(result);
400 }
401
402
403 /*
404 * cat_findname - obtain a descriptor from cnid
405 *
406 * Only a thread lookup is performed.
407 *
408 * Note: The caller is responsible for releasing the output
409 * catalog descriptor (when supplied outdescp is non-null).
410
411 */
412 __private_extern__
413 int
414 cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
415 {
416 struct BTreeIterator * iterator;
417 FSBufferDescriptor btdata;
418 CatalogKey * keyp;
419 CatalogRecord * recp;
420 int isdir;
421 int result;
422 int std_hfs;
423
424 isdir = 0;
425 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
426
427 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
428 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
429 iterator->hint.nodeNum = 0;
430
431 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
432 BDINIT(btdata, recp);
433
434 result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
435 if (result)
436 goto exit;
437
438 /* Turn thread record into a cnode key (in place). */
439 switch (recp->recordType) {
440 case kHFSFolderThreadRecord:
441 isdir = 1;
442 /* fall through */
443 case kHFSFileThreadRecord:
444 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
445 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
446 break;
447
448 case kHFSPlusFolderThreadRecord:
449 isdir = 1;
450 /* fall through */
451 case kHFSPlusFileThreadRecord:
452 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
453 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
454 (keyp->hfsPlus.nodeName.length * 2);
455 break;
456 default:
457 result = ENOENT;
458 goto exit;
459 }
460 if (std_hfs) {
461 HFSPlusCatalogKey * pluskey = NULL;
462 u_int32_t encoding;
463
464 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
465 promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
466 builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
467 FREE(pluskey, M_TEMP);
468
469 } else {
470 builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
471 }
472 exit:
473 FREE(recp, M_TEMP);
474 FREE(iterator, M_TEMP);
475
476 return MacToVFSError(result);
477 }
478
479 /*
480 * cat_idlookup - lookup a catalog node using a cnode id
481 *
482 * Note: The caller is responsible for releasing the output
483 * catalog descriptor (when supplied outdescp is non-null).
484 */
485 __private_extern__
486 int
487 cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
488 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
489 {
490 struct BTreeIterator * iterator;
491 FSBufferDescriptor btdata;
492 u_int16_t datasize;
493 CatalogKey * keyp;
494 CatalogRecord * recp;
495 int result;
496 int std_hfs;
497
498 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
499
500 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
501 bzero(iterator, sizeof(*iterator));
502 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
503
504 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
505 BDINIT(btdata, recp);
506
507 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
508 &btdata, &datasize, iterator);
509 if (result)
510 goto exit;
511
512 /* Turn thread record into a cnode key (in place) */
513 switch (recp->recordType) {
514 case kHFSFileThreadRecord:
515 case kHFSFolderThreadRecord:
516 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
517
518 /* check for NULL name */
519 if (keyp->hfs.nodeName[0] == 0) {
520 result = ENOENT;
521 goto exit;
522 }
523
524 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
525 break;
526
527 case kHFSPlusFileThreadRecord:
528 case kHFSPlusFolderThreadRecord:
529 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
530
531 /* check for NULL name */
532 if (keyp->hfsPlus.nodeName.length == 0) {
533 result = ENOENT;
534 goto exit;
535 }
536
537 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
538 (keyp->hfsPlus.nodeName.length * 2);
539 break;
540
541 default:
542 result = ENOENT;
543 goto exit;
544 }
545
546 result = cat_lookupbykey(hfsmp, keyp, allow_system_files, 0, 0, outdescp, attrp, forkp, NULL);
547 /* No corresponding file/folder record found for a thread record,
548 * mark the volume inconsistent.
549 */
550 if (result == 0 && outdescp) {
551 cnid_t dcnid = outdescp->cd_cnid;
552 /*
553 * Just for sanity's case, let's make sure that
554 * the key in the thread matches the key in the record.
555 */
556 if (cnid != dcnid) {
557 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
558 result = ENOENT;
559 }
560 }
561 exit:
562 FREE(recp, M_TEMP);
563 FREE(iterator, M_TEMP);
564
565 return MacToVFSError(result);
566 }
567
568
569 /*
570 * cat_lookupmangled - lookup a catalog node using a mangled name
571 */
572 static int
573 cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
574 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
575 {
576 cnid_t fileID;
577 u_int32_t prefixlen;
578 int result;
579 int extlen1, extlen2;
580
581 if (wantrsrc)
582 return (ENOENT);
583
584 fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
585 if (fileID < (cnid_t)kHFSFirstUserCatalogNodeID)
586 return (ENOENT);
587
588 if (fileID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
589 fileID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid ||
590 fileID == hfsmp->hfs_jnlfileid ||
591 fileID == hfsmp->hfs_jnlinfoblkid) {
592 return (ENOENT);
593 }
594
595 result = cat_idlookup(hfsmp, fileID, 0, outdescp, attrp, forkp);
596 if (result)
597 return (ENOENT);
598 /* It must be in the correct directory */
599 if (descp->cd_parentcnid != outdescp->cd_parentcnid)
600 goto falsematch;
601
602 if (((u_int16_t)outdescp->cd_namelen < prefixlen) ||
603 bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
604 goto falsematch;
605
606 extlen1 = CountFilenameExtensionChars(descp->cd_nameptr, descp->cd_namelen);
607 extlen2 = CountFilenameExtensionChars(outdescp->cd_nameptr, outdescp->cd_namelen);
608 if (extlen1 != extlen2)
609 goto falsematch;
610
611 if (bcmp(outdescp->cd_nameptr + (outdescp->cd_namelen - extlen2),
612 descp->cd_nameptr + (descp->cd_namelen - extlen1),
613 extlen1) != 0)
614 goto falsematch;
615
616 return (0);
617
618 falsematch:
619 cat_releasedesc(outdescp);
620 return (ENOENT);
621 }
622
623
624 /*
625 * cat_lookupbykey - lookup a catalog node using a cnode key
626 */
627 static int
628 cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_int32_t hint, int wantrsrc,
629 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
630 {
631 struct BTreeIterator * iterator;
632 FSBufferDescriptor btdata;
633 CatalogRecord * recp;
634 u_int16_t datasize;
635 int result;
636 int std_hfs;
637 u_int32_t ilink = 0;
638 cnid_t cnid = 0;
639 u_int32_t encoding = 0;
640
641 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
642
643 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
644 BDINIT(btdata, recp);
645 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
646 bzero(iterator, sizeof(*iterator));
647 iterator->hint.nodeNum = hint;
648 bcopy(keyp, &iterator->key, sizeof(CatalogKey));
649
650 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
651 &btdata, &datasize, iterator);
652 if (result)
653 goto exit;
654
655 /* Save the cnid and encoding now in case there's a hard link */
656 cnid = getcnid(recp);
657 encoding = getencoding(recp);
658 hint = iterator->hint.nodeNum;
659
660 /* Hide the journal files (if any) */
661 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
662 ((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)) &&
663 !allow_system_files) {
664
665 result = ENOENT;
666 goto exit;
667 }
668
669 /*
670 * When a hardlink link is encountered, auto resolve it.
671 *
672 * The catalog record will change, and possibly its type.
673 */
674 if (!std_hfs
675 && (attrp || forkp)
676 && (recp->recordType == kHFSPlusFileRecord)
677 && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->vcbCrDate) ||
678 (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
679 int isdirlink = 0;
680 int isfilelink = 0;
681
682 if ((SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
683 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
684 isfilelink = 1;
685 } else if ((recp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
686 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
687 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
688 isdirlink = 1;
689 }
690 if (isfilelink || isdirlink) {
691 ilink = recp->hfsPlusFile.hl_linkReference;
692 (void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
693 }
694 }
695
696 if (attrp != NULL) {
697 if (std_hfs) {
698 struct HFSPlusCatalogFile cnoderec;
699
700 promoteattr(hfsmp, recp, &cnoderec);
701 getbsdattr(hfsmp, &cnoderec, attrp);
702 } else {
703 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
704 if (ilink)
705 attrp->ca_linkref = ilink;
706 }
707 }
708 if (forkp != NULL) {
709 if (isadir(recp)) {
710 bzero(forkp, sizeof(*forkp));
711 } else if (std_hfs) {
712 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
713 } else if (wantrsrc) {
714 /* Convert the resource fork. */
715 forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
716 forkp->cf_new_size = 0;
717 forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
718 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
719 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
720 forkp->cf_bytesread =
721 recp->hfsPlusFile.resourceFork.clumpSize *
722 HFSTOVCB(hfsmp)->blockSize;
723 } else {
724 forkp->cf_bytesread = 0;
725 }
726 forkp->cf_vblocks = 0;
727 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
728 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
729 } else {
730 int i;
731 u_int32_t validblks;
732
733 /* Convert the data fork. */
734 forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
735 forkp->cf_new_size = 0;
736 forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
737 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
738 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
739 forkp->cf_bytesread =
740 recp->hfsPlusFile.dataFork.clumpSize *
741 HFSTOVCB(hfsmp)->blockSize;
742 } else {
743 forkp->cf_bytesread = 0;
744 }
745 forkp->cf_vblocks = 0;
746 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
747 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
748
749 /* Validate the fork's resident extents. */
750 validblks = 0;
751 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
752 if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) {
753 /* Suppress any bad extents so a remove can succeed. */
754 forkp->cf_extents[i].startBlock = 0;
755 forkp->cf_extents[i].blockCount = 0;
756 /* Disable writes */
757 if (attrp != NULL) {
758 attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH;
759 }
760 } else {
761 validblks += forkp->cf_extents[i].blockCount;
762 }
763 }
764 /* Adjust for any missing blocks. */
765 if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
766 off_t psize;
767
768 forkp->cf_blocks = validblks;
769 if (attrp != NULL) {
770 attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
771 }
772 psize = (off_t)validblks * (off_t)hfsmp->blockSize;
773 if (psize < forkp->cf_size) {
774 forkp->cf_size = psize;
775 }
776
777 }
778 }
779 }
780 if (descp != NULL) {
781 HFSPlusCatalogKey * pluskey = NULL;
782
783 if (std_hfs) {
784 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
785 promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
786
787 } else {
788 pluskey = (HFSPlusCatalogKey *)&iterator->key;
789 }
790 builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
791 if (std_hfs) {
792 FREE(pluskey, M_TEMP);
793 }
794 }
795
796 if (desc_cnid != NULL) {
797 *desc_cnid = cnid;
798 }
799 exit:
800 FREE(iterator, M_TEMP);
801 FREE(recp, M_TEMP);
802
803 return MacToVFSError(result);
804 }
805
806
807 /*
808 * cat_create - create a node in the catalog
809 *
810 * NOTE: both the catalog file and attribute file locks must
811 * be held before calling this function.
812 *
813 * The caller is responsible for releasing the output
814 * catalog descriptor (when supplied outdescp is non-null).
815 */
816 __private_extern__
817 int
818 cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
819 struct cat_desc *out_descp)
820 {
821 FCB * fcb;
822 struct btobj * bto;
823 FSBufferDescriptor btdata;
824 u_int32_t nextCNID;
825 u_int32_t datalen;
826 int std_hfs;
827 int result = 0;
828 u_int32_t encoding = kTextEncodingMacRoman;
829 int modeformat;
830
831 modeformat = attrp->ca_mode & S_IFMT;
832
833 fcb = hfsmp->hfs_catalog_cp->c_datafork;
834 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
835
836 /*
837 * Get the next CNID. We can change it since we hold the catalog lock.
838 */
839 nextCNID = hfsmp->vcbNxtCNID;
840 if (nextCNID == 0xFFFFFFFF) {
841 if (std_hfs) {
842 return (ENOSPC);
843 } else {
844 HFS_MOUNT_LOCK(hfsmp, TRUE)
845 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
846 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
847 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
848 }
849 } else {
850 hfsmp->vcbNxtCNID++;
851 }
852 MarkVCBDirty(hfsmp);
853
854 /* Get space for iterator, key and data */
855 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
856 bto->iterator.hint.nodeNum = 0;
857
858 result = buildkey(hfsmp, descp, &bto->key, 0);
859 if (result)
860 goto exit;
861
862 if (!std_hfs) {
863 encoding = hfs_pickencoding(bto->key.nodeName.unicode,
864 bto->key.nodeName.length);
865 hfs_setencodingbits(hfsmp, encoding);
866 }
867
868 /*
869 * Insert the thread record first
870 */
871 if (!std_hfs || (modeformat == S_IFDIR)) {
872 datalen = buildthread((void*)&bto->key, &bto->data, std_hfs,
873 S_ISDIR(attrp->ca_mode));
874 btdata.bufferAddress = &bto->data;
875 btdata.itemSize = datalen;
876 btdata.itemCount = 1;
877
878 for (;;) {
879 // this call requires the attribute file lock to be held
880 result = file_attribute_exist(hfsmp, nextCNID);
881 if (result == EEXIST) {
882 // that cnid has orphaned attributes so just skip it.
883 if (++nextCNID < kHFSFirstUserCatalogNodeID) {
884 nextCNID = kHFSFirstUserCatalogNodeID;
885 }
886 continue;
887 }
888 if (result) goto exit;
889
890 buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
891
892 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
893 if ((result == btExists) && !std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
894 /*
895 * Allow CNIDs on HFS Plus volumes to wrap around
896 */
897 if (++nextCNID < kHFSFirstUserCatalogNodeID) {
898 nextCNID = kHFSFirstUserCatalogNodeID;
899 }
900 continue;
901 }
902 break;
903 }
904 if (result) goto exit;
905 }
906
907 /*
908 * CNID is now established. If we have wrapped then
909 * update the vcbNxtCNID.
910 */
911 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
912 hfsmp->vcbNxtCNID = nextCNID + 1;
913 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
914 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
915 }
916 }
917
918 /*
919 * Now insert the file/directory record
920 */
921 buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen);
922 btdata.bufferAddress = &bto->data;
923 btdata.itemSize = datalen;
924 btdata.itemCount = 1;
925
926 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
927
928 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
929 if (result) {
930 if (result == btExists)
931 result = EEXIST;
932
933 /* Back out the thread record */
934 if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
935 buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key);
936 if (BTDeleteRecord(fcb, &bto->iterator)) {
937 /* Error on deleting extra thread record, mark
938 * volume inconsistent
939 */
940 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
941 hfs_mark_volume_inconsistent(hfsmp);
942 }
943 }
944 goto exit;
945 }
946
947 /*
948 * Insert was successful, update name, parent and volume
949 */
950 if (out_descp != NULL) {
951 HFSPlusCatalogKey * pluskey = NULL;
952
953 if (std_hfs) {
954 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
955 promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
956
957 } else
958 pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
959
960 builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum,
961 encoding, S_ISDIR(attrp->ca_mode), out_descp);
962 if (std_hfs) {
963 FREE(pluskey, M_TEMP);
964 }
965 }
966 attrp->ca_fileid = nextCNID;
967
968 exit:
969 (void) BTFlushPath(fcb);
970 FREE(bto, M_TEMP);
971
972 return MacToVFSError(result);
973 }
974
975
976 /*
977 * cnode_rename - rename a catalog node
978 *
979 * Assumes that the target's directory exists.
980 *
981 * Order of B-tree operations:
982 * 1. BTSearchRecord(from_cnode, &data);
983 * 2. BTInsertRecord(to_cnode, &data);
984 * 3. BTDeleteRecord(from_cnode);
985 * 4. BTDeleteRecord(from_thread);
986 * 5. BTInsertRecord(to_thread);
987 *
988 * Note: The caller is responsible for releasing the output
989 * catalog descriptor (when supplied out_cdp is non-null).
990 */
991 __private_extern__
992 int
993 cat_rename (
994 struct hfsmount * hfsmp,
995 struct cat_desc * from_cdp,
996 struct cat_desc * todir_cdp,
997 struct cat_desc * to_cdp,
998 struct cat_desc * out_cdp )
999 {
1000 struct BTreeIterator * to_iterator = NULL;
1001 struct BTreeIterator * from_iterator = NULL;
1002 FSBufferDescriptor btdata;
1003 CatalogRecord * recp = NULL;
1004 HFSPlusCatalogKey * to_key;
1005 ExtendedVCB * vcb;
1006 FCB * fcb;
1007 u_int16_t datasize;
1008 int result = 0;
1009 int sourcegone = 0;
1010 int skipthread = 0;
1011 int directory = from_cdp->cd_flags & CD_ISDIR;
1012 int is_dirlink = 0;
1013 int std_hfs;
1014 u_int32_t encoding = 0;
1015
1016 vcb = HFSTOVCB(hfsmp);
1017 fcb = GetFileControlBlock(vcb->catalogRefNum);
1018 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1019
1020 if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
1021 return (EINVAL);
1022
1023 MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK);
1024 bzero(from_iterator, sizeof(*from_iterator));
1025 if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0)))
1026 goto exit;
1027
1028 MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK);
1029 bzero(to_iterator, sizeof(*to_iterator));
1030 if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
1031 goto exit;
1032
1033 to_key = (HFSPlusCatalogKey *)&to_iterator->key;
1034 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
1035 BDINIT(btdata, recp);
1036
1037 /*
1038 * When moving a directory, make sure its a valid move.
1039 */
1040 if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
1041 struct BTreeIterator iterator;
1042 cnid_t cnid = from_cdp->cd_cnid;
1043 cnid_t pathcnid = todir_cdp->cd_parentcnid;
1044
1045 /* First check the obvious ones */
1046 if (cnid == fsRtDirID ||
1047 cnid == to_cdp->cd_parentcnid ||
1048 cnid == pathcnid) {
1049 result = EINVAL;
1050 goto exit;
1051 }
1052 bzero(&iterator, sizeof(iterator));
1053 /*
1054 * Traverse destination path all the way back to the root
1055 * making sure that source directory is not encountered.
1056 *
1057 */
1058 while (pathcnid > fsRtDirID) {
1059 buildthreadkey(pathcnid, std_hfs,
1060 (CatalogKey *)&iterator.key);
1061 result = BTSearchRecord(fcb, &iterator, &btdata,
1062 &datasize, NULL);
1063 if (result) goto exit;
1064
1065 pathcnid = getparentcnid(recp);
1066 if (pathcnid == cnid || pathcnid == 0) {
1067 result = EINVAL;
1068 goto exit;
1069 }
1070 }
1071 }
1072
1073 /*
1074 * Step 1: Find cnode data at old location
1075 */
1076 result = BTSearchRecord(fcb, from_iterator, &btdata,
1077 &datasize, from_iterator);
1078 if (result) {
1079 if (std_hfs || (result != btNotFound))
1080 goto exit;
1081
1082 struct cat_desc temp_desc;
1083
1084 /* Probably the node has mangled name */
1085 result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL);
1086 if (result)
1087 goto exit;
1088
1089 /* The file has mangled name. Search the cnode data using full name */
1090 bzero(from_iterator, sizeof(*from_iterator));
1091 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
1092 if (result) {
1093 cat_releasedesc(&temp_desc);
1094 goto exit;
1095 }
1096
1097 result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
1098 if (result) {
1099 cat_releasedesc(&temp_desc);
1100 goto exit;
1101 }
1102
1103 cat_releasedesc(&temp_desc);
1104 }
1105
1106 /* Check if the source is directory hard link. We do not change
1107 * directory flag because it is later used to initialize result descp
1108 */
1109 if ((!std_hfs) &&
1110 (directory) &&
1111 (recp->recordType == kHFSPlusFileRecord) &&
1112 (recp->hfsPlusFile.flags & kHFSHasLinkChainMask)) {
1113 is_dirlink = 1;
1114 }
1115
1116 /*
1117 * Update the text encoding (on disk and in descriptor).
1118 *
1119 * Note that hardlink inodes don't require a text encoding hint.
1120 */
1121 if (!std_hfs &&
1122 todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid &&
1123 todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
1124 encoding = hfs_pickencoding(to_key->nodeName.unicode, to_key->nodeName.length);
1125 hfs_setencodingbits(hfsmp, encoding);
1126 recp->hfsPlusFile.textEncoding = encoding;
1127 if (out_cdp)
1128 out_cdp->cd_encoding = encoding;
1129 }
1130
1131 if (std_hfs && !directory &&
1132 !(recp->hfsFile.flags & kHFSThreadExistsMask))
1133 skipthread = 1;
1134 #if 0
1135 /*
1136 * If the keys are identical then there's nothing left to do!
1137 *
1138 * update the hint and exit
1139 *
1140 */
1141 if (std_hfs && hfskeycompare(to_key, iter->key) == 0)
1142 goto exit;
1143 if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0)
1144 goto exit;
1145 #endif
1146
1147 /* Step 2: Insert cnode at new location */
1148 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1149 if (result == btExists) {
1150 int fromtype = recp->recordType;
1151
1152 if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
1153 goto exit; /* EEXIST */
1154
1155 /* Find cnode data at new location */
1156 result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
1157 if (result)
1158 goto exit;
1159
1160 if ((fromtype != recp->recordType) ||
1161 (from_cdp->cd_cnid != getcnid(recp))) {
1162 result = EEXIST;
1163 goto exit; /* EEXIST */
1164 }
1165 /* The old name is a case variant and must be removed */
1166 result = BTDeleteRecord(fcb, from_iterator);
1167 if (result)
1168 goto exit;
1169
1170 /* Insert cnode (now that case duplicate is gone) */
1171 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1172 if (result) {
1173 /* Try and restore original before leaving */
1174 // XXXdbg
1175 #if 1
1176 {
1177 int err;
1178 err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1179 if (err) {
1180 printf("hfs: cat_create: could not undo (BTInsert = %d)", err);
1181 hfs_mark_volume_inconsistent(hfsmp);
1182 result = err;
1183 goto exit;
1184 }
1185 }
1186 #else
1187 (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1188 #endif
1189 goto exit;
1190 }
1191 sourcegone = 1;
1192 }
1193 if (result)
1194 goto exit;
1195
1196 /* Step 3: Remove cnode from old location */
1197 if (!sourcegone) {
1198 result = BTDeleteRecord(fcb, from_iterator);
1199 if (result) {
1200 /* Try and delete new record before leaving */
1201 // XXXdbg
1202 #if 1
1203 {
1204 int err;
1205 err = BTDeleteRecord(fcb, to_iterator);
1206 if (err) {
1207 printf("hfs: cat_create: could not undo (BTDelete = %d)", err);
1208 hfs_mark_volume_inconsistent(hfsmp);
1209 result = err;
1210 goto exit;
1211 }
1212 }
1213 #else
1214 (void) BTDeleteRecord(fcb, to_iterator);
1215 #endif
1216 goto exit;
1217 }
1218 }
1219
1220 /* #### POINT OF NO RETURN #### */
1221
1222 /*
1223 * Step 4: Remove cnode's old thread record
1224 */
1225 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1226 (void) BTDeleteRecord(fcb, from_iterator);
1227
1228 /*
1229 * Step 5: Insert cnode's new thread record
1230 * (optional for HFS files)
1231 */
1232 if (!skipthread) {
1233 /* For directory hard links, always create a file thread
1234 * record. For everything else, use the directory flag.
1235 */
1236 if (is_dirlink) {
1237 datasize = buildthread(&to_iterator->key, recp, std_hfs, false);
1238 } else {
1239 datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
1240 }
1241 btdata.itemSize = datasize;
1242 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1243 result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1244 }
1245
1246 if (out_cdp) {
1247 HFSPlusCatalogKey * pluskey = NULL;
1248
1249 if (std_hfs) {
1250 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1251 promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
1252
1253 /* Save the real encoding hint in the Finder Info (field 4). */
1254 if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
1255 u_int32_t realhint;
1256
1257 realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
1258 vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
1259 }
1260
1261 } else
1262 pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
1263
1264 builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
1265 encoding, directory, out_cdp);
1266 if (std_hfs) {
1267 FREE(pluskey, M_TEMP);
1268 }
1269 }
1270 exit:
1271 (void) BTFlushPath(fcb);
1272 if (from_iterator)
1273 FREE(from_iterator, M_TEMP);
1274 if (to_iterator)
1275 FREE(to_iterator, M_TEMP);
1276 if (recp)
1277 FREE(recp, M_TEMP);
1278 return MacToVFSError(result);
1279 }
1280
1281
1282 /*
1283 * cat_delete - delete a node from the catalog
1284 *
1285 * Order of B-tree operations:
1286 * 1. BTDeleteRecord(cnode);
1287 * 2. BTDeleteRecord(thread);
1288 * 3. BTUpdateRecord(parent);
1289 */
1290 __private_extern__
1291 int
1292 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
1293 {
1294 FCB * fcb;
1295 BTreeIterator *iterator;
1296 cnid_t cnid;
1297 int std_hfs;
1298 int result;
1299
1300 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1301 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
1302
1303 /* Preflight check:
1304 *
1305 * The root directory cannot be deleted
1306 * A directory must be empty
1307 * A file must be zero length (no blocks)
1308 */
1309 if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
1310 descp->cd_parentcnid == kHFSRootParentID)
1311 return (EINVAL);
1312
1313 /* XXX Preflight Missing */
1314
1315 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1316 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
1317 iterator->hint.nodeNum = 0;
1318
1319 /*
1320 * Derive a key from either the file ID (for a virtual inode)
1321 * or the descriptor.
1322 */
1323 if (descp->cd_namelen == 0) {
1324 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1325 cnid = attrp->ca_fileid;
1326 } else {
1327 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1328 cnid = descp->cd_cnid;
1329 }
1330 if (result)
1331 goto exit;
1332
1333 /* Delete record */
1334 result = BTDeleteRecord(fcb, iterator);
1335 if (result) {
1336 if (std_hfs || (result != btNotFound))
1337 goto exit;
1338
1339 struct cat_desc temp_desc;
1340
1341 /* Probably the node has mangled name */
1342 result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL);
1343 if (result)
1344 goto exit;
1345
1346 /* The file has mangled name. Delete the file using full name */
1347 bzero(iterator, sizeof(*iterator));
1348 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
1349 cnid = temp_desc.cd_cnid;
1350 if (result) {
1351 cat_releasedesc(&temp_desc);
1352 goto exit;
1353 }
1354
1355 result = BTDeleteRecord(fcb, iterator);
1356 if (result) {
1357 cat_releasedesc(&temp_desc);
1358 goto exit;
1359 }
1360
1361 cat_releasedesc(&temp_desc);
1362 }
1363
1364 /* Delete thread record. On error, mark volume inconsistent */
1365 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
1366 if (BTDeleteRecord(fcb, iterator)) {
1367 if (!std_hfs) {
1368 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
1369 hfs_mark_volume_inconsistent(hfsmp);
1370 }
1371 }
1372
1373 exit:
1374 (void) BTFlushPath(fcb);
1375
1376 return MacToVFSError(result);
1377 }
1378
1379
1380 /*
1381 * cnode_update - update the catalog node described by descp
1382 * using the data from attrp and forkp.
1383 */
1384 __private_extern__
1385 int
1386 cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
1387 struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
1388 {
1389 FCB * fcb;
1390 BTreeIterator * iterator;
1391 struct update_state state;
1392 int std_hfs;
1393 int result;
1394
1395 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1396 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
1397
1398 state.s_desc = descp;
1399 state.s_attr = attrp;
1400 state.s_datafork = dataforkp;
1401 state.s_rsrcfork = rsrcforkp;
1402 state.s_hfsmp = hfsmp;
1403
1404 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1405 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
1406
1407 /*
1408 * For open-deleted files we need to do a lookup by cnid
1409 * (using thread rec).
1410 *
1411 * For hard links, the target of the update is the inode
1412 * itself (not the link record) so a lookup by fileid
1413 * (i.e. thread rec) is needed.
1414 */
1415 if ((descp->cd_cnid != attrp->ca_fileid) ||
1416 (descp->cd_namelen == 0) ||
1417 (attrp->ca_recflags & kHFSHasLinkChainMask)) {
1418 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1419 } else {
1420 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1421 }
1422 if (result)
1423 goto exit;
1424
1425 /* Pass a node hint */
1426 iterator->hint.nodeNum = descp->cd_hint;
1427
1428 result = BTUpdateRecord(fcb, iterator,
1429 (IterateCallBackProcPtr)catrec_update, &state);
1430 if (result)
1431 goto exit;
1432
1433 /* Update the node hint. */
1434 descp->cd_hint = iterator->hint.nodeNum;
1435
1436 exit:
1437 (void) BTFlushPath(fcb);
1438
1439 return MacToVFSError(result);
1440 }
1441
1442 /*
1443 * catrec_update - Update the fields of a catalog record
1444 * This is called from within BTUpdateRecord.
1445 */
1446 static int
1447 catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
1448 {
1449 struct cat_desc *descp;
1450 struct cat_attr *attrp;
1451 struct cat_fork *forkp;
1452 struct hfsmount *hfsmp;
1453 long blksize;
1454 int i;
1455
1456 descp = state->s_desc;
1457 attrp = state->s_attr;
1458 hfsmp = state->s_hfsmp;
1459 blksize = HFSTOVCB(hfsmp)->blockSize;
1460
1461 switch (crp->recordType) {
1462 case kHFSFolderRecord: {
1463 HFSCatalogFolder *dir;
1464
1465 dir = (struct HFSCatalogFolder *)crp;
1466 /* Do a quick sanity check */
1467 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1468 (dir->folderID != descp->cd_cnid))
1469 return (btNotFound);
1470 dir->valence = attrp->ca_entries;
1471 dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1472 dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1473 dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1474 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16);
1475 bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16);
1476 break;
1477 }
1478 case kHFSFileRecord: {
1479 HFSCatalogFile *file;
1480
1481 file = (struct HFSCatalogFile *)crp;
1482 /* Do a quick sanity check */
1483 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1484 (file->fileID != attrp->ca_fileid))
1485 return (btNotFound);
1486 file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1487 file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1488 file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1489 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16);
1490 bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16);
1491 if (state->s_rsrcfork) {
1492 forkp = state->s_rsrcfork;
1493 file->rsrcLogicalSize = forkp->cf_size;
1494 file->rsrcPhysicalSize = forkp->cf_blocks * blksize;
1495 for (i = 0; i < kHFSExtentDensity; ++i) {
1496 file->rsrcExtents[i].startBlock =
1497 (u_int16_t)forkp->cf_extents[i].startBlock;
1498 file->rsrcExtents[i].blockCount =
1499 (u_int16_t)forkp->cf_extents[i].blockCount;
1500 }
1501 }
1502 if (state->s_datafork) {
1503 forkp = state->s_datafork;
1504 file->dataLogicalSize = forkp->cf_size;
1505 file->dataPhysicalSize = forkp->cf_blocks * blksize;
1506 for (i = 0; i < kHFSExtentDensity; ++i) {
1507 file->dataExtents[i].startBlock =
1508 (u_int16_t)forkp->cf_extents[i].startBlock;
1509 file->dataExtents[i].blockCount =
1510 (u_int16_t)forkp->cf_extents[i].blockCount;
1511 }
1512 }
1513
1514 /* Synchronize the lock state */
1515 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
1516 file->flags |= kHFSFileLockedMask;
1517 else
1518 file->flags &= ~kHFSFileLockedMask;
1519 break;
1520 }
1521 case kHFSPlusFolderRecord: {
1522 HFSPlusCatalogFolder *dir;
1523
1524 dir = (struct HFSPlusCatalogFolder *)crp;
1525 /* Do a quick sanity check */
1526 if (dir->folderID != attrp->ca_fileid) {
1527 printf("hfs: catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid);
1528 return (btNotFound);
1529 }
1530 dir->flags = attrp->ca_recflags;
1531 dir->valence = attrp->ca_entries;
1532 dir->createDate = to_hfs_time(attrp->ca_itime);
1533 dir->contentModDate = to_hfs_time(attrp->ca_mtime);
1534 dir->backupDate = to_hfs_time(attrp->ca_btime);
1535 dir->accessDate = to_hfs_time(attrp->ca_atime);
1536 attrp->ca_atimeondisk = attrp->ca_atime;
1537 dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
1538 /* Note: directory hardlink inodes don't require a text encoding hint. */
1539 if (ckp->hfsPlus.parentID != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
1540 dir->textEncoding = descp->cd_encoding;
1541 }
1542 dir->folderCount = attrp->ca_dircount;
1543 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
1544 /*
1545 * Update the BSD Info if it was already initialized on
1546 * disk or if the runtime values have been modified.
1547 *
1548 * If the BSD info was already initialized, but
1549 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1550 * probably different than what was on disk. We don't want
1551 * to overwrite the on-disk values (so if we turn off
1552 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1553 * This way, we can still change fields like the mode or
1554 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1555 *
1556 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1557 * won't change the uid or gid from their defaults. So, if
1558 * the BSD info wasn't set, and the runtime values are not
1559 * default, then what changed was the mode or flags. We
1560 * have to set the uid and gid to something, so use the
1561 * supplied values (which will be default), which has the
1562 * same effect as creating a new file while
1563 * MNT_UNKNOWNPERMISSIONS is set.
1564 */
1565 if ((dir->bsdInfo.fileMode != 0) ||
1566 (attrp->ca_flags != 0) ||
1567 (attrp->ca_uid != hfsmp->hfs_uid) ||
1568 (attrp->ca_gid != hfsmp->hfs_gid) ||
1569 ((attrp->ca_mode & ALLPERMS) !=
1570 (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
1571 if ((dir->bsdInfo.fileMode == 0) ||
1572 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1573 dir->bsdInfo.ownerID = attrp->ca_uid;
1574 dir->bsdInfo.groupID = attrp->ca_gid;
1575 }
1576 dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1577 dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1578 dir->bsdInfo.fileMode = attrp->ca_mode;
1579 /* A directory hardlink has a link count. */
1580 if (attrp->ca_linkcount > 1 || dir->hl_linkCount > 1) {
1581 dir->hl_linkCount = attrp->ca_linkcount;
1582 }
1583 }
1584 break;
1585 }
1586 case kHFSPlusFileRecord: {
1587 HFSPlusCatalogFile *file;
1588
1589 file = (struct HFSPlusCatalogFile *)crp;
1590 /* Do a quick sanity check */
1591 if (file->fileID != attrp->ca_fileid)
1592 return (btNotFound);
1593 file->flags = attrp->ca_recflags;
1594 file->createDate = to_hfs_time(attrp->ca_itime);
1595 file->contentModDate = to_hfs_time(attrp->ca_mtime);
1596 file->backupDate = to_hfs_time(attrp->ca_btime);
1597 file->accessDate = to_hfs_time(attrp->ca_atime);
1598 attrp->ca_atimeondisk = attrp->ca_atime;
1599 file->attributeModDate = to_hfs_time(attrp->ca_ctime);
1600 /*
1601 * Note: file hardlink inodes don't require a text encoding
1602 * hint, but they do have a first link value.
1603 */
1604 if (ckp->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
1605 file->hl_firstLinkID = attrp->ca_firstlink;
1606 } else {
1607 file->textEncoding = descp->cd_encoding;
1608 }
1609 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
1610 /*
1611 * Update the BSD Info if it was already initialized on
1612 * disk or if the runtime values have been modified.
1613 *
1614 * If the BSD info was already initialized, but
1615 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1616 * probably different than what was on disk. We don't want
1617 * to overwrite the on-disk values (so if we turn off
1618 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1619 * This way, we can still change fields like the mode or
1620 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1621 *
1622 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1623 * won't change the uid or gid from their defaults. So, if
1624 * the BSD info wasn't set, and the runtime values are not
1625 * default, then what changed was the mode or flags. We
1626 * have to set the uid and gid to something, so use the
1627 * supplied values (which will be default), which has the
1628 * same effect as creating a new file while
1629 * MNT_UNKNOWNPERMISSIONS is set.
1630 */
1631 if ((file->bsdInfo.fileMode != 0) ||
1632 (attrp->ca_flags != 0) ||
1633 (attrp->ca_uid != hfsmp->hfs_uid) ||
1634 (attrp->ca_gid != hfsmp->hfs_gid) ||
1635 ((attrp->ca_mode & ALLPERMS) !=
1636 (hfsmp->hfs_file_mask & ACCESSPERMS))) {
1637 if ((file->bsdInfo.fileMode == 0) ||
1638 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1639 file->bsdInfo.ownerID = attrp->ca_uid;
1640 file->bsdInfo.groupID = attrp->ca_gid;
1641 }
1642 file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1643 file->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1644 file->bsdInfo.fileMode = attrp->ca_mode;
1645 }
1646 if (state->s_rsrcfork) {
1647 forkp = state->s_rsrcfork;
1648 file->resourceFork.logicalSize = forkp->cf_size;
1649 file->resourceFork.totalBlocks = forkp->cf_blocks;
1650 bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
1651 sizeof(HFSPlusExtentRecord));
1652 /* Push blocks read to disk */
1653 file->resourceFork.clumpSize =
1654 howmany(forkp->cf_bytesread, blksize);
1655 }
1656 if (state->s_datafork) {
1657 forkp = state->s_datafork;
1658 file->dataFork.logicalSize = forkp->cf_size;
1659 file->dataFork.totalBlocks = forkp->cf_blocks;
1660 bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
1661 sizeof(HFSPlusExtentRecord));
1662 /* Push blocks read to disk */
1663 file->dataFork.clumpSize =
1664 howmany(forkp->cf_bytesread, blksize);
1665 }
1666
1667 if ((file->resourceFork.extents[0].startBlock != 0) &&
1668 (file->resourceFork.extents[0].startBlock ==
1669 file->dataFork.extents[0].startBlock)) {
1670 panic("hfs: catrec_update: rsrc fork == data fork");
1671 }
1672
1673 /* Synchronize the lock state */
1674 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
1675 file->flags |= kHFSFileLockedMask;
1676 else
1677 file->flags &= ~kHFSFileLockedMask;
1678
1679 /* Push out special field if necessary */
1680 if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode)) {
1681 file->bsdInfo.special.rawDevice = attrp->ca_rdev;
1682 } else if (descp->cd_cnid != attrp->ca_fileid || attrp->ca_linkcount == 2) {
1683 file->hl_linkCount = attrp->ca_linkcount;
1684 }
1685 break;
1686 }
1687 default:
1688 return (btNotFound);
1689 }
1690 return (0);
1691 }
1692
1693 /* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
1694 * catalog btree of given cnid by walking up the parent chain till it reaches
1695 * either the root folder, or the private metadata directory for storing
1696 * directory hard links. This function updates the corresponding in-core
1697 * cnode, if any, and the directory record in the catalog btree.
1698 * On success, returns zero. On failure, returns non-zero value.
1699 */
1700 __private_extern__
1701 int
1702 cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
1703 {
1704 int retval = 0;
1705 int lockflags = 0;
1706 struct cat_desc desc;
1707 struct cat_attr attr;
1708
1709 while ((cnid != kHFSRootFolderID) && (cnid != kHFSRootParentID) &&
1710 (cnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
1711 /* Update the bit in corresponding cnode, if any, in the hash.
1712 * If the cnode has the bit already set, stop the traversal.
1713 */
1714 retval = hfs_chash_set_childlinkbit(hfsmp, cnid);
1715 if (retval == 0) {
1716 break;
1717 }
1718
1719 /* Update the catalog record on disk if either cnode was not
1720 * found in the hash, or if a cnode was found and the cnode
1721 * did not have the bit set previously.
1722 */
1723 retval = hfs_start_transaction(hfsmp);
1724 if (retval) {
1725 break;
1726 }
1727 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1728
1729 /* Look up our catalog folder record */
1730 retval = cat_idlookup(hfsmp, cnid, 0, &desc, &attr, NULL);
1731 if (retval) {
1732 hfs_systemfile_unlock(hfsmp, lockflags);
1733 hfs_end_transaction(hfsmp);
1734 break;
1735 }
1736
1737 /* Update the bit in the catalog record */
1738 attr.ca_recflags |= kHFSHasChildLinkMask;
1739 retval = cat_update(hfsmp, &desc, &attr, NULL, NULL);
1740 if (retval) {
1741 hfs_systemfile_unlock(hfsmp, lockflags);
1742 hfs_end_transaction(hfsmp);
1743 cat_releasedesc(&desc);
1744 break;
1745 }
1746
1747 hfs_systemfile_unlock(hfsmp, lockflags);
1748 hfs_end_transaction(hfsmp);
1749
1750 cnid = desc.cd_parentcnid;
1751 cat_releasedesc(&desc);
1752 }
1753
1754 return retval;
1755 }
1756
1757 /* This function traverses the parent directory hierarchy from the given
1758 * directory to one level below root directory and checks if any of its
1759 * ancestors is -
1760 * 1. A directory hard link.
1761 * 2. The 'pointed at' directory.
1762 * If any of these conditions fail or an internal error is encountered
1763 * during look up of the catalog record, this function returns non-zero value.
1764 */
1765 __private_extern__
1766 int
1767 cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_cnid)
1768 {
1769 HFSPlusCatalogKey *keyp;
1770 BTreeIterator *ip;
1771 FSBufferDescriptor btdata;
1772 HFSPlusCatalogFolder folder;
1773 FCB *fcb;
1774 int invalid;
1775 int result;
1776
1777 invalid = 0;
1778 BDINIT(btdata, &folder);
1779 MALLOC(ip, BTreeIterator *, sizeof(*ip), M_TEMP, M_WAITOK);
1780 keyp = (HFSPlusCatalogKey *)&ip->key;
1781 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1782
1783 while (cnid != kHFSRootParentID) {
1784 /* Check if the 'pointed at' directory is an ancestor */
1785 if (pointed_at_cnid == cnid) {
1786 invalid = 1;
1787 break;
1788 }
1789 if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
1790 printf("hfs: cat_check_link_ancestry: getkey for %u failed\n", cnid);
1791 invalid = 1; /* On errors, assume an invalid parent */
1792 break;
1793 }
1794 if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) {
1795 printf("hfs: cat_check_link_ancestry: cannot find %u\n", cnid);
1796 invalid = 1; /* On errors, assume an invalid parent */
1797 break;
1798 }
1799 /* Check if this ancestor is a directory hard link */
1800 if (folder.flags & kHFSHasLinkChainMask) {
1801 invalid = 1;
1802 break;
1803 }
1804 cnid = keyp->parentID;
1805 }
1806 FREE(ip, M_TEMP);
1807 return (invalid);
1808 }
1809
1810
1811 /*
1812 * updatelink_callback - update a link's chain
1813 */
1814
1815 struct linkupdate_state {
1816 cnid_t filelinkid;
1817 cnid_t prevlinkid;
1818 cnid_t nextlinkid;
1819 };
1820
1821 static int
1822 updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
1823 {
1824 HFSPlusCatalogFile *file;
1825
1826 if (crp->recordType != kHFSPlusFileRecord) {
1827 printf("hfs: updatelink_callback: unexpected rec type %d\n", crp->recordType);
1828 return (btNotFound);
1829 }
1830
1831 file = (struct HFSPlusCatalogFile *)crp;
1832 if (file->flags & kHFSHasLinkChainMask) {
1833 if (state->prevlinkid != HFS_IGNORABLE_LINK) {
1834 file->hl_prevLinkID = state->prevlinkid;
1835 }
1836 if (state->nextlinkid != HFS_IGNORABLE_LINK) {
1837 file->hl_nextLinkID = state->nextlinkid;
1838 }
1839 } else {
1840 printf("hfs: updatelink_callback: file %d isn't a chain\n", file->fileID);
1841 }
1842 return (0);
1843 }
1844
1845 /*
1846 * cat_updatelink - update a link's chain
1847 */
1848 __private_extern__
1849 int
1850 cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
1851 {
1852 FCB * fcb;
1853 BTreeIterator * iterator;
1854 struct linkupdate_state state;
1855 int result;
1856
1857 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1858 state.filelinkid = linkfileid;
1859 state.prevlinkid = prevlinkid;
1860 state.nextlinkid = nextlinkid;
1861
1862 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1863 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
1864 iterator->hint.nodeNum = 0;
1865
1866 result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
1867 if (result == 0) {
1868 result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)updatelink_callback, &state);
1869 (void) BTFlushPath(fcb);
1870 } else {
1871 printf("hfs: cat_updatelink: couldn't resolve cnid %d\n", linkfileid);
1872 }
1873 return MacToVFSError(result);
1874 }
1875
1876 /*
1877 * cat_lookuplink - lookup a link by it's name
1878 */
1879 __private_extern__
1880 int
1881 cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
1882 {
1883 FCB * fcb;
1884 BTreeIterator * iterator;
1885 struct FSBufferDescriptor btdata;
1886 struct HFSPlusCatalogFile file;
1887 int result;
1888
1889 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1890
1891 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1892 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
1893 iterator->hint.nodeNum = 0;
1894
1895 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
1896 goto exit;
1897 }
1898 BDINIT(btdata, &file);
1899
1900 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
1901 goto exit;
1902 }
1903 if (file.recordType != kHFSPlusFileRecord) {
1904 result = ENOENT;
1905 goto exit;
1906 }
1907 *linkfileid = file.fileID;
1908
1909 if (file.flags & kHFSHasLinkChainMask) {
1910 *prevlinkid = file.hl_prevLinkID;
1911 *nextlinkid = file.hl_nextLinkID;
1912 } else {
1913 *prevlinkid = 0;
1914 *nextlinkid = 0;
1915 }
1916 exit:
1917 return MacToVFSError(result);
1918 }
1919
1920
1921 /*
1922 * cat_lookuplink - lookup a link by its cnid
1923 */
1924 __private_extern__
1925 int
1926 cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
1927 {
1928 FCB * fcb;
1929 BTreeIterator * iterator;
1930 struct FSBufferDescriptor btdata;
1931 struct HFSPlusCatalogFile file;
1932 int result;
1933
1934 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1935
1936 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1937 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
1938 iterator->hint.nodeNum = 0;
1939
1940 if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
1941 printf("hfs: cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid, result);
1942 goto exit;
1943 }
1944 BDINIT(btdata, &file);
1945
1946 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
1947 printf("hfs: cat_lookuplinkbyid: cannot find %d\n", linkfileid);
1948 goto exit;
1949 }
1950 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
1951 if (file.flags & kHFSHasLinkChainMask) {
1952 cnid_t parent;
1953
1954 parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
1955
1956 /* ADL inodes don't have a chain (its in an EA) */
1957 if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
1958 result = ENOLINK; /* signal to caller to get head of list */
1959 } else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
1960 *prevlinkid = 0;
1961 *nextlinkid = file.hl_firstLinkID;
1962 } else {
1963 *prevlinkid = file.hl_prevLinkID;
1964 *nextlinkid = file.hl_nextLinkID;
1965 }
1966 } else {
1967 *prevlinkid = 0;
1968 *nextlinkid = 0;
1969 }
1970 exit:
1971 return MacToVFSError(result);
1972 }
1973
1974
1975 /*
1976 * cat_createlink - create a link in the catalog
1977 *
1978 * The following cat_attr fields are expected to be set:
1979 * ca_linkref
1980 * ca_itime
1981 * ca_mode (S_IFREG)
1982 * ca_recflags
1983 * ca_flags
1984 * ca_finderinfo (type and creator)
1985 */
1986 __private_extern__
1987 int
1988 cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
1989 cnid_t nextlinkid, cnid_t *linkfileid)
1990 {
1991 FCB * fcb;
1992 struct btobj * bto;
1993 FSBufferDescriptor btdata;
1994 HFSPlusForkData *rsrcforkp;
1995 u_int32_t nextCNID;
1996 u_int32_t datalen;
1997 u_int32_t encoding;
1998 int thread_inserted = 0;
1999 int alias_allocated = 0;
2000 int result = 0;
2001
2002 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2003
2004 /*
2005 * Get the next CNID. We can change it since we hold the catalog lock.
2006 */
2007 nextCNID = hfsmp->vcbNxtCNID;
2008 if (nextCNID == 0xFFFFFFFF) {
2009 HFS_MOUNT_LOCK(hfsmp, TRUE)
2010 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
2011 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
2012 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
2013 } else {
2014 hfsmp->vcbNxtCNID++;
2015 }
2016 MarkVCBDirty(hfsmp);
2017
2018 /* Get space for iterator, key and data */
2019 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
2020 bto->iterator.hint.nodeNum = 0;
2021 rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
2022
2023 result = buildkey(hfsmp, descp, &bto->key, 0);
2024 if (result) {
2025 printf("hfs: cat_createlink: err %d from buildkey\n", result);
2026 goto exit;
2027 }
2028
2029 /* This is our only chance to set the encoding (other than a rename). */
2030 encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
2031
2032 /* Insert the thread record first. */
2033 datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
2034 btdata.bufferAddress = &bto->data;
2035 btdata.itemSize = datalen;
2036 btdata.itemCount = 1;
2037
2038 for (;;) {
2039 buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
2040
2041 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
2042 if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
2043 /*
2044 * Allow CNIDs on HFS Plus volumes to wrap around
2045 */
2046 if (++nextCNID < kHFSFirstUserCatalogNodeID) {
2047 nextCNID = kHFSFirstUserCatalogNodeID;
2048 }
2049 continue;
2050 }
2051 if (result == 0) {
2052 thread_inserted = 1;
2053 }
2054 break;
2055 }
2056 if (result)
2057 goto exit;
2058
2059 /*
2060 * CNID is now established. If we have wrapped then
2061 * update the vcbNxtCNID.
2062 */
2063 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
2064 hfsmp->vcbNxtCNID = nextCNID + 1;
2065 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
2066 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
2067 }
2068 }
2069
2070 /*
2071 * Now insert the link record.
2072 */
2073 buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen);
2074
2075 bto->data.hfsPlusFile.hl_prevLinkID = 0;
2076 bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid;
2077 bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref;
2078
2079 /* For directory hard links, create alias in resource fork */
2080 if (descp->cd_flags & CD_ISDIR) {
2081 if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) {
2082 goto exit;
2083 }
2084 alias_allocated = 1;
2085 }
2086 btdata.bufferAddress = &bto->data;
2087 btdata.itemSize = datalen;
2088 btdata.itemCount = 1;
2089
2090 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
2091
2092 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
2093 if (result) {
2094 if (result == btExists)
2095 result = EEXIST;
2096 goto exit;
2097 }
2098 if (linkfileid != NULL) {
2099 *linkfileid = nextCNID;
2100 }
2101 exit:
2102 if (result) {
2103 if (thread_inserted) {
2104 printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result));
2105
2106 buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
2107 if (BTDeleteRecord(fcb, &bto->iterator)) {
2108 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
2109 hfs_mark_volume_inconsistent(hfsmp);
2110 }
2111 }
2112 if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
2113 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
2114 rsrcforkp->extents[0].blockCount);
2115 rsrcforkp->extents[0].startBlock = 0;
2116 rsrcforkp->extents[0].blockCount = 0;
2117 }
2118 }
2119 (void) BTFlushPath(fcb);
2120 FREE(bto, M_TEMP);
2121
2122 return MacToVFSError(result);
2123 }
2124
2125 /* Directory hard links are visible as aliases on pre-Leopard systems and
2126 * as normal directories on Leopard or later. All directory hard link aliases
2127 * have the same resource fork content except for the three uniquely
2128 * identifying values that are updated in the resource fork data when the alias
2129 * is created. The following array is the constant resource fork data used
2130 * only for creating directory hard link aliases.
2131 */
2132 static const char hfs_dirlink_alias_rsrc[] = {
2133 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2149 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2159 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2161 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2162 };
2163
2164 /* Constants for directory hard link alias */
2165 enum {
2166 /* Size of resource fork data array for directory hard link alias */
2167 kHFSAliasSize = 0x1d0,
2168
2169 /* Volume type for ejectable devices like disk image */
2170 kHFSAliasVolTypeEjectable = 0x5,
2171
2172 /* Offset for volume create date, in Mac OS local time */
2173 kHFSAliasVolCreateDateOffset = 0x12a,
2174
2175 /* Offset for the type of volume */
2176 kHFSAliasVolTypeOffset = 0x130,
2177
2178 /* Offset for folder ID of the parent directory of the directory inode */
2179 kHFSAliasParentIDOffset = 0x132,
2180
2181 /* Offset for folder ID of the directory inode */
2182 kHFSAliasTargetIDOffset = 0x176,
2183 };
2184
2185 /* Create and write an alias that points at the directory represented by given
2186 * inode number on the same volume. Directory hard links are visible as
2187 * aliases in pre-Leopard systems and this function creates these aliases.
2188 *
2189 * Note: This code is very specific to creating alias for the purpose
2190 * of directory hard links only, and should not be generalized.
2191 */
2192 static int
2193 cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
2194 {
2195 struct buf *bp;
2196 daddr64_t blkno;
2197 u_int32_t blkcount;
2198 int blksize;
2199 int sectorsize;
2200 int result;
2201 HFSPlusForkData *rsrcforkp;
2202 char *alias;
2203 uint32_t *valptr;
2204
2205 rsrcforkp = &(crp->resourceFork);
2206
2207 blksize = hfsmp->blockSize;
2208 blkcount = howmany(kHFSAliasSize, blksize);
2209 sectorsize = hfsmp->hfs_logical_block_size;
2210 bzero(rsrcforkp, sizeof(HFSPlusForkData));
2211
2212 /* Allocate some disk space for the alias content. */
2213 result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 1, 1,
2214 &rsrcforkp->extents[0].startBlock,
2215 &rsrcforkp->extents[0].blockCount);
2216 if (result) {
2217 rsrcforkp->extents[0].startBlock = 0;
2218 goto exit;
2219 }
2220
2221 /* Acquire a buffer cache block for our block. */
2222 blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
2223 blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
2224
2225 bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META);
2226 if (hfsmp->jnl) {
2227 journal_modify_block_start(hfsmp->jnl, bp);
2228 }
2229
2230 /* Generate alias content */
2231 alias = (char *)buf_dataptr(bp);
2232 bzero(alias, buf_size(bp));
2233 bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
2234
2235 /* Set the volume create date, local time in Mac OS format */
2236 valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
2237 *valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
2238
2239 /* If the file system is on a virtual device like disk image,
2240 * update the volume type to be ejectable device.
2241 */
2242 if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) {
2243 *(uint16_t *)(alias + kHFSAliasVolTypeOffset) =
2244 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable);
2245 }
2246
2247 /* Set id of the parent of the target directory */
2248 valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset);
2249 *valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid);
2250
2251 /* Set id of the target directory */
2252 valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset);
2253 *valptr = OSSwapHostToBigInt32(inode_num);
2254
2255 /* Write alias content to disk. */
2256 if (hfsmp->jnl) {
2257 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
2258 } else if ((result = buf_bwrite(bp))) {
2259 goto exit;
2260 }
2261
2262 /* Finish initializing the fork data. */
2263 rsrcforkp->logicalSize = kHFSAliasSize;
2264 rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
2265
2266 exit:
2267 if (result && rsrcforkp->extents[0].startBlock != 0) {
2268 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount);
2269 rsrcforkp->extents[0].startBlock = 0;
2270 rsrcforkp->extents[0].blockCount = 0;
2271 rsrcforkp->logicalSize = 0;
2272 rsrcforkp->totalBlocks = 0;
2273 }
2274 return (result);
2275 }
2276
2277 /*
2278 * cat_deletelink - delete a link from the catalog
2279 */
2280 __private_extern__
2281 int
2282 cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
2283 {
2284 struct HFSPlusCatalogFile file;
2285 struct cat_attr cattr;
2286 uint32_t totalBlocks;
2287 int i;
2288 int result;
2289
2290 bzero(&file, sizeof (file));
2291 bzero(&cattr, sizeof (cattr));
2292 cattr.ca_fileid = descp->cd_cnid;
2293
2294 /* Directory links have alias content to remove. */
2295 if (descp->cd_flags & CD_ISDIR) {
2296 FCB * fcb;
2297 BTreeIterator * iterator;
2298 struct FSBufferDescriptor btdata;
2299
2300 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2301
2302 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2303 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
2304 iterator->hint.nodeNum = 0;
2305
2306 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
2307 goto exit;
2308 }
2309 BDINIT(btdata, &file);
2310
2311 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
2312 goto exit;
2313 }
2314 }
2315
2316 result = cat_delete(hfsmp, descp, &cattr);
2317
2318 if ((result == 0) &&
2319 (descp->cd_flags & CD_ISDIR) &&
2320 (file.recordType == kHFSPlusFileRecord)) {
2321
2322 totalBlocks = file.resourceFork.totalBlocks;
2323
2324 for (i = 0; (i < 8) && (totalBlocks > 0); i++) {
2325 if ((file.resourceFork.extents[i].blockCount == 0) &&
2326 (file.resourceFork.extents[i].startBlock == 0)) {
2327 break;
2328 }
2329
2330 (void) BlockDeallocate(hfsmp,
2331 file.resourceFork.extents[i].startBlock,
2332 file.resourceFork.extents[i].blockCount);
2333
2334 totalBlocks -= file.resourceFork.extents[i].blockCount;
2335 file.resourceFork.extents[i].startBlock = 0;
2336 file.resourceFork.extents[i].blockCount = 0;
2337 }
2338 }
2339 exit:
2340 return (result);
2341 }
2342
2343
2344 /*
2345 * Callback to collect directory entries.
2346 * Called with readattr_state for each item in a directory.
2347 */
2348 struct readattr_state {
2349 struct hfsmount *hfsmp;
2350 struct cat_entrylist *list;
2351 cnid_t dir_cnid;
2352 int stdhfs;
2353 int error;
2354 };
2355
2356 static int
2357 getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
2358 struct readattr_state *state)
2359 {
2360 struct cat_entrylist *list = state->list;
2361 struct hfsmount *hfsmp = state->hfsmp;
2362 struct cat_entry *cep;
2363 cnid_t parentcnid;
2364
2365 if (list->realentries >= list->maxentries)
2366 return (0); /* stop */
2367
2368 parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
2369
2370 switch(rec->recordType) {
2371 case kHFSPlusFolderRecord:
2372 case kHFSPlusFileRecord:
2373 case kHFSFolderRecord:
2374 case kHFSFileRecord:
2375 if (parentcnid != state->dir_cnid) {
2376 state->error = ENOENT;
2377 return (0); /* stop */
2378 }
2379 break;
2380 default:
2381 state->error = ENOENT;
2382 return (0); /* stop */
2383 }
2384
2385 /* Hide the private system directories and journal files */
2386 if (parentcnid == kHFSRootFolderID) {
2387 if (rec->recordType == kHFSPlusFolderRecord) {
2388 if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
2389 rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2390 list->skipentries++;
2391 return (1); /* continue */
2392 }
2393 }
2394 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
2395 (rec->recordType == kHFSPlusFileRecord) &&
2396 ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
2397 (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
2398 list->skipentries++;
2399 return (1); /* continue */
2400 }
2401 }
2402
2403 cep = &list->entry[list->realentries++];
2404
2405 if (state->stdhfs) {
2406 struct HFSPlusCatalogFile cnoderec;
2407 HFSPlusCatalogKey * pluskey;
2408 u_int32_t encoding;
2409
2410 promoteattr(hfsmp, rec, &cnoderec);
2411 getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
2412
2413 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
2414 promotekey(hfsmp, (const HFSCatalogKey *)key, pluskey, &encoding);
2415 builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
2416 FREE(pluskey, M_TEMP);
2417
2418 if (rec->recordType == kHFSFileRecord) {
2419 int blksize = HFSTOVCB(hfsmp)->blockSize;
2420
2421 cep->ce_datasize = rec->hfsFile.dataLogicalSize;
2422 cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
2423 cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
2424 cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
2425 }
2426 } else {
2427 getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
2428 builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
2429 isadir(rec), &cep->ce_desc);
2430
2431 if (rec->recordType == kHFSPlusFileRecord) {
2432 cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
2433 cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
2434 cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
2435 cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
2436
2437 /* Save link reference for later processing. */
2438 if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
2439 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
2440 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
2441 } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
2442 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
2443 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
2444 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
2445 }
2446 }
2447 }
2448
2449 return (list->realentries < list->maxentries);
2450 }
2451
2452 /*
2453 * Pack a cat_entrylist buffer with attributes from the catalog
2454 *
2455 * Note: index is zero relative
2456 */
2457 __private_extern__
2458 int
2459 cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
2460 {
2461 FCB* fcb;
2462 CatalogKey * key;
2463 BTreeIterator * iterator;
2464 struct readattr_state state;
2465 cnid_t parentcnid;
2466 int i;
2467 int std_hfs;
2468 int index;
2469 int have_key;
2470 int result = 0;
2471
2472 ce_list->realentries = 0;
2473
2474 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
2475 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
2476 parentcnid = dirhint->dh_desc.cd_parentcnid;
2477
2478 state.hfsmp = hfsmp;
2479 state.list = ce_list;
2480 state.dir_cnid = parentcnid;
2481 state.stdhfs = std_hfs;
2482 state.error = 0;
2483
2484 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2485 bzero(iterator, sizeof(*iterator));
2486 key = (CatalogKey *)&iterator->key;
2487 have_key = 0;
2488 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
2489 index = dirhint->dh_index + 1;
2490
2491 /*
2492 * Attempt to build a key from cached filename
2493 */
2494 if (dirhint->dh_desc.cd_namelen != 0) {
2495 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2496 have_key = 1;
2497 }
2498 }
2499
2500 /*
2501 * If the last entry wasn't cached then position the btree iterator
2502 */
2503 if ((index == 0) || !have_key) {
2504 /*
2505 * Position the iterator at the directory's thread record.
2506 * (i.e. just before the first entry)
2507 */
2508 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
2509 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
2510 if (result) {
2511 result = MacToVFSError(result);
2512 goto exit;
2513 }
2514
2515 /*
2516 * Iterate until we reach the entry just
2517 * before the one we want to start with.
2518 */
2519 if (index > 0) {
2520 struct position_state ps;
2521
2522 ps.error = 0;
2523 ps.count = 0;
2524 ps.index = index;
2525 ps.parentID = dirhint->dh_desc.cd_parentcnid;
2526 ps.hfsmp = hfsmp;
2527
2528 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2529 (IterateCallBackProcPtr)cat_findposition, &ps);
2530 if (ps.error)
2531 result = ps.error;
2532 else
2533 result = MacToVFSError(result);
2534 if (result) {
2535 result = MacToVFSError(result);
2536 goto exit;
2537 }
2538 }
2539 }
2540
2541 /* Fill list with entries starting at iterator->key. */
2542 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2543 (IterateCallBackProcPtr)getentriesattr_callback, &state);
2544
2545 if (state.error)
2546 result = state.error;
2547 else if (ce_list->realentries == 0)
2548 result = ENOENT;
2549 else
2550 result = MacToVFSError(result);
2551
2552 if (std_hfs)
2553 goto exit;
2554
2555 /*
2556 * Resolve any hard links.
2557 */
2558 for (i = 0; i < (int)ce_list->realentries; ++i) {
2559 struct FndrFileInfo *fip;
2560 struct cat_entry *cep;
2561 struct HFSPlusCatalogFile filerec;
2562 int isdirlink = 0;
2563 int isfilelink = 0;
2564
2565 cep = &ce_list->entry[i];
2566 if (cep->ce_attr.ca_linkref == 0)
2567 continue;
2568
2569 /* Note: Finder info is still in Big Endian */
2570 fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
2571
2572 if (S_ISREG(cep->ce_attr.ca_mode) &&
2573 (SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
2574 (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
2575 isfilelink = 1;
2576 }
2577 if (S_ISREG(cep->ce_attr.ca_mode) &&
2578 (SWAP_BE32(fip->fdType) == kHFSAliasType) &&
2579 (SWAP_BE32(fip->fdCreator) == kHFSAliasCreator) &&
2580 (cep->ce_attr.ca_recflags & kHFSHasLinkChainMask)) {
2581 isdirlink = 1;
2582 }
2583 if (isfilelink || isdirlink) {
2584 if (cat_resolvelink(hfsmp, cep->ce_attr.ca_linkref, isdirlink, &filerec) != 0)
2585 continue;
2586 /* Repack entry from inode record. */
2587 getbsdattr(hfsmp, &filerec, &cep->ce_attr);
2588 cep->ce_datasize = filerec.dataFork.logicalSize;
2589 cep->ce_datablks = filerec.dataFork.totalBlocks;
2590 cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
2591 cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
2592 }
2593 }
2594 exit:
2595 FREE(iterator, M_TEMP);
2596
2597 return MacToVFSError(result);
2598 }
2599
2600 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
2601
2602 /*
2603 * Callback to pack directory entries.
2604 * Called with packdirentry_state for each item in a directory.
2605 */
2606
2607 /* Hard link information collected during cat_getdirentries. */
2608 struct linkinfo {
2609 u_int32_t link_ref;
2610 user_addr_t dirent_addr;
2611 };
2612 typedef struct linkinfo linkinfo_t;
2613
2614 /* State information for the getdirentries_callback function. */
2615 struct packdirentry_state {
2616 int cbs_extended;
2617 u_int32_t cbs_parentID;
2618 u_int32_t cbs_index;
2619 uio_t cbs_uio;
2620 ExtendedVCB * cbs_hfsmp;
2621 int cbs_result;
2622 int32_t cbs_nlinks;
2623 int32_t cbs_maxlinks;
2624 linkinfo_t * cbs_linkinfo;
2625 struct cat_desc * cbs_desc;
2626 u_int8_t * cbs_namebuf;
2627 /*
2628 * The following fields are only used for NFS readdir, which
2629 * uses the next file id as the seek offset of each entry.
2630 */
2631 struct direntry * cbs_direntry;
2632 struct direntry * cbs_prevdirentry;
2633 u_int32_t cbs_previlinkref;
2634 Boolean cbs_hasprevdirentry;
2635 Boolean cbs_eof;
2636 };
2637
2638 /*
2639 * getdirentries callback for HFS Plus directories.
2640 */
2641 static int
2642 getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
2643 struct packdirentry_state *state)
2644 {
2645 struct hfsmount *hfsmp;
2646 const CatalogName *cnp;
2647 cnid_t curID;
2648 OSErr result;
2649 struct dirent catent;
2650 struct direntry * entry = NULL;
2651 time_t itime;
2652 u_int32_t ilinkref = 0;
2653 u_int32_t curlinkref = 0;
2654 cnid_t cnid;
2655 int hide = 0;
2656 u_int8_t type = DT_UNKNOWN;
2657 u_int8_t is_mangled = 0;
2658 u_int8_t is_link = 0;
2659 u_int8_t *nameptr;
2660 user_addr_t uiobase = USER_ADDR_NULL;
2661 size_t namelen = 0;
2662 size_t maxnamelen;
2663 size_t uiosize = 0;
2664 caddr_t uioaddr;
2665 Boolean stop_after_pack = false;
2666
2667 hfsmp = state->cbs_hfsmp;
2668 curID = ckp->hfsPlus.parentID;
2669
2670 /* We're done when parent directory changes */
2671 if (state->cbs_parentID != curID) {
2672 if (state->cbs_extended) {
2673 /* The last record has not been returned yet, so we
2674 * want to stop after packing the last item
2675 */
2676 if (state->cbs_hasprevdirentry) {
2677 stop_after_pack = true;
2678 } else {
2679 state->cbs_result = ENOENT;
2680 return (0); /* stop */
2681 }
2682 } else {
2683 state->cbs_result = ENOENT;
2684 return (0); /* stop */
2685 }
2686 }
2687
2688 if (state->cbs_extended) {
2689 entry = state->cbs_direntry;
2690 nameptr = (u_int8_t *)&entry->d_name[0];
2691 maxnamelen = NAME_MAX;
2692 } else {
2693 nameptr = (u_int8_t *)&catent.d_name[0];
2694 maxnamelen = NAME_MAX;
2695 }
2696
2697 if (state->cbs_extended && stop_after_pack) {
2698 /* The last item returns a non-zero invalid cookie */
2699 cnid = INT_MAX;
2700 } else {
2701 switch(crp->recordType) {
2702 case kHFSPlusFolderRecord:
2703 type = DT_DIR;
2704 cnid = crp->hfsPlusFolder.folderID;
2705 /* Hide our private system directories. */
2706 if (curID == kHFSRootFolderID) {
2707 if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
2708 cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2709 hide = 1;
2710 }
2711 }
2712 break;
2713 case kHFSPlusFileRecord:
2714 itime = to_bsd_time(crp->hfsPlusFile.createDate);
2715 type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
2716 cnid = crp->hfsPlusFile.fileID;
2717 /*
2718 * When a hardlink link is encountered save its link ref.
2719 */
2720 if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
2721 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
2722 ((itime == (time_t)hfsmp->hfs_itime) ||
2723 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
2724 /* If link ref is inode's file id then use it directly. */
2725 if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) {
2726 cnid = crp->hfsPlusFile.hl_linkReference;
2727 } else {
2728 ilinkref = crp->hfsPlusFile.hl_linkReference;
2729 }
2730 is_link =1;
2731 } else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
2732 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
2733 (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
2734 (crp->hfsPlusFile.hl_linkReference >= kHFSFirstUserCatalogNodeID) &&
2735 ((itime == (time_t)hfsmp->hfs_itime) ||
2736 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
2737 /* A directory's link resolves to a directory. */
2738 type = DT_DIR;
2739 /* A directory's link ref is always inode's file id. */
2740 cnid = crp->hfsPlusFile.hl_linkReference;
2741 is_link = 1;
2742 }
2743 /* Hide the journal files */
2744 if ((curID == kHFSRootFolderID) &&
2745 ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
2746 ((cnid == hfsmp->hfs_jnlfileid) ||
2747 (cnid == hfsmp->hfs_jnlinfoblkid))) {
2748 hide = 1;
2749 }
2750 break;
2751 default:
2752 return (0); /* stop */
2753 };
2754
2755 cnp = (const CatalogName*) &ckp->hfsPlus.nodeName;
2756
2757 namelen = cnp->ustr.length;
2758 /*
2759 * For MacRoman encoded names, assume that its ascii and
2760 * convert it directly in an attempt to avoid the more
2761 * expensive utf8_encodestr conversion.
2762 */
2763 if ((namelen < maxnamelen) && (crp->hfsPlusFile.textEncoding == 0)) {
2764 int i;
2765 u_int16_t ch;
2766 const u_int16_t *chp;
2767
2768 chp = &cnp->ustr.unicode[0];
2769 for (i = 0; i < (int)namelen; ++i) {
2770 ch = *chp++;
2771 if (ch > 0x007f || ch == 0x0000) {
2772 /* Perform expensive utf8_encodestr conversion */
2773 goto encodestr;
2774 }
2775 nameptr[i] = (ch == '/') ? ':' : (u_int8_t)ch;
2776 }
2777 nameptr[namelen] = '\0';
2778 result = 0;
2779 } else {
2780 encodestr:
2781 result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar),
2782 nameptr, &namelen, maxnamelen + 1, ':', 0);
2783 }
2784
2785 /* Check result returned from encoding the filename to utf8 */
2786 if (result == ENAMETOOLONG) {
2787 /*
2788 * If we were looking at a catalog record for a hardlink (not the inode),
2789 * then we want to use its link ID as opposed to the inode ID for
2790 * a mangled name. For all other cases, they are the same. Note that
2791 * due to the way directory hardlinks are implemented, the actual link
2792 * is going to be counted as a file record, so we can catch both
2793 * with is_link.
2794 */
2795 cnid_t linkid = cnid;
2796 if (is_link) {
2797 linkid = crp->hfsPlusFile.fileID;
2798 }
2799
2800 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
2801 cnp->ustr.unicode, maxnamelen + 1,
2802 (ByteCount*)&namelen, nameptr, linkid);
2803 is_mangled = 1;
2804 }
2805 }
2806
2807 if (state->cbs_extended) {
2808 /*
2809 * The index is 1 relative and includes "." and ".."
2810 *
2811 * Also stuff the cnid in the upper 32 bits of the cookie.
2812 * The cookie is stored to the previous entry, which will
2813 * be packed and copied this time
2814 */
2815 state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
2816 uiosize = state->cbs_prevdirentry->d_reclen;
2817 uioaddr = (caddr_t) state->cbs_prevdirentry;
2818 } else {
2819 catent.d_type = type;
2820 catent.d_namlen = namelen;
2821 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
2822 if (hide)
2823 catent.d_fileno = 0; /* file number = 0 means skip entry */
2824 else
2825 catent.d_fileno = cnid;
2826 uioaddr = (caddr_t) &catent;
2827 }
2828
2829 /* Save current base address for post processing of hard-links. */
2830 if (ilinkref || state->cbs_previlinkref) {
2831 uiobase = uio_curriovbase(state->cbs_uio);
2832 }
2833 /* If this entry won't fit then we're done */
2834 if ((uiosize > (user_size_t)uio_resid(state->cbs_uio)) ||
2835 (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
2836 return (0); /* stop */
2837 }
2838
2839 if (!state->cbs_extended || state->cbs_hasprevdirentry) {
2840 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
2841 if (state->cbs_result == 0) {
2842 ++state->cbs_index;
2843
2844 /* Remember previous entry */
2845 state->cbs_desc->cd_cnid = cnid;
2846 if (type == DT_DIR) {
2847 state->cbs_desc->cd_flags |= CD_ISDIR;
2848 } else {
2849 state->cbs_desc->cd_flags &= ~CD_ISDIR;
2850 }
2851 if (state->cbs_desc->cd_nameptr != NULL) {
2852 state->cbs_desc->cd_namelen = 0;
2853 }
2854 #if 0
2855 state->cbs_desc->cd_encoding = xxxx;
2856 #endif
2857 if (!is_mangled) {
2858 state->cbs_desc->cd_namelen = namelen;
2859 bcopy(nameptr, state->cbs_namebuf, namelen + 1);
2860 } else {
2861 /* Store unmangled name for the directory hint else it will
2862 * restart readdir at the last location again
2863 */
2864 u_int8_t *new_nameptr;
2865 size_t bufsize;
2866 size_t tmp_namelen = 0;
2867
2868 cnp = (const CatalogName *)&ckp->hfsPlus.nodeName;
2869 bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
2870 cnp->ustr.length * sizeof(UniChar),
2871 ':', 0);
2872 MALLOC(new_nameptr, u_int8_t *, bufsize, M_TEMP, M_WAITOK);
2873 result = utf8_encodestr(cnp->ustr.unicode,
2874 cnp->ustr.length * sizeof(UniChar),
2875 new_nameptr, &tmp_namelen, bufsize, ':', 0);
2876
2877 state->cbs_desc->cd_namelen = tmp_namelen;
2878 bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1);
2879
2880 FREE(new_nameptr, M_TEMP);
2881 }
2882 }
2883 if (state->cbs_hasprevdirentry) {
2884 curlinkref = ilinkref; /* save current */
2885 ilinkref = state->cbs_previlinkref; /* use previous */
2886 }
2887 /*
2888 * Record any hard links for post processing.
2889 */
2890 if ((ilinkref != 0) &&
2891 (state->cbs_result == 0) &&
2892 (state->cbs_nlinks < state->cbs_maxlinks)) {
2893 state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
2894 state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
2895 state->cbs_nlinks++;
2896 }
2897 if (state->cbs_hasprevdirentry) {
2898 ilinkref = curlinkref; /* restore current */
2899 }
2900 }
2901
2902 /* Fill the direntry to be used the next time */
2903 if (state->cbs_extended) {
2904 if (stop_after_pack) {
2905 state->cbs_eof = true;
2906 return (0); /* stop */
2907 }
2908 entry->d_type = type;
2909 entry->d_namlen = namelen;
2910 entry->d_reclen = EXT_DIRENT_LEN(namelen);
2911 if (hide) {
2912 /* File number = 0 means skip entry */
2913 entry->d_fileno = 0;
2914 } else {
2915 entry->d_fileno = cnid;
2916 }
2917 /* swap the current and previous entry */
2918 struct direntry * tmp;
2919 tmp = state->cbs_direntry;
2920 state->cbs_direntry = state->cbs_prevdirentry;
2921 state->cbs_prevdirentry = tmp;
2922 state->cbs_hasprevdirentry = true;
2923 state->cbs_previlinkref = ilinkref;
2924 }
2925
2926 /* Continue iteration if there's room */
2927 return (state->cbs_result == 0 &&
2928 uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
2929 }
2930
2931 /*
2932 * getdirentries callback for standard HFS (non HFS+) directories.
2933 */
2934 static int
2935 getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
2936 struct packdirentry_state *state)
2937 {
2938 struct hfsmount *hfsmp;
2939 const CatalogName *cnp;
2940 cnid_t curID;
2941 OSErr result;
2942 struct dirent catent;
2943 cnid_t cnid;
2944 u_int8_t type = DT_UNKNOWN;
2945 u_int8_t *nameptr;
2946 size_t namelen = 0;
2947 size_t maxnamelen;
2948 size_t uiosize = 0;
2949 caddr_t uioaddr;
2950
2951 hfsmp = state->cbs_hfsmp;
2952
2953 curID = ckp->hfs.parentID;
2954
2955 /* We're done when parent directory changes */
2956 if (state->cbs_parentID != curID) {
2957 state->cbs_result = ENOENT;
2958 return (0); /* stop */
2959 }
2960
2961 nameptr = (u_int8_t *)&catent.d_name[0];
2962 maxnamelen = NAME_MAX;
2963
2964 switch(crp->recordType) {
2965 case kHFSFolderRecord:
2966 type = DT_DIR;
2967 cnid = crp->hfsFolder.folderID;
2968 break;
2969 case kHFSFileRecord:
2970 type = DT_REG;
2971 cnid = crp->hfsFile.fileID;
2972 break;
2973 default:
2974 return (0); /* stop */
2975 };
2976
2977 cnp = (const CatalogName*) ckp->hfs.nodeName;
2978 result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr);
2979 /*
2980 * When an HFS name cannot be encoded with the current
2981 * volume encoding we use MacRoman as a fallback.
2982 */
2983 if (result) {
2984 result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr);
2985 }
2986 catent.d_type = type;
2987 catent.d_namlen = namelen;
2988 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
2989 catent.d_fileno = cnid;
2990 uioaddr = (caddr_t) &catent;
2991
2992 /* If this entry won't fit then we're done */
2993 if (uiosize > (user_size_t)uio_resid(state->cbs_uio)) {
2994 return (0); /* stop */
2995 }
2996
2997 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
2998 if (state->cbs_result == 0) {
2999 ++state->cbs_index;
3000
3001 /* Remember previous entry */
3002 state->cbs_desc->cd_cnid = cnid;
3003 if (type == DT_DIR) {
3004 state->cbs_desc->cd_flags |= CD_ISDIR;
3005 } else {
3006 state->cbs_desc->cd_flags &= ~CD_ISDIR;
3007 }
3008 if (state->cbs_desc->cd_nameptr != NULL) {
3009 state->cbs_desc->cd_namelen = 0;
3010 }
3011 state->cbs_desc->cd_namelen = namelen;
3012 bcopy(nameptr, state->cbs_namebuf, namelen + 1);
3013 }
3014
3015 /* Continue iteration if there's room */
3016 return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
3017 }
3018
3019 /*
3020 * Pack a uio buffer with directory entries from the catalog
3021 */
3022 __private_extern__
3023 int
3024 cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
3025 uio_t uio, int extended, int * items, int * eofflag)
3026 {
3027 FCB* fcb;
3028 BTreeIterator * iterator;
3029 CatalogKey * key;
3030 struct packdirentry_state state;
3031 void * buffer;
3032 int bufsize;
3033 int maxlinks;
3034 int result;
3035 int index;
3036 int have_key;
3037
3038 if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
3039 return (ENOTSUP);
3040 }
3041 fcb = hfsmp->hfs_catalog_cp->c_datafork;
3042
3043 /*
3044 * Get a buffer for link info array, btree iterator and a direntry:
3045 */
3046 maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
3047 bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
3048 if (extended) {
3049 bufsize += 2*sizeof(struct direntry);
3050 }
3051 MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
3052 bzero(buffer, bufsize);
3053
3054 state.cbs_extended = extended;
3055 state.cbs_hasprevdirentry = false;
3056 state.cbs_previlinkref = 0;
3057 state.cbs_nlinks = 0;
3058 state.cbs_maxlinks = maxlinks;
3059 state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
3060
3061 iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
3062 key = (CatalogKey *)&iterator->key;
3063 have_key = 0;
3064 index = dirhint->dh_index + 1;
3065 if (extended) {
3066 state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
3067 state.cbs_prevdirentry = state.cbs_direntry + 1;
3068 state.cbs_eof = false;
3069 }
3070 /*
3071 * Attempt to build a key from cached filename
3072 */
3073 if (dirhint->dh_desc.cd_namelen != 0) {
3074 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
3075 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
3076 have_key = 1;
3077 }
3078 }
3079
3080 if (index == 0 && dirhint->dh_threadhint != 0) {
3081 /*
3082 * Position the iterator at the directory's thread record.
3083 * (i.e. just before the first entry)
3084 */
3085 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
3086 iterator->hint.nodeNum = dirhint->dh_threadhint;
3087 iterator->hint.index = 0;
3088 have_key = 1;
3089 }
3090
3091 /*
3092 * If the last entry wasn't cached then position the btree iterator
3093 */
3094 if (!have_key) {
3095 /*
3096 * Position the iterator at the directory's thread record.
3097 * (i.e. just before the first entry)
3098 */
3099 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
3100 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
3101 if (result) {
3102 result = MacToVFSError(result);
3103 goto cleanup;
3104 }
3105 if (index == 0) {
3106 dirhint->dh_threadhint = iterator->hint.nodeNum;
3107 }
3108 /*
3109 * Iterate until we reach the entry just
3110 * before the one we want to start with.
3111 */
3112 if (index > 0) {
3113 struct position_state ps;
3114
3115 ps.error = 0;
3116 ps.count = 0;
3117 ps.index = index;
3118 ps.parentID = dirhint->dh_desc.cd_parentcnid;
3119 ps.hfsmp = hfsmp;
3120
3121 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
3122 (IterateCallBackProcPtr)cat_findposition, &ps);
3123 if (ps.error)
3124 result = ps.error;
3125 else
3126 result = MacToVFSError(result);
3127 if (result) {
3128 result = MacToVFSError(result);
3129 goto cleanup;
3130 }
3131 }
3132 }
3133
3134 state.cbs_index = index;
3135 state.cbs_hfsmp = hfsmp;
3136 state.cbs_uio = uio;
3137 state.cbs_desc = &dirhint->dh_desc;
3138 state.cbs_namebuf = (u_int8_t *)buffer;
3139 state.cbs_result = 0;
3140 state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
3141
3142 /* Use a temporary buffer to hold intermediate descriptor names. */
3143 if (dirhint->dh_desc.cd_namelen > 0 && dirhint->dh_desc.cd_nameptr != NULL) {
3144 bcopy(dirhint->dh_desc.cd_nameptr, buffer, dirhint->dh_desc.cd_namelen+1);
3145 if (dirhint->dh_desc.cd_flags & CD_HASBUF) {
3146 dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
3147 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
3148 }
3149 }
3150 dirhint->dh_desc.cd_nameptr = (u_int8_t *)buffer;
3151
3152 enum BTreeIterationOperations op;
3153 if (extended && index != 0 && have_key)
3154 op = kBTreeCurrentRecord;
3155 else
3156 op = kBTreeNextRecord;
3157
3158 /*
3159 * Process as many entries as possible starting at iterator->key.
3160 */
3161 if (hfsmp->hfs_flags & HFS_STANDARD)
3162 result = BTIterateRecords(fcb, op, iterator,
3163 (IterateCallBackProcPtr)getdirentries_std_callback, &state);
3164 else {
3165 result = BTIterateRecords(fcb, op, iterator,
3166 (IterateCallBackProcPtr)getdirentries_callback, &state);
3167
3168 /* For extended calls, every call to getdirentries_callback()
3169 * transfers the previous directory entry found to the user
3170 * buffer. Therefore when BTIterateRecords reaches the end of
3171 * Catalog BTree, call getdirentries_callback() again with
3172 * dummy values to copy the last directory entry stored in
3173 * packdirentry_state
3174 */
3175 if (state.cbs_extended && (result == fsBTRecordNotFoundErr)) {
3176 CatalogKey ckp;
3177 CatalogRecord crp;
3178
3179 bzero(&ckp, sizeof(ckp));
3180 bzero(&crp, sizeof(crp));
3181
3182 result = getdirentries_callback(&ckp, &crp, &state);
3183 }
3184 }
3185
3186 /* Note that state.cbs_index is still valid on errors */
3187 *items = state.cbs_index - index;
3188 index = state.cbs_index;
3189
3190 if (state.cbs_eof) {
3191 *eofflag = 1;
3192 }
3193
3194 /* Finish updating the catalog iterator. */
3195 dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
3196 dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
3197 dirhint->dh_index = index - 1;
3198
3199 /* Fix up the name. */
3200 if (dirhint->dh_desc.cd_namelen > 0) {
3201 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)vfs_addname((char *)buffer, dirhint->dh_desc.cd_namelen, 0, 0);
3202 dirhint->dh_desc.cd_flags |= CD_HASBUF;
3203 } else {
3204 dirhint->dh_desc.cd_nameptr = NULL;
3205 dirhint->dh_desc.cd_namelen = 0;
3206 }
3207
3208 /*
3209 * Post process any hard links to get the real file id.
3210 */
3211 if (state.cbs_nlinks > 0) {
3212 ino_t fileid = 0;
3213 user_addr_t address;
3214 int i;
3215
3216 for (i = 0; i < state.cbs_nlinks; ++i) {
3217 if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
3218 continue;
3219 /* This assumes that d_ino is always first field. */
3220 address = state.cbs_linkinfo[i].dirent_addr;
3221 if (address == (user_addr_t)0)
3222 continue;
3223 if (uio_isuserspace(uio)) {
3224 if (extended) {
3225 ino64_t fileid_64 = (ino64_t)fileid;
3226 (void) copyout(&fileid_64, address, sizeof(fileid_64));
3227 } else {
3228 (void) copyout(&fileid, address, sizeof(fileid));
3229 }
3230 } else /* system space */ {
3231 if (extended) {
3232 ino64_t fileid_64 = (ino64_t)fileid;
3233 bcopy(&fileid_64, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid_64));
3234 } else {
3235 bcopy(&fileid, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid));
3236 }
3237 }
3238 }
3239 }
3240
3241 if (state.cbs_result)
3242 result = state.cbs_result;
3243 else
3244 result = MacToVFSError(result);
3245
3246 if (result == ENOENT) {
3247 result = 0;
3248 }
3249
3250 cleanup:
3251 FREE(buffer, M_TEMP);
3252
3253 return (result);
3254 }
3255
3256
3257 /*
3258 * Callback to establish directory position.
3259 * Called with position_state for each item in a directory.
3260 */
3261 static int
3262 cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
3263 struct position_state *state)
3264 {
3265 cnid_t curID;
3266
3267 if (state->hfsmp->hfs_flags & HFS_STANDARD)
3268 curID = ckp->hfs.parentID;
3269 else
3270 curID = ckp->hfsPlus.parentID;
3271
3272 /* Make sure parent directory didn't change */
3273 if (state->parentID != curID) {
3274 state->error = EINVAL;
3275 return (0); /* stop */
3276 }
3277
3278 /* Count this entry */
3279 switch(crp->recordType) {
3280 case kHFSPlusFolderRecord:
3281 case kHFSPlusFileRecord:
3282 case kHFSFolderRecord:
3283 case kHFSFileRecord:
3284 ++state->count;
3285 break;
3286 default:
3287 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3288 crp->recordType, curID);
3289 state->error = EINVAL;
3290 return (0); /* stop */
3291 };
3292
3293 return (state->count < state->index);
3294 }
3295
3296
3297 /*
3298 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3299
3300 * The name portion of the key is compared using a 16-bit binary comparison.
3301 * This is called from the b-tree code.
3302 */
3303 __private_extern__
3304 int
3305 cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
3306 {
3307 u_int32_t searchParentID, trialParentID;
3308 int result;
3309
3310 searchParentID = searchKey->parentID;
3311 trialParentID = trialKey->parentID;
3312 result = 0;
3313
3314 if (searchParentID > trialParentID) {
3315 ++result;
3316 } else if (searchParentID < trialParentID) {
3317 --result;
3318 } else {
3319 u_int16_t * str1 = &searchKey->nodeName.unicode[0];
3320 u_int16_t * str2 = &trialKey->nodeName.unicode[0];
3321 int length1 = searchKey->nodeName.length;
3322 int length2 = trialKey->nodeName.length;
3323 u_int16_t c1, c2;
3324 int length;
3325
3326 if (length1 < length2) {
3327 length = length1;
3328 --result;
3329 } else if (length1 > length2) {
3330 length = length2;
3331 ++result;
3332 } else {
3333 length = length1;
3334 }
3335
3336 while (length--) {
3337 c1 = *(str1++);
3338 c2 = *(str2++);
3339
3340 if (c1 > c2) {
3341 result = 1;
3342 break;
3343 }
3344 if (c1 < c2) {
3345 result = -1;
3346 break;
3347 }
3348 }
3349 }
3350
3351 return result;
3352 }
3353
3354
3355 /*
3356 * Compare two standard HFS catalog keys
3357 *
3358 * Result: +n search key > trial key
3359 * 0 search key = trial key
3360 * -n search key < trial key
3361 */
3362 int
3363 CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
3364 {
3365 cnid_t searchParentID, trialParentID;
3366 int result;
3367
3368 searchParentID = searchKey->parentID;
3369 trialParentID = trialKey->parentID;
3370
3371 if (searchParentID > trialParentID)
3372 result = 1;
3373 else if (searchParentID < trialParentID)
3374 result = -1;
3375 else /* parent dirID's are equal, compare names */
3376 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
3377
3378 return result;
3379 }
3380
3381
3382 /*
3383 * Compare two HFS+ catalog keys
3384 *
3385 * Result: +n search key > trial key
3386 * 0 search key = trial key
3387 * -n search key < trial key
3388 */
3389 int
3390 CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
3391 {
3392 cnid_t searchParentID, trialParentID;
3393 int result;
3394
3395 searchParentID = searchKey->parentID;
3396 trialParentID = trialKey->parentID;
3397
3398 if (searchParentID > trialParentID) {
3399 result = 1;
3400 }
3401 else if (searchParentID < trialParentID) {
3402 result = -1;
3403 } else {
3404 /* parent node ID's are equal, compare names */
3405 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
3406 result = searchKey->nodeName.length - trialKey->nodeName.length;
3407 else
3408 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
3409 searchKey->nodeName.length,
3410 &trialKey->nodeName.unicode[0],
3411 trialKey->nodeName.length);
3412 }
3413
3414 return result;
3415 }
3416
3417
3418 /*
3419 * buildkey - build a Catalog b-tree key from a cnode descriptor
3420 */
3421 static int
3422 buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
3423 HFSPlusCatalogKey *key, int retry)
3424 {
3425 int utf8_flags = UTF_ESCAPE_ILLEGAL;
3426 int result = 0;
3427 size_t unicodeBytes = 0;
3428
3429 if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
3430 return (EINVAL); /* invalid name */
3431
3432 key->parentID = descp->cd_parentcnid;
3433 key->nodeName.length = 0;
3434 /*
3435 * Convert filename from UTF-8 into Unicode
3436 */
3437
3438 if ((descp->cd_flags & CD_DECOMPOSED) == 0)
3439 utf8_flags |= UTF_DECOMPOSED;
3440 result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
3441 key->nodeName.unicode, &unicodeBytes,
3442 sizeof(key->nodeName.unicode), ':', utf8_flags);
3443 key->nodeName.length = unicodeBytes / sizeof(UniChar);
3444 key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
3445 if (result) {
3446 if (result != ENAMETOOLONG)
3447 result = EINVAL; /* name has invalid characters */
3448 return (result);
3449 }
3450
3451 /*
3452 * For HFS volumes convert to an HFS compatible key
3453 *
3454 * XXX need to save the encoding that succeeded
3455 */
3456 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
3457 HFSCatalogKey hfskey;
3458
3459 bzero(&hfskey, sizeof(hfskey));
3460 hfskey.keyLength = kHFSCatalogKeyMinimumLength;
3461 hfskey.parentID = key->parentID;
3462 hfskey.nodeName[0] = 0;
3463 if (key->nodeName.length > 0) {
3464 int res;
3465 if ((res = unicode_to_hfs(HFSTOVCB(hfsmp),
3466 key->nodeName.length * 2,
3467 key->nodeName.unicode,
3468 &hfskey.nodeName[0], retry)) != 0) {
3469 if (res != ENAMETOOLONG)
3470 res = EINVAL;
3471
3472 return res;
3473 }
3474 hfskey.keyLength += hfskey.nodeName[0];
3475 }
3476 bcopy(&hfskey, key, sizeof(hfskey));
3477 }
3478 return (0);
3479 }
3480
3481
3482 /*
3483 * Resolve hard link reference to obtain the inode record.
3484 */
3485 __private_extern__
3486 int
3487 cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
3488 {
3489 FSBufferDescriptor btdata;
3490 struct BTreeIterator *iterator;
3491 struct cat_desc idesc;
3492 char inodename[32];
3493 cnid_t parentcnid;
3494 int result = 0;
3495
3496 BDINIT(btdata, recp);
3497
3498 if (isdirlink) {
3499 MAKE_DIRINODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
3500 parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
3501 } else {
3502 MAKE_INODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
3503 parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
3504 }
3505
3506 /* Get space for iterator */
3507 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
3508 bzero(iterator, sizeof(*iterator));
3509
3510 /* Build a descriptor for private dir. */
3511 idesc.cd_parentcnid = parentcnid;
3512 idesc.cd_nameptr = (const u_int8_t *)inodename;
3513 idesc.cd_namelen = strlen(inodename);
3514 idesc.cd_flags = 0;
3515 idesc.cd_hint = 0;
3516 idesc.cd_encoding = 0;
3517 (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
3518
3519 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
3520 &btdata, NULL, NULL);
3521
3522 if (result == 0) {
3523 /* Make sure there's a reference */
3524 if (recp->hl_linkCount == 0)
3525 recp->hl_linkCount = 2;
3526 } else {
3527 printf("hfs: cat_resolvelink: can't find %s\n", inodename);
3528 }
3529
3530 FREE(iterator, M_TEMP);
3531
3532 return (result ? ENOENT : 0);
3533 }
3534
3535 /*
3536 * Resolve hard link reference to obtain the inode number.
3537 */
3538 static int
3539 resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino)
3540 {
3541 struct HFSPlusCatalogFile record;
3542 int error;
3543
3544 /*
3545 * Since we know resolvelinkid is only called from
3546 * cat_getdirentries, we can assume that only file
3547 * hardlinks need to be resolved (cat_getdirentries
3548 * can resolve directory hardlinks in place).
3549 */
3550 error = cat_resolvelink(hfsmp, linkref, 0, &record);
3551 if (error == 0) {
3552 if (record.fileID == 0)
3553 error = ENOENT;
3554 else
3555 *ino = record.fileID;
3556 }
3557 return (error);
3558 }
3559
3560 /*
3561 * getkey - get a key from id by doing a thread lookup
3562 */
3563 static int
3564 getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
3565 {
3566 struct BTreeIterator * iterator;
3567 FSBufferDescriptor btdata;
3568 u_int16_t datasize;
3569 CatalogKey * keyp;
3570 CatalogRecord * recp;
3571 int result;
3572 int std_hfs;
3573
3574 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
3575
3576 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
3577 bzero(iterator, sizeof(*iterator));
3578 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
3579
3580 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
3581 BDINIT(btdata, recp);
3582
3583 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
3584 &btdata, &datasize, iterator);
3585 if (result)
3586 goto exit;
3587
3588 /* Turn thread record into a cnode key (in place) */
3589 switch (recp->recordType) {
3590 case kHFSFileThreadRecord:
3591 case kHFSFolderThreadRecord:
3592 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
3593 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
3594 bcopy(keyp, key, keyp->hfs.keyLength + 1);
3595 break;
3596
3597 case kHFSPlusFileThreadRecord:
3598 case kHFSPlusFolderThreadRecord:
3599 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
3600 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
3601 (keyp->hfsPlus.nodeName.length * 2);
3602 bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
3603 break;
3604
3605 default:
3606 result = ENOENT;
3607 break;
3608 }
3609
3610 exit:
3611 FREE(iterator, M_TEMP);
3612 FREE(recp, M_TEMP);
3613
3614 return MacToVFSError(result);
3615 }
3616
3617 /*
3618 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
3619 * null arguments to cat_idlookup instead, but we save around 10% by not building the
3620 * cat_desc here). Both key and attrp must point to real structures.
3621 *
3622 * The key's parent id is the only part of the key expected to be used by the caller.
3623 * The name portion of the key may not always be valid (ie in the case of a hard link).
3624 */
3625 __private_extern__
3626 int
3627 cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
3628 {
3629 int result;
3630
3631 result = getkey(hfsmp, cnid, key);
3632
3633 if (result == 0) {
3634 result = cat_lookupbykey(hfsmp, key, 0, 0, 0, NULL, attrp, NULL, NULL);
3635 }
3636 /*
3637 * Check for a raw file hardlink inode.
3638 * Fix up the parent id in the key if necessary.
3639 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
3640 */
3641 if ((result == 0) &&
3642 (key->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
3643 (attrp->ca_recflags & kHFSHasLinkChainMask)) {
3644 cnid_t nextlinkid = 0;
3645 cnid_t prevlinkid = 0;
3646 struct cat_desc linkdesc;
3647
3648 /*
3649 * Pick up the first link in the chain and get a descriptor for it.
3650 * This allows blind bulk access checks to work for hardlinks.
3651 */
3652 if ((cat_lookuplinkbyid(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
3653 (nextlinkid != 0)) {
3654 if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
3655 key->hfsPlus.parentID = linkdesc.cd_parentcnid;
3656 cat_releasedesc(&linkdesc);
3657 }
3658 }
3659 }
3660 return MacToVFSError(result);
3661 }
3662
3663
3664 /*
3665 * buildrecord - build a default catalog directory or file record
3666 */
3667 static void
3668 buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
3669 CatalogRecord *crp, u_int32_t *recordSize)
3670 {
3671 int type = attrp->ca_mode & S_IFMT;
3672 u_int32_t createtime = to_hfs_time(attrp->ca_itime);
3673
3674 if (std_hfs) {
3675 createtime = UTCToLocal(createtime);
3676 if (type == S_IFDIR) {
3677 bzero(crp, sizeof(HFSCatalogFolder));
3678 crp->recordType = kHFSFolderRecord;
3679 crp->hfsFolder.folderID = cnid;
3680 crp->hfsFolder.createDate = createtime;
3681 crp->hfsFolder.modifyDate = createtime;
3682 bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
3683 *recordSize = sizeof(HFSCatalogFolder);
3684 } else {
3685 bzero(crp, sizeof(HFSCatalogFile));
3686 crp->recordType = kHFSFileRecord;
3687 crp->hfsFile.fileID = cnid;
3688 crp->hfsFile.createDate = createtime;
3689 crp->hfsFile.modifyDate = createtime;
3690 bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
3691 bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
3692 *recordSize = sizeof(HFSCatalogFile);
3693 }
3694 } else {
3695 struct HFSPlusBSDInfo * bsdp = NULL;
3696
3697 if (type == S_IFDIR) {
3698 crp->recordType = kHFSPlusFolderRecord;
3699 crp->hfsPlusFolder.flags = attrp->ca_recflags;
3700 crp->hfsPlusFolder.valence = 0;
3701 crp->hfsPlusFolder.folderID = cnid;
3702 crp->hfsPlusFolder.createDate = createtime;
3703 crp->hfsPlusFolder.contentModDate = createtime;
3704 crp->hfsPlusFolder.attributeModDate = createtime;
3705 crp->hfsPlusFolder.accessDate = createtime;
3706 crp->hfsPlusFolder.backupDate = 0;
3707 crp->hfsPlusFolder.textEncoding = encoding;
3708 crp->hfsPlusFolder.folderCount = 0;
3709 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
3710 bsdp = &crp->hfsPlusFolder.bsdInfo;
3711 bsdp->special.linkCount = 1;
3712 *recordSize = sizeof(HFSPlusCatalogFolder);
3713 } else {
3714 crp->recordType = kHFSPlusFileRecord;
3715 crp->hfsPlusFile.flags = attrp->ca_recflags;
3716 crp->hfsPlusFile.reserved1 = 0;
3717 crp->hfsPlusFile.fileID = cnid;
3718 crp->hfsPlusFile.createDate = createtime;
3719 crp->hfsPlusFile.contentModDate = createtime;
3720 crp->hfsPlusFile.accessDate = createtime;
3721 crp->hfsPlusFile.attributeModDate = createtime;
3722 crp->hfsPlusFile.backupDate = 0;
3723 crp->hfsPlusFile.textEncoding = encoding;
3724 crp->hfsPlusFile.reserved2 = 0;
3725 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
3726 bsdp = &crp->hfsPlusFile.bsdInfo;
3727 /* BLK/CHR need to save the device info */
3728 if (type == S_IFBLK || type == S_IFCHR) {
3729 bsdp->special.rawDevice = attrp->ca_rdev;
3730 } else {
3731 bsdp->special.linkCount = 1;
3732 }
3733 bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
3734 *recordSize = sizeof(HFSPlusCatalogFile);
3735 }
3736 bsdp->ownerID = attrp->ca_uid;
3737 bsdp->groupID = attrp->ca_gid;
3738 bsdp->fileMode = attrp->ca_mode;
3739 bsdp->adminFlags = attrp->ca_flags >> 16;
3740 bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
3741 }
3742 }
3743
3744
3745 /*
3746 * builddesc - build a cnode descriptor from an HFS+ key
3747 */
3748 static int
3749 builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
3750 int isdir, struct cat_desc *descp)
3751 {
3752 int result = 0;
3753 unsigned char * nameptr;
3754 size_t bufsize;
3755 size_t utf8len;
3756 unsigned char tmpbuff[128];
3757
3758 /* guess a size... */
3759 bufsize = (3 * key->nodeName.length) + 1;
3760 if (bufsize >= sizeof(tmpbuff) - 1) {
3761 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
3762 } else {
3763 nameptr = &tmpbuff[0];
3764 }
3765
3766 result = utf8_encodestr(key->nodeName.unicode,
3767 key->nodeName.length * sizeof(UniChar),
3768 nameptr, (size_t *)&utf8len,
3769 bufsize, ':', 0);
3770
3771 if (result == ENAMETOOLONG) {
3772 bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
3773 key->nodeName.length * sizeof(UniChar),
3774 ':', 0);
3775 FREE(nameptr, M_TEMP);
3776 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
3777
3778 result = utf8_encodestr(key->nodeName.unicode,
3779 key->nodeName.length * sizeof(UniChar),
3780 nameptr, (size_t *)&utf8len,
3781 bufsize, ':', 0);
3782 }
3783 descp->cd_parentcnid = key->parentID;
3784 descp->cd_nameptr = (const u_int8_t *)vfs_addname((char *)nameptr, utf8len, 0, 0);
3785 descp->cd_namelen = utf8len;
3786 descp->cd_cnid = cnid;
3787 descp->cd_hint = hint;
3788 descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
3789 if (isdir)
3790 descp->cd_flags |= CD_ISDIR;
3791 descp->cd_encoding = encoding;
3792 if (nameptr != &tmpbuff[0]) {
3793 FREE(nameptr, M_TEMP);
3794 }
3795 return result;
3796 }
3797
3798
3799 /*
3800 * getbsdattr - get attributes in bsd format
3801 *
3802 */
3803 static void
3804 getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
3805 {
3806 int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
3807 const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
3808
3809 attrp->ca_recflags = crp->flags;
3810 attrp->ca_atime = to_bsd_time(crp->accessDate);
3811 attrp->ca_atimeondisk = attrp->ca_atime;
3812 attrp->ca_mtime = to_bsd_time(crp->contentModDate);
3813 attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
3814 attrp->ca_itime = to_bsd_time(crp->createDate);
3815 attrp->ca_btime = to_bsd_time(crp->backupDate);
3816
3817 if ((bsd->fileMode & S_IFMT) == 0) {
3818 attrp->ca_flags = 0;
3819 attrp->ca_uid = hfsmp->hfs_uid;
3820 attrp->ca_gid = hfsmp->hfs_gid;
3821 if (isDirectory) {
3822 attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
3823 } else {
3824 attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
3825 }
3826 attrp->ca_linkcount = 1;
3827 attrp->ca_rdev = 0;
3828 } else {
3829 attrp->ca_linkcount = 1; /* may be overridden below */
3830 attrp->ca_rdev = 0;
3831 attrp->ca_uid = bsd->ownerID;
3832 attrp->ca_gid = bsd->groupID;
3833 attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
3834 attrp->ca_mode = (mode_t)bsd->fileMode;
3835 switch (attrp->ca_mode & S_IFMT) {
3836 case S_IFCHR: /* fall through */
3837 case S_IFBLK:
3838 attrp->ca_rdev = bsd->special.rawDevice;
3839 break;
3840
3841 case S_IFDIR: /* fall through */
3842 case S_IFREG:
3843 /* Pick up the hard link count */
3844 if (bsd->special.linkCount > 0)
3845 attrp->ca_linkcount = bsd->special.linkCount;
3846 break;
3847 }
3848
3849 /*
3850 * Override the permissions as determined by the mount auguments
3851 * in ALMOST the same way unset permissions are treated but keep
3852 * track of whether or not the file or folder is hfs locked
3853 * by leaving the h_pflags field unchanged from what was unpacked
3854 * out of the catalog.
3855 */
3856 /*
3857 * This code was used to do UID translation with MNT_IGNORE_OWNERS
3858 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
3859 * at the VFS layer, so there is no need to do it here now; this also
3860 * allows VFS to let root see the real UIDs.
3861 *
3862 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
3863 * attrp->ca_uid = hfsmp->hfs_uid;
3864 * attrp->ca_gid = hfsmp->hfs_gid;
3865 * }
3866 */
3867 }
3868
3869 if (isDirectory) {
3870 if (!S_ISDIR(attrp->ca_mode)) {
3871 attrp->ca_mode &= ~S_IFMT;
3872 attrp->ca_mode |= S_IFDIR;
3873 }
3874 attrp->ca_entries = ((const HFSPlusCatalogFolder *)crp)->valence;
3875 attrp->ca_dircount = ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && (attrp->ca_recflags & kHFSHasFolderCountMask)) ?
3876 ((const HFSPlusCatalogFolder *)crp)->folderCount : 0;
3877
3878 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3879 if (((const HFSPlusCatalogFolder *)crp)->userInfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
3880 attrp->ca_flags |= UF_HIDDEN;
3881 } else {
3882 /* Keep IMMUTABLE bits in sync with HFS locked flag */
3883 if (crp->flags & kHFSFileLockedMask) {
3884 /* The file's supposed to be locked:
3885 Make sure at least one of the IMMUTABLE bits is set: */
3886 if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
3887 attrp->ca_flags |= UF_IMMUTABLE;
3888 } else {
3889 /* The file's supposed to be unlocked: */
3890 attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
3891 }
3892 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
3893 if (crp->userInfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
3894 attrp->ca_flags |= UF_HIDDEN;
3895 /* get total blocks (both forks) */
3896 attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
3897
3898 /* On HFS+ the ThreadExists flag must always be set. */
3899 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
3900 attrp->ca_recflags |= kHFSThreadExistsMask;
3901
3902 /* Pick up the hardlink first link, if any. */
3903 attrp->ca_firstlink = (attrp->ca_recflags & kHFSHasLinkChainMask) ? crp->hl_firstLinkID : 0;
3904 }
3905
3906 attrp->ca_fileid = crp->fileID;
3907
3908 bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
3909 }
3910
3911 /*
3912 * promotekey - promote hfs key to hfs plus key
3913 *
3914 */
3915 static void
3916 promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
3917 HFSPlusCatalogKey *keyp, u_int32_t *encoding)
3918 {
3919 hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
3920 u_int32_t uniCount;
3921 int error;
3922
3923 *encoding = hfsmp->hfs_encoding;
3924
3925 error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
3926 kHFSPlusMaxFileNameChars, &uniCount);
3927 /*
3928 * When an HFS name cannot be encoded with the current
3929 * encoding use MacRoman as a fallback.
3930 */
3931 if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
3932 *encoding = 0;
3933 (void) mac_roman_to_unicode(hfskey->nodeName,
3934 keyp->nodeName.unicode,
3935 kHFSPlusMaxFileNameChars,
3936 &uniCount);
3937 }
3938
3939 keyp->nodeName.length = uniCount;
3940 keyp->parentID = hfskey->parentID;
3941 }
3942
3943 /*
3944 * promotefork - promote hfs fork info to hfs plus
3945 *
3946 */
3947 static void
3948 promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
3949 int resource, struct cat_fork * forkp)
3950 {
3951 struct HFSPlusExtentDescriptor *xp;
3952 u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
3953
3954 bzero(forkp, sizeof(*forkp));
3955 xp = &forkp->cf_extents[0];
3956 if (resource) {
3957 forkp->cf_size = filep->rsrcLogicalSize;
3958 forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
3959 forkp->cf_bytesread = 0;
3960 forkp->cf_vblocks = 0;
3961 xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
3962 xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
3963 xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
3964 xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
3965 xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
3966 xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
3967 } else {
3968 forkp->cf_size = filep->dataLogicalSize;
3969 forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
3970 forkp->cf_bytesread = 0;
3971 forkp->cf_vblocks = 0;
3972 xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
3973 xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
3974 xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
3975 xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
3976 xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
3977 xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
3978 }
3979 }
3980
3981 /*
3982 * promoteattr - promote standard hfs catalog attributes to hfs plus
3983 *
3984 */
3985 static void
3986 promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
3987 {
3988 u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
3989
3990 if (dataPtr->recordType == kHFSFolderRecord) {
3991 const struct HFSCatalogFolder * folder;
3992
3993 folder = (const struct HFSCatalogFolder *) dataPtr;
3994 crp->recordType = kHFSPlusFolderRecord;
3995 crp->flags = folder->flags;
3996 crp->fileID = folder->folderID;
3997 crp->createDate = LocalToUTC(folder->createDate);
3998 crp->contentModDate = LocalToUTC(folder->modifyDate);
3999 crp->backupDate = LocalToUTC(folder->backupDate);
4000 crp->reserved1 = folder->valence;
4001 crp->reserved2 = 0;
4002 bcopy(&folder->userInfo, &crp->userInfo, 32);
4003 } else /* file */ {
4004 const struct HFSCatalogFile * file;
4005
4006 file = (const struct HFSCatalogFile *) dataPtr;
4007 crp->recordType = kHFSPlusFileRecord;
4008 crp->flags = file->flags;
4009 crp->fileID = file->fileID;
4010 crp->createDate = LocalToUTC(file->createDate);
4011 crp->contentModDate = LocalToUTC(file->modifyDate);
4012 crp->backupDate = LocalToUTC(file->backupDate);
4013 crp->reserved1 = 0;
4014 crp->reserved2 = 0;
4015 bcopy(&file->userInfo, &crp->userInfo, 16);
4016 bcopy(&file->finderInfo, &crp->finderInfo, 16);
4017 crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
4018 crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
4019 }
4020 crp->textEncoding = 0;
4021 crp->attributeModDate = crp->contentModDate;
4022 crp->accessDate = crp->contentModDate;
4023 bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
4024 }
4025
4026 /*
4027 * Build a catalog node thread record from a catalog key
4028 * and return the size of the record.
4029 */
4030 static int
4031 buildthread(void *keyp, void *recp, int std_hfs, int directory)
4032 {
4033 int size = 0;
4034
4035 if (std_hfs) {
4036 HFSCatalogKey *key = (HFSCatalogKey *)keyp;
4037 HFSCatalogThread *rec = (HFSCatalogThread *)recp;
4038
4039 size = sizeof(HFSCatalogThread);
4040 bzero(rec, size);
4041 if (directory)
4042 rec->recordType = kHFSFolderThreadRecord;
4043 else
4044 rec->recordType = kHFSFileThreadRecord;
4045 rec->parentID = key->parentID;
4046 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
4047
4048 } else /* HFS+ */ {
4049 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
4050 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
4051
4052 size = sizeof(HFSPlusCatalogThread);
4053 if (directory)
4054 rec->recordType = kHFSPlusFolderThreadRecord;
4055 else
4056 rec->recordType = kHFSPlusFileThreadRecord;
4057 rec->reserved = 0;
4058 rec->parentID = key->parentID;
4059 bcopy(&key->nodeName, &rec->nodeName,
4060 sizeof(UniChar) * (key->nodeName.length + 1));
4061
4062 /* HFS Plus has varaible sized thread records */
4063 size -= (sizeof(rec->nodeName.unicode) -
4064 (rec->nodeName.length * sizeof(UniChar)));
4065 }
4066
4067 return (size);
4068 }
4069
4070 /*
4071 * Build a catalog node thread key.
4072 */
4073 static void
4074 buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
4075 {
4076 if (std_hfs) {
4077 key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
4078 key->hfs.reserved = 0;
4079 key->hfs.parentID = parentID;
4080 key->hfs.nodeName[0] = 0;
4081 } else {
4082 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
4083 key->hfsPlus.parentID = parentID;
4084 key->hfsPlus.nodeName.length = 0;
4085 }
4086 }
4087
4088 /*
4089 * Extract the text encoding from a catalog node record.
4090 */
4091 static u_int32_t
4092 getencoding(const CatalogRecord *crp)
4093 {
4094 u_int32_t encoding;
4095
4096 if (crp->recordType == kHFSPlusFolderRecord)
4097 encoding = crp->hfsPlusFolder.textEncoding;
4098 else if (crp->recordType == kHFSPlusFileRecord)
4099 encoding = crp->hfsPlusFile.textEncoding;
4100 else
4101 encoding = 0;
4102
4103 return (encoding);
4104 }
4105
4106 /*
4107 * Extract the CNID from a catalog node record.
4108 */
4109 static cnid_t
4110 getcnid(const CatalogRecord *crp)
4111 {
4112 cnid_t cnid = 0;
4113
4114 switch (crp->recordType) {
4115 case kHFSFolderRecord:
4116 cnid = crp->hfsFolder.folderID;
4117 break;
4118 case kHFSFileRecord:
4119 cnid = crp->hfsFile.fileID;
4120 break;
4121 case kHFSPlusFolderRecord:
4122 cnid = crp->hfsPlusFolder.folderID;
4123 break;
4124 case kHFSPlusFileRecord:
4125 cnid = crp->hfsPlusFile.fileID;
4126 break;
4127 default:
4128 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp);
4129 break;
4130 }
4131
4132 return (cnid);
4133 }
4134
4135 /*
4136 * Extract the parent ID from a catalog node record.
4137 */
4138 static cnid_t
4139 getparentcnid(const CatalogRecord *recp)
4140 {
4141 cnid_t cnid = 0;
4142
4143 switch (recp->recordType) {
4144 case kHFSFileThreadRecord:
4145 case kHFSFolderThreadRecord:
4146 cnid = recp->hfsThread.parentID;
4147 break;
4148
4149 case kHFSPlusFileThreadRecord:
4150 case kHFSPlusFolderThreadRecord:
4151 cnid = recp->hfsPlusThread.parentID;
4152 break;
4153 default:
4154 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp);
4155 break;
4156 }
4157
4158 return (cnid);
4159 }
4160
4161 /*
4162 * Determine if a catalog node record is a directory.
4163 */
4164 static int
4165 isadir(const CatalogRecord *crp)
4166 {
4167 return (crp->recordType == kHFSFolderRecord ||
4168 crp->recordType == kHFSPlusFolderRecord);
4169 }
4170