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