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