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