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