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