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