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