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