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