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