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