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