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