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