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