]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_catalog.c
xnu-2422.90.20.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
1 /*
2 * Copyright (c) 2000-2013 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 struct cat_fork * s_datafork;
71 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 struct cat_fork *dataforkp, 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 MarkVCBDirty(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_volume_inconsistent (hfsmp);
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_volume_inconsistent (hfsmp);
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_volume_inconsistent (hfsmp);
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_volume_inconsistent(hfsmp);
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_volume_inconsistent(hfsmp);
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_volume_inconsistent(hfsmp);
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_volume_inconsistent(hfsmp);
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_volume_inconsistent(hfsmp);
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 struct cat_fork *dataforkp, 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 struct cat_fork *dataforkp, 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 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. We can change it since we hold the catalog lock.
2464 */
2465 nextCNID = hfsmp->vcbNxtCNID;
2466 if (nextCNID == 0xFFFFFFFF) {
2467 hfs_lock_mount (hfsmp);
2468 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
2469 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
2470 hfs_unlock_mount(hfsmp);
2471 } else {
2472 hfsmp->vcbNxtCNID++;
2473 }
2474 MarkVCBDirty(hfsmp);
2475
2476 /* Get space for iterator, key and data */
2477 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
2478 bto->iterator.hint.nodeNum = 0;
2479 rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
2480
2481 result = buildkey(hfsmp, descp, &bto->key, 0);
2482 if (result) {
2483 printf("hfs: cat_createlink: err %d from buildkey\n", result);
2484 goto exit;
2485 }
2486
2487 /* This is our only chance to set the encoding (other than a rename). */
2488 encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
2489
2490 /* Insert the thread record first. */
2491 datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
2492 btdata.bufferAddress = &bto->data;
2493 btdata.itemSize = datalen;
2494 btdata.itemCount = 1;
2495
2496 for (;;) {
2497 buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
2498
2499 /*
2500 * If the CNID wraparound bit is set, then we need to validate if there
2501 * is a cnode in the hash already with this ID (even if it no longer exists
2502 * on disk). If so, then just skip this ID and move on to the next one.
2503 */
2504 if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
2505 /* Verify that the CNID does not already exist in the cnode hash... */
2506 if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) {
2507 /* It was found in the cnode hash!*/
2508 result = btExists;
2509 }
2510 }
2511
2512 if (result == 0) {
2513 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
2514 }
2515
2516 if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
2517 /*
2518 * Allow CNIDs on HFS Plus volumes to wrap around
2519 */
2520 if (++nextCNID < kHFSFirstUserCatalogNodeID) {
2521 nextCNID = kHFSFirstUserCatalogNodeID;
2522 }
2523 continue;
2524 }
2525 if (result == 0) {
2526 thread_inserted = 1;
2527 }
2528 break;
2529 }
2530 if (result)
2531 goto exit;
2532
2533 /*
2534 * CNID is now established. If we have wrapped then
2535 * update the vcbNxtCNID.
2536 */
2537 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
2538 hfsmp->vcbNxtCNID = nextCNID + 1;
2539 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
2540 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
2541 }
2542 }
2543
2544 /*
2545 * Now insert the link record.
2546 */
2547 buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen);
2548
2549 bto->data.hfsPlusFile.hl_prevLinkID = 0;
2550 bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid;
2551 bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref;
2552
2553 /* For directory hard links, create alias in resource fork */
2554 if (descp->cd_flags & CD_ISDIR) {
2555 if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) {
2556 goto exit;
2557 }
2558 alias_allocated = 1;
2559 }
2560 btdata.bufferAddress = &bto->data;
2561 btdata.itemSize = datalen;
2562 btdata.itemCount = 1;
2563
2564 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
2565
2566 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
2567 if (result) {
2568 if (result == btExists)
2569 result = EEXIST;
2570 goto exit;
2571 }
2572 if (linkfileid != NULL) {
2573 *linkfileid = nextCNID;
2574 }
2575 exit:
2576 if (result) {
2577 if (thread_inserted) {
2578 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result), hfsmp->vcbVN);
2579
2580 buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
2581 if (BTDeleteRecord(fcb, &bto->iterator)) {
2582 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
2583 hfs_mark_volume_inconsistent(hfsmp);
2584 }
2585 }
2586 if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
2587 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
2588 rsrcforkp->extents[0].blockCount, 0);
2589 rsrcforkp->extents[0].startBlock = 0;
2590 rsrcforkp->extents[0].blockCount = 0;
2591 }
2592 }
2593 (void) BTFlushPath(fcb);
2594 FREE(bto, M_TEMP);
2595
2596 return MacToVFSError(result);
2597 }
2598
2599 /* Directory hard links are visible as aliases on pre-Leopard systems and
2600 * as normal directories on Leopard or later. All directory hard link aliases
2601 * have the same resource fork content except for the three uniquely
2602 * identifying values that are updated in the resource fork data when the alias
2603 * is created. The following array is the constant resource fork data used
2604 * only for creating directory hard link aliases.
2605 */
2606 static const char hfs_dirlink_alias_rsrc[] = {
2607 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2609 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2610 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2611 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2612 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2613 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2615 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2616 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2617 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2618 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2619 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2620 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2621 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2622 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2623 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2625 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2627 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2629 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2630 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2631 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2632 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2633 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2634 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2635 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2636 };
2637
2638 /* Constants for directory hard link alias */
2639 enum {
2640 /* Size of resource fork data array for directory hard link alias */
2641 kHFSAliasSize = 0x1d0,
2642
2643 /* Volume type for ejectable devices like disk image */
2644 kHFSAliasVolTypeEjectable = 0x5,
2645
2646 /* Offset for volume create date, in Mac OS local time */
2647 kHFSAliasVolCreateDateOffset = 0x12a,
2648
2649 /* Offset for the type of volume */
2650 kHFSAliasVolTypeOffset = 0x130,
2651
2652 /* Offset for folder ID of the parent directory of the directory inode */
2653 kHFSAliasParentIDOffset = 0x132,
2654
2655 /* Offset for folder ID of the directory inode */
2656 kHFSAliasTargetIDOffset = 0x176,
2657 };
2658
2659 /* Create and write an alias that points at the directory represented by given
2660 * inode number on the same volume. Directory hard links are visible as
2661 * aliases in pre-Leopard systems and this function creates these aliases.
2662 *
2663 * Note: This code is very specific to creating alias for the purpose
2664 * of directory hard links only, and should not be generalized.
2665 */
2666 static int
2667 cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
2668 {
2669 struct buf *bp;
2670 daddr64_t blkno;
2671 u_int32_t blkcount;
2672 int blksize;
2673 int sectorsize;
2674 int result;
2675 HFSPlusForkData *rsrcforkp;
2676 char *alias;
2677 uint32_t *valptr;
2678
2679 rsrcforkp = &(crp->resourceFork);
2680
2681 blksize = hfsmp->blockSize;
2682 blkcount = howmany(kHFSAliasSize, blksize);
2683 sectorsize = hfsmp->hfs_logical_block_size;
2684 bzero(rsrcforkp, sizeof(HFSPlusForkData));
2685
2686 /* Allocate some disk space for the alias content. */
2687 result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
2688 HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE,
2689 &rsrcforkp->extents[0].startBlock,
2690 &rsrcforkp->extents[0].blockCount);
2691 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2692 if (result == dskFulErr ) {
2693 result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
2694 HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE | HFS_ALLOC_FLUSHTXN,
2695 &rsrcforkp->extents[0].startBlock,
2696 &rsrcforkp->extents[0].blockCount);
2697 }
2698 if (result) {
2699 rsrcforkp->extents[0].startBlock = 0;
2700 goto exit;
2701 }
2702
2703 /* Acquire a buffer cache block for our block. */
2704 blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
2705 blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
2706
2707 bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META);
2708 if (hfsmp->jnl) {
2709 journal_modify_block_start(hfsmp->jnl, bp);
2710 }
2711
2712 /* Generate alias content */
2713 alias = (char *)buf_dataptr(bp);
2714 bzero(alias, buf_size(bp));
2715 bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
2716
2717 /* Set the volume create date, local time in Mac OS format */
2718 valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
2719 *valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
2720
2721 /* If the file system is on a virtual device like disk image,
2722 * update the volume type to be ejectable device.
2723 */
2724 if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) {
2725 *(uint16_t *)(alias + kHFSAliasVolTypeOffset) =
2726 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable);
2727 }
2728
2729 /* Set id of the parent of the target directory */
2730 valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset);
2731 *valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid);
2732
2733 /* Set id of the target directory */
2734 valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset);
2735 *valptr = OSSwapHostToBigInt32(inode_num);
2736
2737 /* Write alias content to disk. */
2738 if (hfsmp->jnl) {
2739 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
2740 } else if ((result = buf_bwrite(bp))) {
2741 goto exit;
2742 }
2743
2744 /* Finish initializing the fork data. */
2745 rsrcforkp->logicalSize = kHFSAliasSize;
2746 rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
2747
2748 exit:
2749 if (result && rsrcforkp->extents[0].startBlock != 0) {
2750 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount, 0);
2751 rsrcforkp->extents[0].startBlock = 0;
2752 rsrcforkp->extents[0].blockCount = 0;
2753 rsrcforkp->logicalSize = 0;
2754 rsrcforkp->totalBlocks = 0;
2755 }
2756 return (result);
2757 }
2758
2759 /*
2760 * cat_deletelink - delete a link from the catalog
2761 */
2762 int
2763 cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
2764 {
2765 struct HFSPlusCatalogFile file;
2766 struct cat_attr cattr;
2767 uint32_t totalBlocks;
2768 int i;
2769 int result;
2770
2771 bzero(&file, sizeof (file));
2772 bzero(&cattr, sizeof (cattr));
2773 cattr.ca_fileid = descp->cd_cnid;
2774
2775 /* Directory links have alias content to remove. */
2776 if (descp->cd_flags & CD_ISDIR) {
2777 FCB * fcb;
2778 BTreeIterator * iterator;
2779 struct FSBufferDescriptor btdata;
2780
2781 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2782
2783 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2784 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
2785 iterator->hint.nodeNum = 0;
2786
2787 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
2788 goto exit;
2789 }
2790 BDINIT(btdata, &file);
2791
2792 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
2793 goto exit;
2794 }
2795 }
2796
2797 result = cat_delete(hfsmp, descp, &cattr);
2798
2799 if ((result == 0) &&
2800 (descp->cd_flags & CD_ISDIR) &&
2801 (file.recordType == kHFSPlusFileRecord)) {
2802
2803 totalBlocks = file.resourceFork.totalBlocks;
2804
2805 for (i = 0; (i < 8) && (totalBlocks > 0); i++) {
2806 if ((file.resourceFork.extents[i].blockCount == 0) &&
2807 (file.resourceFork.extents[i].startBlock == 0)) {
2808 break;
2809 }
2810
2811 (void) BlockDeallocate(hfsmp,
2812 file.resourceFork.extents[i].startBlock,
2813 file.resourceFork.extents[i].blockCount, 0);
2814
2815 totalBlocks -= file.resourceFork.extents[i].blockCount;
2816 file.resourceFork.extents[i].startBlock = 0;
2817 file.resourceFork.extents[i].blockCount = 0;
2818 }
2819 }
2820 exit:
2821 return (result);
2822 }
2823
2824
2825 /*
2826 * Callback to collect directory entries.
2827 * Called with readattr_state for each item in a directory.
2828 */
2829 struct readattr_state {
2830 struct hfsmount *hfsmp;
2831 struct cat_entrylist *list;
2832 cnid_t dir_cnid;
2833 int stdhfs;
2834 int error;
2835 };
2836
2837 static int
2838 getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
2839 struct readattr_state *state)
2840 {
2841 struct cat_entrylist *list = state->list;
2842 struct hfsmount *hfsmp = state->hfsmp;
2843 struct cat_entry *cep;
2844 cnid_t parentcnid;
2845
2846 if (list->realentries >= list->maxentries)
2847 return (0); /* stop */
2848
2849 parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
2850
2851 switch(rec->recordType) {
2852 case kHFSPlusFolderRecord:
2853 case kHFSPlusFileRecord:
2854 #if CONFIG_HFS_STD
2855 case kHFSFolderRecord:
2856 case kHFSFileRecord:
2857 #endif
2858 if (parentcnid != state->dir_cnid) {
2859 state->error = ENOENT;
2860 return (0); /* stop */
2861 }
2862 break;
2863 default:
2864 state->error = ENOENT;
2865 return (0); /* stop */
2866 }
2867
2868 /* Hide the private system directories and journal files */
2869 if (parentcnid == kHFSRootFolderID) {
2870 if (rec->recordType == kHFSPlusFolderRecord) {
2871 if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
2872 rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2873 list->skipentries++;
2874 return (1); /* continue */
2875 }
2876 }
2877 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
2878 (rec->recordType == kHFSPlusFileRecord) &&
2879 ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
2880 (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
2881 list->skipentries++;
2882 return (1); /* continue */
2883 }
2884 }
2885
2886 cep = &list->entry[list->realentries++];
2887
2888 if (state->stdhfs == 0) {
2889 getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
2890 builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
2891 isadir(rec), &cep->ce_desc);
2892
2893 if (rec->recordType == kHFSPlusFileRecord) {
2894 cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
2895 cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
2896 cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
2897 cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
2898
2899 /* Save link reference for later processing. */
2900 if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
2901 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
2902 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
2903 } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
2904 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
2905 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
2906 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
2907 }
2908 }
2909 }
2910 #if CONFIG_HFS_STD
2911 else {
2912 struct HFSPlusCatalogFile cnoderec;
2913 HFSPlusCatalogKey * pluskey;
2914 u_int32_t encoding;
2915
2916 promoteattr(hfsmp, rec, &cnoderec);
2917 getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
2918
2919 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
2920 promotekey(hfsmp, (const HFSCatalogKey *)key, pluskey, &encoding);
2921 builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
2922 FREE(pluskey, M_TEMP);
2923
2924 if (rec->recordType == kHFSFileRecord) {
2925 int blksize = HFSTOVCB(hfsmp)->blockSize;
2926
2927 cep->ce_datasize = rec->hfsFile.dataLogicalSize;
2928 cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
2929 cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
2930 cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
2931 }
2932 }
2933 #endif
2934
2935 return (list->realentries < list->maxentries);
2936 }
2937
2938 /*
2939 * Pack a cat_entrylist buffer with attributes from the catalog
2940 *
2941 * Note: index is zero relative
2942 */
2943 int
2944 cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
2945 {
2946 FCB* fcb;
2947 CatalogKey * key;
2948 BTreeIterator * iterator;
2949 struct readattr_state state;
2950 cnid_t parentcnid;
2951 int i;
2952 int std_hfs;
2953 int index;
2954 int have_key;
2955 int result = 0;
2956
2957 ce_list->realentries = 0;
2958
2959 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
2960 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
2961 parentcnid = dirhint->dh_desc.cd_parentcnid;
2962
2963 state.hfsmp = hfsmp;
2964 state.list = ce_list;
2965 state.dir_cnid = parentcnid;
2966 state.stdhfs = std_hfs;
2967 state.error = 0;
2968
2969 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2970 bzero(iterator, sizeof(*iterator));
2971 key = (CatalogKey *)&iterator->key;
2972 have_key = 0;
2973 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
2974 index = dirhint->dh_index + 1;
2975
2976 /*
2977 * Attempt to build a key from cached filename
2978 */
2979 if (dirhint->dh_desc.cd_namelen != 0) {
2980 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2981 have_key = 1;
2982 }
2983 }
2984
2985 /*
2986 * If the last entry wasn't cached then position the btree iterator
2987 */
2988 if ((index == 0) || !have_key) {
2989 /*
2990 * Position the iterator at the directory's thread record.
2991 * (i.e. just before the first entry)
2992 */
2993 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
2994 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
2995 if (result) {
2996 result = MacToVFSError(result);
2997 goto exit;
2998 }
2999
3000 /*
3001 * Iterate until we reach the entry just
3002 * before the one we want to start with.
3003 */
3004 if (index > 0) {
3005 struct position_state ps;
3006
3007 ps.error = 0;
3008 ps.count = 0;
3009 ps.index = index;
3010 ps.parentID = dirhint->dh_desc.cd_parentcnid;
3011 ps.hfsmp = hfsmp;
3012
3013 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
3014 (IterateCallBackProcPtr)cat_findposition, &ps);
3015 if (ps.error)
3016 result = ps.error;
3017 else
3018 result = MacToVFSError(result);
3019 if (result) {
3020 result = MacToVFSError(result);
3021 goto exit;
3022 }
3023 }
3024 }
3025
3026 /* Fill list with entries starting at iterator->key. */
3027 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
3028 (IterateCallBackProcPtr)getentriesattr_callback, &state);
3029
3030 if (state.error)
3031 result = state.error;
3032 else if (ce_list->realentries == 0)
3033 result = ENOENT;
3034 else
3035 result = MacToVFSError(result);
3036
3037 if (std_hfs)
3038 goto exit;
3039
3040 /*
3041 * Resolve any hard links.
3042 */
3043 for (i = 0; i < (int)ce_list->realentries; ++i) {
3044 struct FndrFileInfo *fip;
3045 struct cat_entry *cep;
3046 struct HFSPlusCatalogFile filerec;
3047 int isdirlink = 0;
3048 int isfilelink = 0;
3049
3050 cep = &ce_list->entry[i];
3051 if (cep->ce_attr.ca_linkref == 0)
3052 continue;
3053
3054 /* Note: Finder info is still in Big Endian */
3055 fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
3056
3057 if (S_ISREG(cep->ce_attr.ca_mode) &&
3058 (SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
3059 (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
3060 isfilelink = 1;
3061 }
3062 if (S_ISREG(cep->ce_attr.ca_mode) &&
3063 (SWAP_BE32(fip->fdType) == kHFSAliasType) &&
3064 (SWAP_BE32(fip->fdCreator) == kHFSAliasCreator) &&
3065 (cep->ce_attr.ca_recflags & kHFSHasLinkChainMask)) {
3066 isdirlink = 1;
3067 }
3068 if (isfilelink || isdirlink) {
3069 if (cat_resolvelink(hfsmp, cep->ce_attr.ca_linkref, isdirlink, &filerec) != 0)
3070 continue;
3071 /* Repack entry from inode record. */
3072 getbsdattr(hfsmp, &filerec, &cep->ce_attr);
3073 cep->ce_datasize = filerec.dataFork.logicalSize;
3074 cep->ce_datablks = filerec.dataFork.totalBlocks;
3075 cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
3076 cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
3077 }
3078 }
3079 exit:
3080 FREE(iterator, M_TEMP);
3081
3082 return MacToVFSError(result);
3083 }
3084
3085 #define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
3086
3087 /*
3088 * Callback to pack directory entries.
3089 * Called with packdirentry_state for each item in a directory.
3090 */
3091
3092 /* Hard link information collected during cat_getdirentries. */
3093 struct linkinfo {
3094 u_int32_t link_ref;
3095 user_addr_t dirent_addr;
3096 };
3097 typedef struct linkinfo linkinfo_t;
3098
3099 /* State information for the getdirentries_callback function. */
3100 struct packdirentry_state {
3101 int cbs_flags; /* VNODE_READDIR_* flags */
3102 u_int32_t cbs_parentID;
3103 u_int32_t cbs_index;
3104 uio_t cbs_uio;
3105 ExtendedVCB * cbs_hfsmp;
3106 int cbs_result;
3107 int32_t cbs_nlinks;
3108 int32_t cbs_maxlinks;
3109 linkinfo_t * cbs_linkinfo;
3110 struct cat_desc * cbs_desc;
3111 u_int8_t * cbs_namebuf;
3112 /*
3113 * The following fields are only used for NFS readdir, which
3114 * uses the next file id as the seek offset of each entry.
3115 */
3116 struct direntry * cbs_direntry;
3117 struct direntry * cbs_prevdirentry;
3118 u_int32_t cbs_previlinkref;
3119 Boolean cbs_hasprevdirentry;
3120 Boolean cbs_eof;
3121 };
3122
3123 /*
3124 * getdirentries callback for HFS Plus directories.
3125 */
3126 static int
3127 getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
3128 struct packdirentry_state *state)
3129 {
3130 struct hfsmount *hfsmp;
3131 const CatalogName *cnp;
3132 cnid_t curID;
3133 OSErr result;
3134 struct dirent catent;
3135 struct direntry * entry = NULL;
3136 time_t itime;
3137 u_int32_t ilinkref = 0;
3138 u_int32_t curlinkref = 0;
3139 cnid_t cnid;
3140 int hide = 0;
3141 u_int8_t type = DT_UNKNOWN;
3142 u_int8_t is_mangled = 0;
3143 u_int8_t is_link = 0;
3144 u_int8_t *nameptr;
3145 user_addr_t uiobase = USER_ADDR_NULL;
3146 size_t namelen = 0;
3147 size_t maxnamelen;
3148 size_t uiosize = 0;
3149 caddr_t uioaddr;
3150 Boolean stop_after_pack = false;
3151
3152 hfsmp = state->cbs_hfsmp;
3153 curID = ckp->hfsPlus.parentID;
3154
3155 /* We're done when parent directory changes */
3156 if (state->cbs_parentID != curID) {
3157 /*
3158 * If the parent ID is different from curID this means we've hit
3159 * the EOF for the directory. To help future callers, we mark
3160 * the cbs_eof boolean. However, we should only mark the EOF
3161 * boolean if we're about to return from this function.
3162 *
3163 * This is because this callback function does its own uiomove
3164 * to get the data to userspace. If we set the boolean before determining
3165 * whether or not the current entry has enough room to write its
3166 * data to userland, we could fool the callers of this catalog function
3167 * into thinking they've hit EOF earlier than they really would have.
3168 * In that case, we'd know that we have more entries to process and
3169 * send to userland, but we didn't have enough room.
3170 *
3171 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3172 * about to return and won't write any new data back
3173 * to userland. In the stop_after_pack case, we'll set this boolean
3174 * regardless, so it's slightly safer to let that logic mark the boolean,
3175 * especially since it's closer to the return of this function.
3176 */
3177
3178 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
3179 /* The last record has not been returned yet, so we
3180 * want to stop after packing the last item
3181 */
3182 if (state->cbs_hasprevdirentry) {
3183 stop_after_pack = true;
3184 } else {
3185 state->cbs_eof = true;
3186 state->cbs_result = ENOENT;
3187 return (0); /* stop */
3188 }
3189 } else {
3190 state->cbs_eof = true;
3191 state->cbs_result = ENOENT;
3192 return (0); /* stop */
3193 }
3194 }
3195
3196 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
3197 entry = state->cbs_direntry;
3198 nameptr = (u_int8_t *)&entry->d_name[0];
3199 if (state->cbs_flags & VNODE_READDIR_NAMEMAX) {
3200 /*
3201 * The NFS server sometimes needs to make filenames fit in
3202 * NAME_MAX bytes (since its client may not be able to
3203 * handle a longer name). In that case, NFS will ask us
3204 * to mangle the name to keep it short enough.
3205 */
3206 maxnamelen = NAME_MAX + 1;
3207 } else {
3208 maxnamelen = sizeof(entry->d_name);
3209 }
3210 } else {
3211 nameptr = (u_int8_t *)&catent.d_name[0];
3212 maxnamelen = sizeof(catent.d_name);
3213 }
3214
3215 if ((state->cbs_flags & VNODE_READDIR_EXTENDED) && stop_after_pack) {
3216 /* The last item returns a non-zero invalid cookie */
3217 cnid = INT_MAX;
3218 } else {
3219 switch(crp->recordType) {
3220 case kHFSPlusFolderRecord:
3221 type = DT_DIR;
3222 cnid = crp->hfsPlusFolder.folderID;
3223 /* Hide our private system directories. */
3224 if (curID == kHFSRootFolderID) {
3225 if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
3226 cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
3227 hide = 1;
3228 }
3229 }
3230 break;
3231 case kHFSPlusFileRecord:
3232 itime = to_bsd_time(crp->hfsPlusFile.createDate);
3233 type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
3234 cnid = crp->hfsPlusFile.fileID;
3235 /*
3236 * When a hardlink link is encountered save its link ref.
3237 */
3238 if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
3239 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
3240 ((itime == (time_t)hfsmp->hfs_itime) ||
3241 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
3242 /* If link ref is inode's file id then use it directly. */
3243 if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) {
3244 cnid = crp->hfsPlusFile.hl_linkReference;
3245 } else {
3246 ilinkref = crp->hfsPlusFile.hl_linkReference;
3247 }
3248 is_link =1;
3249 } else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
3250 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
3251 (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
3252 (crp->hfsPlusFile.hl_linkReference >= kHFSFirstUserCatalogNodeID) &&
3253 ((itime == (time_t)hfsmp->hfs_itime) ||
3254 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
3255 /* A directory's link resolves to a directory. */
3256 type = DT_DIR;
3257 /* A directory's link ref is always inode's file id. */
3258 cnid = crp->hfsPlusFile.hl_linkReference;
3259 is_link = 1;
3260 }
3261 /* Hide the journal files */
3262 if ((curID == kHFSRootFolderID) &&
3263 ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
3264 ((cnid == hfsmp->hfs_jnlfileid) ||
3265 (cnid == hfsmp->hfs_jnlinfoblkid))) {
3266 hide = 1;
3267 }
3268 break;
3269 default:
3270 return (0); /* stop */
3271 };
3272
3273 cnp = (const CatalogName*) &ckp->hfsPlus.nodeName;
3274
3275 namelen = cnp->ustr.length;
3276 /*
3277 * For MacRoman encoded names, assume that its ascii and
3278 * convert it directly in an attempt to avoid the more
3279 * expensive utf8_encodestr conversion.
3280 */
3281 if ((namelen < maxnamelen) && (crp->hfsPlusFile.textEncoding == 0)) {
3282 int i;
3283 u_int16_t ch;
3284 const u_int16_t *chp;
3285
3286 chp = &cnp->ustr.unicode[0];
3287 for (i = 0; i < (int)namelen; ++i) {
3288 ch = *chp++;
3289 if (ch > 0x007f || ch == 0x0000) {
3290 /* Perform expensive utf8_encodestr conversion */
3291 goto encodestr;
3292 }
3293 nameptr[i] = (ch == '/') ? ':' : (u_int8_t)ch;
3294 }
3295 nameptr[namelen] = '\0';
3296 result = 0;
3297 } else {
3298 encodestr:
3299 result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar),
3300 nameptr, &namelen, maxnamelen, ':', 0);
3301 }
3302
3303 /* Check result returned from encoding the filename to utf8 */
3304 if (result == ENAMETOOLONG) {
3305 /*
3306 * If we were looking at a catalog record for a hardlink (not the inode),
3307 * then we want to use its link ID as opposed to the inode ID for
3308 * a mangled name. For all other cases, they are the same. Note that
3309 * due to the way directory hardlinks are implemented, the actual link
3310 * is going to be counted as a file record, so we can catch both
3311 * with is_link.
3312 */
3313 cnid_t linkid = cnid;
3314 if (is_link) {
3315 linkid = crp->hfsPlusFile.fileID;
3316 }
3317
3318 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
3319 cnp->ustr.unicode, maxnamelen,
3320 (ByteCount*)&namelen, nameptr, linkid);
3321 is_mangled = 1;
3322 }
3323 }
3324
3325 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
3326 /*
3327 * The index is 1 relative and includes "." and ".."
3328 *
3329 * Also stuff the cnid in the upper 32 bits of the cookie.
3330 * The cookie is stored to the previous entry, which will
3331 * be packed and copied this time
3332 */
3333 state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
3334 uiosize = state->cbs_prevdirentry->d_reclen;
3335 uioaddr = (caddr_t) state->cbs_prevdirentry;
3336 } else {
3337 catent.d_type = type;
3338 catent.d_namlen = namelen;
3339 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
3340 if (hide)
3341 catent.d_fileno = 0; /* file number = 0 means skip entry */
3342 else
3343 catent.d_fileno = cnid;
3344 uioaddr = (caddr_t) &catent;
3345 }
3346
3347 /* Save current base address for post processing of hard-links. */
3348 if (ilinkref || state->cbs_previlinkref) {
3349 uiobase = uio_curriovbase(state->cbs_uio);
3350 }
3351 /* If this entry won't fit then we're done */
3352 if ((uiosize > (user_size_t)uio_resid(state->cbs_uio)) ||
3353 (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
3354 return (0); /* stop */
3355 }
3356
3357 if (!(state->cbs_flags & VNODE_READDIR_EXTENDED) || state->cbs_hasprevdirentry) {
3358 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
3359 if (state->cbs_result == 0) {
3360 ++state->cbs_index;
3361
3362 /* Remember previous entry */
3363 state->cbs_desc->cd_cnid = cnid;
3364 if (type == DT_DIR) {
3365 state->cbs_desc->cd_flags |= CD_ISDIR;
3366 } else {
3367 state->cbs_desc->cd_flags &= ~CD_ISDIR;
3368 }
3369 if (state->cbs_desc->cd_nameptr != NULL) {
3370 state->cbs_desc->cd_namelen = 0;
3371 }
3372 #if 0
3373 state->cbs_desc->cd_encoding = xxxx;
3374 #endif
3375 if (!is_mangled) {
3376 state->cbs_desc->cd_namelen = namelen;
3377 bcopy(nameptr, state->cbs_namebuf, namelen + 1);
3378 } else {
3379 /* Store unmangled name for the directory hint else it will
3380 * restart readdir at the last location again
3381 */
3382 u_int8_t *new_nameptr;
3383 size_t bufsize;
3384 size_t tmp_namelen = 0;
3385
3386 cnp = (const CatalogName *)&ckp->hfsPlus.nodeName;
3387 bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
3388 cnp->ustr.length * sizeof(UniChar),
3389 ':', 0);
3390 MALLOC(new_nameptr, u_int8_t *, bufsize, M_TEMP, M_WAITOK);
3391 result = utf8_encodestr(cnp->ustr.unicode,
3392 cnp->ustr.length * sizeof(UniChar),
3393 new_nameptr, &tmp_namelen, bufsize, ':', 0);
3394
3395 state->cbs_desc->cd_namelen = tmp_namelen;
3396 bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1);
3397
3398 FREE(new_nameptr, M_TEMP);
3399 }
3400 }
3401 if (state->cbs_hasprevdirentry) {
3402 curlinkref = ilinkref; /* save current */
3403 ilinkref = state->cbs_previlinkref; /* use previous */
3404 }
3405 /*
3406 * Record any hard links for post processing.
3407 */
3408 if ((ilinkref != 0) &&
3409 (state->cbs_result == 0) &&
3410 (state->cbs_nlinks < state->cbs_maxlinks)) {
3411 state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
3412 state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
3413 state->cbs_nlinks++;
3414 }
3415 if (state->cbs_hasprevdirentry) {
3416 ilinkref = curlinkref; /* restore current */
3417 }
3418 }
3419
3420 /* Fill the direntry to be used the next time */
3421 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
3422 if (stop_after_pack) {
3423 state->cbs_eof = true;
3424 return (0); /* stop */
3425 }
3426 entry->d_type = type;
3427 entry->d_namlen = namelen;
3428 entry->d_reclen = EXT_DIRENT_LEN(namelen);
3429 if (hide) {
3430 /* File number = 0 means skip entry */
3431 entry->d_fileno = 0;
3432 } else {
3433 entry->d_fileno = cnid;
3434 }
3435 /* swap the current and previous entry */
3436 struct direntry * tmp;
3437 tmp = state->cbs_direntry;
3438 state->cbs_direntry = state->cbs_prevdirentry;
3439 state->cbs_prevdirentry = tmp;
3440 state->cbs_hasprevdirentry = true;
3441 state->cbs_previlinkref = ilinkref;
3442 }
3443
3444 /* Continue iteration if there's room */
3445 return (state->cbs_result == 0 &&
3446 uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
3447 }
3448
3449 #if CONFIG_HFS_STD
3450 /*
3451 * getdirentries callback for standard HFS (non HFS+) directories.
3452 */
3453 static int
3454 getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
3455 struct packdirentry_state *state)
3456 {
3457 struct hfsmount *hfsmp;
3458 const CatalogName *cnp;
3459 cnid_t curID;
3460 OSErr result;
3461 struct dirent catent;
3462 cnid_t cnid;
3463 u_int8_t type = DT_UNKNOWN;
3464 u_int8_t *nameptr;
3465 size_t namelen = 0;
3466 size_t maxnamelen;
3467 size_t uiosize = 0;
3468 caddr_t uioaddr;
3469
3470 hfsmp = state->cbs_hfsmp;
3471
3472 curID = ckp->hfs.parentID;
3473
3474 /* We're done when parent directory changes */
3475 if (state->cbs_parentID != curID) {
3476 state->cbs_result = ENOENT;
3477 return (0); /* stop */
3478 }
3479
3480 nameptr = (u_int8_t *)&catent.d_name[0];
3481 maxnamelen = sizeof(catent.d_name);
3482
3483 switch(crp->recordType) {
3484 case kHFSFolderRecord:
3485 type = DT_DIR;
3486 cnid = crp->hfsFolder.folderID;
3487 break;
3488 case kHFSFileRecord:
3489 type = DT_REG;
3490 cnid = crp->hfsFile.fileID;
3491 break;
3492 default:
3493 return (0); /* stop */
3494 };
3495
3496 cnp = (const CatalogName*) ckp->hfs.nodeName;
3497 result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr);
3498 /*
3499 * When an HFS name cannot be encoded with the current
3500 * volume encoding we use MacRoman as a fallback.
3501 */
3502 if (result) {
3503 result = mac_roman_to_utf8(cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr);
3504 }
3505 catent.d_type = type;
3506 catent.d_namlen = namelen;
3507 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
3508 catent.d_fileno = cnid;
3509 uioaddr = (caddr_t) &catent;
3510
3511 /* If this entry won't fit then we're done */
3512 if (uiosize > (user_size_t)uio_resid(state->cbs_uio)) {
3513 return (0); /* stop */
3514 }
3515
3516 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
3517 if (state->cbs_result == 0) {
3518 ++state->cbs_index;
3519
3520 /* Remember previous entry */
3521 state->cbs_desc->cd_cnid = cnid;
3522 if (type == DT_DIR) {
3523 state->cbs_desc->cd_flags |= CD_ISDIR;
3524 } else {
3525 state->cbs_desc->cd_flags &= ~CD_ISDIR;
3526 }
3527 if (state->cbs_desc->cd_nameptr != NULL) {
3528 state->cbs_desc->cd_namelen = 0;
3529 }
3530 state->cbs_desc->cd_namelen = namelen;
3531 bcopy(nameptr, state->cbs_namebuf, namelen + 1);
3532 }
3533
3534 /* Continue iteration if there's room */
3535 return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
3536 }
3537 #endif
3538
3539 /*
3540 * Pack a uio buffer with directory entries from the catalog
3541 */
3542 int
3543 cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *dirhint,
3544 uio_t uio, int flags, int * items, int * eofflag)
3545 {
3546 FCB* fcb;
3547 BTreeIterator * iterator;
3548 CatalogKey * key;
3549 struct packdirentry_state state;
3550 void * buffer;
3551 int bufsize;
3552 int maxlinks;
3553 int result;
3554 int index;
3555 int have_key;
3556 int extended;
3557
3558 extended = flags & VNODE_READDIR_EXTENDED;
3559
3560 if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
3561 return (ENOTSUP);
3562 }
3563 fcb = hfsmp->hfs_catalog_cp->c_datafork;
3564
3565 /*
3566 * Get a buffer for link info array, btree iterator and a direntry:
3567 */
3568 maxlinks = MIN(entrycnt, (u_int32_t)(uio_resid(uio) / SMALL_DIRENTRY_SIZE));
3569 bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
3570 if (extended) {
3571 bufsize += 2*sizeof(struct direntry);
3572 }
3573 MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
3574 bzero(buffer, bufsize);
3575
3576 state.cbs_flags = flags;
3577 state.cbs_hasprevdirentry = false;
3578 state.cbs_previlinkref = 0;
3579 state.cbs_nlinks = 0;
3580 state.cbs_maxlinks = maxlinks;
3581 state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
3582 /*
3583 * We need to set cbs_eof to false regardless of whether or not the
3584 * control flow is actually in the extended case, since we use this
3585 * field to track whether or not we've returned EOF from the iterator function.
3586 */
3587 state.cbs_eof = false;
3588
3589 iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
3590 key = (CatalogKey *)&iterator->key;
3591 have_key = 0;
3592 index = dirhint->dh_index + 1;
3593 if (extended) {
3594 state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
3595 state.cbs_prevdirentry = state.cbs_direntry + 1;
3596 }
3597 /*
3598 * Attempt to build a key from cached filename
3599 */
3600 if (dirhint->dh_desc.cd_namelen != 0) {
3601 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
3602 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
3603 have_key = 1;
3604 }
3605 }
3606
3607 if (index == 0 && dirhint->dh_threadhint != 0) {
3608 /*
3609 * Position the iterator at the directory's thread record.
3610 * (i.e. just before the first entry)
3611 */
3612 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
3613 iterator->hint.nodeNum = dirhint->dh_threadhint;
3614 iterator->hint.index = 0;
3615 have_key = 1;
3616 }
3617
3618 /*
3619 * If the last entry wasn't cached then position the btree iterator
3620 */
3621 if (!have_key) {
3622 /*
3623 * Position the iterator at the directory's thread record.
3624 * (i.e. just before the first entry)
3625 */
3626 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
3627 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
3628 if (result) {
3629 result = MacToVFSError(result);
3630 goto cleanup;
3631 }
3632 if (index == 0) {
3633 dirhint->dh_threadhint = iterator->hint.nodeNum;
3634 }
3635 /*
3636 * Iterate until we reach the entry just
3637 * before the one we want to start with.
3638 */
3639 if (index > 0) {
3640 struct position_state ps;
3641
3642 ps.error = 0;
3643 ps.count = 0;
3644 ps.index = index;
3645 ps.parentID = dirhint->dh_desc.cd_parentcnid;
3646 ps.hfsmp = hfsmp;
3647
3648 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
3649 (IterateCallBackProcPtr)cat_findposition, &ps);
3650 if (ps.error)
3651 result = ps.error;
3652 else
3653 result = MacToVFSError(result);
3654 if (result) {
3655 result = MacToVFSError(result);
3656 goto cleanup;
3657 }
3658 }
3659 }
3660
3661 state.cbs_index = index;
3662 state.cbs_hfsmp = hfsmp;
3663 state.cbs_uio = uio;
3664 state.cbs_desc = &dirhint->dh_desc;
3665 state.cbs_namebuf = (u_int8_t *)buffer;
3666 state.cbs_result = 0;
3667 state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
3668
3669 /* Use a temporary buffer to hold intermediate descriptor names. */
3670 if (dirhint->dh_desc.cd_namelen > 0 && dirhint->dh_desc.cd_nameptr != NULL) {
3671 bcopy(dirhint->dh_desc.cd_nameptr, buffer, dirhint->dh_desc.cd_namelen+1);
3672 if (dirhint->dh_desc.cd_flags & CD_HASBUF) {
3673 dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
3674 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
3675 }
3676 }
3677 dirhint->dh_desc.cd_nameptr = (u_int8_t *)buffer;
3678
3679 enum BTreeIterationOperations op;
3680 if (extended && index != 0 && have_key)
3681 op = kBTreeCurrentRecord;
3682 else
3683 op = kBTreeNextRecord;
3684
3685 /*
3686 * Process as many entries as possible starting at iterator->key.
3687 */
3688 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) {
3689 /* HFS+ */
3690 result = BTIterateRecords(fcb, op, iterator,
3691 (IterateCallBackProcPtr)getdirentries_callback, &state);
3692
3693 /* For extended calls, every call to getdirentries_callback()
3694 * transfers the previous directory entry found to the user
3695 * buffer. Therefore when BTIterateRecords reaches the end of
3696 * Catalog BTree, call getdirentries_callback() again with
3697 * dummy values to copy the last directory entry stored in
3698 * packdirentry_state
3699 */
3700 if (extended && (result == fsBTRecordNotFoundErr)) {
3701 CatalogKey ckp;
3702 CatalogRecord crp;
3703
3704 bzero(&ckp, sizeof(ckp));
3705 bzero(&crp, sizeof(crp));
3706
3707 result = getdirentries_callback(&ckp, &crp, &state);
3708 }
3709 }
3710 #if CONFIG_HFS_STD
3711 else {
3712 /* HFS (standard) */
3713 result = BTIterateRecords(fcb, op, iterator,
3714 (IterateCallBackProcPtr)getdirentries_std_callback, &state);
3715 }
3716 #endif
3717
3718 /* Note that state.cbs_index is still valid on errors */
3719 *items = state.cbs_index - index;
3720 index = state.cbs_index;
3721
3722 /*
3723 * Also note that cbs_eof is set in all cases if we ever hit EOF
3724 * during the enumeration by the catalog callback. Mark the directory's hint
3725 * descriptor as having hit EOF.
3726 */
3727
3728 if (state.cbs_eof) {
3729 dirhint->dh_desc.cd_flags |= CD_EOF;
3730 *eofflag = 1;
3731 }
3732
3733 /* Finish updating the catalog iterator. */
3734 dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
3735 dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
3736 dirhint->dh_index = index - 1;
3737
3738 /* Fix up the name. */
3739 if (dirhint->dh_desc.cd_namelen > 0) {
3740 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)vfs_addname((char *)buffer, dirhint->dh_desc.cd_namelen, 0, 0);
3741 dirhint->dh_desc.cd_flags |= CD_HASBUF;
3742 } else {
3743 dirhint->dh_desc.cd_nameptr = NULL;
3744 dirhint->dh_desc.cd_namelen = 0;
3745 }
3746
3747 /*
3748 * Post process any hard links to get the real file id.
3749 */
3750 if (state.cbs_nlinks > 0) {
3751 ino_t fileid = 0;
3752 user_addr_t address;
3753 int i;
3754
3755 for (i = 0; i < state.cbs_nlinks; ++i) {
3756 if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
3757 continue;
3758 /* This assumes that d_ino is always first field. */
3759 address = state.cbs_linkinfo[i].dirent_addr;
3760 if (address == (user_addr_t)0)
3761 continue;
3762 if (uio_isuserspace(uio)) {
3763 if (extended) {
3764 ino64_t fileid_64 = (ino64_t)fileid;
3765 (void) copyout(&fileid_64, address, sizeof(fileid_64));
3766 } else {
3767 (void) copyout(&fileid, address, sizeof(fileid));
3768 }
3769 } else /* system space */ {
3770 if (extended) {
3771 ino64_t fileid_64 = (ino64_t)fileid;
3772 bcopy(&fileid_64, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid_64));
3773 } else {
3774 bcopy(&fileid, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid));
3775 }
3776 }
3777 }
3778 }
3779
3780 if (state.cbs_result)
3781 result = state.cbs_result;
3782 else
3783 result = MacToVFSError(result);
3784
3785 if (result == ENOENT) {
3786 result = 0;
3787 }
3788
3789 cleanup:
3790 FREE(buffer, M_TEMP);
3791
3792 return (result);
3793 }
3794
3795
3796 /*
3797 * Callback to establish directory position.
3798 * Called with position_state for each item in a directory.
3799 */
3800 static int
3801 cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
3802 struct position_state *state)
3803 {
3804 cnid_t curID = 0;
3805
3806 if ((state->hfsmp->hfs_flags & HFS_STANDARD) == 0) {
3807 curID = ckp->hfsPlus.parentID;
3808 }
3809 #if CONFIG_HFS_STD
3810 else {
3811 curID = ckp->hfs.parentID;
3812 }
3813 #endif
3814
3815 /* Make sure parent directory didn't change */
3816 if (state->parentID != curID) {
3817 state->error = EINVAL;
3818 return (0); /* stop */
3819 }
3820
3821 /* Count this entry */
3822 switch(crp->recordType) {
3823 case kHFSPlusFolderRecord:
3824 case kHFSPlusFileRecord:
3825 #if CONFIG_HFS_STD
3826 case kHFSFolderRecord:
3827 case kHFSFileRecord:
3828 #endif
3829 ++state->count;
3830 break;
3831 default:
3832 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
3833 crp->recordType, curID);
3834 state->error = EINVAL;
3835 return (0); /* stop */
3836 };
3837
3838 return (state->count < state->index);
3839 }
3840
3841
3842 /*
3843 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3844
3845 * The name portion of the key is compared using a 16-bit binary comparison.
3846 * This is called from the b-tree code.
3847 */
3848 int
3849 cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
3850 {
3851 u_int32_t searchParentID, trialParentID;
3852 int result;
3853
3854 searchParentID = searchKey->parentID;
3855 trialParentID = trialKey->parentID;
3856 result = 0;
3857
3858 if (searchParentID > trialParentID) {
3859 ++result;
3860 } else if (searchParentID < trialParentID) {
3861 --result;
3862 } else {
3863 u_int16_t * str1 = &searchKey->nodeName.unicode[0];
3864 u_int16_t * str2 = &trialKey->nodeName.unicode[0];
3865 int length1 = searchKey->nodeName.length;
3866 int length2 = trialKey->nodeName.length;
3867
3868 result = UnicodeBinaryCompare (str1, length1, str2, length2);
3869 }
3870
3871 return result;
3872 }
3873
3874
3875 #if CONFIG_HFS_STD
3876 /*
3877 * Compare two standard HFS catalog keys
3878 *
3879 * Result: +n search key > trial key
3880 * 0 search key = trial key
3881 * -n search key < trial key
3882 */
3883 int
3884 CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
3885 {
3886 cnid_t searchParentID, trialParentID;
3887 int result;
3888
3889 searchParentID = searchKey->parentID;
3890 trialParentID = trialKey->parentID;
3891
3892 if (searchParentID > trialParentID)
3893 result = 1;
3894 else if (searchParentID < trialParentID)
3895 result = -1;
3896 else /* parent dirID's are equal, compare names */
3897 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
3898
3899 return result;
3900 }
3901 #endif
3902
3903
3904 /*
3905 * Compare two HFS+ catalog keys
3906 *
3907 * Result: +n search key > trial key
3908 * 0 search key = trial key
3909 * -n search key < trial key
3910 */
3911 int
3912 CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
3913 {
3914 cnid_t searchParentID, trialParentID;
3915 int result;
3916
3917 searchParentID = searchKey->parentID;
3918 trialParentID = trialKey->parentID;
3919
3920 if (searchParentID > trialParentID) {
3921 result = 1;
3922 }
3923 else if (searchParentID < trialParentID) {
3924 result = -1;
3925 } else {
3926 /* parent node ID's are equal, compare names */
3927 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
3928 result = searchKey->nodeName.length - trialKey->nodeName.length;
3929 else
3930 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
3931 searchKey->nodeName.length,
3932 &trialKey->nodeName.unicode[0],
3933 trialKey->nodeName.length);
3934 }
3935
3936 return result;
3937 }
3938
3939
3940 /*
3941 * buildkey - build a Catalog b-tree key from a cnode descriptor
3942 */
3943 static int
3944 buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
3945 HFSPlusCatalogKey *key, int retry)
3946 {
3947 int std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
3948 int utf8_flags = UTF_ESCAPE_ILLEGAL;
3949 int result = 0;
3950 size_t unicodeBytes = 0;
3951
3952 if (std_hfs == 0) {
3953 retry = 0;
3954 }
3955
3956 if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
3957 return (EINVAL); /* invalid name */
3958
3959 key->parentID = descp->cd_parentcnid;
3960 key->nodeName.length = 0;
3961 /*
3962 * Convert filename from UTF-8 into Unicode
3963 */
3964
3965 if ((descp->cd_flags & CD_DECOMPOSED) == 0)
3966 utf8_flags |= UTF_DECOMPOSED;
3967 result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
3968 key->nodeName.unicode, &unicodeBytes,
3969 sizeof(key->nodeName.unicode), ':', utf8_flags);
3970 key->nodeName.length = unicodeBytes / sizeof(UniChar);
3971 key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
3972 if (result) {
3973 if (result != ENAMETOOLONG)
3974 result = EINVAL; /* name has invalid characters */
3975 return (result);
3976 }
3977
3978 #if CONFIG_HFS_STD
3979 /*
3980 * For HFS volumes convert to an HFS compatible key
3981 *
3982 * XXX need to save the encoding that succeeded
3983 */
3984 if (std_hfs) {
3985 HFSCatalogKey hfskey;
3986
3987 bzero(&hfskey, sizeof(hfskey));
3988 hfskey.keyLength = kHFSCatalogKeyMinimumLength;
3989 hfskey.parentID = key->parentID;
3990 hfskey.nodeName[0] = 0;
3991 if (key->nodeName.length > 0) {
3992 int res;
3993 if ((res = unicode_to_hfs(HFSTOVCB(hfsmp),
3994 key->nodeName.length * 2,
3995 key->nodeName.unicode,
3996 &hfskey.nodeName[0], retry)) != 0) {
3997 if (res != ENAMETOOLONG)
3998 res = EINVAL;
3999
4000 return res;
4001 }
4002 hfskey.keyLength += hfskey.nodeName[0];
4003 }
4004 bcopy(&hfskey, key, sizeof(hfskey));
4005 }
4006 #endif
4007
4008 return (0);
4009 }
4010
4011
4012 /*
4013 * Resolve hard link reference to obtain the inode record.
4014 */
4015 int
4016 cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
4017 {
4018 FSBufferDescriptor btdata;
4019 struct BTreeIterator *iterator;
4020 struct cat_desc idesc;
4021 char inodename[32];
4022 cnid_t parentcnid;
4023 int result = 0;
4024
4025 BDINIT(btdata, recp);
4026
4027 if (isdirlink) {
4028 MAKE_DIRINODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
4029 parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
4030 } else {
4031 MAKE_INODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
4032 parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
4033 }
4034
4035 /* Get space for iterator */
4036 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
4037 bzero(iterator, sizeof(*iterator));
4038
4039 /* Build a descriptor for private dir. */
4040 idesc.cd_parentcnid = parentcnid;
4041 idesc.cd_nameptr = (const u_int8_t *)inodename;
4042 idesc.cd_namelen = strlen(inodename);
4043 idesc.cd_flags = 0;
4044 idesc.cd_hint = 0;
4045 idesc.cd_encoding = 0;
4046 (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
4047
4048 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
4049 &btdata, NULL, NULL);
4050
4051 if (result == 0) {
4052 /* Make sure there's a reference */
4053 if (recp->hl_linkCount == 0)
4054 recp->hl_linkCount = 2;
4055 } else {
4056 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename, hfsmp->vcbVN);
4057 }
4058
4059 FREE(iterator, M_TEMP);
4060
4061 return (result ? ENOENT : 0);
4062 }
4063
4064 /*
4065 * Resolve hard link reference to obtain the inode number.
4066 */
4067 static int
4068 resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino)
4069 {
4070 struct HFSPlusCatalogFile record;
4071 int error;
4072
4073 /*
4074 * Since we know resolvelinkid is only called from
4075 * cat_getdirentries, we can assume that only file
4076 * hardlinks need to be resolved (cat_getdirentries
4077 * can resolve directory hardlinks in place).
4078 */
4079 error = cat_resolvelink(hfsmp, linkref, 0, &record);
4080 if (error == 0) {
4081 if (record.fileID == 0)
4082 error = ENOENT;
4083 else
4084 *ino = record.fileID;
4085 }
4086 return (error);
4087 }
4088
4089 /*
4090 * getkey - get a key from id by doing a thread lookup
4091 */
4092 static int
4093 getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
4094 {
4095 struct BTreeIterator * iterator;
4096 FSBufferDescriptor btdata;
4097 u_int16_t datasize;
4098 CatalogKey * keyp;
4099 CatalogRecord * recp;
4100 int result;
4101 int std_hfs;
4102
4103 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
4104
4105 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
4106 bzero(iterator, sizeof(*iterator));
4107 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
4108
4109 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
4110 BDINIT(btdata, recp);
4111
4112 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
4113 &btdata, &datasize, iterator);
4114 if (result)
4115 goto exit;
4116
4117 /* Turn thread record into a cnode key (in place) */
4118 switch (recp->recordType) {
4119
4120 #if CONFIG_HFS_STD
4121 case kHFSFileThreadRecord:
4122 case kHFSFolderThreadRecord:
4123 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
4124 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
4125 bcopy(keyp, key, keyp->hfs.keyLength + 1);
4126 break;
4127 #endif
4128
4129 case kHFSPlusFileThreadRecord:
4130 case kHFSPlusFolderThreadRecord:
4131 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
4132 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
4133 (keyp->hfsPlus.nodeName.length * 2);
4134 bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
4135 break;
4136
4137 default:
4138 result = ENOENT;
4139 break;
4140 }
4141
4142 exit:
4143 FREE(iterator, M_TEMP);
4144 FREE(recp, M_TEMP);
4145
4146 return MacToVFSError(result);
4147 }
4148
4149 /*
4150 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4151 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4152 * cat_desc here). Both key and attrp must point to real structures.
4153 *
4154 * The key's parent id is the only part of the key expected to be used by the caller.
4155 * The name portion of the key may not always be valid (ie in the case of a hard link).
4156 */
4157 int
4158 cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
4159 {
4160 int result;
4161
4162 result = getkey(hfsmp, cnid, key);
4163
4164 if (result == 0) {
4165 result = cat_lookupbykey(hfsmp, key, 0, 0, 0, NULL, attrp, NULL, NULL);
4166 }
4167 /*
4168 * Check for a raw file hardlink inode.
4169 * Fix up the parent id in the key if necessary.
4170 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4171 */
4172 if ((result == 0) &&
4173 (key->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
4174 (attrp->ca_recflags & kHFSHasLinkChainMask)) {
4175 cnid_t nextlinkid = 0;
4176 cnid_t prevlinkid = 0;
4177 struct cat_desc linkdesc;
4178
4179 /*
4180 * Pick up the first link in the chain and get a descriptor for it.
4181 * This allows blind bulk access checks to work for hardlinks.
4182 */
4183 if ((cat_lookup_siblinglinks(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
4184 (nextlinkid != 0)) {
4185 if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
4186 key->hfsPlus.parentID = linkdesc.cd_parentcnid;
4187 cat_releasedesc(&linkdesc);
4188 }
4189 }
4190 }
4191 return MacToVFSError(result);
4192 }
4193
4194
4195 /*
4196 * buildrecord - build a default catalog directory or file record
4197 */
4198 static void
4199 buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
4200 CatalogRecord *crp, u_int32_t *recordSize)
4201 {
4202 int type = attrp->ca_mode & S_IFMT;
4203 u_int32_t createtime = to_hfs_time(attrp->ca_itime);
4204
4205 if (std_hfs == 0) {
4206 struct HFSPlusBSDInfo * bsdp = NULL;
4207
4208 if (type == S_IFDIR) {
4209 crp->recordType = kHFSPlusFolderRecord;
4210 crp->hfsPlusFolder.flags = attrp->ca_recflags;
4211 crp->hfsPlusFolder.valence = 0;
4212 crp->hfsPlusFolder.folderID = cnid;
4213 crp->hfsPlusFolder.createDate = createtime;
4214 crp->hfsPlusFolder.contentModDate = createtime;
4215 crp->hfsPlusFolder.attributeModDate = createtime;
4216 crp->hfsPlusFolder.accessDate = createtime;
4217 crp->hfsPlusFolder.backupDate = 0;
4218 crp->hfsPlusFolder.textEncoding = encoding;
4219 crp->hfsPlusFolder.folderCount = 0;
4220 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
4221 bsdp = &crp->hfsPlusFolder.bsdInfo;
4222 bsdp->special.linkCount = 1;
4223 *recordSize = sizeof(HFSPlusCatalogFolder);
4224 } else {
4225 crp->recordType = kHFSPlusFileRecord;
4226 crp->hfsPlusFile.flags = attrp->ca_recflags;
4227 crp->hfsPlusFile.reserved1 = 0;
4228 crp->hfsPlusFile.fileID = cnid;
4229 crp->hfsPlusFile.createDate = createtime;
4230 crp->hfsPlusFile.contentModDate = createtime;
4231 crp->hfsPlusFile.accessDate = createtime;
4232 crp->hfsPlusFile.attributeModDate = createtime;
4233 crp->hfsPlusFile.backupDate = 0;
4234 crp->hfsPlusFile.textEncoding = encoding;
4235 crp->hfsPlusFile.reserved2 = 0;
4236 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
4237 bsdp = &crp->hfsPlusFile.bsdInfo;
4238 /* BLK/CHR need to save the device info */
4239 if (type == S_IFBLK || type == S_IFCHR) {
4240 bsdp->special.rawDevice = attrp->ca_rdev;
4241 } else {
4242 bsdp->special.linkCount = 1;
4243 }
4244 bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
4245 *recordSize = sizeof(HFSPlusCatalogFile);
4246 }
4247 bsdp->ownerID = attrp->ca_uid;
4248 bsdp->groupID = attrp->ca_gid;
4249 bsdp->fileMode = attrp->ca_mode;
4250 bsdp->adminFlags = attrp->ca_flags >> 16;
4251 bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
4252 }
4253 #if CONFIG_HFS_STD
4254 else {
4255 createtime = UTCToLocal(createtime);
4256 if (type == S_IFDIR) {
4257 bzero(crp, sizeof(HFSCatalogFolder));
4258 crp->recordType = kHFSFolderRecord;
4259 crp->hfsFolder.folderID = cnid;
4260 crp->hfsFolder.createDate = createtime;
4261 crp->hfsFolder.modifyDate = createtime;
4262 bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
4263 *recordSize = sizeof(HFSCatalogFolder);
4264 } else {
4265 bzero(crp, sizeof(HFSCatalogFile));
4266 crp->recordType = kHFSFileRecord;
4267 crp->hfsFile.fileID = cnid;
4268 crp->hfsFile.createDate = createtime;
4269 crp->hfsFile.modifyDate = createtime;
4270 bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
4271 bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
4272 *recordSize = sizeof(HFSCatalogFile);
4273 }
4274 }
4275 #endif
4276
4277 }
4278
4279
4280 /*
4281 * builddesc - build a cnode descriptor from an HFS+ key
4282 */
4283 static int
4284 builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
4285 int isdir, struct cat_desc *descp)
4286 {
4287 int result = 0;
4288 unsigned char * nameptr;
4289 size_t bufsize;
4290 size_t utf8len;
4291 unsigned char tmpbuff[128];
4292
4293 /* guess a size... */
4294 bufsize = (3 * key->nodeName.length) + 1;
4295 if (bufsize >= sizeof(tmpbuff) - 1) {
4296 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
4297 } else {
4298 nameptr = &tmpbuff[0];
4299 }
4300
4301 result = utf8_encodestr(key->nodeName.unicode,
4302 key->nodeName.length * sizeof(UniChar),
4303 nameptr, (size_t *)&utf8len,
4304 bufsize, ':', 0);
4305
4306 if (result == ENAMETOOLONG) {
4307 bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
4308 key->nodeName.length * sizeof(UniChar),
4309 ':', 0);
4310 FREE(nameptr, M_TEMP);
4311 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
4312
4313 result = utf8_encodestr(key->nodeName.unicode,
4314 key->nodeName.length * sizeof(UniChar),
4315 nameptr, (size_t *)&utf8len,
4316 bufsize, ':', 0);
4317 }
4318 descp->cd_parentcnid = key->parentID;
4319 descp->cd_nameptr = (const u_int8_t *)vfs_addname((char *)nameptr, utf8len, 0, 0);
4320 descp->cd_namelen = utf8len;
4321 descp->cd_cnid = cnid;
4322 descp->cd_hint = hint;
4323 descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
4324 if (isdir)
4325 descp->cd_flags |= CD_ISDIR;
4326 descp->cd_encoding = encoding;
4327 if (nameptr != &tmpbuff[0]) {
4328 FREE(nameptr, M_TEMP);
4329 }
4330 return result;
4331 }
4332
4333
4334 /*
4335 * getbsdattr - get attributes in bsd format
4336 *
4337 */
4338 static void
4339 getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
4340 {
4341 int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
4342 const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
4343
4344 attrp->ca_recflags = crp->flags;
4345 attrp->ca_atime = to_bsd_time(crp->accessDate);
4346 attrp->ca_atimeondisk = attrp->ca_atime;
4347 attrp->ca_mtime = to_bsd_time(crp->contentModDate);
4348 attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
4349 attrp->ca_itime = to_bsd_time(crp->createDate);
4350 attrp->ca_btime = to_bsd_time(crp->backupDate);
4351
4352 if ((bsd->fileMode & S_IFMT) == 0) {
4353 attrp->ca_flags = 0;
4354 attrp->ca_uid = hfsmp->hfs_uid;
4355 attrp->ca_gid = hfsmp->hfs_gid;
4356 if (isDirectory) {
4357 attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
4358 } else {
4359 attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
4360 }
4361 attrp->ca_linkcount = 1;
4362 attrp->ca_rdev = 0;
4363 } else {
4364 attrp->ca_linkcount = 1; /* may be overridden below */
4365 attrp->ca_rdev = 0;
4366 attrp->ca_uid = bsd->ownerID;
4367 attrp->ca_gid = bsd->groupID;
4368 attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
4369 attrp->ca_mode = (mode_t)bsd->fileMode;
4370 switch (attrp->ca_mode & S_IFMT) {
4371 case S_IFCHR: /* fall through */
4372 case S_IFBLK:
4373 attrp->ca_rdev = bsd->special.rawDevice;
4374 break;
4375
4376 case S_IFDIR: /* fall through */
4377 case S_IFREG:
4378 /* Pick up the hard link count */
4379 if (bsd->special.linkCount > 0)
4380 attrp->ca_linkcount = bsd->special.linkCount;
4381 break;
4382 }
4383
4384 /*
4385 * Override the permissions as determined by the mount auguments
4386 * in ALMOST the same way unset permissions are treated but keep
4387 * track of whether or not the file or folder is hfs locked
4388 * by leaving the h_pflags field unchanged from what was unpacked
4389 * out of the catalog.
4390 */
4391 /*
4392 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4393 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4394 * at the VFS layer, so there is no need to do it here now; this also
4395 * allows VFS to let root see the real UIDs.
4396 *
4397 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4398 * attrp->ca_uid = hfsmp->hfs_uid;
4399 * attrp->ca_gid = hfsmp->hfs_gid;
4400 * }
4401 */
4402 }
4403
4404 if (isDirectory) {
4405 if (!S_ISDIR(attrp->ca_mode)) {
4406 attrp->ca_mode &= ~S_IFMT;
4407 attrp->ca_mode |= S_IFDIR;
4408 }
4409 attrp->ca_entries = ((const HFSPlusCatalogFolder *)crp)->valence;
4410 attrp->ca_dircount = ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && (attrp->ca_recflags & kHFSHasFolderCountMask)) ?
4411 ((const HFSPlusCatalogFolder *)crp)->folderCount : 0;
4412
4413 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4414 if (((const HFSPlusCatalogFolder *)crp)->userInfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
4415 attrp->ca_flags |= UF_HIDDEN;
4416 } else {
4417 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4418 if (crp->flags & kHFSFileLockedMask) {
4419 /* The file's supposed to be locked:
4420 Make sure at least one of the IMMUTABLE bits is set: */
4421 if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
4422 attrp->ca_flags |= UF_IMMUTABLE;
4423 } else {
4424 /* The file's supposed to be unlocked: */
4425 attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
4426 }
4427 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4428 if (crp->userInfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
4429 attrp->ca_flags |= UF_HIDDEN;
4430 /* get total blocks (both forks) */
4431 attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
4432
4433 /* On HFS+ the ThreadExists flag must always be set. */
4434 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
4435 attrp->ca_recflags |= kHFSThreadExistsMask;
4436
4437 /* Pick up the hardlink first link, if any. */
4438 attrp->ca_firstlink = (attrp->ca_recflags & kHFSHasLinkChainMask) ? crp->hl_firstLinkID : 0;
4439 }
4440
4441 attrp->ca_fileid = crp->fileID;
4442
4443 bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
4444 }
4445
4446 #if CONFIG_HFS_STD
4447 /*
4448 * promotekey - promote hfs key to hfs plus key
4449 *
4450 */
4451 static void
4452 promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
4453 HFSPlusCatalogKey *keyp, u_int32_t *encoding)
4454 {
4455 hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
4456 u_int32_t uniCount;
4457 int error;
4458
4459 *encoding = hfsmp->hfs_encoding;
4460
4461 error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
4462 kHFSPlusMaxFileNameChars, &uniCount);
4463 /*
4464 * When an HFS name cannot be encoded with the current
4465 * encoding use MacRoman as a fallback.
4466 */
4467 if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
4468 *encoding = 0;
4469 (void) mac_roman_to_unicode(hfskey->nodeName,
4470 keyp->nodeName.unicode,
4471 kHFSPlusMaxFileNameChars,
4472 &uniCount);
4473 }
4474
4475 keyp->nodeName.length = uniCount;
4476 keyp->parentID = hfskey->parentID;
4477 }
4478
4479 /*
4480 * promotefork - promote hfs fork info to hfs plus
4481 *
4482 */
4483 static void
4484 promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
4485 int resource, struct cat_fork * forkp)
4486 {
4487 struct HFSPlusExtentDescriptor *xp;
4488 u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
4489
4490 bzero(forkp, sizeof(*forkp));
4491 xp = &forkp->cf_extents[0];
4492 if (resource) {
4493 forkp->cf_size = filep->rsrcLogicalSize;
4494 forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
4495 forkp->cf_bytesread = 0;
4496 forkp->cf_vblocks = 0;
4497 xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
4498 xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
4499 xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
4500 xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
4501 xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
4502 xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
4503 } else {
4504 forkp->cf_size = filep->dataLogicalSize;
4505 forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
4506 forkp->cf_bytesread = 0;
4507 forkp->cf_vblocks = 0;
4508 xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
4509 xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
4510 xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
4511 xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
4512 xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
4513 xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
4514 }
4515 }
4516
4517 /*
4518 * promoteattr - promote standard hfs catalog attributes to hfs plus
4519 *
4520 */
4521 static void
4522 promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
4523 {
4524 u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
4525
4526 if (dataPtr->recordType == kHFSFolderRecord) {
4527 const struct HFSCatalogFolder * folder;
4528
4529 folder = (const struct HFSCatalogFolder *) dataPtr;
4530 crp->recordType = kHFSPlusFolderRecord;
4531 crp->flags = folder->flags;
4532 crp->fileID = folder->folderID;
4533 crp->createDate = LocalToUTC(folder->createDate);
4534 crp->contentModDate = LocalToUTC(folder->modifyDate);
4535 crp->backupDate = LocalToUTC(folder->backupDate);
4536 crp->reserved1 = folder->valence;
4537 crp->reserved2 = 0;
4538 bcopy(&folder->userInfo, &crp->userInfo, 32);
4539 } else /* file */ {
4540 const struct HFSCatalogFile * file;
4541
4542 file = (const struct HFSCatalogFile *) dataPtr;
4543 crp->recordType = kHFSPlusFileRecord;
4544 crp->flags = file->flags;
4545 crp->fileID = file->fileID;
4546 crp->createDate = LocalToUTC(file->createDate);
4547 crp->contentModDate = LocalToUTC(file->modifyDate);
4548 crp->backupDate = LocalToUTC(file->backupDate);
4549 crp->reserved1 = 0;
4550 crp->reserved2 = 0;
4551 bcopy(&file->userInfo, &crp->userInfo, 16);
4552 bcopy(&file->finderInfo, &crp->finderInfo, 16);
4553 crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
4554 crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
4555 }
4556 crp->textEncoding = 0;
4557 crp->attributeModDate = crp->contentModDate;
4558 crp->accessDate = crp->contentModDate;
4559 bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
4560 }
4561 #endif
4562
4563 /*
4564 * Build a catalog node thread record from a catalog key
4565 * and return the size of the record.
4566 */
4567 static int
4568 buildthread(void *keyp, void *recp, int std_hfs, int directory)
4569 {
4570 int size = 0;
4571
4572 if (std_hfs == 0) {
4573 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
4574 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
4575
4576 size = sizeof(HFSPlusCatalogThread);
4577 if (directory)
4578 rec->recordType = kHFSPlusFolderThreadRecord;
4579 else
4580 rec->recordType = kHFSPlusFileThreadRecord;
4581 rec->reserved = 0;
4582 rec->parentID = key->parentID;
4583 bcopy(&key->nodeName, &rec->nodeName,
4584 sizeof(UniChar) * (key->nodeName.length + 1));
4585
4586 /* HFS Plus has variable sized thread records */
4587 size -= (sizeof(rec->nodeName.unicode) -
4588 (rec->nodeName.length * sizeof(UniChar)));
4589
4590 }
4591 #if CONFIG_HFS_STD
4592 else {
4593 HFSCatalogKey *key = (HFSCatalogKey *)keyp;
4594 HFSCatalogThread *rec = (HFSCatalogThread *)recp;
4595
4596 size = sizeof(HFSCatalogThread);
4597 bzero(rec, size);
4598 if (directory)
4599 rec->recordType = kHFSFolderThreadRecord;
4600 else
4601 rec->recordType = kHFSFileThreadRecord;
4602 rec->parentID = key->parentID;
4603 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
4604
4605 }
4606 #endif
4607
4608 return (size);
4609 }
4610
4611 /*
4612 * Build a catalog node thread key.
4613 */
4614 static void
4615 buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
4616 {
4617 if (std_hfs == 0) {
4618 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
4619 key->hfsPlus.parentID = parentID;
4620 key->hfsPlus.nodeName.length = 0;
4621 }
4622 #if CONFIG_HFS_STD
4623 else {
4624 key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
4625 key->hfs.reserved = 0;
4626 key->hfs.parentID = parentID;
4627 key->hfs.nodeName[0] = 0;
4628 }
4629 #endif
4630
4631 }
4632
4633 /*
4634 * Extract the text encoding from a catalog node record.
4635 */
4636 static u_int32_t
4637 getencoding(const CatalogRecord *crp)
4638 {
4639 u_int32_t encoding;
4640
4641 if (crp->recordType == kHFSPlusFolderRecord)
4642 encoding = crp->hfsPlusFolder.textEncoding;
4643 else if (crp->recordType == kHFSPlusFileRecord)
4644 encoding = crp->hfsPlusFile.textEncoding;
4645 else
4646 encoding = 0;
4647
4648 return (encoding);
4649 }
4650
4651 /*
4652 * Extract the CNID from a catalog node record.
4653 */
4654 static cnid_t
4655 getcnid(const CatalogRecord *crp)
4656 {
4657 cnid_t cnid = 0;
4658
4659 switch (crp->recordType) {
4660
4661 #if CONFIG_HFS_STD
4662 case kHFSFolderRecord:
4663 cnid = crp->hfsFolder.folderID;
4664 break;
4665 case kHFSFileRecord:
4666 cnid = crp->hfsFile.fileID;
4667 break;
4668 #endif
4669
4670 case kHFSPlusFolderRecord:
4671 cnid = crp->hfsPlusFolder.folderID;
4672 break;
4673 case kHFSPlusFileRecord:
4674 cnid = crp->hfsPlusFile.fileID;
4675 break;
4676 default:
4677 printf("hfs: getcnid: unknown recordType=%d\n", crp->recordType);
4678 break;
4679 }
4680
4681 return (cnid);
4682 }
4683
4684 /*
4685 * Extract the parent ID from a catalog node record.
4686 */
4687 static cnid_t
4688 getparentcnid(const CatalogRecord *recp)
4689 {
4690 cnid_t cnid = 0;
4691
4692 switch (recp->recordType) {
4693
4694 #if CONFIG_HFS_STD
4695 case kHFSFileThreadRecord:
4696 case kHFSFolderThreadRecord:
4697 cnid = recp->hfsThread.parentID;
4698 break;
4699 #endif
4700
4701 case kHFSPlusFileThreadRecord:
4702 case kHFSPlusFolderThreadRecord:
4703 cnid = recp->hfsPlusThread.parentID;
4704 break;
4705 default:
4706 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp);
4707 break;
4708 }
4709
4710 return (cnid);
4711 }
4712
4713 /*
4714 * Determine if a catalog node record is a directory.
4715 */
4716 static int
4717 isadir(const CatalogRecord *crp)
4718 {
4719 if (crp->recordType == kHFSPlusFolderRecord) {
4720 return 1;
4721 }
4722 #if CONFIG_HFS_STD
4723 if (crp->recordType == kHFSFolderRecord) {
4724 return 1;
4725 }
4726 #endif
4727
4728 return 0;
4729 }
4730
4731 /*
4732 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4733 * (not inode) using catalog record id. Note that this function does
4734 * NOT resolve directory hard link to its directory inode and return
4735 * the link record.
4736 *
4737 * Note: The caller is responsible for releasing the output catalog
4738 * descriptor (when supplied outdescp is non-null).
4739 */
4740 int
4741 cat_lookup_dirlink(struct hfsmount *hfsmp, cnid_t dirlink_id,
4742 u_int8_t forktype, struct cat_desc *outdescp,
4743 struct cat_attr *attrp, struct cat_fork *forkp)
4744 {
4745 struct BTreeIterator *iterator = NULL;
4746 FSBufferDescriptor btdata;
4747 u_int16_t datasize;
4748 CatalogKey *keyp;
4749 CatalogRecord *recp = NULL;
4750 int error;
4751
4752 /* No directory hard links on standard HFS */
4753 if (hfsmp->vcbSigWord == kHFSSigWord) {
4754 return ENOTSUP;
4755 }
4756
4757 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
4758 if (iterator == NULL) {
4759 return ENOMEM;
4760 }
4761 bzero(iterator, sizeof(*iterator));
4762 buildthreadkey(dirlink_id, 1, (CatalogKey *)&iterator->key);
4763
4764 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
4765 if (recp == NULL) {
4766 error = ENOMEM;
4767 goto out;
4768 }
4769 BDINIT(btdata, recp);
4770
4771 error = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
4772 &btdata, &datasize, iterator);
4773 if (error) {
4774 goto out;
4775 }
4776 /* Directory hard links are catalog file record */
4777 if (recp->recordType != kHFSPlusFileThreadRecord) {
4778 error = ENOENT;
4779 goto out;
4780 }
4781
4782 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
4783 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
4784 (keyp->hfsPlus.nodeName.length * 2);
4785 if (forktype == kHFSResourceForkType) {
4786 /* Lookup resource fork for directory hard link */
4787 error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, true, outdescp, attrp, forkp, NULL);
4788 } else {
4789 /* Lookup data fork, if any, for directory hard link */
4790 error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, false, outdescp, attrp, forkp, NULL);
4791 }
4792 if (error) {
4793 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id, error);
4794 hfs_mark_volume_inconsistent(hfsmp);
4795 goto out;
4796 }
4797 /* Just for sanity, make sure that id in catalog record and thread record match */
4798 if ((outdescp != NULL) && (dirlink_id != outdescp->cd_cnid)) {
4799 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id, outdescp->cd_cnid);
4800 hfs_mark_volume_inconsistent(hfsmp);
4801 error = ENOENT;
4802 }
4803
4804 out:
4805 if (recp) {
4806 FREE(recp, M_TEMP);
4807 }
4808 FREE(iterator, M_TEMP);
4809
4810 return MacToVFSError(error);
4811 }
4812
4813 /*
4814 * cnode_update_dirlink - update the catalog node for directory hard link
4815 * described by descp using the data from attrp and forkp.
4816 */
4817 int
4818 cat_update_dirlink(struct hfsmount *hfsmp, u_int8_t forktype,
4819 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp)
4820 {
4821 if (forktype == kHFSResourceForkType) {
4822 return cat_update_internal(hfsmp, true, descp, attrp, NULL, forkp);
4823 } else {
4824 return cat_update_internal(hfsmp, true, descp, attrp, forkp, NULL);
4825 }
4826 }
4827