1/*
2 * Copyright (c) 2004-2007 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/param.h>
30#include <sys/systm.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33#include <sys/ubc.h>
34#include <sys/utfconv.h>
35#include <sys/vnode.h>
36#include <sys/xattr.h>
37#include <sys/fcntl.h>
38#include <sys/vnode_internal.h>
39#include <sys/kauth.h>
40
41#include "hfs.h"
42#include "hfs_cnode.h"
43#include "hfs_mount.h"
44#include "hfs_format.h"
45#include "hfs_endian.h"
46#include "hfs_btreeio.h"
47#include "hfs_fsctl.h"
48
49#include "hfscommon/headers/BTreesInternal.h"
50
51#define HFS_XATTR_VERBOSE  0
52
53#define  ATTRIBUTE_FILE_NODE_SIZE   8192
54
55
56/* State information for the listattr_callback callback function. */
57struct listattr_callback_state {
58	u_int32_t   fileID;
59	int         result;
60	uio_t       uio;
61	size_t      size;
62};
63
64#define HFS_MAXATTRIBUTESIZE    (128 * 1024)
65#define HFS_MAXATTRBLKS         (32 * 1024)
66
67
68/* HFS Internal Names */
69#define	XATTR_EXTENDEDSECURITY_NAME   "system.extendedsecurity"
70#define XATTR_XATTREXTENTS_NAME	      "system.xattrextents"
71
72/* Faster version if we already know this is the data fork. */
73#define RSRC_FORK_EXISTS(CP)   \
74	(((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0)
75
76static u_int32_t emptyfinfo[8] = {0};
77
78const char hfs_attrdatafilename[] = "Attribute Data";
79
80static int  listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data,
81                       struct listattr_callback_state *state);
82
83static int  remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator);
84
85static int  getnodecount(struct hfsmount *hfsmp, size_t nodesize);
86
87static size_t  getmaxinlineattrsize(struct vnode * attrvp);
88
89static int  read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents);
90
91static int  write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents);
92
93static int  alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks);
94
95static void  free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents);
96
97static int  has_overflow_extents(HFSPlusForkData *forkdata);
98
99static int  count_extent_blocks(int maxblks, HFSPlusExtentRecord extents);
100
101#if NAMEDSTREAMS
102/*
103 * Obtain the vnode for a stream.
104 */
105int
106hfs_vnop_getnamedstream(struct vnop_getnamedstream_args* ap)
107{
108	vnode_t vp = ap->a_vp;
109	vnode_t *svpp = ap->a_svpp;
110	struct cnode *cp;
111	int error = 0;
112
113	*svpp = NULL;
114
115	/*
116	 * We only support the "com.apple.ResourceFork" stream.
117	 */
118	if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
119		return (ENOATTR);
120	}
121	cp = VTOC(vp);
122	if ( !S_ISREG(cp->c_mode) ) {
123		return (EPERM);
124	}
125	if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
126		return (error);
127	}
128	if (!RSRC_FORK_EXISTS(cp) && (ap->a_operation != NS_OPEN)) {
129		hfs_unlock(cp);
130		return (ENOATTR);
131	}
132	error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE);
133	hfs_unlock(cp);
134
135	return (error);
136}
137
138/*
139 * Create a stream.
140 */
141int
142hfs_vnop_makenamedstream(struct vnop_makenamedstream_args* ap)
143{
144	vnode_t vp = ap->a_vp;
145	vnode_t *svpp = ap->a_svpp;
146	struct cnode *cp;
147	int error = 0;
148
149	*svpp = NULL;
150
151	/*
152	 * We only support the "com.apple.ResourceFork" stream.
153	 */
154	if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
155		return (ENOATTR);
156	}
157	cp = VTOC(vp);
158	if ( !S_ISREG(cp->c_mode) ) {
159		return (EPERM);
160	}
161	if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
162		return (error);
163	}
164	error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE);
165	hfs_unlock(cp);
166
167	return (error);
168}
169
170/*
171 * Remove a stream.
172 */
173int
174hfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap)
175{
176	vnode_t svp = ap->a_svp;
177	struct cnode *scp;
178	int error = 0;
179
180	/*
181	 * We only support the "com.apple.ResourceFork" stream.
182	 */
183	if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
184		return (ENOATTR);
185	}
186	scp = VTOC(svp);
187
188	/* Take truncate lock before taking cnode lock. */
189	hfs_lock_truncate(scp, TRUE);
190	if ((error = hfs_lock(scp, HFS_EXCLUSIVE_LOCK))) {
191		goto out;
192	}
193	if (VTOF(svp)->ff_size != 0) {
194		error = hfs_truncate(svp, 0, IO_NDELAY, 0, ap->a_context);
195	}
196	hfs_unlock(scp);
197out:
198	hfs_unlock_truncate(scp, TRUE);
199	return (error);
200}
201#endif
202
203
204/*
205 * Retrieve the data of an extended attribute.
206 */
207__private_extern__
208int
209hfs_vnop_getxattr(struct vnop_getxattr_args *ap)
210/*
211	struct vnop_getxattr_args {
212		struct vnodeop_desc *a_desc;
213		vnode_t a_vp;
214		char * a_name;
215		uio_t a_uio;
216		size_t *a_size;
217		int a_options;
218		vfs_context_t a_context;
219	};
220*/
221{
222	struct vnode *vp = ap->a_vp;
223	struct cnode *cp;
224	struct hfsmount *hfsmp;
225	uio_t uio = ap->a_uio;
226	struct BTreeIterator * iterator = NULL;
227	struct filefork *btfile;
228	FSBufferDescriptor btdata;
229	HFSPlusAttrRecord * recp = NULL;
230	size_t bufsize;
231	u_int16_t datasize;
232	int lockflags;
233	int result;
234
235	cp = VTOC(vp);
236	if (vp == cp->c_vp) {
237		/* Get the Finder Info. */
238		if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
239			u_int8_t finderinfo[32];
240			bufsize = 32;
241
242			if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) {
243				return (result);
244			}
245			/* Make a copy since we may not export all of it. */
246			bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
247			hfs_unlock(cp);
248
249			/* Don't expose a symlink's private type/creator. */
250			if (vnode_islnk(vp)) {
251				struct FndrFileInfo *fip;
252
253				fip = (struct FndrFileInfo *)&finderinfo;
254				fip->fdType = 0;
255				fip->fdCreator = 0;
256			}
257			/* If Finder Info is empty then it doesn't exist. */
258			if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
259				return (ENOATTR);
260			}
261			if (uio == NULL) {
262				*ap->a_size = bufsize;
263				return (0);
264			}
265			if (uio_resid(uio) < bufsize)
266				return (ERANGE);
267
268			result = uiomove((caddr_t)&finderinfo , bufsize, uio);
269
270			return (result);
271		}
272		/* Read the Resource Fork. */
273		if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
274			struct vnode *rvp = NULL;
275
276			if ( !S_ISREG(cp->c_mode) ) {
277				return (EPERM);
278			}
279			if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
280				return (result);
281			}
282			if ( !RSRC_FORK_EXISTS(cp)) {
283				hfs_unlock(cp);
284				return (ENOATTR);
285			}
286			hfsmp = VTOHFS(vp);
287			result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
288			hfs_unlock(cp);
289			if (result) {
290				return (result);
291			}
292			if (uio == NULL) {
293				*ap->a_size = (size_t)VTOF(rvp)->ff_size;
294			} else {
295				result = VNOP_READ(rvp, uio, 0, ap->a_context);
296			}
297			vnode_put(rvp);
298			return (result);
299		}
300	}
301	hfsmp = VTOHFS(vp);
302	/*
303	 * Standard HFS only supports native FinderInfo and Resource Forks.
304	 */
305	if (hfsmp->hfs_flags & HFS_STANDARD) {
306		return (EPERM);
307	}
308
309	if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) {
310		return (result);
311	}
312	/* Bail if we don't have any extended attributes. */
313	if ((hfsmp->hfs_attribute_vp == NULL) ||
314	    (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
315	    	result = ENOATTR;
316		goto exit;
317	}
318	btfile = VTOF(hfsmp->hfs_attribute_vp);
319
320	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
321	if (iterator == NULL) {
322		result = ENOMEM;
323		goto exit;
324	}
325	bzero(iterator, sizeof(*iterator));
326
327	bufsize = sizeof(HFSPlusAttrData) - 2;
328	if (uio)
329		bufsize += uio_resid(uio);
330	bufsize = MAX(bufsize, sizeof(HFSPlusAttrRecord));
331	MALLOC(recp, HFSPlusAttrRecord *, bufsize, M_TEMP, M_WAITOK);
332	if (recp == NULL) {
333		result = ENOMEM;
334		goto exit;
335	}
336	btdata.bufferAddress = recp;
337	btdata.itemSize = bufsize;
338	btdata.itemCount = 1;
339
340	result = hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
341	if (result)
342		goto exit;
343
344	/* Lookup the attribute. */
345	lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
346	result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
347	hfs_systemfile_unlock(hfsmp, lockflags);
348
349	if (result) {
350		if (result == btNotFound)
351			result = ENOATTR;
352		goto exit;
353	}
354
355	switch (recp->recordType) {
356	case kHFSPlusAttrInlineData:
357		/*
358		 * Sanity check record size. It's not required to have any
359		 * user data, so the minimum size is 2 bytes less that the
360		 * size of HFSPlusAttrData (since HFSPlusAttrData struct
361		 * has 2 bytes set aside for attribute data).
362		 */
363		if (datasize < (sizeof(HFSPlusAttrData) - 2)) {
364			printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
365				VTOC(vp)->c_fileid, ap->a_name, datasize, sizeof(HFSPlusAttrData));
366			result = ENOATTR;
367			break;
368		}
369		*ap->a_size = recp->attrData.attrSize;
370		if (uio && recp->attrData.attrSize != 0) {
371			if (*ap->a_size > uio_resid(uio))
372				result = ERANGE;
373			else
374				result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio);
375		}
376		break;
377
378	case kHFSPlusAttrForkData:
379		if (datasize < sizeof(HFSPlusAttrForkData)) {
380			printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
381				VTOC(vp)->c_fileid, ap->a_name, datasize, sizeof(HFSPlusAttrForkData));
382			result = ENOATTR;
383			break;
384		}
385		*ap->a_size = recp->forkData.theFork.logicalSize;
386		if (uio == NULL) {
387			break;
388		}
389		if (*ap->a_size > uio_resid(uio)) {
390			result = ERANGE;
391			break;
392		}
393		/* Process overflow extents if necessary. */
394		if (has_overflow_extents(&recp->forkData.theFork)) {
395			HFSPlusExtentDescriptor *extentbuf;
396			HFSPlusExtentDescriptor *extentptr;
397			size_t extentbufsize;
398			u_int32_t totalblocks;
399			u_int32_t blkcnt;
400			u_int32_t attrlen;
401
402			totalblocks = recp->forkData.theFork.totalBlocks;
403			/* Ignore bogus block counts. */
404			if (totalblocks > HFS_MAXATTRBLKS) {
405				result = ERANGE;
406				break;
407			}
408			attrlen = recp->forkData.theFork.logicalSize;
409
410			/* Get a buffer to hold the worst case amount of extents. */
411			extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor);
412			extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
413			MALLOC(extentbuf, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
414			if (extentbuf == NULL) {
415				result = ENOMEM;
416				break;
417			}
418			bzero(extentbuf, extentbufsize);
419			extentptr = extentbuf;
420
421			/* Grab the first 8 extents. */
422			bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
423			extentptr += kHFSPlusExtentDensity;
424			blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents);
425
426			/* Now lookup the overflow extents. */
427			lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
428			while (blkcnt < totalblocks) {
429				((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt;
430				result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
431				if (result ||
432				    (recp->recordType != kHFSPlusAttrExtents) ||
433				    (datasize < sizeof(HFSPlusAttrExtents))) {
434					printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
435						ap->a_name, blkcnt, totalblocks);
436					result = ENOATTR;
437					break;   /* break from while */
438				}
439				/* Grab the next 8 extents. */
440				bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
441				extentptr += kHFSPlusExtentDensity;
442				blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents);
443			}
444			hfs_systemfile_unlock(hfsmp, lockflags);
445
446			if (blkcnt < totalblocks) {
447				result = ENOATTR;
448			} else {
449				result = read_attr_data(hfsmp, uio, attrlen, extentbuf);
450			}
451			FREE(extentbuf, M_TEMP);
452
453		} else /* No overflow extents. */ {
454			result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents);
455		}
456		break;
457
458	default:
459		result = ENOATTR;
460		break;
461	}
462exit:
463	hfs_unlock(cp);
464
465	if (iterator) {
466		FREE(iterator, M_TEMP);
467	}
468	if (recp) {
469		FREE(recp, M_TEMP);
470	}
471
472	return MacToVFSError(result);
473}
474
475/*
476 * Set the data of an extended attribute.
477 */
478__private_extern__
479int
480hfs_vnop_setxattr(struct vnop_setxattr_args *ap)
481/*
482	struct vnop_setxattr_args {
483		struct vnodeop_desc *a_desc;
484		vnode_t a_vp;
485		char * a_name;
486		uio_t a_uio;
487		int a_options;
488		vfs_context_t a_context;
489	};
490*/
491{
492	struct vnode *vp = ap->a_vp;
493	struct cnode *cp = NULL;
494	struct hfsmount *hfsmp;
495	uio_t uio = ap->a_uio;
496	struct BTreeIterator * iterator = NULL;
497	struct filefork *btfile = NULL;
498	size_t attrsize;
499	FSBufferDescriptor btdata;
500	HFSPlusAttrRecord *recp = NULL;
501	HFSPlusExtentDescriptor *extentptr = NULL;
502	HFSPlusAttrRecord attrdata;  /* 90 bytes */
503	void * user_data_ptr = NULL;
504	int started_transaction = 0;
505	int lockflags = 0;
506	int exists;
507	int allocatedblks = 0;
508	int result;
509
510	if (ap->a_name == NULL || ap->a_name[0] == '\0') {
511		return (EINVAL);  /* invalid name */
512	}
513	hfsmp = VTOHFS(vp);
514	if (VNODE_IS_RSRC(vp)) {
515		return (EPERM);
516	}
517	/* Set the Finder Info. */
518	if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
519		u_int8_t finderinfo[32];
520		struct FndrFileInfo *fip;
521		void * finderinfo_start;
522		u_int16_t fdFlags;
523
524		attrsize = sizeof(VTOC(vp)->c_finderinfo);
525
526		if (uio_resid(uio) != attrsize) {
527			return (ERANGE);
528		}
529		/* Grab the new Finder Info data. */
530		if ((result = uiomove((caddr_t)&finderinfo , attrsize, uio))) {
531			return (result);
532		}
533		fip = (struct FndrFileInfo *)&finderinfo;
534
535		if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
536			return (result);
537		}
538		cp = VTOC(vp);
539
540		/* Symlink's don't have an external type/creator. */
541		if (vnode_islnk(vp)) {
542			/* Skip over type/creator fields. */
543			finderinfo_start = &cp->c_finderinfo[8];
544			attrsize -= 8;
545		} else {
546			finderinfo_start = &cp->c_finderinfo[0];
547			/*
548			 * Don't allow the external setting of
549			 * file type to kHardLinkFileType.
550			 */
551			if (fip->fdType == SWAP_BE32(kHardLinkFileType)) {
552				hfs_unlock(cp);
553				return (EPERM);
554			}
555		}
556
557		if (bcmp(finderinfo_start, emptyfinfo, attrsize)) {
558			/* attr exists and "create" was specified. */
559			if (ap->a_options & XATTR_CREATE) {
560				hfs_unlock(cp);
561				return (EEXIST);
562			}
563		} else /* empty */ {
564			/* attr doesn't exists and "replace" was specified. */
565			if (ap->a_options & XATTR_REPLACE) {
566				hfs_unlock(cp);
567				return (ENOATTR);
568			}
569		}
570		/* Set the cnode's Finder Info. */
571		if (attrsize == sizeof(cp->c_finderinfo))
572			bcopy(&finderinfo[0], finderinfo_start, attrsize);
573		else
574			bcopy(&finderinfo[8], finderinfo_start, attrsize);
575
576		/* Updating finderInfo updates change time and modified time */
577		cp->c_touch_chgtime = TRUE;
578		cp->c_flag |= C_MODIFIED;
579
580		/*
581		 * Mirror the invisible bit to the UF_HIDDEN flag.
582		 *
583		 * The fdFlags for files and frFlags for folders are both 8 bytes
584		 * into the userInfo (the first 16 bytes of the Finder Info).  They
585		 * are both 16-bit fields.
586		 */
587		fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]);
588		if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
589			cp->c_flags |= UF_HIDDEN;
590		else
591			cp->c_flags &= ~UF_HIDDEN;
592
593		result = hfs_update(vp, FALSE);
594
595		hfs_unlock(cp);
596		return (result);
597	}
598	/* Write the Resource Fork. */
599	if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
600		struct vnode *rvp = NULL;
601
602		if (!vnode_isreg(vp)) {
603			return (EPERM);
604		}
605		if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
606			return (result);
607		}
608		cp = VTOC(vp);
609
610		if (RSRC_FORK_EXISTS(cp)) {
611			/* attr exists and "create" was specified. */
612			if (ap->a_options & XATTR_CREATE) {
613				hfs_unlock(cp);
614				return (EEXIST);
615			}
616		} else {
617			/* attr doesn't exists and "replace" was specified. */
618			if (ap->a_options & XATTR_REPLACE) {
619				hfs_unlock(cp);
620				return (ENOATTR);
621			}
622		}
623		result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
624		hfs_unlock(cp);
625		if (result) {
626			return (result);
627		}
628		/* VNOP_WRITE will update timestamps accordingly */
629		result = VNOP_WRITE(rvp, uio, 0, ap->a_context);
630		vnode_put(rvp);
631		return (result);
632	}
633	/*
634	 * Standard HFS only supports native FinderInfo and Resource Forks.
635	 */
636	if (hfsmp->hfs_flags & HFS_STANDARD) {
637		return (EPERM);
638	}
639	attrsize = uio_resid(uio);
640
641	/* Enforce an upper limit. */
642	if (attrsize > HFS_MAXATTRIBUTESIZE) {
643		return (E2BIG);
644	}
645
646	/*
647	 * Attempt to copy the users attr data before taking any locks.
648	 */
649	if (attrsize > 0 &&
650	    hfsmp->hfs_max_inline_attrsize != 0 &&
651	    attrsize < hfsmp->hfs_max_inline_attrsize) {
652		MALLOC(user_data_ptr, void *, attrsize, M_TEMP, M_WAITOK);
653		if (user_data_ptr == NULL) {
654			return (ENOMEM);
655		}
656
657		result = uiomove((caddr_t)user_data_ptr, attrsize, uio);
658		if (result) {
659			goto exit;
660		}
661	}
662
663	result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
664	if (result) {
665		goto exit;
666	}
667	cp = VTOC(vp);
668
669	/* Start a transaction for our changes. */
670	if (hfs_start_transaction(hfsmp) != 0) {
671	    result = EINVAL;
672	    goto exit;
673	}
674	started_transaction = 1;
675
676	/*
677	 * Once we started the transaction, nobody can compete
678	 * with us, so make sure this file is still there.
679	 */
680	if (cp->c_flag & C_NOEXISTS) {
681		result = ENOENT;
682		goto exit;
683	}
684
685	/*
686	 * If there isn't an attributes b-tree then create one.
687	 */
688	if (hfsmp->hfs_attribute_vp == NULL) {
689		result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
690		                               getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
691		if (result) {
692			goto exit;
693		}
694	}
695	if (hfsmp->hfs_max_inline_attrsize == 0) {
696		hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
697	}
698
699	/* Take exclusive access to the attributes b-tree. */
700	lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
701
702	/* Build the b-tree key. */
703	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
704	if (iterator == NULL) {
705		result = ENOMEM;
706		goto exit;
707	}
708	bzero(iterator, sizeof(*iterator));
709	result = hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
710	if (result) {
711		goto exit;
712	}
713
714	/* Preflight for replace/create semantics. */
715	btfile = VTOF(hfsmp->hfs_attribute_vp);
716	btdata.bufferAddress = &attrdata;
717	btdata.itemSize = sizeof(attrdata);
718	btdata.itemCount = 1;
719	exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0;
720
721	/* Replace requires that the attribute already exists. */
722	if ((ap->a_options & XATTR_REPLACE) && !exists) {
723		result = ENOATTR;
724		goto exit;
725	}
726	/* Create requires that the attribute doesn't exist. */
727	if ((ap->a_options & XATTR_CREATE) && exists) {
728		result = EEXIST;
729		goto exit;
730	}
731	/* If it won't fit inline then use extent-based attributes. */
732	if (attrsize > hfsmp->hfs_max_inline_attrsize) {
733		size_t extentbufsize;
734		int blkcnt;
735		int extentblks;
736		u_int32_t *keystartblk;
737		int i;
738
739		/* Check if volume supports extent-based attributes */
740		if ((hfsmp->hfs_flags & HFS_XATTR_EXTENTS) == 0) {
741			result = E2BIG;
742			goto exit;
743		}
744
745		/* Get some blocks. */
746		blkcnt = howmany(attrsize, hfsmp->blockSize);
747		extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor);
748		extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
749		MALLOC(extentptr, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
750		if (extentptr == NULL) {
751			result = ENOMEM;
752			goto exit;
753		}
754		bzero(extentptr, extentbufsize);
755		result = alloc_attr_blks(hfsmp, attrsize, extentbufsize, extentptr, &allocatedblks);
756		if (result) {
757			allocatedblks = 0;
758			goto exit;  /* no more space */
759		}
760		/* Copy data into the blocks. */
761		result = write_attr_data(hfsmp, uio, attrsize, extentptr);
762		if (result) {
763			printf("hfs_setxattr: write_attr_data err (%d) %s:%s\n",
764				result, vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
765			goto exit;
766		}
767
768		/* Now remove any previous attribute. */
769		if (exists) {
770			result = remove_attribute_records(hfsmp, iterator);
771			if (result) {
772				printf("hfs_setxattr: remove_attribute_records err (%d) %s:%s\n",
773					result, vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
774				goto exit;
775			}
776		}
777
778		/* Create attribute fork data record. */
779		MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK);
780		if (recp == NULL) {
781			result = ENOMEM;
782			goto exit;
783		}
784		btdata.bufferAddress = recp;
785		btdata.itemCount = 1;
786		btdata.itemSize = sizeof(HFSPlusAttrForkData);
787
788		recp->recordType = kHFSPlusAttrForkData;
789		recp->forkData.reserved = 0;
790		recp->forkData.theFork.logicalSize = attrsize;
791		recp->forkData.theFork.clumpSize = 0;
792		recp->forkData.theFork.totalBlocks = blkcnt;
793		bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord));
794
795		(void) hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
796
797		result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
798		if (result) {
799#if HFS_XATTR_VERBOSE
800			printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
801			       MacToVFSError(result), vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
802#endif
803			goto exit;
804		}
805		extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents);
806		blkcnt -= extentblks;
807		keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
808		i = 0;
809
810		/* Create overflow extents as needed. */
811		while (blkcnt > 0) {
812			/* Initialize the key and record. */
813			*keystartblk += (u_int32_t)extentblks;
814			btdata.itemSize = sizeof(HFSPlusAttrExtents);
815			recp->recordType = kHFSPlusAttrExtents;
816			recp->overflowExtents.reserved = 0;
817
818			/* Copy the next set of extents. */
819			i += kHFSPlusExtentDensity;
820			bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord));
821
822			result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
823			if (result) {
824				printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
825					MacToVFSError(result), vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
826				goto exit;
827			}
828			extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents);
829			blkcnt -= extentblks;
830		}
831	} else /* Inline data */ {
832		if (exists) {
833			result = remove_attribute_records(hfsmp, iterator);
834			if (result) {
835				goto exit;
836			}
837		}
838
839		/* Calculate size of record rounded up to multiple of 2 bytes. */
840		btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
841		MALLOC(recp, HFSPlusAttrRecord *, btdata.itemSize, M_TEMP, M_WAITOK);
842		if (recp == NULL) {
843			result = ENOMEM;
844			goto exit;
845		}
846		recp->recordType = kHFSPlusAttrInlineData;
847		recp->attrData.reserved[0] = 0;
848		recp->attrData.reserved[1] = 0;
849		recp->attrData.attrSize = attrsize;
850
851		/* Copy in the attribute data (if any). */
852		if (attrsize > 0) {
853			if (user_data_ptr)
854				bcopy(user_data_ptr, &recp->attrData.attrData, attrsize);
855			else
856				result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio);
857			if (result) {
858				goto exit;
859			}
860		}
861
862		(void) hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
863
864		btdata.bufferAddress = recp;
865		btdata.itemCount = 1;
866		result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
867	}
868exit:
869	if (btfile && started_transaction) {
870		(void) BTFlushPath(btfile);
871	}
872	if (lockflags) {
873		hfs_systemfile_unlock(hfsmp, lockflags);
874	}
875	if (result == 0) {
876		cp = VTOC(vp);
877		/* Setting an attribute only updates change time and not
878		 * modified time of the file.
879		 */
880		cp->c_touch_chgtime = TRUE;
881		cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
882		if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
883			cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
884		}
885		(void) hfs_update(vp, 0);
886	}
887	if (started_transaction) {
888		if (result && allocatedblks) {
889			free_attr_blks(hfsmp, allocatedblks, extentptr);
890		}
891		hfs_end_transaction(hfsmp);
892	}
893	if (cp) {
894		hfs_unlock(cp);
895	}
896	if (result == 0) {
897		HFS_KNOTE(vp, NOTE_ATTRIB);
898	}
899	if (user_data_ptr) {
900		FREE(user_data_ptr, M_TEMP);
901	}
902	if (recp) {
903		FREE(recp, M_TEMP);
904	}
905	if (extentptr) {
906		FREE(extentptr, M_TEMP);
907	}
908	if (iterator) {
909		FREE(iterator, M_TEMP);
910	}
911	return (result == btNotFound ? ENOATTR : MacToVFSError(result));
912}
913
914/*
915 * Remove an extended attribute.
916 */
917__private_extern__
918int
919hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
920/*
921	struct vnop_removexattr_args {
922		struct vnodeop_desc *a_desc;
923		vnode_t a_vp;
924		char * a_name;
925		int a_options;
926		vfs_context_t a_context;
927	};
928*/
929{
930	struct vnode *vp = ap->a_vp;
931	struct cnode *cp = VTOC(vp);
932	struct hfsmount *hfsmp;
933	struct BTreeIterator * iterator = NULL;
934	int lockflags;
935	int result;
936
937	if (ap->a_name == NULL || ap->a_name[0] == '\0') {
938		return (EINVAL);  /* invalid name */
939	}
940	hfsmp = VTOHFS(vp);
941	if (VNODE_IS_RSRC(vp)) {
942		return (EPERM);
943	}
944
945	/* If Resource Fork is non-empty then truncate it. */
946	if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
947		struct vnode *rvp = NULL;
948
949		if ( !vnode_isreg(vp) ) {
950			return (EPERM);
951		}
952		if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
953			return (result);
954		}
955		if ( !RSRC_FORK_EXISTS(cp)) {
956			hfs_unlock(cp);
957			return (ENOATTR);
958		}
959		result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
960		hfs_unlock(cp);
961		if (result) {
962			return (result);
963		}
964
965		hfs_lock_truncate(VTOC(rvp), TRUE);
966		if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK))) {
967			hfs_unlock_truncate(cp, TRUE);
968			vnode_put(rvp);
969			return (result);
970		}
971
972		/* Start a transaction for encapsulating changes in
973		 * hfs_truncate() and hfs_update()
974		 */
975		if ((result = hfs_start_transaction(hfsmp))) {
976			hfs_unlock_truncate(cp, TRUE);
977			hfs_unlock(cp);
978			vnode_put(rvp);
979			return (result);
980		}
981
982		result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
983		if (result == 0) {
984			cp->c_touch_chgtime = TRUE;
985			cp->c_flag |= C_MODIFIED;
986			result = hfs_update(vp, FALSE);
987		}
988
989		hfs_end_transaction(hfsmp);
990		hfs_unlock_truncate(VTOC(rvp), TRUE);
991		hfs_unlock(VTOC(rvp));
992
993		vnode_put(rvp);
994		return (result);
995	}
996	/* Clear out the Finder Info. */
997	if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
998		void * finderinfo_start;
999		int finderinfo_size;
1000
1001		if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
1002			return (result);
1003		}
1004
1005		/* Symlink's don't have an external type/creator. */
1006		if (vnode_islnk(vp)) {
1007			/* Skip over type/creator fields. */
1008			finderinfo_start = &cp->c_finderinfo[8];
1009			finderinfo_size = sizeof(cp->c_finderinfo) - 8;
1010		} else {
1011			finderinfo_start = &cp->c_finderinfo[0];
1012			finderinfo_size = sizeof(cp->c_finderinfo);
1013		}
1014		if (bcmp(finderinfo_start, emptyfinfo, finderinfo_size) == 0) {
1015			hfs_unlock(cp);
1016			return (ENOATTR);
1017		}
1018
1019		bzero(finderinfo_start, finderinfo_size);
1020
1021		/* Updating finderInfo updates change time and modified time */
1022		cp->c_touch_chgtime = TRUE;
1023		cp->c_flag |= C_MODIFIED;
1024		hfs_update(vp, FALSE);
1025
1026		hfs_unlock(cp);
1027
1028		return (0);
1029	}
1030	/*
1031	 * Standard HFS only supports native FinderInfo and Resource Forks.
1032	 */
1033	if (hfsmp->hfs_flags & HFS_STANDARD) {
1034		return (EPERM);
1035	}
1036	if (hfsmp->hfs_attribute_vp == NULL) {
1037		return (ENOATTR);
1038	}
1039
1040	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1041	if (iterator == NULL) {
1042		return (ENOMEM);
1043	}
1044	bzero(iterator, sizeof(*iterator));
1045
1046	if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
1047		return (result);
1048	}
1049
1050	result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
1051	if (result) {
1052		goto exit;
1053	}
1054
1055	if (hfs_start_transaction(hfsmp) != 0) {
1056	    result = EINVAL;
1057	    goto exit;
1058	}
1059	lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1060
1061	result = remove_attribute_records(hfsmp, iterator);
1062
1063	hfs_systemfile_unlock(hfsmp, lockflags);
1064
1065	if (result == 0) {
1066		cp->c_touch_chgtime = TRUE;
1067
1068		lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1069
1070		/* If no more attributes exist, clear attribute bit */
1071		result = file_attribute_exist(hfsmp, cp->c_fileid);
1072		if (result == 0) {
1073			cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
1074		}
1075		if (result == EEXIST) {
1076			result = 0;
1077		}
1078
1079		hfs_systemfile_unlock(hfsmp, lockflags);
1080
1081		/* If ACL was removed, clear security bit */
1082		if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
1083			cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
1084		}
1085		(void) hfs_update(vp, 0);
1086	}
1087
1088	hfs_end_transaction(hfsmp);
1089exit:
1090	hfs_unlock(cp);
1091	if (result == 0) {
1092		HFS_KNOTE(vp, NOTE_ATTRIB);
1093	}
1094	FREE(iterator, M_TEMP);
1095	return MacToVFSError(result);
1096}
1097
1098/* Check if any attribute record exist for given fileID.  This function
1099 * is called by hfs_vnop_removexattr to determine if it should clear the
1100 * attribute bit in the catalog record or not.
1101 *
1102 * Note - you must acquire a shared lock on the attribute btree before
1103 *        calling this function.
1104 *
1105 * Output:
1106 * 	EEXIST	- If attribute record was found
1107 *	0	- Attribute was not found
1108 *	(other)	- Other error (such as EIO)
1109 */
1110int
1111file_attribute_exist(struct hfsmount *hfsmp, uint32_t fileID)
1112{
1113	HFSPlusAttrKey *key;
1114	struct BTreeIterator * iterator = NULL;
1115	struct filefork *btfile;
1116	int result = 0;
1117
1118	// if there's no attribute b-tree we sure as heck
1119	// can't have any attributes!
1120	if (hfsmp->hfs_attribute_vp == NULL) {
1121	    return false;
1122	}
1123
1124	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1125	if (iterator == NULL) {
1126		result = ENOMEM;
1127		goto out;
1128	}
1129	bzero(iterator, sizeof(*iterator));
1130	key = (HFSPlusAttrKey *)&iterator->key;
1131
1132	result = hfs_buildattrkey(fileID, NULL, key);
1133	if (result) {
1134		goto out;
1135	}
1136
1137	btfile = VTOF(hfsmp->hfs_attribute_vp);
1138	result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1139	if (result && (result != btNotFound)) {
1140		goto out;
1141	}
1142
1143	result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1144	/* If no next record was found or fileID for next record did not match,
1145	 * no more attributes exist for this fileID
1146	 */
1147	if ((result && (result == btNotFound)) || (key->fileID != fileID)) {
1148		result = 0;
1149	} else {
1150		result = EEXIST;
1151	}
1152
1153out:
1154	if (iterator) {
1155		FREE(iterator, M_TEMP);
1156	}
1157	return result;
1158}
1159
1160
1161/*
1162 * Remove all the records for a given attribute.
1163 *
1164 * - Used by hfs_vnop_removexattr, hfs_vnop_setxattr and hfs_removeallattr.
1165 * - A transaction must have been started.
1166 * - The Attribute b-tree file must be locked exclusive.
1167 * - The Allocation Bitmap file must be locked exclusive.
1168 * - The iterator key must be initialized.
1169 */
1170static int
1171remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator)
1172{
1173	struct filefork *btfile;
1174	FSBufferDescriptor btdata;
1175	HFSPlusAttrRecord attrdata;  /* 90 bytes */
1176	u_int16_t datasize;
1177	int result;
1178
1179	btfile = VTOF(hfsmp->hfs_attribute_vp);
1180
1181	btdata.bufferAddress = &attrdata;
1182	btdata.itemSize = sizeof(attrdata);
1183	btdata.itemCount = 1;
1184	result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1185	if (result) {
1186		goto exit; /* no records. */
1187	}
1188	/*
1189	 * Free the blocks from extent based attributes.
1190	 *
1191	 * Note that the block references (btree records) are removed
1192	 * before releasing the blocks in the allocation bitmap.
1193	 */
1194	if (attrdata.recordType == kHFSPlusAttrForkData) {
1195		int totalblks;
1196		int extentblks;
1197		u_int32_t *keystartblk;
1198
1199#if HFS_XATTR_VERBOSE
1200		if (datasize < sizeof(HFSPlusAttrForkData)) {
1201			printf("remove_attribute_records: bad record size %d (expecting %d)\n", datasize, sizeof(HFSPlusAttrForkData));
1202		}
1203#endif
1204		totalblks = attrdata.forkData.theFork.totalBlocks;
1205
1206		/* Process the first 8 extents. */
1207		extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents);
1208		if (extentblks > totalblks)
1209			panic("remove_attribute_records: corruption...");
1210		if (BTDeleteRecord(btfile, iterator) == 0) {
1211			free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents);
1212		}
1213		totalblks -= extentblks;
1214		keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
1215
1216		/* Process any overflow extents. */
1217		while (totalblks) {
1218			*keystartblk += (u_int32_t)extentblks;
1219
1220			result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
1221			if (result ||
1222			    (attrdata.recordType != kHFSPlusAttrExtents) ||
1223			    (datasize < sizeof(HFSPlusAttrExtents))) {
1224				printf("remove_attribute_records: BTSearchRecord %d (%d), totalblks %d\n",
1225					MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
1226				result = ENOATTR;
1227				break;   /* break from while */
1228			}
1229			/* Process the next 8 extents. */
1230			extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents);
1231			if (extentblks > totalblks)
1232				panic("remove_attribute_records: corruption...");
1233			if (BTDeleteRecord(btfile, iterator) == 0) {
1234				free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents);
1235			}
1236			totalblks -= extentblks;
1237		}
1238	} else {
1239		result = BTDeleteRecord(btfile, iterator);
1240	}
1241	(void) BTFlushPath(btfile);
1242exit:
1243	return (result == btNotFound ? ENOATTR :  MacToVFSError(result));
1244}
1245
1246
1247/*
1248 * Retrieve the list of extended attribute names.
1249 */
1250__private_extern__
1251int
1252hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
1253/*
1254	struct vnop_listxattr_args {
1255		struct vnodeop_desc *a_desc;
1256		vnode_t a_vp;
1257		uio_t a_uio;
1258		size_t *a_size;
1259		int a_options;
1260		vfs_context_t a_context;
1261*/
1262{
1263	struct vnode *vp = ap->a_vp;
1264	struct cnode *cp = VTOC(vp);
1265	struct hfsmount *hfsmp;
1266	uio_t uio = ap->a_uio;
1267	struct BTreeIterator * iterator = NULL;
1268	struct filefork *btfile;
1269	struct listattr_callback_state state;
1270	void * finderinfo_start;
1271	int finderinfo_size;
1272	user_addr_t user_start = 0;
1273	user_size_t user_len = 0;
1274	int lockflags;
1275	int result;
1276
1277	if (VNODE_IS_RSRC(vp)) {
1278		return (EPERM);
1279	}
1280	hfsmp = VTOHFS(vp);
1281	*ap->a_size = 0;
1282
1283	if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) {
1284		return (result);
1285	}
1286
1287	/* Don't expose a symlink's private type/creator. */
1288	if (vnode_islnk(vp)) {
1289		/* Skip over type/creator fields. */
1290		finderinfo_start = &cp->c_finderinfo[8];
1291		finderinfo_size = sizeof(cp->c_finderinfo) - 8;
1292	} else {
1293		finderinfo_start = &cp->c_finderinfo[0];
1294		finderinfo_size = sizeof(cp->c_finderinfo);
1295	}
1296	/* If Finder Info is non-empty then export it's name. */
1297	if (bcmp(finderinfo_start, emptyfinfo, finderinfo_size) != 0) {
1298		if (uio == NULL) {
1299			*ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
1300		} else if (uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
1301			result = ERANGE;
1302			goto exit;
1303		} else {
1304			result = uiomove(XATTR_FINDERINFO_NAME,
1305			                  sizeof(XATTR_FINDERINFO_NAME), uio);
1306			if (result)
1307				goto exit;
1308		}
1309	}
1310	/* If Resource Fork is non-empty then export it's name. */
1311	if (S_ISREG(cp->c_mode) && RSRC_FORK_EXISTS(cp)) {
1312		if (uio == NULL) {
1313			*ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
1314		} else if (uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
1315			result = ERANGE;
1316			goto exit;
1317		} else {
1318			result = uiomove(XATTR_RESOURCEFORK_NAME,
1319			                 sizeof(XATTR_RESOURCEFORK_NAME), uio);
1320			if (result)
1321				goto exit;
1322		}
1323	}
1324	/*
1325	 * Standard HFS only supports native FinderInfo and Resource Forks.
1326	 * Return at this point.
1327	 */
1328	if (hfsmp->hfs_flags & HFS_STANDARD) {
1329		result = 0;
1330		goto exit;
1331	}
1332	/* Bail if we don't have any extended attributes. */
1333	if ((hfsmp->hfs_attribute_vp == NULL) ||
1334	    (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
1335		result = 0;
1336		goto exit;
1337	}
1338	btfile = VTOF(hfsmp->hfs_attribute_vp);
1339
1340	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1341	if (iterator == NULL) {
1342		result = ENOMEM;
1343		goto exit;
1344	}
1345	bzero(iterator, sizeof(*iterator));
1346	result = hfs_buildattrkey(cp->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
1347	if (result)
1348		goto exit;
1349
1350	/*
1351	 * Lock the user's buffer here so that we won't fault on
1352	 * it in uiomove while holding the attributes b-tree lock.
1353	 */
1354	if (uio && uio_isuserspace(uio)) {
1355		user_start = uio_curriovbase(uio);
1356		user_len = uio_curriovlen(uio);
1357
1358		if ((result = vslock(user_start, user_len)) != 0) {
1359			user_start = 0;
1360			goto exit;
1361		}
1362	}
1363	lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1364
1365	result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1366	if (result && result != btNotFound) {
1367		hfs_systemfile_unlock(hfsmp, lockflags);
1368		goto exit;
1369	}
1370
1371	state.fileID = cp->c_fileid;
1372	state.result = 0;
1373	state.uio = uio;
1374	state.size = 0;
1375
1376	/*
1377	 * Process entries starting just after iterator->key.
1378	 */
1379	result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
1380	                          (IterateCallBackProcPtr)listattr_callback, &state);
1381	hfs_systemfile_unlock(hfsmp, lockflags);
1382	if (uio == NULL) {
1383		*ap->a_size += state.size;
1384	}
1385
1386	if (state.result || result == btNotFound)
1387		result = state.result;
1388
1389exit:
1390	if (user_start) {
1391		vsunlock(user_start, user_len, TRUE);
1392	}
1393	FREE(iterator, M_TEMP);
1394
1395	hfs_unlock(cp);
1396
1397	return MacToVFSError(result);
1398}
1399
1400
1401/*
1402 * Callback - called for each attribute
1403 */
1404static int
1405listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
1406{
1407	char attrname[XATTR_MAXNAMELEN + 1];
1408	size_t bytecount;
1409	int result;
1410
1411	if (state->fileID != key->fileID) {
1412		state->result = 0;
1413		return (0);	/* stop */
1414	}
1415	/*
1416	 * Skip over non-primary keys
1417	 */
1418	if (key->startBlock != 0) {
1419		return (1);	/* continue */
1420	}
1421
1422	/* Convert the attribute name into UTF-8. */
1423	result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
1424				(u_int8_t *)attrname, &bytecount, sizeof(attrname), '/', 0);
1425	if (result) {
1426		state->result = result;
1427		return (0);	/* stop */
1428	}
1429	bytecount++; /* account for null termination char */
1430
1431	if (xattr_protected(attrname))
1432		return (1);     /* continue */
1433
1434	if (state->uio == NULL) {
1435		state->size += bytecount;
1436	} else {
1437		if (bytecount > uio_resid(state->uio)) {
1438			state->result = ERANGE;
1439			return (0);	/* stop */
1440		}
1441		result = uiomove((caddr_t) attrname, bytecount, state->uio);
1442		if (result) {
1443			state->result = result;
1444			return (0);	/* stop */
1445		}
1446	}
1447	return (1); /* continue */
1448}
1449
1450/*
1451 * Remove all the attributes from a cnode.
1452 *
1453 * This function creates/ends its own transaction so that each
1454 * attribute is deleted in its own transaction (to avoid having
1455 * a transaction grow too large).
1456 *
1457 * This function takes the necessary locks on the attribute
1458 * b-tree file and the allocation (bitmap) file.
1459 */
1460__private_extern__
1461int
1462hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid)
1463{
1464	BTreeIterator *iterator;
1465	HFSPlusAttrKey *key;
1466	struct filefork *btfile;
1467	int result, lockflags;
1468
1469	if (hfsmp->hfs_attribute_vp == NULL) {
1470		return (0);
1471	}
1472	btfile = VTOF(hfsmp->hfs_attribute_vp);
1473
1474	MALLOC(iterator, BTreeIterator *, sizeof(BTreeIterator), M_TEMP, M_WAITOK);
1475	bzero(iterator, sizeof(BTreeIterator));
1476	key = (HFSPlusAttrKey *)&iterator->key;
1477
1478	/* Loop until there are no more attributes for this file id */
1479	for(;;) {
1480		if (hfs_start_transaction(hfsmp) != 0) {
1481			result = EINVAL;
1482			goto exit;
1483		}
1484
1485		/* Lock the attribute b-tree and the allocation (bitmap) files */
1486		lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
1487
1488		/*
1489		 * Go to first possible attribute key/record pair
1490		 */
1491		(void) hfs_buildattrkey(fileid, NULL, key);
1492		result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
1493		if (result || key->fileID != fileid) {
1494			hfs_systemfile_unlock(hfsmp, lockflags);
1495			hfs_end_transaction(hfsmp);
1496			goto exit;
1497		}
1498		result = remove_attribute_records(hfsmp, iterator);
1499
1500#if HFS_XATTR_VERBOSE
1501		if (result) {
1502			printf("hfs_removeallattr: unexpected err %d\n", result);
1503		}
1504#endif
1505		hfs_systemfile_unlock(hfsmp, lockflags);
1506		hfs_end_transaction(hfsmp);
1507		if (result)
1508			break;
1509	}
1510exit:
1511	FREE(iterator, M_TEMP);
1512	return (result == btNotFound ? 0: MacToVFSError(result));
1513}
1514
1515__private_extern__
1516void
1517hfs_xattr_init(struct hfsmount * hfsmp)
1518{
1519	/*
1520	 * If there isn't an attributes b-tree then create one.
1521	 */
1522	if (!(hfsmp->hfs_flags & HFS_STANDARD) &&
1523	    (hfsmp->hfs_attribute_vp == NULL) &&
1524	    !(hfsmp->hfs_flags & HFS_READ_ONLY)) {
1525		(void) hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
1526		                             getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
1527	}
1528	if (hfsmp->hfs_attribute_vp)
1529		hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
1530}
1531
1532/*
1533 * Enable/Disable volume attributes stored as EA for root file system.
1534 * Supported attributes are -
1535 *	1. ACLs
1536 *	2. Extent-based Extended Attributes
1537 */
1538__private_extern__
1539int
1540hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
1541{
1542	struct BTreeIterator * iterator = NULL;
1543	struct filefork *btfile;
1544	int lockflags;
1545	int result;
1546
1547	if (hfsmp->hfs_flags & HFS_STANDARD) {
1548		return (ENOTSUP);
1549	}
1550
1551	/*
1552	 * If there isn't an attributes b-tree then create one.
1553	 */
1554	if (hfsmp->hfs_attribute_vp == NULL) {
1555		result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
1556		                               getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
1557		if (result) {
1558			return (result);
1559		}
1560	}
1561
1562	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1563	if (iterator == NULL) {
1564		return (ENOMEM);
1565	}
1566	bzero(iterator, sizeof(*iterator));
1567
1568	/*
1569	 * Build a b-tree key.
1570	 * We use the root's parent id (1) to hold this volume attribute.
1571	 */
1572	if (xattrtype == HFS_SETACLSTATE) {
1573		/* ACL */
1574		(void) hfs_buildattrkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
1575		                      (HFSPlusAttrKey *)&iterator->key);
1576	} else if (xattrtype == HFS_SET_XATTREXTENTS_STATE) {
1577		/* Extent-based extended attributes */
1578		(void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
1579		                      (HFSPlusAttrKey *)&iterator->key);
1580	} else {
1581		result = EINVAL;
1582		goto exit;
1583	}
1584
1585	/* Start a transaction for our changes. */
1586	if (hfs_start_transaction(hfsmp) != 0) {
1587		result = EINVAL;
1588		goto exit;
1589	}
1590	btfile = VTOF(hfsmp->hfs_attribute_vp);
1591
1592	lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1593
1594	if (state == 0) {
1595		/* Remove the attribute. */
1596		result = BTDeleteRecord(btfile, iterator);
1597		if (result == btNotFound)
1598			result = 0;
1599	} else {
1600		FSBufferDescriptor btdata;
1601		HFSPlusAttrData attrdata;
1602		u_int16_t datasize;
1603
1604		datasize = sizeof(attrdata);
1605		btdata.bufferAddress = &attrdata;
1606		btdata.itemSize = datasize;
1607		btdata.itemCount = 1;
1608		attrdata.recordType = kHFSPlusAttrInlineData;
1609		attrdata.reserved[0] = 0;
1610		attrdata.reserved[1] = 0;
1611		attrdata.attrSize    = 2;
1612		attrdata.attrData[0] = 0;
1613		attrdata.attrData[1] = 0;
1614
1615		/* Insert the attribute. */
1616		result = BTInsertRecord(btfile, iterator, &btdata, datasize);
1617		if (result == btExists)
1618			result = 0;
1619	}
1620	(void) BTFlushPath(btfile);
1621
1622	hfs_systemfile_unlock(hfsmp, lockflags);
1623
1624	/* Finish the transaction of our changes. */
1625	hfs_end_transaction(hfsmp);
1626exit:
1627	if (iterator) {
1628		FREE(iterator, M_TEMP);
1629	}
1630	if (result == 0) {
1631		if (xattrtype == HFS_SETACLSTATE) {
1632			if (state == 0) {
1633				vfs_clearextendedsecurity(HFSTOVFS(hfsmp));
1634			} else {
1635				vfs_setextendedsecurity(HFSTOVFS(hfsmp));
1636			}
1637		} else {
1638			/* HFS_SET_XATTREXTENTS_STATE */
1639			HFS_MOUNT_LOCK(hfsmp, TRUE);
1640			if (state == 0) {
1641				hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
1642			} else {
1643				hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
1644			}
1645			HFS_MOUNT_UNLOCK(hfsmp, TRUE);
1646		}
1647	}
1648
1649	return MacToVFSError(result);
1650}
1651
1652
1653 /*
1654 * Check for volume attributes stored as EA for root file system.
1655 * Supported attributes are -
1656 *	1. ACLs
1657 *	2. Extent-based Extended Attributes
1658 */
1659__private_extern__
1660void
1661hfs_check_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype)
1662{
1663	struct BTreeIterator * iterator;
1664	struct filefork *btfile;
1665	int lockflags;
1666	int result;
1667
1668	if (hfsmp->hfs_flags & HFS_STANDARD ||
1669	    hfsmp->hfs_attribute_vp == NULL) {
1670		return;
1671	}
1672
1673	MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1674	if (iterator == NULL) {
1675		return;
1676	}
1677	bzero(iterator, sizeof(*iterator));
1678
1679	/*
1680	 * Build a b-tree key.
1681	 * We use the root's parent id (1) to hold this volume attribute.
1682	 */
1683	if (xattrtype == HFS_SETACLSTATE) {
1684		/* ACLs */
1685		(void) hfs_buildattrkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
1686	        	              (HFSPlusAttrKey *)&iterator->key);
1687	} else {
1688		/* Extent-based extended attributes */
1689		(void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
1690	        	              (HFSPlusAttrKey *)&iterator->key);
1691	}
1692	btfile = VTOF(hfsmp->hfs_attribute_vp);
1693
1694	lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1695
1696	/* Check for our attribute. */
1697	result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
1698
1699	hfs_systemfile_unlock(hfsmp, lockflags);
1700	FREE(iterator, M_TEMP);
1701
1702	if (result == 0) {
1703		if (xattrtype == HFS_SETACLSTATE) {
1704			vfs_setextendedsecurity(HFSTOVFS(hfsmp));
1705		} else {
1706			HFS_MOUNT_LOCK(hfsmp, TRUE);
1707			hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
1708			HFS_MOUNT_UNLOCK(hfsmp, TRUE);
1709		}
1710	}
1711}
1712
1713
1714/*
1715 * hfs_attrkeycompare - compare two attribute b-tree keys.
1716 *
1717 * The name portion of the key is compared using a 16-bit binary comparison.
1718 * This is called from the b-tree code.
1719 */
1720__private_extern__
1721int
1722hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
1723{
1724	u_int32_t searchFileID, trialFileID;
1725	int result;
1726
1727	searchFileID = searchKey->fileID;
1728	trialFileID = trialKey->fileID;
1729	result = 0;
1730
1731	if (searchFileID > trialFileID) {
1732		++result;
1733	} else if (searchFileID < trialFileID) {
1734		--result;
1735	} else {
1736		u_int16_t * str1 = &searchKey->attrName[0];
1737		u_int16_t * str2 = &trialKey->attrName[0];
1738		int length1 = searchKey->attrNameLen;
1739		int length2 = trialKey->attrNameLen;
1740		u_int16_t c1, c2;
1741		int length;
1742
1743		if (length1 < length2) {
1744			length = length1;
1745			--result;
1746		} else if (length1 > length2) {
1747			length = length2;
1748			++result;
1749		} else {
1750			length = length1;
1751		}
1752
1753		while (length--) {
1754			c1 = *(str1++);
1755			c2 = *(str2++);
1756
1757			if (c1 > c2) {
1758				result = 1;
1759				break;
1760			}
1761			if (c1 < c2) {
1762				result = -1;
1763				break;
1764			}
1765		}
1766		if (result)
1767			return (result);
1768		/*
1769		 * Names are equal; compare startBlock
1770		 */
1771		if (searchKey->startBlock == trialKey->startBlock)
1772			return (0);
1773		else
1774			return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
1775		}
1776
1777	return result;
1778}
1779
1780
1781/*
1782 * hfs_buildattrkey - build an Attribute b-tree key
1783 */
1784__private_extern__
1785int
1786hfs_buildattrkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
1787{
1788	int result = 0;
1789	size_t unicodeBytes = 0;
1790
1791	if (attrname != NULL) {
1792		/*
1793		 * Convert filename from UTF-8 into Unicode
1794		 */
1795		result = utf8_decodestr((const u_int8_t *)attrname, strlen(attrname), key->attrName,
1796					&unicodeBytes, sizeof(key->attrName), 0, 0);
1797		if (result) {
1798			if (result != ENAMETOOLONG)
1799				result = EINVAL;  /* name has invalid characters */
1800			return (result);
1801		}
1802		key->attrNameLen = unicodeBytes / sizeof(UniChar);
1803		key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
1804	} else {
1805		key->attrNameLen = 0;
1806		key->keyLength = kHFSPlusAttrKeyMinimumLength;
1807	}
1808	key->pad = 0;
1809	key->fileID = fileID;
1810	key->startBlock = 0;
1811
1812	return (0);
1813 }
1814
1815/*
1816 * getnodecount - calculate starting node count for attributes b-tree.
1817 */
1818static int
1819getnodecount(struct hfsmount *hfsmp, size_t nodesize)
1820{
1821	u_int64_t freebytes;
1822	u_int64_t calcbytes;
1823
1824	/*
1825	 * 10.4: Scale base on current catalog file size (20 %) up to 20 MB.
1826	 * 10.5: Attempt to be as big as the catalog clump size.
1827	 *
1828	 * Use no more than 10 % of the remaining free space.
1829	 */
1830	freebytes = (u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize;
1831
1832	calcbytes = MIN(hfsmp->hfs_catalog_cp->c_datafork->ff_size / 5, 20 * 1024 * 1024);
1833
1834	calcbytes = MAX(calcbytes, hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize);
1835
1836	calcbytes = MIN(calcbytes, freebytes / 10);
1837
1838	return (MAX(2, (int)(calcbytes / nodesize)));
1839}
1840
1841
1842/*
1843 * getmaxinlineattrsize - calculate maximum inline attribute size.
1844 *
1845 * This yields 3,802 bytes for an 8K node size.
1846 */
1847static size_t
1848getmaxinlineattrsize(struct vnode * attrvp)
1849{
1850	struct BTreeInfoRec btinfo;
1851	size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
1852	size_t maxsize;
1853
1854	if (attrvp != NULL) {
1855		(void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK);
1856		if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
1857			nodesize = btinfo.nodeSize;
1858		hfs_unlock(VTOC(attrvp));
1859	}
1860	maxsize = nodesize;
1861	maxsize -= sizeof(BTNodeDescriptor);     /* minus node descriptor */
1862	maxsize -= 3 * sizeof(u_int16_t);        /* minus 3 index slots */
1863	maxsize /= 2;                            /* 2 key/rec pairs minumum */
1864	maxsize -= sizeof(HFSPlusAttrKey);       /* minus maximum key size */
1865	maxsize -= sizeof(HFSPlusAttrData) - 2;  /* minus data header */
1866	maxsize &= 0xFFFFFFFE;                   /* multiple of 2 bytes */
1867
1868	return (maxsize);
1869}
1870
1871/*
1872 * Get a referenced vnode for attribute data I/O.
1873 */
1874static int
1875get_attr_data_vnode(struct hfsmount *hfsmp, vnode_t *vpp)
1876{
1877	vnode_t vp;
1878	int result = 0;
1879
1880	vp = hfsmp->hfs_attrdata_vp;
1881	if (vp == NULLVP) {
1882		struct cat_desc cat_desc;
1883		struct cat_attr cat_attr;
1884		struct cat_fork cat_fork;
1885
1886		/* We don't tag it as a system file since we intend to use cluster I/O. */
1887		bzero(&cat_desc, sizeof(cat_desc));
1888		cat_desc.cd_parentcnid = kHFSRootParentID;
1889		cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
1890		cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
1891		cat_desc.cd_cnid = kHFSAttributeDataFileID;
1892
1893		bzero(&cat_attr, sizeof(cat_attr));
1894		cat_attr.ca_linkcount = 1;
1895		cat_attr.ca_mode = S_IFREG;
1896		cat_attr.ca_fileid = cat_desc.cd_cnid;
1897		cat_attr.ca_blocks = hfsmp->totalBlocks;
1898
1899		/*
1900		 * The attribute data file is a virtual file that spans the
1901		 * entire file system space.
1902		 *
1903		 * Each extent-based attribute occupies a unique portion of
1904		 * in this virtual file.  The cluster I/O is done using actual
1905		 * allocation block offsets so no additional mapping is needed
1906		 * for the VNOP_BLOCKMAP call.
1907		 *
1908		 * This approach allows the attribute data to be cached without
1909		 * incurring the high cost of using a separate vnode per attribute.
1910		 *
1911		 * Since we need to acquire the attribute b-tree file lock anyways,
1912		 * the virtual file doesn't introduce any additional serialization.
1913		 */
1914		bzero(&cat_fork, sizeof(cat_fork));
1915		cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
1916		cat_fork.cf_blocks = hfsmp->totalBlocks;
1917		cat_fork.cf_extents[0].startBlock = 0;
1918		cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
1919
1920		result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr, &cat_fork, &vp);
1921		if (result == 0) {
1922			HFS_MOUNT_LOCK(hfsmp, 1);
1923			/* Check if someone raced us for creating this vnode. */
1924			if (hfsmp->hfs_attrdata_vp != NULLVP) {
1925				HFS_MOUNT_UNLOCK(hfsmp, 1);
1926				vnode_put(vp);
1927				vnode_recycle(vp);
1928				vp = hfsmp->hfs_attrdata_vp;
1929			} else {
1930				hfsmp->hfs_attrdata_vp = vp;
1931				HFS_MOUNT_UNLOCK(hfsmp, 1);
1932				/* Keep a reference on this vnode until unmount */
1933				vnode_ref_ext(vp, O_EVTONLY);
1934				hfs_unlock(VTOC(vp));
1935			}
1936		}
1937	} else {
1938		if ((result = vnode_get(vp)))
1939			vp = NULLVP;
1940	}
1941	*vpp = vp;
1942	return (result);
1943}
1944
1945/*
1946 * Read an extent based attribute.
1947 */
1948static int
1949read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
1950{
1951	vnode_t evp = NULLVP;
1952	int bufsize;
1953	int iosize;
1954	int attrsize;
1955	int blksize;
1956	int i;
1957	int result = 0;
1958
1959	if ((result = get_attr_data_vnode(hfsmp, &evp))) {
1960		return (result);
1961	}
1962	hfs_lock_truncate(VTOC(evp), 0);
1963
1964	bufsize = (int)uio_resid(uio);
1965	attrsize = (int)datasize;
1966	blksize = (int)hfsmp->blockSize;
1967
1968	/*
1969	 * Read the attribute data one extent at a time.
1970	 * For the typical case there is only one extent.
1971	 */
1972	for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
1973		iosize = (int)extents[i].blockCount * blksize;
1974		iosize = MIN(iosize, attrsize);
1975		iosize = MIN(iosize, bufsize);
1976		uio_setresid(uio, iosize);
1977		uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
1978
1979		result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT);
1980
1981#if HFS_XATTR_VERBOSE
1982		printf("read_attr_data: cr iosize %d [%d, %d] (%d)\n",
1983			iosize, extents[i].startBlock, extents[i].blockCount, result);
1984#endif
1985		if (result)
1986			break;
1987		attrsize -= iosize;
1988		bufsize -= iosize;
1989	}
1990	uio_setresid(uio, bufsize);
1991	uio_setoffset(uio, datasize);
1992
1993	hfs_unlock_truncate(VTOC(evp), 0);
1994	vnode_put(evp);
1995	return (result);
1996}
1997
1998/*
1999 * Write an extent based attribute.
2000 */
2001static int
2002write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
2003{
2004	vnode_t evp = NULLVP;
2005	off_t filesize;
2006	int bufsize;
2007	int attrsize;
2008	int iosize;
2009	int blksize;
2010	int i;
2011	int result = 0;
2012
2013	/* Get exclusive use of attribute data vnode. */
2014	if ((result = get_attr_data_vnode(hfsmp, &evp))) {
2015		return (result);
2016	}
2017	hfs_lock_truncate(VTOC(evp), 0);
2018
2019	bufsize = uio_resid(uio);
2020	attrsize = (int) datasize;
2021	blksize = (int) hfsmp->blockSize;
2022	filesize = VTOF(evp)->ff_size;
2023
2024	/*
2025	 * Write the attribute data one extent at a time.
2026	 */
2027	for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
2028		iosize = (int)extents[i].blockCount * blksize;
2029		iosize = MIN(iosize, attrsize);
2030		iosize = MIN(iosize, bufsize);
2031		uio_setresid(uio, iosize);
2032		uio_setoffset(uio, (u_int64_t)extents[i].startBlock * (u_int64_t)blksize);
2033
2034		result = cluster_write(evp, uio, filesize, filesize, filesize,
2035		                       (off_t) 0, IO_SYNC | IO_UNIT);
2036#if HFS_XATTR_VERBOSE
2037		printf("write_attr_data: cw iosize %d [%d, %d] (%d)\n",
2038			iosize, extents[i].startBlock, extents[i].blockCount, result);
2039#endif
2040		if (result)
2041			break;
2042		attrsize -= iosize;
2043		bufsize -= iosize;
2044	}
2045	uio_setresid(uio, bufsize);
2046	uio_setoffset(uio, datasize);
2047
2048	hfs_unlock_truncate(VTOC(evp), 0);
2049	vnode_put(evp);
2050	return (result);
2051}
2052
2053/*
2054 * Allocate blocks for an extent based attribute.
2055 */
2056static int
2057alloc_attr_blks(struct hfsmount *hfsmp, size_t attrsize, size_t extentbufsize, HFSPlusExtentDescriptor *extents, int *blocks)
2058{
2059	int blkcnt;
2060	int startblk;
2061	int lockflags;
2062	int i;
2063	int maxextents;
2064	int result = 0;
2065
2066	startblk = hfsmp->hfs_metazone_end;
2067	blkcnt = howmany(attrsize, hfsmp->blockSize);
2068	if (blkcnt > (int)hfs_freeblks(hfsmp, 0)) {
2069		return (ENOSPC);
2070	}
2071	*blocks = blkcnt;
2072	maxextents = extentbufsize / sizeof(HFSPlusExtentDescriptor);
2073
2074	lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2075
2076	for (i = 0; (blkcnt > 0) && (i < maxextents); i++) {
2077		result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0, 0,
2078				       &extents[i].startBlock, &extents[i].blockCount);
2079#if HFS_XATTR_VERBOSE
2080		printf("alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
2081			blkcnt, extents[i].startBlock, extents[i].blockCount, result);
2082#endif
2083		if (result) {
2084			extents[i].startBlock = 0;
2085			extents[i].blockCount = 0;
2086			break;
2087		}
2088		blkcnt -= extents[i].blockCount;
2089		startblk = extents[i].startBlock + extents[i].blockCount;
2090	}
2091	/*
2092	 * If it didn't fit in the extents buffer then bail.
2093	 */
2094	if (blkcnt) {
2095		result = ENOSPC;
2096
2097#if HFS_XATTR_VERBOSE
2098		printf("alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
2099#endif
2100		for (; i <= 0; i--) {
2101			if ((blkcnt = extents[i].blockCount) != 0) {
2102				(void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt);
2103				extents[i].startBlock = 0;
2104				extents[i].blockCount = 0;
2105		    }
2106		}
2107	}
2108
2109	hfs_systemfile_unlock(hfsmp, lockflags);
2110	return MacToVFSError(result);
2111}
2112
2113/*
2114 * Release blocks from an extent based attribute.
2115 */
2116static void
2117free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents)
2118{
2119	vnode_t evp = NULLVP;
2120	int remblks = blkcnt;
2121	int lockflags;
2122	int i;
2123
2124	if (get_attr_data_vnode(hfsmp, &evp) != 0) {
2125		evp = NULLVP;
2126	}
2127	lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2128
2129	for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) {
2130		if (extents[i].blockCount > (u_int32_t)blkcnt) {
2131#if HFS_XATTR_VERBOSE
2132			printf("free_attr_blks: skipping bad extent [%d, %d]\n",
2133				extents[i].startBlock, extents[i].blockCount);
2134#endif
2135			extents[i].blockCount = 0;
2136			continue;
2137		}
2138		if (extents[i].startBlock == 0) {
2139			break;
2140		}
2141		(void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount);
2142		extents[i].startBlock = 0;
2143		extents[i].blockCount = 0;
2144		remblks -= extents[i].blockCount;
2145
2146#if HFS_XATTR_VERBOSE
2147		printf("free_attr_blks: BlockDeallocate [%d, %d]\n",
2148		       extents[i].startBlock, extents[i].blockCount);
2149#endif
2150		/* Discard any resident pages for this block range. */
2151		if (evp) {
2152			off_t  start, end;
2153
2154			start = (u_int64_t)extents[i].startBlock * (u_int64_t)hfsmp->blockSize;
2155			end = start + (u_int64_t)extents[i].blockCount * (u_int64_t)hfsmp->blockSize;
2156			(void) ubc_msync(hfsmp->hfs_attrdata_vp, start, end, &start, UBC_INVALIDATE);
2157		}
2158	}
2159
2160	hfs_systemfile_unlock(hfsmp, lockflags);
2161	if (evp) {
2162		vnode_put(evp);
2163	}
2164}
2165
2166static int
2167has_overflow_extents(HFSPlusForkData *forkdata)
2168{
2169	u_int32_t blocks;
2170
2171	if (forkdata->extents[7].blockCount == 0)
2172		return (0);
2173
2174	blocks = forkdata->extents[0].blockCount +
2175		 forkdata->extents[1].blockCount +
2176		 forkdata->extents[2].blockCount +
2177		 forkdata->extents[3].blockCount +
2178		 forkdata->extents[4].blockCount +
2179		 forkdata->extents[5].blockCount +
2180		 forkdata->extents[6].blockCount +
2181		 forkdata->extents[7].blockCount;
2182
2183	return (forkdata->totalBlocks > blocks);
2184}
2185
2186static int
2187count_extent_blocks(int maxblks, HFSPlusExtentRecord extents)
2188{
2189	int blocks;
2190	int i;
2191
2192	for (i = 0, blocks = 0; i < kHFSPlusExtentDensity; ++i) {
2193		/* Ignore obvious bogus extents. */
2194		if (extents[i].blockCount > (u_int32_t)maxblks)
2195			continue;
2196		if (extents[i].startBlock == 0 || extents[i].blockCount == 0)
2197			break;
2198		blocks += extents[i].blockCount;
2199	}
2200	return (blocks);
2201}
2202