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