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 80int ufs_extattr_sync = 1; 81int ufs_extattr_autocreate = 1024; 82 83static int ufs_extattr_valid_attrname(int attrnamespace, 84 const char *attrname); 85static int ufs_extattr_enable_with_open(struct ufsmount *ump, 86 struct vnode *vp, int attrnamespace, const char *attrname, 87 struct lwp *l); 88static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 89 const char *attrname, struct vnode *backing_vnode, 90 struct lwp *l); 91static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 92 const char *attrname, struct lwp *l); 93static 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); 96static 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); 99static int ufs_extattr_set(struct vnode *vp, int attrnamespace, 100 const char *name, struct uio *uio, kauth_cred_t cred, 101 struct lwp *l); 102static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, 103 const char *name, kauth_cred_t cred, struct lwp *l); 104static struct ufs_extattr_list_entry *ufs_extattr_find_attr(struct ufsmount *, 105 int, const char *); 106static 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 */ 116static void 117ufs_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 134static void 135ufs_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 */ 154static int 155ufs_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 */ 168static int 169ufs_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 */ 306static struct ufs_extattr_list_entry * 307ufs_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 */ 329void 330ufs_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 */ 345void 346ufs_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 */ 368int 369ufs_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 */ 408static int 409ufs_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 */ 477static int 478ufs_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 */ 511static int 512ufs_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 602static int 603ufs_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 */ 633int 634ufs_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 */ 714void 715ufs_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 */ 749static int 750ufs_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 */ 851static int 852ufs_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 */ 879int 880ufs_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 */ 949static int 950ufs_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 */ 1028int 1029ufs_getextattr(struct vop_getextattr_args *ap) 1030/* 1031vop_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 */ 1062static int 1063ufs_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 */ 1144int 1145ufs_listextattr(struct vop_listextattr_args *ap) 1146/* 1147vop_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 */ 1179static int 1180ufs_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 */ 1273int 1274ufs_deleteextattr(struct vop_deleteextattr_args *ap) 1275/* 1276vop_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 */ 1304int 1305ufs_setextattr(struct vop_setextattr_args *ap) 1306/* 1307vop_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 */ 1345static int 1346ufs_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 */ 1464static int 1465ufs_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 */ 1536void 1537ufs_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 1563void 1564ufs_extattr_init(void) 1565{ 1566 1567} 1568 1569void 1570ufs_extattr_done(void) 1571{ 1572 1573} 1574