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