ufs_extattr.c revision 85845
1/*-
2 * Copyright (c) 1999-2001 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * This software was developed by Robert Watson for the TrustedBSD Project.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/ufs/ufs/ufs_extattr.c 85845 2001-11-01 21:37:07Z rwatson $
29 */
30/*
31 * Developed by the TrustedBSD Project.
32 * Support for file system extended attribute: UFS-specific support functions.
33 */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/namei.h>
39#include <sys/malloc.h>
40#include <sys/fcntl.h>
41#include <sys/proc.h>
42#include <sys/vnode.h>
43#include <sys/mount.h>
44#include <sys/lock.h>
45#include <sys/dirent.h>
46#include <sys/extattr.h>
47#include <sys/sysctl.h>
48
49#include <vm/vm_zone.h>
50
51#include <ufs/ufs/dir.h>
52#include <ufs/ufs/extattr.h>
53#include <ufs/ufs/quota.h>
54#include <ufs/ufs/ufsmount.h>
55#include <ufs/ufs/inode.h>
56#include <ufs/ufs/ufs_extern.h>
57
58#include "opt_ufs.h"
59
60#ifdef UFS_EXTATTR
61
62#define	MIN(a,b) (((a)<(b))?(a):(b))
63
64static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
65
66static int ufs_extattr_sync = 0;
67SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync,
68    0, "");
69
70static int	ufs_extattr_valid_attrname(int attrnamespace,
71    const char *attrname);
72static int	ufs_extattr_credcheck(struct vnode *vp,
73    struct ufs_extattr_list_entry *uele, struct ucred *cred,
74    struct thread *td, int access);
75static int	ufs_extattr_enable_with_open(struct ufsmount *ump,
76    struct vnode *vp, int attrnamespace, const char *attrname,
77    struct thread *td);
78static int	ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
79    const char *attrname, struct vnode *backing_vnode, struct thread *td);
80static int	ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
81    const char *attrname, struct thread *td);
82static int	ufs_extattr_get(struct vnode *vp, int attrnamespace,
83    const char *name, struct uio *uio, struct ucred *cred, struct thread *td);
84static int	ufs_extattr_set(struct vnode *vp, int attrnamespace,
85    const char *name, struct uio *uio, struct ucred *cred, struct thread *td);
86static int	ufs_extattr_rm(struct vnode *vp, int attrnamespace,
87    const char *name, struct ucred *cred, struct thread *td);
88
89/*
90 * Per-FS attribute lock protecting attribute operations.
91 * XXX Right now there is a lot of lock contention due to having a single
92 * lock per-FS; really, this should be far more fine-grained.
93 */
94static void
95ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td)
96{
97
98	/* Ideally, LK_CANRECURSE would not be used, here. */
99	lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY |
100	    LK_CANRECURSE, 0, td);
101}
102
103static void
104ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td)
105{
106
107	lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, td);
108}
109
110/*
111 * Determine whether the name passed is a valid name for an actual
112 * attribute.
113 *
114 * Invalid currently consists of:
115 *	 NULL pointer for attrname
116 *	 zero-length attrname (used to retrieve application attribute list)
117 */
118static int
119ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
120{
121
122	if (attrname == NULL)
123		return (0);
124	if (strlen(attrname) == 0)
125		return (0);
126	return (1);
127}
128
129/*
130 * Locate an attribute given a name and mountpoint.
131 * Must be holding uepm lock for the mount point.
132 */
133static struct ufs_extattr_list_entry *
134ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
135    const char *attrname)
136{
137	struct ufs_extattr_list_entry	*search_attribute;
138
139	for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
140	    search_attribute;
141	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
142		if (!(strncmp(attrname, search_attribute->uele_attrname,
143		    UFS_EXTATTR_MAXEXTATTRNAME)) &&
144		    (attrnamespace == search_attribute->uele_attrnamespace)) {
145			return (search_attribute);
146		}
147	}
148
149	return (0);
150}
151
152/*
153 * Initialize per-FS structures supporting extended attributes.  Do not
154 * start extended attributes yet.
155 */
156void
157ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
158{
159
160	uepm->uepm_flags = 0;
161
162	LIST_INIT(&uepm->uepm_list);
163	/* XXX is PVFS right, here? */
164	lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0);
165	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
166}
167
168/*
169 * Destroy per-FS structures supporting extended attributes.  Assumes
170 * that EAs have already been stopped, and will panic if not.
171 */
172void
173ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
174{
175
176	if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
177		panic("ufs_extattr_uepm_destroy: not initialized");
178
179	if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
180		panic("ufs_extattr_uepm_destroy: called while still started");
181
182	/*
183	 * It's not clear that either order for the next two lines is
184	 * ideal, and it should never be a problem if this is only called
185	 * during unmount, and with vfs_busy().
186	 */
187	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
188	lockdestroy(&uepm->uepm_lock);
189}
190
191/*
192 * Start extended attribute support on an FS.
193 */
194int
195ufs_extattr_start(struct mount *mp, struct thread *td)
196{
197	struct ufsmount	*ump;
198	int	error = 0;
199
200	ump = VFSTOUFS(mp);
201
202	ufs_extattr_uepm_lock(ump, td);
203
204	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
205		error = EOPNOTSUPP;
206		goto unlock;
207	}
208	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
209		error = EBUSY;
210		goto unlock;
211	}
212
213	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
214
215	ump->um_extattr.uepm_ucred = crhold(td->td_proc->p_ucred);
216
217unlock:
218	ufs_extattr_uepm_unlock(ump, td);
219
220	return (error);
221}
222
223#ifdef UFS_EXTATTR_AUTOSTART
224/*
225 * Helper routine: given a locked parent directory and filename, return
226 * the locked vnode of the inode associated with the name.  Will not
227 * follow symlinks, may return any type of vnode.  Lock on parent will
228 * be released even in the event of a failure.  In the event that the
229 * target is the parent (i.e., "."), there will be two references and
230 * one lock, requiring the caller to possibly special-case.
231 */
232#define	UE_GETDIR_LOCKPARENT	1
233#define	UE_GETDIR_LOCKPARENT_DONT	2
234static int
235ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname,
236    struct vnode **vp, struct thread *td)
237{
238	struct vop_cachedlookup_args vargs;
239	struct componentname cnp;
240	struct vnode *target_vp;
241	int error;
242
243	bzero(&cnp, sizeof(cnp));
244	cnp.cn_nameiop = LOOKUP;
245	cnp.cn_flags = ISLASTCN;
246	if (lockparent == UE_GETDIR_LOCKPARENT)
247		cnp.cn_flags |= LOCKPARENT;
248	cnp.cn_thread = td;
249	cnp.cn_cred = td->td_proc->p_ucred;
250	cnp.cn_pnbuf = zalloc(namei_zone);
251	cnp.cn_nameptr = cnp.cn_pnbuf;
252	error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN,
253	    (size_t *) &cnp.cn_namelen);
254	if (error) {
255		if (lockparent == UE_GETDIR_LOCKPARENT_DONT) {
256			VOP_UNLOCK(start_dvp, 0, td);
257		}
258		zfree(namei_zone, cnp.cn_pnbuf);
259		printf("ufs_extattr_lookup: copystr failed\n");
260		return (error);
261	}
262	cnp.cn_namelen--;	/* trim nul termination */
263	vargs.a_desc = NULL;
264	vargs.a_dvp = start_dvp;
265	vargs.a_vpp = &target_vp;
266	vargs.a_cnp = &cnp;
267	error = ufs_lookup(&vargs);
268	zfree(namei_zone, cnp.cn_pnbuf);
269	if (error) {
270		/*
271		 * Error condition, may have to release the lock on the parent
272		 * if ufs_lookup() didn't.
273		 */
274		if (!(cnp.cn_flags & PDIRUNLOCK) &&
275		    (lockparent == UE_GETDIR_LOCKPARENT_DONT))
276			VOP_UNLOCK(start_dvp, 0, td);
277
278		/*
279		 * Check that ufs_lookup() didn't release the lock when we
280		 * didn't want it to.
281		 */
282		if ((cnp.cn_flags & PDIRUNLOCK) &&
283		    (lockparent == UE_GETDIR_LOCKPARENT))
284			panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK");
285
286		return (error);
287	}
288/*
289	if (target_vp == start_dvp)
290		panic("ufs_extattr_lookup: target_vp == start_dvp");
291*/
292
293	if (target_vp != start_dvp &&
294	    !(cnp.cn_flags & PDIRUNLOCK) &&
295	    (lockparent == UE_GETDIR_LOCKPARENT_DONT))
296		panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK");
297
298	if ((cnp.cn_flags & PDIRUNLOCK) &&
299	    (lockparent == UE_GETDIR_LOCKPARENT))
300		panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK");
301
302	/* printf("ufs_extattr_lookup: success\n"); */
303	*vp = target_vp;
304	return (0);
305}
306#endif /* !UFS_EXTATTR_AUTOSTART */
307
308/*
309 * Enable an EA using the passed file system, backing vnode, attribute name,
310 * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
311 * to be locked when passed in.  The vnode will be returned unlocked,
312 * regardless of success/failure of the function.  As a result, the caller
313 * will always need to vrele(), but not vput().
314 */
315static int
316ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
317    int attrnamespace, const char *attrname, struct thread *td)
318{
319	int error;
320
321	error = VOP_OPEN(vp, FREAD|FWRITE, td->td_proc->p_ucred, td);
322	if (error) {
323		printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
324		    "with %d\n", error);
325		VOP_UNLOCK(vp, 0, td);
326		return (error);
327	}
328
329	/*
330	 * XXX: Note, should VOP_CLOSE() if vfs_object_create() fails, but due
331	 * to a similar piece of code in vn_open(), we don't.
332	 */
333	if (vn_canvmio(vp) == TRUE)
334		if ((error = vfs_object_create(vp, td,
335		    td->td_proc->p_ucred)) != 0) {
336			/*
337			 * XXX: bug replicated from vn_open(): should
338			 * VOP_CLOSE() here.
339			 */
340			VOP_UNLOCK(vp, 0, td);
341			return (error);
342		}
343
344	vp->v_writecount++;
345
346	vref(vp);
347
348	VOP_UNLOCK(vp, 0, td);
349
350	error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td);
351	if (error != 0)
352		vn_close(vp, FREAD|FWRITE, td->td_proc->p_ucred, td);
353	return (error);
354}
355
356#ifdef UFS_EXTATTR_AUTOSTART
357/*
358 * Given a locked directory vnode, iterate over the names in the directory
359 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
360 * attribute files.  Then invoke ufs_extattr_enable_with_open() on each
361 * to attempt to start the attribute.  Leaves the directory locked on
362 * exit.
363 */
364static int
365ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
366    int attrnamespace, struct thread *td)
367{
368	struct vop_readdir_args vargs;
369	struct dirent *dp, *edp;
370	struct vnode *attr_vp;
371	struct uio auio;
372	struct iovec aiov;
373	char *dirbuf;
374	int error, eofflag = 0;
375
376	if (dvp->v_type != VDIR)
377		return (ENOTDIR);
378
379	MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK);
380
381	auio.uio_iov = &aiov;
382	auio.uio_iovcnt = 1;
383	auio.uio_rw = UIO_READ;
384	auio.uio_segflg = UIO_SYSSPACE;
385	auio.uio_td = td;
386	auio.uio_offset = 0;
387
388	vargs.a_desc = NULL;
389	vargs.a_vp = dvp;
390	vargs.a_uio = &auio;
391	vargs.a_cred = td->td_proc->p_ucred;
392	vargs.a_eofflag = &eofflag;
393	vargs.a_ncookies = NULL;
394	vargs.a_cookies = NULL;
395
396	while (!eofflag) {
397		auio.uio_resid = DIRBLKSIZ;
398		aiov.iov_base = dirbuf;
399		aiov.iov_len = DIRBLKSIZ;
400		error = ufs_readdir(&vargs);
401		if (error) {
402			printf("ufs_extattr_iterate_directory: ufs_readdir "
403			    "%d\n", error);
404			return (error);
405		}
406
407		edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
408		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
409#if (BYTE_ORDER == LITTLE_ENDIAN)
410			dp->d_type = dp->d_namlen;
411			dp->d_namlen = 0;
412#else
413			dp->d_type = 0;
414#endif
415			if (dp->d_reclen == 0)
416				break;
417			error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,
418			    dp->d_name, &attr_vp, td);
419			if (error) {
420				printf("ufs_extattr_iterate_directory: lookup "
421				    "%s %d\n", dp->d_name, error);
422			} else if (attr_vp == dvp) {
423				vrele(attr_vp);
424			} else if (attr_vp->v_type != VREG) {
425/*
426 * Eventually, this will be uncommented, but in the mean time, the ".."
427 * entry causes unnecessary console warnings.
428				printf("ufs_extattr_iterate_directory: "
429				    "%s not VREG\n", dp->d_name);
430*/
431				vput(attr_vp);
432			} else {
433				error = ufs_extattr_enable_with_open(ump,
434				    attr_vp, attrnamespace, dp->d_name, td);
435				vrele(attr_vp);
436				if (error) {
437					printf("ufs_extattr_iterate_directory: "
438					    "enable %s %d\n", dp->d_name,
439					    error);
440				} else if (bootverbose) {
441					printf("UFS autostarted EA %s\n",
442					    dp->d_name);
443				}
444			}
445			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
446			if (dp >= edp)
447				break;
448		}
449	}
450	FREE(dirbuf, M_TEMP);
451
452	return (0);
453}
454
455/*
456 * Auto-start of extended attributes, to be executed (optionally) at
457 * mount-time.
458 */
459int
460ufs_extattr_autostart(struct mount *mp, struct thread *td)
461{
462	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
463	int error;
464
465	/*
466	 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the file system root?
467	 * If so, automatically start EA's.
468	 */
469	error = VFS_ROOT(mp, &rvp);
470	if (error) {
471		printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
472		    error);
473		return (error);
474	}
475
476	error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT,
477	    UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td);
478	if (error) {
479		/* rvp ref'd but now unlocked */
480		vrele(rvp);
481		return (error);
482	}
483	if (rvp == attr_dvp) {
484		/* Should never happen. */
485		vrele(attr_dvp);
486		vput(rvp);
487		return (EINVAL);
488	}
489	vrele(rvp);
490
491	if (attr_dvp->v_type != VDIR) {
492		printf("ufs_extattr_autostart: %s != VDIR\n",
493		    UFS_EXTATTR_FSROOTSUBDIR);
494		goto return_vput_attr_dvp;
495	}
496
497	error = ufs_extattr_start(mp, td);
498	if (error) {
499		printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
500		    error);
501		goto return_vput_attr_dvp;
502	}
503
504	/*
505	 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
506	 * UFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
507	 * and start with appropriate type.  Failures in either don't
508	 * result in an over-all failure.  attr_dvp is left locked to
509	 * be cleaned up on exit.
510	 */
511	error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
512	    UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td);
513	if (!error) {
514		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
515		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td);
516		if (error)
517			printf("ufs_extattr_iterate_directory returned %d\n",
518			    error);
519		vput(attr_system_dvp);
520	}
521
522	error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
523	    UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td);
524	if (!error) {
525		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
526		    attr_user_dvp, EXTATTR_NAMESPACE_USER, td);
527		if (error)
528			printf("ufs_extattr_iterate_directory returned %d\n",
529			    error);
530		vput(attr_user_dvp);
531	}
532
533	/* Mask startup failures in sub-directories. */
534	error = 0;
535
536return_vput_attr_dvp:
537	vput(attr_dvp);
538
539	return (error);
540}
541#endif /* !UFS_EXTATTR_AUTOSTART */
542
543/*
544 * Stop extended attribute support on an FS.
545 */
546int
547ufs_extattr_stop(struct mount *mp, struct thread *td)
548{
549	struct ufs_extattr_list_entry	*uele;
550	struct ufsmount	*ump = VFSTOUFS(mp);
551	int	error = 0;
552
553	ufs_extattr_uepm_lock(ump, td);
554
555	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
556		error = EOPNOTSUPP;
557		goto unlock;
558	}
559
560	while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
561		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
562		ufs_extattr_disable(ump, uele->uele_attrnamespace,
563		    uele->uele_attrname, td);
564	}
565
566	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
567
568	crfree(ump->um_extattr.uepm_ucred);
569	ump->um_extattr.uepm_ucred = NULL;
570
571unlock:
572	ufs_extattr_uepm_unlock(ump, td);
573
574	return (error);
575}
576
577/*
578 * Enable a named attribute on the specified file system; provide an
579 * unlocked backing vnode to hold the attribute data.
580 */
581static int
582ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
583    const char *attrname, struct vnode *backing_vnode, struct thread *td)
584{
585	struct ufs_extattr_list_entry	*attribute;
586	struct iovec	aiov;
587	struct uio	auio;
588	int	error = 0;
589
590	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
591		return (EINVAL);
592	if (backing_vnode->v_type != VREG)
593		return (EINVAL);
594
595	MALLOC(attribute, struct ufs_extattr_list_entry *,
596	    sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
597	if (attribute == NULL)
598		return (ENOMEM);
599
600	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
601		error = EOPNOTSUPP;
602		goto free_exit;
603	}
604
605	if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
606		error = EEXIST;
607		goto free_exit;
608	}
609
610	strncpy(attribute->uele_attrname, attrname,
611	    UFS_EXTATTR_MAXEXTATTRNAME);
612	attribute->uele_attrnamespace = attrnamespace;
613	bzero(&attribute->uele_fileheader,
614	    sizeof(struct ufs_extattr_fileheader));
615
616	attribute->uele_backing_vnode = backing_vnode;
617
618	auio.uio_iov = &aiov;
619	auio.uio_iovcnt = 1;
620	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
621	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
622	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
623	auio.uio_offset = (off_t) 0;
624	auio.uio_segflg = UIO_SYSSPACE;
625	auio.uio_rw = UIO_READ;
626	auio.uio_td = td;
627
628	VOP_LEASE(backing_vnode, td, td->td_proc->p_ucred, LEASE_WRITE);
629	vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, td);
630	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
631	    ump->um_extattr.uepm_ucred);
632	VOP_UNLOCK(backing_vnode, 0, td);
633
634	if (error)
635		goto free_exit;
636
637	if (auio.uio_resid != 0) {
638		printf("ufs_extattr_enable: malformed attribute header\n");
639		error = EINVAL;
640		goto free_exit;
641	}
642
643	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
644		printf("ufs_extattr_enable: invalid attribute header magic\n");
645		error = EINVAL;
646		goto free_exit;
647	}
648
649	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
650		printf("ufs_extattr_enable: incorrect attribute header "
651		    "version\n");
652		error = EINVAL;
653		goto free_exit;
654	}
655
656	backing_vnode->v_flag |= VSYSTEM;
657	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
658	    uele_entries);
659
660	return (0);
661
662free_exit:
663	FREE(attribute, M_UFS_EXTATTR);
664	return (error);
665}
666
667/*
668 * Disable extended attribute support on an FS.
669 */
670static int
671ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
672    const char *attrname, struct thread *td)
673{
674	struct ufs_extattr_list_entry	*uele;
675	int	error = 0;
676
677	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
678		return (EINVAL);
679
680	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
681	if (!uele)
682		/* XXX: ENOENT here will eventually be ENOATTR. */
683		return (ENOENT);
684
685	LIST_REMOVE(uele, uele_entries);
686
687	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
688	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
689	    td->td_proc->p_ucred, td);
690
691	FREE(uele, M_UFS_EXTATTR);
692
693	return (error);
694}
695
696/*
697 * VFS call to manage extended attributes in UFS.  If filename_vp is
698 * non-NULL, it must be passed in locked, and regardless of errors in
699 * processing, will be unlocked.
700 */
701int
702ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
703    int attrnamespace, const char *attrname, struct thread *td)
704{
705	struct ufsmount	*ump = VFSTOUFS(mp);
706	int	error;
707
708	/*
709	 * Processes with privilege, but in jail, are not allowed to
710	 * configure extended attributes.
711	 */
712	if ((error = suser_xxx(td->td_proc->p_ucred, td->td_proc, 0))) {
713		if (filename_vp != NULL)
714			VOP_UNLOCK(filename_vp, 0, td);
715		return (error);
716	}
717
718	switch(cmd) {
719	case UFS_EXTATTR_CMD_START:
720		if (filename_vp != NULL) {
721			VOP_UNLOCK(filename_vp, 0, td);
722			return (EINVAL);
723		}
724		if (attrname != NULL)
725			return (EINVAL);
726
727		error = ufs_extattr_start(mp, td);
728
729		return (error);
730
731	case UFS_EXTATTR_CMD_STOP:
732		if (filename_vp != NULL) {
733			VOP_UNLOCK(filename_vp, 0, td);
734			return (EINVAL);
735		}
736		if (attrname != NULL)
737			return (EINVAL);
738
739		error = ufs_extattr_stop(mp, td);
740
741		return (error);
742
743	case UFS_EXTATTR_CMD_ENABLE:
744
745		if (filename_vp == NULL)
746			return (EINVAL);
747		if (attrname == NULL) {
748			VOP_UNLOCK(filename_vp, 0, td);
749			return (EINVAL);
750		}
751
752		/*
753		 * ufs_extattr_enable_with_open() will always unlock the
754		 * vnode, regardless of failure.
755		 */
756		ufs_extattr_uepm_lock(ump, td);
757		error = ufs_extattr_enable_with_open(ump, filename_vp,
758		    attrnamespace, attrname, td);
759		ufs_extattr_uepm_unlock(ump, td);
760
761		return (error);
762
763	case UFS_EXTATTR_CMD_DISABLE:
764
765		if (filename_vp != NULL) {
766			VOP_UNLOCK(filename_vp, 0, td);
767			return (EINVAL);
768		}
769		if (attrname == NULL)
770			return (EINVAL);
771
772		ufs_extattr_uepm_lock(ump, td);
773		error = ufs_extattr_disable(ump, attrnamespace, attrname,
774		    td);
775		ufs_extattr_uepm_unlock(ump, td);
776
777		return (error);
778
779	default:
780		return (EINVAL);
781	}
782}
783
784/*
785 * Credential check based on process requesting service, and per-attribute
786 * permissions.
787 */
788static int
789ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
790    struct ucred *cred, struct thread *td, int access)
791{
792
793	/*
794	 * Kernel-invoked always succeeds.
795	 */
796	if (cred == NULL)
797		return (0);
798
799	/*
800	 * Do not allow privileged processes in jail to directly
801	 * manipulate system attributes.
802	 *
803	 * XXX What capability should apply here?
804	 * Probably CAP_SYS_SETFFLAG.
805	 */
806	switch (uele->uele_attrnamespace) {
807	case EXTATTR_NAMESPACE_SYSTEM:
808		/* Potentially should be: return (EPERM); */
809		return (suser_xxx(cred, td->td_proc, 0));
810	case EXTATTR_NAMESPACE_USER:
811		return (VOP_ACCESS(vp, access, cred, td));
812	default:
813		return (EPERM);
814	}
815}
816
817/*
818 * Vnode operating to retrieve a named extended attribute.
819 */
820int
821ufs_vop_getextattr(struct vop_getextattr_args *ap)
822/*
823vop_getextattr {
824	IN struct vnode *a_vp;
825	IN int a_attrnamespace;
826	IN const char *a_name;
827	INOUT struct uio *a_uio;
828	IN struct ucred *a_cred;
829	IN struct thread *a_td;
830};
831*/
832{
833	struct mount	*mp = ap->a_vp->v_mount;
834	struct ufsmount	*ump = VFSTOUFS(mp);
835	int	error;
836
837	ufs_extattr_uepm_lock(ump, ap->a_td);
838
839	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
840	    ap->a_uio, ap->a_cred, ap->a_td);
841
842	ufs_extattr_uepm_unlock(ump, ap->a_td);
843
844	return (error);
845}
846
847/*
848 * Real work associated with retrieving a named attribute--assumes that
849 * the attribute lock has already been grabbed.
850 */
851static int
852ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
853    struct uio *uio, struct ucred *cred, struct thread *td)
854{
855	struct ufs_extattr_list_entry	*attribute;
856	struct ufs_extattr_header	ueh;
857	struct iovec	local_aiov;
858	struct uio	local_aio;
859	struct mount	*mp = vp->v_mount;
860	struct ufsmount	*ump = VFSTOUFS(mp);
861	struct inode	*ip = VTOI(vp);
862	off_t	base_offset;
863	size_t	size, old_size;
864	int	error = 0;
865
866	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
867		return (EOPNOTSUPP);
868
869	if (strlen(name) == 0) {
870		/* XXX retrieve attribute lists. */
871		/* XXX should probably be checking for name == NULL? */
872		return (EINVAL);
873	}
874
875	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
876	if (!attribute)
877		/* XXX: ENOENT here will eventually be ENOATTR. */
878		return (ENOENT);
879
880	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IREAD)))
881		return (error);
882
883	/*
884	 * Allow only offsets of zero to encourage the read/replace
885	 * extended attribute semantic.  Otherwise we can't guarantee
886	 * atomicity, as we don't provide locks for extended attributes.
887	 */
888	if (uio->uio_offset != 0)
889		return (ENXIO);
890
891	/*
892	 * Find base offset of header in file based on file header size, and
893	 * data header size + maximum data size, indexed by inode number.
894	 */
895	base_offset = sizeof(struct ufs_extattr_fileheader) +
896	    ip->i_number * (sizeof(struct ufs_extattr_header) +
897	    attribute->uele_fileheader.uef_size);
898
899	/*
900	 * Read in the data header to see if the data is defined, and if so
901	 * how much.
902	 */
903	bzero(&ueh, sizeof(struct ufs_extattr_header));
904	local_aiov.iov_base = (caddr_t) &ueh;
905	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
906	local_aio.uio_iov = &local_aiov;
907	local_aio.uio_iovcnt = 1;
908	local_aio.uio_rw = UIO_READ;
909	local_aio.uio_segflg = UIO_SYSSPACE;
910	local_aio.uio_td = td;
911	local_aio.uio_offset = base_offset;
912	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
913
914	/*
915	 * Acquire locks.
916	 */
917	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_READ);
918	/*
919	 * Don't need to get a lock on the backing file if the getattr is
920	 * being applied to the backing file, as the lock is already held.
921	 */
922	if (attribute->uele_backing_vnode != vp)
923		vn_lock(attribute->uele_backing_vnode, LK_SHARED |
924		    LK_NOPAUSE | LK_RETRY, td);
925
926	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
927	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
928	if (error)
929		goto vopunlock_exit;
930
931	/* Defined? */
932	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
933		/* XXX: ENOENT here will eventually be ENOATTR. */
934		error = ENOENT;
935		goto vopunlock_exit;
936	}
937
938	/* Valid for the current inode generation? */
939	if (ueh.ueh_i_gen != ip->i_gen) {
940		/*
941		 * The inode itself has a different generation number
942		 * than the attribute data.  For now, the best solution
943		 * is to coerce this to undefined, and let it get cleaned
944		 * up by the next write or extattrctl clean.
945		 */
946		printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n",
947		    ueh.ueh_i_gen, ip->i_gen);
948		/* XXX: ENOENT here will eventually be ENOATTR. */
949		error = ENOENT;
950		goto vopunlock_exit;
951	}
952
953	/* Local size consistency check. */
954	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
955		error = ENXIO;
956		goto vopunlock_exit;
957	}
958
959	/* Allow for offset into the attribute data. */
960	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
961
962	/*
963	 * Figure out maximum to transfer -- use buffer size and local data
964	 * limit.
965	 */
966	size = MIN(uio->uio_resid, ueh.ueh_len);
967	old_size = uio->uio_resid;
968	uio->uio_resid = size;
969
970	error = VOP_READ(attribute->uele_backing_vnode, uio,
971	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
972	if (error)
973		goto vopunlock_exit;
974
975	uio->uio_resid = old_size - (size - uio->uio_resid);
976
977vopunlock_exit:
978
979	uio->uio_offset = 0;
980
981	if (attribute->uele_backing_vnode != vp)
982		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
983
984	return (error);
985}
986
987/*
988 * Vnode operation to set a named attribute.
989 */
990int
991ufs_vop_setextattr(struct vop_setextattr_args *ap)
992/*
993vop_setextattr {
994	IN struct vnode *a_vp;
995	IN int a_attrnamespace;
996	IN const char *a_name;
997	INOUT struct uio *a_uio;
998	IN struct ucred *a_cred;
999	IN struct thread *a_td;
1000};
1001*/
1002{
1003	struct mount	*mp = ap->a_vp->v_mount;
1004	struct ufsmount	*ump = VFSTOUFS(mp);
1005
1006	int	error;
1007
1008	ufs_extattr_uepm_lock(ump, ap->a_td);
1009
1010	if (ap->a_uio != NULL)
1011		error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace,
1012		    ap->a_name, ap->a_uio, ap->a_cred, ap->a_td);
1013	else
1014		error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace,
1015		    ap->a_name, ap->a_cred, ap->a_td);
1016
1017	ufs_extattr_uepm_unlock(ump, ap->a_td);
1018
1019	return (error);
1020}
1021
1022/*
1023 * Real work associated with setting a vnode's extended attributes;
1024 * assumes that the attribute lock has already been grabbed.
1025 */
1026static int
1027ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1028    struct uio *uio, struct ucred *cred, struct thread *td)
1029{
1030	struct ufs_extattr_list_entry	*attribute;
1031	struct ufs_extattr_header	ueh;
1032	struct iovec	local_aiov;
1033	struct uio	local_aio;
1034	struct mount	*mp = vp->v_mount;
1035	struct ufsmount	*ump = VFSTOUFS(mp);
1036	struct inode	*ip = VTOI(vp);
1037	off_t	base_offset;
1038	int	error = 0, ioflag;
1039
1040	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1041		return (EROFS);
1042	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1043		return (EOPNOTSUPP);
1044	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1045		return (EINVAL);
1046
1047	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1048	if (!attribute)
1049		/* XXX: ENOENT here will eventually be ENOATTR. */
1050		return (ENOENT);
1051
1052	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1053		return (error);
1054
1055	/*
1056	 * Early rejection of invalid offsets/length.
1057	 * Reject: any offset but 0 (replace)
1058	 *	 Any size greater than attribute size limit
1059 	 */
1060	if (uio->uio_offset != 0 ||
1061	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1062		return (ENXIO);
1063
1064	/*
1065	 * Find base offset of header in file based on file header size, and
1066	 * data header size + maximum data size, indexed by inode number.
1067	 */
1068	base_offset = sizeof(struct ufs_extattr_fileheader) +
1069	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1070	    attribute->uele_fileheader.uef_size);
1071
1072	/*
1073	 * Write out a data header for the data.
1074	 */
1075	ueh.ueh_len = uio->uio_resid;
1076	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
1077	ueh.ueh_i_gen = ip->i_gen;
1078	local_aiov.iov_base = (caddr_t) &ueh;
1079	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1080	local_aio.uio_iov = &local_aiov;
1081	local_aio.uio_iovcnt = 1;
1082	local_aio.uio_rw = UIO_WRITE;
1083	local_aio.uio_segflg = UIO_SYSSPACE;
1084	local_aio.uio_td = td;
1085	local_aio.uio_offset = base_offset;
1086	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1087
1088	/*
1089	 * Acquire locks.
1090	 */
1091	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1092
1093	/*
1094	 * Don't need to get a lock on the backing file if the setattr is
1095	 * being applied to the backing file, as the lock is already held.
1096	 */
1097	if (attribute->uele_backing_vnode != vp)
1098		vn_lock(attribute->uele_backing_vnode,
1099		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1100
1101	ioflag = IO_NODELOCKED;
1102	if (ufs_extattr_sync)
1103		ioflag |= IO_SYNC;
1104	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1105	    ump->um_extattr.uepm_ucred);
1106	if (error)
1107		goto vopunlock_exit;
1108
1109	if (local_aio.uio_resid != 0) {
1110		error = ENXIO;
1111		goto vopunlock_exit;
1112	}
1113
1114	/*
1115	 * Write out user data.
1116	 */
1117	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1118
1119	ioflag = IO_NODELOCKED;
1120	if (ufs_extattr_sync)
1121		ioflag |= IO_SYNC;
1122	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1123	    ump->um_extattr.uepm_ucred);
1124
1125vopunlock_exit:
1126	uio->uio_offset = 0;
1127
1128	if (attribute->uele_backing_vnode != vp)
1129		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1130
1131	return (error);
1132}
1133
1134/*
1135 * Real work associated with removing an extended attribute from a vnode.
1136 * Assumes the attribute lock has already been grabbed.
1137 */
1138static int
1139ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1140    struct ucred *cred, struct thread *td)
1141{
1142	struct ufs_extattr_list_entry	*attribute;
1143	struct ufs_extattr_header	ueh;
1144	struct iovec	local_aiov;
1145	struct uio	local_aio;
1146	struct mount	*mp = vp->v_mount;
1147	struct ufsmount	*ump = VFSTOUFS(mp);
1148	struct inode	*ip = VTOI(vp);
1149	off_t	base_offset;
1150	int	error = 0, ioflag;
1151
1152	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1153		return (EROFS);
1154	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1155		return (EOPNOTSUPP);
1156	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1157		return (EINVAL);
1158
1159	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1160	if (!attribute)
1161		/* XXX: ENOENT here will eventually be ENOATTR. */
1162		return (ENOENT);
1163
1164	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1165		return (error);
1166
1167	/*
1168	 * Find base offset of header in file based on file header size, and
1169	 * data header size + maximum data size, indexed by inode number.
1170	 */
1171	base_offset = sizeof(struct ufs_extattr_fileheader) +
1172	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1173	    attribute->uele_fileheader.uef_size);
1174
1175	/*
1176	 * Check to see if currently defined.
1177	 */
1178	bzero(&ueh, sizeof(struct ufs_extattr_header));
1179
1180	local_aiov.iov_base = (caddr_t) &ueh;
1181	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1182	local_aio.uio_iov = &local_aiov;
1183	local_aio.uio_iovcnt = 1;
1184	local_aio.uio_rw = UIO_READ;
1185	local_aio.uio_segflg = UIO_SYSSPACE;
1186	local_aio.uio_td = td;
1187	local_aio.uio_offset = base_offset;
1188	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1189
1190	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1191
1192	/*
1193	 * Don't need to get the lock on the backing vnode if the vnode we're
1194	 * modifying is it, as we already hold the lock.
1195	 */
1196	if (attribute->uele_backing_vnode != vp)
1197		vn_lock(attribute->uele_backing_vnode,
1198		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1199
1200	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
1201	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1202	if (error)
1203		goto vopunlock_exit;
1204
1205	/* Defined? */
1206	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
1207		/* XXX: ENOENT here will eventually be ENOATTR. */
1208		error = ENOENT;
1209		goto vopunlock_exit;
1210	}
1211
1212	/* Valid for the current inode generation? */
1213	if (ueh.ueh_i_gen != ip->i_gen) {
1214		/*
1215		 * The inode itself has a different generation number than
1216		 * the attribute data.  For now, the best solution is to
1217		 * coerce this to undefined, and let it get cleaned up by
1218		 * the next write or extattrctl clean.
1219		 */
1220		printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n",
1221		    ueh.ueh_i_gen, ip->i_gen);
1222		/* XXX: ENOENT here will eventually be ENOATTR. */
1223		error = ENOENT;
1224		goto vopunlock_exit;
1225	}
1226
1227	/* Flag it as not in use. */
1228	ueh.ueh_flags = 0;
1229	ueh.ueh_len = 0;
1230
1231	local_aiov.iov_base = (caddr_t) &ueh;
1232	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1233	local_aio.uio_iov = &local_aiov;
1234	local_aio.uio_iovcnt = 1;
1235	local_aio.uio_rw = UIO_WRITE;
1236	local_aio.uio_segflg = UIO_SYSSPACE;
1237	local_aio.uio_td = td;
1238	local_aio.uio_offset = base_offset;
1239	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1240
1241	ioflag = IO_NODELOCKED;
1242	if (ufs_extattr_sync)
1243		ioflag |= IO_SYNC;
1244	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1245	    ump->um_extattr.uepm_ucred);
1246	if (error)
1247		goto vopunlock_exit;
1248
1249	if (local_aio.uio_resid != 0)
1250		error = ENXIO;
1251
1252vopunlock_exit:
1253	VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1254
1255	return (error);
1256}
1257
1258/*
1259 * Called by UFS when an inode is no longer active and should have its
1260 * attributes stripped.
1261 */
1262void
1263ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
1264{
1265	struct ufs_extattr_list_entry	*uele;
1266	struct mount	*mp = vp->v_mount;
1267	struct ufsmount	*ump = VFSTOUFS(mp);
1268
1269	/*
1270	 * In that case, we cannot lock. We should not have any active vnodes
1271	 * on the fs if this is not yet initialized but is going to be, so
1272	 * this can go unlocked.
1273	 */
1274	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1275		return;
1276
1277	ufs_extattr_uepm_lock(ump, td);
1278
1279	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1280		ufs_extattr_uepm_unlock(ump, td);
1281		return;
1282	}
1283
1284	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1285		ufs_extattr_rm(vp, uele->uele_attrnamespace,
1286		    uele->uele_attrname, NULL, td);
1287
1288	ufs_extattr_uepm_unlock(ump, td);
1289}
1290
1291#endif /* !UFS_EXTATTR */
1292