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