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