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