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