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