1 /*        $NetBSD: ufs_extattr.c,v 1.55 2024/02/10 18:43:53 andvar Exp $        */
2 
3 /*-
4  * Copyright (c) 1999-2002 Robert N. M. Watson
5  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
6  * All rights reserved.
7  *
8  * This software was developed by Robert Watson for the TrustedBSD Project.
9  *
10  * This software was developed for the FreeBSD Project in part by Network
11  * Associates Laboratories, the Security Research Division of Network
12  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
13  * as part of the DARPA CHATS research program.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  */
37 
38 /*
39  * Support for file system extended attributes on the UFS1 file system.
40  *
41  * Extended attributes are defined in the form name=value, where name is
42  * a nul-terminated string in the style of a file name, and value is a
43  * binary blob of zero or more bytes.  The UFS1 extended attribute service
44  * layers support for extended attributes onto a backing file, in the style
45  * of the quota implementation, meaning that it requires no underlying format
46  * changes to the file system.  This design choice exchanges simplicity,
47  * usability, and easy deployment for performance.
48  */
49 
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.55 2024/02/10 18:43:53 andvar Exp $");
52 
53 #ifdef _KERNEL_OPT
54 #include "opt_ffs.h"
55 #endif
56 
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/reboot.h>
60 #include <sys/kauth.h>
61 #include <sys/kernel.h>
62 #include <sys/namei.h>
63 #include <sys/kmem.h>
64 #include <sys/fcntl.h>
65 #include <sys/lwp.h>
66 #include <sys/vnode.h>
67 #include <sys/mount.h>
68 #include <sys/lock.h>
69 #include <sys/dirent.h>
70 #include <sys/extattr.h>
71 #include <sys/sysctl.h>
72 
73 #include <ufs/ufs/dir.h>
74 #include <ufs/ufs/extattr.h>
75 #include <ufs/ufs/ufsmount.h>
76 #include <ufs/ufs/inode.h>
77 #include <ufs/ufs/ufs_bswap.h>
78 #include <ufs/ufs/ufs_extern.h>
79 
80 int ufs_extattr_sync = 1;
81 int ufs_extattr_autocreate = 1024;
82 
83 static int          ufs_extattr_valid_attrname(int attrnamespace,
84                         const char *attrname);
85 static int          ufs_extattr_enable_with_open(struct ufsmount *ump,
86                         struct vnode *vp, int attrnamespace, const char *attrname,
87                         struct lwp *l);
88 static int          ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
89                         const char *attrname, struct vnode *backing_vnode,
90                         struct lwp *l);
91 static int          ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
92                         const char *attrname, struct lwp *l);
93 static int          ufs_extattr_get(struct vnode *vp, int attrnamespace,
94                         const char *name, struct uio *uio, size_t *size,
95                         kauth_cred_t cred, struct lwp *l);
96 static int          ufs_extattr_list(struct vnode *vp, int attrnamespace,
97                         struct uio *uio, size_t *size, int flag,
98                         kauth_cred_t cred, struct lwp *l);
99 static int          ufs_extattr_set(struct vnode *vp, int attrnamespace,
100                         const char *name, struct uio *uio, kauth_cred_t cred,
101                         struct lwp *l);
102 static int          ufs_extattr_rm(struct vnode *vp, int attrnamespace,
103                         const char *name, kauth_cred_t cred, struct lwp *l);
104 static struct ufs_extattr_list_entry *ufs_extattr_find_attr(struct ufsmount *,
105                         int, const char *);
106 static int          ufs_extattr_get_header(struct vnode *,
107                         struct ufs_extattr_list_entry *,
108                         struct ufs_extattr_header *, off_t *);
109 
110 
111 /*
112  * Per-FS attribute lock protecting attribute operations.
113  * XXX Right now there is a lot of lock contention due to having a single
114  * lock per-FS; really, this should be far more fine-grained.
115  */
116 static void
ufs_extattr_uepm_lock(struct ufsmount * ump)117 ufs_extattr_uepm_lock(struct ufsmount *ump)
118 {
119 
120           /*
121            * XXX This needs to be recursive for the following reasons:
122            *   - it is taken in ufs_extattr_vnode_inactive
123            *   - which is called from VOP_INACTIVE
124            *   - which can be triggered by any vrele, vput, or vn_close
125            *   - several of these can happen while it's held
126            */
127           if (mutex_owned(&ump->um_extattr.uepm_lock)) {
128                     ump->um_extattr.uepm_lockcnt++;
129                     return;
130           }
131           mutex_enter(&ump->um_extattr.uepm_lock);
132 }
133 
134 static void
ufs_extattr_uepm_unlock(struct ufsmount * ump)135 ufs_extattr_uepm_unlock(struct ufsmount *ump)
136 {
137 
138           if (ump->um_extattr.uepm_lockcnt != 0) {
139                     KASSERT(mutex_owned(&ump->um_extattr.uepm_lock));
140                     ump->um_extattr.uepm_lockcnt--;
141                     return;
142           }
143           mutex_exit(&ump->um_extattr.uepm_lock);
144 }
145 
146 /*-
147  * Determine whether the name passed is a valid name for an actual
148  * attribute.
149  *
150  * Invalid currently consists of:
151  *         NULL pointer for attrname
152  *         zero-length attrname (used to retrieve application attribute list)
153  */
154 static int
ufs_extattr_valid_attrname(int attrnamespace,const char * attrname)155 ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
156 {
157 
158           if (attrname == NULL)
159                     return 0;
160           if (strlen(attrname) == 0)
161                     return 0;
162           return 1;
163 }
164 
165 /*
166  * Autocreate an attribute storage
167  */
168 static int
ufs_extattr_autocreate_attr(struct vnode * vp,int attrnamespace,const char * attrname,struct lwp * l,struct ufs_extattr_list_entry ** uelep)169 ufs_extattr_autocreate_attr(struct vnode *vp, int attrnamespace,
170     const char *attrname, struct lwp *l, struct ufs_extattr_list_entry **uelep)
171 {
172           struct mount *mp = vp->v_mount;
173           struct ufsmount *ump = VFSTOUFS(mp);
174           struct vnode *backing_vp;
175           struct pathbuf *pb;
176           char *path;
177           struct ufs_extattr_fileheader uef;
178           struct ufs_extattr_list_entry *uele;
179           int error;
180 
181           path = PNBUF_GET();
182 
183           /*
184            * We only support system and user namespace autocreation
185            */
186           switch (attrnamespace) {
187           case EXTATTR_NAMESPACE_SYSTEM:
188                     (void)snprintf(path, PATH_MAX, "%s/%s/%s/%s",
189                         mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR,
190                         UFS_EXTATTR_SUBDIR_SYSTEM, attrname);
191                     break;
192           case EXTATTR_NAMESPACE_USER:
193                     (void)snprintf(path, PATH_MAX, "%s/%s/%s/%s",
194                         mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR,
195                         UFS_EXTATTR_SUBDIR_USER, attrname);
196                     break;
197           default:
198                     PNBUF_PUT(path);
199                     *uelep = NULL;
200                     return EINVAL;
201                     break;
202           }
203 
204           /*
205            * Release extended attribute mount lock, otherwise
206            * we can deadlock with another thread that would lock
207            * vp after we unlock it below, and call
208            * ufs_extattr_uepm_lock(ump), for instance
209            * in ufs_getextattr().
210            */
211           ufs_extattr_uepm_unlock(ump);
212 
213           /*
214            * XXX unlock/lock should only be done when setting extattr
215            * on backing store or one of its parent directory
216            * including root, but we always do it for now.
217            */
218           KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
219           VOP_UNLOCK(vp);
220 
221           pb = pathbuf_create(path);
222 
223           /*
224            * Since we do not hold ufs_extattr_uepm_lock anymore,
225            * another thread may race with us for backend creation,
226            * but only one can succeed here thanks to O_EXCL.
227            *
228            * backing_vp is the backing store.
229            */
230           error = vn_open(NULL, pb, 0, O_CREAT|O_EXCL|O_RDWR, 0600,
231               &backing_vp, NULL, NULL);
232 
233           /*
234            * Reacquire the lock on the vnode
235            */
236           KASSERT(VOP_ISLOCKED(vp) == 0);
237           vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
238 
239           ufs_extattr_uepm_lock(ump);
240 
241           if (error != 0) {
242                     pathbuf_destroy(pb);
243                     PNBUF_PUT(path);
244                     *uelep = NULL;
245                     return error;
246           }
247 
248           KASSERT(backing_vp != NULL);
249           KASSERT(VOP_ISLOCKED(backing_vp) == LK_EXCLUSIVE);
250 
251           pathbuf_destroy(pb);
252           PNBUF_PUT(path);
253 
254           uef.uef_magic = UFS_EXTATTR_MAGIC;
255           uef.uef_version = UFS_EXTATTR_VERSION;
256           uef.uef_size = ufs_extattr_autocreate;
257 
258           error = vn_rdwr(UIO_WRITE, backing_vp, &uef, sizeof(uef), 0,
259                             UIO_SYSSPACE, IO_NODELOCKED|IO_APPEND,
260                               l->l_cred, NULL, l);
261 
262           VOP_UNLOCK(backing_vp);
263 
264           if (error != 0) {
265                     printf("%s: write uef header failed for `%s' (%d)\n",
266                         __func__, attrname, error);
267                     vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
268                     *uelep = NULL;
269                     return error;
270           }
271 
272           /*
273            * Now enable attribute.
274            */
275           error = ufs_extattr_enable(ump,attrnamespace, attrname, backing_vp, l);
276           KASSERT(VOP_ISLOCKED(backing_vp) == 0);
277 
278           if (error != 0) {
279                     printf("%s: enable `%s' failed (%d)\n",
280                         __func__, attrname, error);
281                     vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
282                     *uelep = NULL;
283                     return error;
284           }
285 
286           uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
287           if (uele == NULL) {
288                     printf("%s: attribute `%s' created but not found!\n",
289                         __func__, attrname);
290                     vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
291                     *uelep = NULL;
292                     return ESRCH; /* really internal error */
293           }
294 
295           printf("%s: EA backing store autocreated for %s\n",
296               mp->mnt_stat.f_mntonname, attrname);
297 
298           *uelep = uele;
299           return 0;
300 }
301 
302 /*
303  * Locate an attribute given a name and mountpoint.
304  * Must be holding uepm lock for the mount point.
305  */
306 static struct ufs_extattr_list_entry *
ufs_extattr_find_attr(struct ufsmount * ump,int attrnamespace,const char * attrname)307 ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
308     const char *attrname)
309 {
310           struct ufs_extattr_list_entry *search_attribute;
311 
312           for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
313               search_attribute != NULL;
314               search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
315                     if (!(strncmp(attrname, search_attribute->uele_attrname,
316                                   UFS_EXTATTR_MAXEXTATTRNAME)) &&
317                         (attrnamespace == search_attribute->uele_attrnamespace)) {
318                               return search_attribute;
319                     }
320           }
321 
322           return 0;
323 }
324 
325 /*
326  * Initialize per-FS structures supporting extended attributes.  Do not
327  * start extended attributes yet.
328  */
329 void
ufs_extattr_uepm_init(struct ufs_extattr_per_mount * uepm)330 ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
331 {
332 
333           uepm->uepm_flags = 0;
334           uepm->uepm_lockcnt = 0;
335 
336           LIST_INIT(&uepm->uepm_list);
337           mutex_init(&uepm->uepm_lock, MUTEX_DEFAULT, IPL_NONE);
338           uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
339 }
340 
341 /*
342  * Destroy per-FS structures supporting extended attributes.  Assumes
343  * that EAs have already been stopped, and will panic if not.
344  */
345 void
ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount * uepm)346 ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
347 {
348 
349           if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
350                     panic("ufs_extattr_uepm_destroy: not initialized");
351 
352           if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
353                     panic("ufs_extattr_uepm_destroy: called while still started");
354 
355           /*
356            * It's not clear that either order for the next three lines is
357            * ideal, and it should never be a problem if this is only called
358            * during unmount, and with vfs_busy().
359            */
360           uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
361           uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
362           mutex_destroy(&uepm->uepm_lock);
363 }
364 
365 /*
366  * Start extended attribute support on an FS.
367  */
368 int
ufs_extattr_start(struct mount * mp,struct lwp * l)369 ufs_extattr_start(struct mount *mp, struct lwp *l)
370 {
371           struct ufsmount *ump;
372           int error = 0;
373 
374           ump = VFSTOUFS(mp);
375 
376           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
377                     ufs_extattr_uepm_init(&ump->um_extattr);
378 
379           ufs_extattr_uepm_lock(ump);
380 
381           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
382                     error = EOPNOTSUPP;
383                     goto unlock;
384           }
385           if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
386                     error = EBUSY;
387                     goto unlock;
388           }
389 
390           ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
391 
392           ump->um_extattr.uepm_ucred = l->l_cred;
393           kauth_cred_hold(ump->um_extattr.uepm_ucred);
394 
395  unlock:
396           ufs_extattr_uepm_unlock(ump);
397           return error;
398 }
399 
400 /*
401  * Helper routine: given a locked parent directory and filename, return
402  * the locked vnode of the inode associated with the name.  Will not
403  * follow symlinks, may return any type of vnode.  Lock on parent will
404  * be released even in the event of a failure.  In the event that the
405  * target is the parent (i.e., "."), there will be two references and
406  * one lock, requiring the caller to possibly special-case.
407  */
408 static int
ufs_extattr_lookup(struct vnode * start_dvp,int lockparent,const char * dirname,struct vnode ** vp,struct lwp * l)409 ufs_extattr_lookup(struct vnode *start_dvp, int lockparent,
410     const char *dirname,
411     struct vnode **vp, struct lwp *l)
412 {
413           struct vop_lookup_v2_args vargs;
414           struct componentname cnp;
415           struct vnode *target_vp;
416           char *pnbuf;
417           int error;
418 
419           KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
420 
421           pnbuf = PNBUF_GET();
422 
423           memset(&cnp, 0, sizeof(cnp));
424           cnp.cn_nameiop = LOOKUP;
425           cnp.cn_flags = ISLASTCN | lockparent;
426           cnp.cn_cred = l->l_cred;
427           cnp.cn_nameptr = pnbuf;
428           error = copystr(dirname, pnbuf, MAXPATHLEN, &cnp.cn_namelen);
429           if (error) {
430                     if (lockparent == 0) {
431                               VOP_UNLOCK(start_dvp);
432                     }
433                     PNBUF_PUT(pnbuf);
434                     printf("%s: copystr failed (%d)\n", __func__, error);
435                     return error;
436           }
437           cnp.cn_namelen--;   /* trim nul termination */
438           vargs.a_desc = NULL;
439           vargs.a_dvp = start_dvp;
440           vargs.a_vpp = &target_vp;
441           vargs.a_cnp = &cnp;
442           error = ufs_lookup(&vargs);
443           PNBUF_PUT(pnbuf);
444           if (error) {
445                     if (lockparent == 0) {
446                               VOP_UNLOCK(start_dvp);
447                     }
448                     return error;
449           }
450 #if 0
451           if (target_vp == start_dvp)
452                     panic("%s: target_vp == start_dvp", __func__);
453 #endif
454 
455           if (target_vp != start_dvp) {
456                     error = vn_lock(target_vp, LK_EXCLUSIVE);
457                     if (lockparent == 0)
458                               VOP_UNLOCK(start_dvp);
459                     if (error) {
460                               vrele(target_vp);
461                               return error;
462                     }
463           }
464 
465           KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
466           *vp = target_vp;
467           return 0;
468 }
469 
470 /*
471  * Enable an EA using the passed filesystem, backing vnode, attribute name,
472  * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
473  * to be locked when passed in.  The vnode will be returned unlocked,
474  * regardless of success/failure of the function.  As a result, the caller
475  * will always need to vrele(), but not vput().
476  */
477 static int
ufs_extattr_enable_with_open(struct ufsmount * ump,struct vnode * vp,int attrnamespace,const char * attrname,struct lwp * l)478 ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
479     int attrnamespace, const char *attrname, struct lwp *l)
480 {
481           int error;
482 
483           error = VOP_OPEN(vp, FREAD|FWRITE, l->l_cred);
484           if (error) {
485                     printf("%s: VOP_OPEN(): failed (%d)\n", __func__, error);
486                     VOP_UNLOCK(vp);
487                     return error;
488           }
489 
490           mutex_enter(vp->v_interlock);
491           vp->v_writecount++;
492           mutex_exit(vp->v_interlock);
493 
494           vref(vp);
495 
496           VOP_UNLOCK(vp);
497 
498           error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, l);
499           if (error != 0)
500                     vn_close(vp, FREAD|FWRITE, l->l_cred);
501           return error;
502 }
503 
504 /*
505  * Given a locked directory vnode, iterate over the names in the directory
506  * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
507  * attribute files.  Then invoke ufs_extattr_enable_with_open() on each
508  * to attempt to start the attribute.  Leaves the directory locked on
509  * exit.
510  */
511 static int
ufs_extattr_iterate_directory(struct ufsmount * ump,struct vnode * dvp,int attrnamespace,struct lwp * l)512 ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
513     int attrnamespace, struct lwp *l)
514 {
515           struct vop_readdir_args vargs;
516           struct statvfs *sbp = &ump->um_mountp->mnt_stat;
517           struct dirent *dp, *edp;
518           struct vnode *attr_vp;
519           struct uio auio;
520           struct iovec aiov;
521           char *dirbuf;
522           int error, eofflag = 0;
523 
524           if (dvp->v_type != VDIR)
525                     return ENOTDIR;
526 
527           dirbuf = kmem_alloc(UFS_DIRBLKSIZ, KM_SLEEP);
528 
529           auio.uio_iov = &aiov;
530           auio.uio_iovcnt = 1;
531           auio.uio_rw = UIO_READ;
532           auio.uio_offset = 0;
533           UIO_SETUP_SYSSPACE(&auio);
534 
535           vargs.a_desc = NULL;
536           vargs.a_vp = dvp;
537           vargs.a_uio = &auio;
538           vargs.a_cred = l->l_cred;
539           vargs.a_eofflag = &eofflag;
540           vargs.a_ncookies = NULL;
541           vargs.a_cookies = NULL;
542 
543           while (!eofflag) {
544                     auio.uio_resid = UFS_DIRBLKSIZ;
545                     aiov.iov_base = dirbuf;
546                     aiov.iov_len = UFS_DIRBLKSIZ;
547                     error = ufs_readdir(&vargs);
548                     if (error) {
549                               printf("%s: ufs_readdir (%d)\n", __func__, error);
550                               return error;
551                     }
552 
553                     /*
554                      * XXXRW: While in UFS, we always get UFS_DIRBLKSIZ returns from
555                      * the directory code on success, on other file systems this
556                      * may not be the case.  For portability, we should check the
557                      * read length on return from ufs_readdir().
558                      */
559                     edp = (struct dirent *)&dirbuf[UFS_DIRBLKSIZ];
560                     for (dp = (struct dirent *)dirbuf; dp < edp; ) {
561                               if (dp->d_reclen == 0)
562                                         break;
563                               /* Skip "." and ".." */
564                               if (dp->d_name[0] == '.' &&
565                                   (dp->d_name[1] == '\0' ||
566                                    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
567                                         goto next;
568                               error = ufs_extattr_lookup(dvp, LOCKPARENT,
569                                   dp->d_name, &attr_vp, l);
570                               if (error == ENOENT) {
571                                         goto next; /* keep silent */
572                               } else if (error) {
573                                         printf("%s: lookup `%s' (%d)\n", __func__,
574                                             dp->d_name, error);
575                               } else if (attr_vp == dvp) {
576                                         vrele(attr_vp);
577                               } else if (attr_vp->v_type != VREG) {
578                                         vput(attr_vp);
579                               } else {
580                                         error = ufs_extattr_enable_with_open(ump,
581                                             attr_vp, attrnamespace, dp->d_name, l);
582                                         vrele(attr_vp);
583                                         if (error) {
584                                                   printf("%s: enable `%s' (%d)\n",
585                                                       __func__, dp->d_name, error);
586                                         } else if (bootverbose) {
587                                                   printf("%s: EA %s loaded\n",
588                                                       sbp->f_mntonname, dp->d_name);
589                                         }
590                               }
591  next:
592                               dp = (struct dirent *) ((char *)dp + dp->d_reclen);
593                               if (dp >= edp)
594                                         break;
595                     }
596           }
597           kmem_free(dirbuf, UFS_DIRBLKSIZ);
598 
599           return 0;
600 }
601 
602 static int
ufs_extattr_subdir(struct lwp * l,struct mount * mp,struct vnode * attr_dvp,const char * subdir,int namespace)603 ufs_extattr_subdir(struct lwp *l, struct mount *mp, struct vnode *attr_dvp,
604     const char *subdir, int namespace)
605 {
606           int error;
607           struct vnode *attr_sub;
608           error = ufs_extattr_lookup(attr_dvp, LOCKPARENT, subdir, &attr_sub, l);
609           KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
610           if (error) {
611                     printf("%s: Can't find `%s/%s/%s' (%d)\n",
612                         __func__, mp->mnt_stat.f_mntonname,
613                         UFS_EXTATTR_FSROOTSUBDIR, subdir, error);
614                     return error;
615           }
616           KASSERT(VOP_ISLOCKED(attr_sub) == LK_EXCLUSIVE);
617           error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
618               attr_sub, namespace, l);
619           if (error) {
620                     printf("%s: ufs_extattr_iterate_directory `%s/%s/%s' (%d)\n",
621                         __func__, mp->mnt_stat.f_mntonname,
622                         UFS_EXTATTR_FSROOTSUBDIR, subdir, error);
623           }
624           KASSERT(VOP_ISLOCKED(attr_sub) == LK_EXCLUSIVE);
625           vput(attr_sub);
626           return error;
627 }
628 
629 /*
630  * Auto-start of extended attributes, to be executed (optionally) at
631  * mount-time.
632  */
633 int
ufs_extattr_autostart(struct mount * mp,struct lwp * l)634 ufs_extattr_autostart(struct mount *mp, struct lwp *l)
635 {
636           struct vnode *rvp, *attr_dvp;
637           int error;
638 
639           /*
640            * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
641            * If so, automatically start EA's.
642            */
643           error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp);
644           if (error) {
645                     printf("%s: VFS_ROOT() (%d)\n", __func__, error);
646                     return error;
647           }
648 
649           KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
650 
651           error = ufs_extattr_lookup(rvp, 0,
652               UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
653           if (error) {
654                     /* rvp ref'd but now unlocked */
655                     KASSERT(VOP_ISLOCKED(rvp) == 0);
656                     vrele(rvp);
657                     printf("%s: lookup `%s/%s' (%d)\n", __func__,
658                         mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR, error);
659                     return error;
660           }
661           if (rvp == attr_dvp) {
662                     /* Should never happen. */
663                     KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
664                     vrele(attr_dvp);
665                     vput(rvp);
666                     printf("%s: `/' == `%s/%s' (%d)\n", __func__,
667                         mp->mnt_stat.f_mntonname, UFS_EXTATTR_FSROOTSUBDIR, EINVAL);
668                     return EINVAL;
669           }
670           KASSERT(VOP_ISLOCKED(rvp) == 0);
671           vrele(rvp);
672 
673           KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
674 
675           if (attr_dvp->v_type != VDIR) {
676                     printf("%s: `%s/%s' is not a directory\n",
677                         __func__, mp->mnt_stat.f_mntonname,
678                         UFS_EXTATTR_FSROOTSUBDIR);
679                     goto return_vput_attr_dvp;
680           }
681 
682           error = ufs_extattr_start(mp, l);
683           if (error) {
684                     printf("%s: ufs_extattr_start failed (%d)\n", __func__,
685                         error);
686                     goto return_vput_attr_dvp;
687           }
688 
689           /*
690            * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
691            * UFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
692            * and start with appropriate type.  Failures in either don't
693            * result in an over-all failure.  attr_dvp is left locked to
694            * be cleaned up on exit.
695            */
696           error = ufs_extattr_subdir(l, mp, attr_dvp, UFS_EXTATTR_SUBDIR_SYSTEM,
697               EXTATTR_NAMESPACE_SYSTEM);
698           error = ufs_extattr_subdir(l, mp, attr_dvp, UFS_EXTATTR_SUBDIR_USER,
699               EXTATTR_NAMESPACE_USER);
700 
701           /* Mask startup failures in sub-directories. */
702           error = 0;
703 
704  return_vput_attr_dvp:
705           KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
706           vput(attr_dvp);
707 
708           return error;
709 }
710 
711 /*
712  * Stop extended attribute support on an FS.
713  */
714 void
ufs_extattr_stop(struct mount * mp,struct lwp * l)715 ufs_extattr_stop(struct mount *mp, struct lwp *l)
716 {
717           struct ufs_extattr_list_entry *uele;
718           struct ufsmount *ump = VFSTOUFS(mp);
719 
720           ufs_extattr_uepm_lock(ump);
721 
722           /*
723            * If we haven't been started, no big deal.  Just short-circuit
724            * the processing work.
725            */
726           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
727                     goto unlock;
728           }
729 
730           while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
731                     uele = LIST_FIRST(&ump->um_extattr.uepm_list);
732                     ufs_extattr_disable(ump, uele->uele_attrnamespace,
733                         uele->uele_attrname, l);
734           }
735 
736           ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
737 
738           kauth_cred_free(ump->um_extattr.uepm_ucred);
739           ump->um_extattr.uepm_ucred = NULL;
740 
741  unlock:
742           ufs_extattr_uepm_unlock(ump);
743 }
744 
745 /*
746  * Enable a named attribute on the specified filesystem; provide an
747  * unlocked backing vnode to hold the attribute data.
748  */
749 static int
ufs_extattr_enable(struct ufsmount * ump,int attrnamespace,const char * attrname,struct vnode * backing_vnode,struct lwp * l)750 ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
751     const char *attrname, struct vnode *backing_vnode, struct lwp *l)
752 {
753           struct ufs_extattr_list_entry *attribute;
754           struct iovec aiov;
755           struct uio auio;
756           int error = 0;
757 
758           if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
759                     return EINVAL;
760           if (backing_vnode->v_type != VREG)
761                     return EINVAL;
762 
763           attribute = kmem_zalloc(sizeof(*attribute), KM_SLEEP);
764 
765           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
766                     error = EOPNOTSUPP;
767                     goto free_exit;
768           }
769 
770           if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
771                     error = EEXIST;
772                     goto free_exit;
773           }
774 
775           strncpy(attribute->uele_attrname, attrname,
776               UFS_EXTATTR_MAXEXTATTRNAME);
777           attribute->uele_attrnamespace = attrnamespace;
778           memset(&attribute->uele_fileheader, 0,
779               sizeof(struct ufs_extattr_fileheader));
780 
781           attribute->uele_backing_vnode = backing_vnode;
782 
783           auio.uio_iov = &aiov;
784           auio.uio_iovcnt = 1;
785           aiov.iov_base = (void *) &attribute->uele_fileheader;
786           aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
787           auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
788           auio.uio_offset = (off_t) 0;
789           auio.uio_rw = UIO_READ;
790           UIO_SETUP_SYSSPACE(&auio);
791 
792           vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
793           error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
794               ump->um_extattr.uepm_ucred);
795 
796           if (error)
797                     goto unlock_free_exit;
798 
799           if (auio.uio_resid != 0) {
800                     printf("%s: malformed attribute header\n", __func__);
801                     error = EINVAL;
802                     goto unlock_free_exit;
803           }
804 
805           /*
806            * Try to determine the byte order of the attribute file.
807            */
808           if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
809                     attribute->uele_flags |= UELE_F_NEEDSWAP;
810                     attribute->uele_fileheader.uef_magic =
811                         ufs_rw32(attribute->uele_fileheader.uef_magic,
812                               UELE_NEEDSWAP(attribute));
813                     if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
814                               printf("%s: invalid attribute header magic\n",
815                                   __func__);
816                               error = EINVAL;
817                               goto unlock_free_exit;
818                     }
819           }
820           attribute->uele_fileheader.uef_version =
821               ufs_rw32(attribute->uele_fileheader.uef_version,
822                     UELE_NEEDSWAP(attribute));
823           attribute->uele_fileheader.uef_size =
824               ufs_rw32(attribute->uele_fileheader.uef_size,
825                     UELE_NEEDSWAP(attribute));
826 
827           if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
828                     printf("%s: incorrect attribute header version %d != %d\n",
829                         __func__, attribute->uele_fileheader.uef_version,
830                         UFS_EXTATTR_VERSION);
831                     error = EINVAL;
832                     goto unlock_free_exit;
833           }
834 
835           LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries);
836 
837           VOP_UNLOCK(backing_vnode);
838           return 0;
839 
840  unlock_free_exit:
841           VOP_UNLOCK(backing_vnode);
842 
843  free_exit:
844           kmem_free(attribute, sizeof(*attribute));
845           return error;
846 }
847 
848 /*
849  * Disable extended attribute support on an FS.
850  */
851 static int
ufs_extattr_disable(struct ufsmount * ump,int attrnamespace,const char * attrname,struct lwp * l)852 ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
853     const char *attrname, struct lwp *l)
854 {
855           struct ufs_extattr_list_entry *uele;
856           int error = 0;
857 
858           if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
859                     return EINVAL;
860 
861           uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
862           if (!uele)
863                     return ENODATA;
864 
865           LIST_REMOVE(uele, uele_entries);
866 
867           error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, l->l_cred);
868 
869           kmem_free(uele, sizeof(*uele));
870 
871           return error;
872 }
873 
874 /*
875  * VFS call to manage extended attributes in UFS.  If filename_vp is
876  * non-NULL, it must be passed in locked, and regardless of errors in
877  * processing, will be unlocked.
878  */
879 int
ufs_extattrctl(struct mount * mp,int cmd,struct vnode * filename_vp,int attrnamespace,const char * attrname)880 ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
881     int attrnamespace, const char *attrname)
882 {
883           struct lwp *l = curlwp;
884           struct ufsmount *ump = VFSTOUFS(mp);
885           int error;
886 
887           /*
888            * Only privileged processes can configure extended attributes.
889            */
890           error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_EXTATTR,
891               0, mp, NULL, NULL);
892           if (error) {
893                     if (filename_vp != NULL)
894                               VOP_UNLOCK(filename_vp);
895                     return error;
896           }
897 
898           switch(cmd) {
899           case UFS_EXTATTR_CMD_START:
900           case UFS_EXTATTR_CMD_STOP:
901           case UFS_EXTATTR_CMD_ENABLE:
902           case UFS_EXTATTR_CMD_DISABLE:
903                     if (filename_vp != NULL) {
904                               VOP_UNLOCK(filename_vp);
905                               return EINVAL;
906                     }
907                     if (attrname != NULL)
908                               return EINVAL;
909                     break;
910           default:
911                     return EINVAL;
912           }
913 
914           switch(cmd) {
915           case UFS_EXTATTR_CMD_START:
916                     error = ufs_extattr_autostart(mp, l);
917                     return error;
918 
919           case UFS_EXTATTR_CMD_STOP:
920                     ufs_extattr_stop(mp, l);
921                     return 0;
922 
923           case UFS_EXTATTR_CMD_ENABLE:
924                     /*
925                      * ufs_extattr_enable_with_open() will always unlock the
926                      * vnode, regardless of failure.
927                      */
928                     ufs_extattr_uepm_lock(ump);
929                     error = ufs_extattr_enable_with_open(ump, filename_vp,
930                         attrnamespace, attrname, l);
931                     ufs_extattr_uepm_unlock(ump);
932                     return error;
933 
934           case UFS_EXTATTR_CMD_DISABLE:
935                     ufs_extattr_uepm_lock(ump);
936                     error = ufs_extattr_disable(ump, attrnamespace, attrname, l);
937                     ufs_extattr_uepm_unlock(ump);
938                     return error;
939 
940           default:
941                     return EINVAL;
942           }
943 }
944 
945 /*
946  * Read extended attribute header for a given vnode and attribute.
947  * Backing vnode should be locked and unlocked by caller.
948  */
949 static int
ufs_extattr_get_header(struct vnode * vp,struct ufs_extattr_list_entry * uele,struct ufs_extattr_header * ueh,off_t * bap)950 ufs_extattr_get_header(struct vnode *vp, struct ufs_extattr_list_entry *uele,
951     struct ufs_extattr_header *ueh, off_t *bap)
952 {
953           struct mount *mp = vp->v_mount;
954           struct ufsmount *ump = VFSTOUFS(mp);
955           struct inode *ip = VTOI(vp);
956           off_t base_offset;
957           struct iovec aiov;
958           struct uio aio;
959           int error;
960 
961           /*
962            * Find base offset of header in file based on file header size, and
963            * data header size + maximum data size, indexed by inode number.
964            */
965           base_offset = sizeof(struct ufs_extattr_fileheader) +
966               ip->i_number * (sizeof(struct ufs_extattr_header) +
967               uele->uele_fileheader.uef_size);
968 
969           /*
970            * Read in the data header to see if the data is defined, and if so
971            * how much.
972            */
973           memset(ueh, 0, sizeof(struct ufs_extattr_header));
974           aiov.iov_base = ueh;
975           aiov.iov_len = sizeof(struct ufs_extattr_header);
976           aio.uio_iov = &aiov;
977           aio.uio_iovcnt = 1;
978           aio.uio_rw = UIO_READ;
979           aio.uio_offset = base_offset;
980           aio.uio_resid = sizeof(struct ufs_extattr_header);
981           UIO_SETUP_SYSSPACE(&aio);
982 
983           error = VOP_READ(uele->uele_backing_vnode, &aio,
984               IO_NODELOCKED, ump->um_extattr.uepm_ucred);
985           if (error)
986                     return error;
987 
988           /*
989            * Attribute headers are kept in file system byte order.
990            * XXX What about the blob of data?
991            */
992           ueh->ueh_flags = ufs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele));
993           ueh->ueh_len   = ufs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele));
994           ueh->ueh_i_gen = ufs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele));
995 
996           /* Defined? */
997           if ((ueh->ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0)
998                     return ENODATA;
999 
1000           /* Valid for the current inode generation? */
1001           if (ueh->ueh_i_gen != ip->i_gen) {
1002                     /*
1003                      * The inode itself has a different generation number
1004                      * than the uele data.  For now, the best solution
1005                      * is to coerce this to undefined, and let it get cleaned
1006                      * up by the next write or extattrctl clean.
1007                      */
1008                     printf("%s: %s: inode gen inconsistency (%u, %jd)\n",
1009                            __func__,  mp->mnt_stat.f_mntonname, ueh->ueh_i_gen,
1010                            (intmax_t)ip->i_gen);
1011                     return ENODATA;
1012           }
1013 
1014           /* Local size consistency check. */
1015           if (ueh->ueh_len > uele->uele_fileheader.uef_size)
1016                     return ENXIO;
1017 
1018           /* Return base offset */
1019           if (bap != NULL)
1020                     *bap = base_offset;
1021 
1022           return 0;
1023 }
1024 
1025 /*
1026  * Vnode operation to retrieve a named extended attribute.
1027  */
1028 int
ufs_getextattr(struct vop_getextattr_args * ap)1029 ufs_getextattr(struct vop_getextattr_args *ap)
1030 /*
1031 vop_getextattr {
1032           IN struct vnode *a_vp;
1033           IN int a_attrnamespace;
1034           IN const char *a_name;
1035           INOUT struct uio *a_uio;
1036           OUT size_t *a_size;
1037           IN kauth_cred_t a_cred;
1038 };
1039 */
1040 {
1041           struct mount *mp = ap->a_vp->v_mount;
1042           struct ufsmount *ump = VFSTOUFS(mp);
1043           int error;
1044 
1045           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1046                     return EOPNOTSUPP;
1047 
1048           ufs_extattr_uepm_lock(ump);
1049 
1050           error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1051               ap->a_uio, ap->a_size, ap->a_cred, curlwp);
1052 
1053           ufs_extattr_uepm_unlock(ump);
1054 
1055           return error;
1056 }
1057 
1058 /*
1059  * Real work associated with retrieving a named attribute--assumes that
1060  * the attribute lock has already been grabbed.
1061  */
1062 static int
ufs_extattr_get(struct vnode * vp,int attrnamespace,const char * name,struct uio * uio,size_t * size,kauth_cred_t cred,struct lwp * l)1063 ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
1064     struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
1065 {
1066           struct ufs_extattr_list_entry *attribute;
1067           struct ufs_extattr_header ueh;
1068           struct mount *mp = vp->v_mount;
1069           struct ufsmount *ump = VFSTOUFS(mp);
1070           off_t base_offset;
1071           size_t len, old_len;
1072           int error = 0;
1073 
1074           if (strlen(name) == 0)
1075                     return EINVAL;
1076 
1077           error = extattr_check_cred(vp, attrnamespace, cred, VREAD);
1078           if (error)
1079                     return error;
1080 
1081           attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1082           if (!attribute)
1083                     return ENODATA;
1084 
1085           /*
1086            * Allow only offsets of zero to encourage the read/replace
1087            * extended attribute semantic.  Otherwise we can't guarantee
1088            * atomicity, as we don't provide locks for extended attributes.
1089            */
1090           if (uio != NULL && uio->uio_offset != 0)
1091                     return ENXIO;
1092 
1093           /*
1094            * Don't need to get a lock on the backing file if the getattr is
1095            * being applied to the backing file, as the lock is already held.
1096            */
1097           if (attribute->uele_backing_vnode != vp)
1098                     vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
1099 
1100           error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1101           if (error)
1102                     goto vopunlock_exit;
1103 
1104           /* Return full data size if caller requested it. */
1105           if (size != NULL)
1106                     *size = ueh.ueh_len;
1107 
1108           /* Return data if the caller requested it. */
1109           if (uio != NULL) {
1110                     /* Allow for offset into the attribute data. */
1111                     uio->uio_offset = base_offset + sizeof(struct
1112                         ufs_extattr_header);
1113 
1114                     /*
1115                      * Figure out maximum to transfer -- use buffer size and
1116                      * local data limit.
1117                      */
1118                     len = MIN(uio->uio_resid, ueh.ueh_len);
1119                     old_len = uio->uio_resid;
1120                     uio->uio_resid = len;
1121 
1122                     error = VOP_READ(attribute->uele_backing_vnode, uio,
1123                         IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1124                     if (error)
1125                               goto vopunlock_exit;
1126 
1127                     uio->uio_resid = old_len - (len - uio->uio_resid);
1128           }
1129 
1130  vopunlock_exit:
1131 
1132           if (uio != NULL)
1133                     uio->uio_offset = 0;
1134 
1135           if (attribute->uele_backing_vnode != vp)
1136                     VOP_UNLOCK(attribute->uele_backing_vnode);
1137 
1138           return error;
1139 }
1140 
1141 /*
1142  * Vnode operation to list extended attribute for a vnode
1143  */
1144 int
ufs_listextattr(struct vop_listextattr_args * ap)1145 ufs_listextattr(struct vop_listextattr_args *ap)
1146 /*
1147 vop_listextattr {
1148           IN struct vnode *a_vp;
1149           IN int a_attrnamespace;
1150           INOUT struct uio *a_uio;
1151           OUT size_t *a_size;
1152           IN int flag;
1153           IN kauth_cred_t a_cred;
1154           struct proc *a_p;
1155 };
1156 */
1157 {
1158           struct mount *mp = ap->a_vp->v_mount;
1159           struct ufsmount *ump = VFSTOUFS(mp);
1160           int error;
1161 
1162           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1163                     return EOPNOTSUPP;
1164 
1165           ufs_extattr_uepm_lock(ump);
1166 
1167           error = ufs_extattr_list(ap->a_vp, ap->a_attrnamespace,
1168               ap->a_uio, ap->a_size, ap->a_flag, ap->a_cred, curlwp);
1169 
1170           ufs_extattr_uepm_unlock(ump);
1171 
1172           return error;
1173 }
1174 
1175 /*
1176  * Real work associated with retrieving list of attributes--assumes that
1177  * the attribute lock has already been grabbed.
1178  */
1179 static int
ufs_extattr_list(struct vnode * vp,int attrnamespace,struct uio * uio,size_t * size,int flag,kauth_cred_t cred,struct lwp * l)1180 ufs_extattr_list(struct vnode *vp, int attrnamespace,
1181     struct uio *uio, size_t *size, int flag,
1182     kauth_cred_t cred, struct lwp *l)
1183 {
1184           struct ufs_extattr_list_entry *uele;
1185           struct ufs_extattr_header ueh;
1186           struct mount *mp = vp->v_mount;
1187           struct ufsmount *ump = VFSTOUFS(mp);
1188           size_t listsize = 0;
1189           int error = 0;
1190 
1191           /*
1192            * XXX: We can move this inside the loop and iterate on individual
1193            *        attributes.
1194            */
1195           error = extattr_check_cred(vp, attrnamespace, cred, VREAD);
1196           if (error)
1197                     return error;
1198 
1199           LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) {
1200                     unsigned char attrnamelen;
1201 
1202                     if (uele->uele_attrnamespace != attrnamespace)
1203                               continue;
1204 
1205                     error = ufs_extattr_get_header(vp, uele, &ueh, NULL);
1206                     if (error == ENODATA)
1207                               continue;
1208                     if (error != 0)
1209                               return error;
1210 
1211                     /*
1212                      * Don't need to get a lock on the backing file if
1213                      * the listattr is being applied to the backing file,
1214                      * as the lock is already held.
1215                      */
1216                     if (uele->uele_backing_vnode != vp)
1217                               vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
1218 
1219                     /*
1220                      * +1 for trailing NUL (listxattr flavor)
1221                      *  or leading name length (extattr_list_file flavor)
1222                      */
1223                     attrnamelen = strlen(uele->uele_attrname);
1224                     listsize += attrnamelen + 1;
1225 
1226                     /* Return data if the caller requested it. */
1227                     if (uio != NULL) {
1228                               /*
1229                                * We support two flavors. Either NUL-terminated
1230                                * strings (a la listxattr), or non NUL-terminated,
1231                                * one byte length prefixed strings (for
1232                                * extattr_list_file). EXTATTR_LIST_LENPREFIX switches
1233                                * that second behavior.
1234                                */
1235                               if (flag & EXTATTR_LIST_LENPREFIX) {
1236                                         uint8_t len = (uint8_t)attrnamelen;
1237 
1238                                         /* Copy leading name length */
1239                                         error = uiomove(&len, sizeof(len), uio);
1240                                         if (error != 0)
1241                                                   break;
1242                               } else {
1243                                         /* Include trailing NULL */
1244                                         attrnamelen++;
1245                               }
1246 
1247                               error = uiomove(uele->uele_attrname,
1248                                   (size_t)attrnamelen, uio);
1249                               if (error != 0)
1250                                         break;
1251                     }
1252 
1253                     if (uele->uele_backing_vnode != vp)
1254                               VOP_UNLOCK(uele->uele_backing_vnode);
1255 
1256                     if (error != 0)
1257                               return error;
1258           }
1259 
1260           if (uio != NULL)
1261                     uio->uio_offset = 0;
1262 
1263           /* Return full data size if caller requested it. */
1264           if (size != NULL)
1265                     *size = listsize;
1266 
1267           return 0;
1268 }
1269 
1270 /*
1271  * Vnode operation to remove a named attribute.
1272  */
1273 int
ufs_deleteextattr(struct vop_deleteextattr_args * ap)1274 ufs_deleteextattr(struct vop_deleteextattr_args *ap)
1275 /*
1276 vop_deleteextattr {
1277           IN struct vnode *a_vp;
1278           IN int a_attrnamespace;
1279           IN const char *a_name;
1280           IN kauth_cred_t a_cred;
1281 };
1282 */
1283 {
1284           struct mount *mp = ap->a_vp->v_mount;
1285           struct ufsmount *ump = VFSTOUFS(mp);
1286           int error;
1287 
1288           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1289                     return EOPNOTSUPP;
1290 
1291           ufs_extattr_uepm_lock(ump);
1292 
1293           error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1294               ap->a_cred, curlwp);
1295 
1296           ufs_extattr_uepm_unlock(ump);
1297 
1298           return error;
1299 }
1300 
1301 /*
1302  * Vnode operation to set a named attribute.
1303  */
1304 int
ufs_setextattr(struct vop_setextattr_args * ap)1305 ufs_setextattr(struct vop_setextattr_args *ap)
1306 /*
1307 vop_setextattr {
1308           IN struct vnode *a_vp;
1309           IN int a_attrnamespace;
1310           IN const char *a_name;
1311           INOUT struct uio *a_uio;
1312           IN kauth_cred_t a_cred;
1313 };
1314 */
1315 {
1316           struct mount *mp = ap->a_vp->v_mount;
1317           struct ufsmount *ump = VFSTOUFS(mp);
1318           int error;
1319 
1320           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1321                     return EOPNOTSUPP;
1322 
1323           ufs_extattr_uepm_lock(ump);
1324 
1325           /*
1326            * XXX: No longer a supported way to delete extended attributes.
1327            */
1328           if (ap->a_uio == NULL) {
1329                     ufs_extattr_uepm_unlock(ump);
1330                     return EINVAL;
1331           }
1332 
1333           error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1334               ap->a_uio, ap->a_cred, curlwp);
1335 
1336           ufs_extattr_uepm_unlock(ump);
1337 
1338           return error;
1339 }
1340 
1341 /*
1342  * Real work associated with setting a vnode's extended attributes;
1343  * assumes that the attribute lock has already been grabbed.
1344  */
1345 static int
ufs_extattr_set(struct vnode * vp,int attrnamespace,const char * name,struct uio * uio,kauth_cred_t cred,struct lwp * l)1346 ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1347     struct uio *uio, kauth_cred_t cred, struct lwp *l)
1348 {
1349           struct ufs_extattr_list_entry *attribute;
1350           struct ufs_extattr_header ueh;
1351           struct iovec local_aiov;
1352           struct uio local_aio;
1353           struct mount *mp = vp->v_mount;
1354           struct ufsmount *ump = VFSTOUFS(mp);
1355           struct inode *ip = VTOI(vp);
1356           off_t base_offset;
1357           int error = 0, ioflag;
1358 
1359           if (vp->v_mount->mnt_flag & MNT_RDONLY)
1360                     return EROFS;
1361 
1362           if (!ufs_extattr_valid_attrname(attrnamespace, name))
1363                     return EINVAL;
1364 
1365           error = extattr_check_cred(vp, attrnamespace, cred, VWRITE);
1366           if (error)
1367                     return error;
1368 
1369           attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1370           if (!attribute) {
1371                     error = ufs_extattr_autocreate_attr(vp, attrnamespace,
1372                         name, l, &attribute);
1373                     if (error == EEXIST) {
1374                               /* Another thread raced us for backend creation */
1375                               error = 0;
1376                               attribute =
1377                                   ufs_extattr_find_attr(ump, attrnamespace, name);
1378                     }
1379 
1380                     if (error || !attribute)
1381                               return ENODATA;
1382           }
1383 
1384           /*
1385            * Early rejection of invalid offsets/length.
1386            * Reject: any offset but 0 (replace)
1387            *         Any size greater than attribute size limit
1388            */
1389           if (uio->uio_offset != 0 ||
1390               uio->uio_resid > attribute->uele_fileheader.uef_size)
1391                     return ENXIO;
1392 
1393           /*
1394            * Find base offset of header in file based on file header size, and
1395            * data header size + maximum data size, indexed by inode number.
1396            */
1397           base_offset = sizeof(struct ufs_extattr_fileheader) +
1398               ip->i_number * (sizeof(struct ufs_extattr_header) +
1399               attribute->uele_fileheader.uef_size);
1400 
1401           /*
1402            * Write out a data header for the data.
1403            */
1404           ueh.ueh_len = ufs_rw32((uint32_t) uio->uio_resid,
1405               UELE_NEEDSWAP(attribute));
1406           ueh.ueh_flags = ufs_rw32(UFS_EXTATTR_ATTR_FLAG_INUSE,
1407               UELE_NEEDSWAP(attribute));
1408           ueh.ueh_i_gen = ufs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
1409           local_aiov.iov_base = &ueh;
1410           local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1411           local_aio.uio_iov = &local_aiov;
1412           local_aio.uio_iovcnt = 1;
1413           local_aio.uio_rw = UIO_WRITE;
1414           local_aio.uio_offset = base_offset;
1415           local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1416           UIO_SETUP_SYSSPACE(&local_aio);
1417 
1418           /*
1419            * Don't need to get a lock on the backing file if the setattr is
1420            * being applied to the backing file, as the lock is already held.
1421            */
1422           if (attribute->uele_backing_vnode != vp)
1423                     vn_lock(attribute->uele_backing_vnode,
1424                         LK_EXCLUSIVE | LK_RETRY);
1425 
1426           ioflag = IO_NODELOCKED;
1427           if (ufs_extattr_sync)
1428                     ioflag |= IO_SYNC;
1429           error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1430               ump->um_extattr.uepm_ucred);
1431           if (error)
1432                     goto vopunlock_exit;
1433 
1434           if (local_aio.uio_resid != 0) {
1435                     error = ENXIO;
1436                     goto vopunlock_exit;
1437           }
1438 
1439           /*
1440            * Write out user data.
1441            * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
1442            */
1443           uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1444 
1445           ioflag = IO_NODELOCKED;
1446           if (ufs_extattr_sync)
1447                     ioflag |= IO_SYNC;
1448           error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1449               ump->um_extattr.uepm_ucred);
1450 
1451  vopunlock_exit:
1452           uio->uio_offset = 0;
1453 
1454           if (attribute->uele_backing_vnode != vp)
1455                     VOP_UNLOCK(attribute->uele_backing_vnode);
1456 
1457           return error;
1458 }
1459 
1460 /*
1461  * Real work associated with removing an extended attribute from a vnode.
1462  * Assumes the attribute lock has already been grabbed.
1463  */
1464 static int
ufs_extattr_rm(struct vnode * vp,int attrnamespace,const char * name,kauth_cred_t cred,struct lwp * l)1465 ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1466     kauth_cred_t cred, struct lwp *l)
1467 {
1468           struct ufs_extattr_list_entry *attribute;
1469           struct ufs_extattr_header ueh;
1470           struct mount *mp = vp->v_mount;
1471           struct ufsmount *ump = VFSTOUFS(mp);
1472           struct iovec local_aiov;
1473           struct uio local_aio;
1474           off_t base_offset;
1475           int error = 0, ioflag;
1476 
1477           if (vp->v_mount->mnt_flag & MNT_RDONLY)
1478                     return EROFS;
1479 
1480           if (!ufs_extattr_valid_attrname(attrnamespace, name))
1481                     return EINVAL;
1482 
1483           error = extattr_check_cred(vp, attrnamespace, cred, VWRITE);
1484           if (error)
1485                     return error;
1486 
1487           attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1488           if (!attribute)
1489                     return ENODATA;
1490 
1491           /*
1492            * Don't need to get a lock on the backing file if the getattr is
1493            * being applied to the backing file, as the lock is already held.
1494            */
1495           if (attribute->uele_backing_vnode != vp)
1496                     vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1497 
1498           error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1499           if (error)
1500                     goto vopunlock_exit;
1501 
1502           /* Flag it as not in use. */
1503           ueh.ueh_flags = 0;            /* No need to byte swap 0 */
1504           ueh.ueh_len = 0;              /* ...ditto... */
1505 
1506           local_aiov.iov_base = &ueh;
1507           local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1508           local_aio.uio_iov = &local_aiov;
1509           local_aio.uio_iovcnt = 1;
1510           local_aio.uio_rw = UIO_WRITE;
1511           local_aio.uio_offset = base_offset;
1512           local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1513           UIO_SETUP_SYSSPACE(&local_aio);
1514 
1515           ioflag = IO_NODELOCKED;
1516           if (ufs_extattr_sync)
1517                     ioflag |= IO_SYNC;
1518           error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1519               ump->um_extattr.uepm_ucred);
1520           if (error)
1521                     goto vopunlock_exit;
1522 
1523           if (local_aio.uio_resid != 0)
1524                     error = ENXIO;
1525 
1526  vopunlock_exit:
1527           VOP_UNLOCK(attribute->uele_backing_vnode);
1528 
1529           return error;
1530 }
1531 
1532 /*
1533  * Called by UFS when an inode is no longer active and should have its
1534  * attributes stripped.
1535  */
1536 void
ufs_extattr_vnode_inactive(struct vnode * vp,struct lwp * l)1537 ufs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
1538 {
1539           struct ufs_extattr_list_entry *uele;
1540           struct mount *mp = vp->v_mount;
1541           struct ufsmount *ump = VFSTOUFS(mp);
1542 
1543           /*
1544            * In that case, we cannot lock. We should not have any active vnodes
1545            * on the fs if this is not yet initialized but is going to be, so
1546            * this can go unlocked.
1547            */
1548           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1549                     return;
1550 
1551           if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1552                     return;
1553 
1554           ufs_extattr_uepm_lock(ump);
1555 
1556           LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1557                     ufs_extattr_rm(vp, uele->uele_attrnamespace,
1558                         uele->uele_attrname, lwp0.l_cred, l);
1559 
1560           ufs_extattr_uepm_unlock(ump);
1561 }
1562 
1563 void
ufs_extattr_init(void)1564 ufs_extattr_init(void)
1565 {
1566 
1567 }
1568 
1569 void
ufs_extattr_done(void)1570 ufs_extattr_done(void)
1571 {
1572 
1573 }
1574