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