ufs_extattr.c revision 91406
1/*- 2 * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson 3 * Copyright (c) 2002 Networks Associates Technologies, Inc. 4 * All rights reserved. 5 * 6 * This software was developed by Robert Watson for the TrustedBSD Project. 7 * 8 * This software was developed for the FreeBSD Project in part by NAI Labs, 9 * the Security Research Division of Network Associates, Inc. under 10 * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA 11 * CHATS research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. The names of the authors may not be used to endorse or promote 22 * products derived from this software without specific prior written 23 * permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * $FreeBSD: head/sys/ufs/ufs/ufs_extattr.c 91406 2002-02-27 18:32:23Z jhb $ 38 */ 39/* 40 * Developed by the TrustedBSD Project. 41 * Support for file system extended attribute: UFS-specific support functions. 42 */ 43 44#include "opt_ufs.h" 45 46#include <sys/param.h> 47#include <sys/systm.h> 48#include <sys/kernel.h> 49#include <sys/namei.h> 50#include <sys/malloc.h> 51#include <sys/fcntl.h> 52#include <sys/proc.h> 53#include <sys/vnode.h> 54#include <sys/mount.h> 55#include <sys/lock.h> 56#include <sys/dirent.h> 57#include <sys/extattr.h> 58#include <sys/sysctl.h> 59 60#include <vm/vm_zone.h> 61 62#include <ufs/ufs/dir.h> 63#include <ufs/ufs/extattr.h> 64#include <ufs/ufs/quota.h> 65#include <ufs/ufs/ufsmount.h> 66#include <ufs/ufs/inode.h> 67#include <ufs/ufs/ufs_extern.h> 68 69#ifdef UFS_EXTATTR 70 71#define MIN(a,b) (((a)<(b))?(a):(b)) 72 73static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); 74 75static int ufs_extattr_sync = 0; 76SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync, 77 0, ""); 78 79static int ufs_extattr_valid_attrname(int attrnamespace, 80 const char *attrname); 81static int ufs_extattr_credcheck(struct vnode *vp, 82 struct ufs_extattr_list_entry *uele, struct ucred *cred, 83 struct thread *td, int access); 84static int ufs_extattr_enable_with_open(struct ufsmount *ump, 85 struct vnode *vp, int attrnamespace, const char *attrname, 86 struct thread *td); 87static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 88 const char *attrname, struct vnode *backing_vnode, 89 struct thread *td); 90static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 91 const char *attrname, struct thread *td); 92static int ufs_extattr_get(struct vnode *vp, int attrnamespace, 93 const char *name, struct uio *uio, size_t *size, 94 struct ucred *cred, struct thread *td); 95static int ufs_extattr_set(struct vnode *vp, int attrnamespace, 96 const char *name, struct uio *uio, struct ucred *cred, 97 struct thread *td); 98static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, 99 const char *name, struct ucred *cred, struct thread *td); 100 101/* 102 * Per-FS attribute lock protecting attribute operations. 103 * XXX Right now there is a lot of lock contention due to having a single 104 * lock per-FS; really, this should be far more fine-grained. 105 */ 106static void 107ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td) 108{ 109 110 /* Ideally, LK_CANRECURSE would not be used, here. */ 111 lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY | 112 LK_CANRECURSE, 0, td); 113} 114 115static void 116ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td) 117{ 118 119 lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, td); 120} 121 122/* 123 * Determine whether the name passed is a valid name for an actual 124 * attribute. 125 * 126 * Invalid currently consists of: 127 * NULL pointer for attrname 128 * zero-length attrname (used to retrieve application attribute list) 129 */ 130static int 131ufs_extattr_valid_attrname(int attrnamespace, const char *attrname) 132{ 133 134 if (attrname == NULL) 135 return (0); 136 if (strlen(attrname) == 0) 137 return (0); 138 return (1); 139} 140 141/* 142 * Locate an attribute given a name and mountpoint. 143 * Must be holding uepm lock for the mount point. 144 */ 145static struct ufs_extattr_list_entry * 146ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, 147 const char *attrname) 148{ 149 struct ufs_extattr_list_entry *search_attribute; 150 151 for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); 152 search_attribute; 153 search_attribute = LIST_NEXT(search_attribute, uele_entries)) { 154 if (!(strncmp(attrname, search_attribute->uele_attrname, 155 UFS_EXTATTR_MAXEXTATTRNAME)) && 156 (attrnamespace == search_attribute->uele_attrnamespace)) { 157 return (search_attribute); 158 } 159 } 160 161 return (0); 162} 163 164/* 165 * Initialize per-FS structures supporting extended attributes. Do not 166 * start extended attributes yet. 167 */ 168void 169ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) 170{ 171 172 uepm->uepm_flags = 0; 173 174 LIST_INIT(&uepm->uepm_list); 175 /* XXX is PVFS right, here? */ 176 lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0); 177 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; 178} 179 180/* 181 * Destroy per-FS structures supporting extended attributes. Assumes 182 * that EAs have already been stopped, and will panic if not. 183 */ 184void 185ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) 186{ 187 188 if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 189 panic("ufs_extattr_uepm_destroy: not initialized"); 190 191 if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 192 panic("ufs_extattr_uepm_destroy: called while still started"); 193 194 /* 195 * It's not clear that either order for the next two lines is 196 * ideal, and it should never be a problem if this is only called 197 * during unmount, and with vfs_busy(). 198 */ 199 uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; 200 lockdestroy(&uepm->uepm_lock); 201} 202 203/* 204 * Start extended attribute support on an FS. 205 */ 206int 207ufs_extattr_start(struct mount *mp, struct thread *td) 208{ 209 struct ufsmount *ump; 210 int error = 0; 211 212 ump = VFSTOUFS(mp); 213 214 ufs_extattr_uepm_lock(ump, td); 215 216 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) { 217 error = EOPNOTSUPP; 218 goto unlock; 219 } 220 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) { 221 error = EBUSY; 222 goto unlock; 223 } 224 225 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; 226 227 ump->um_extattr.uepm_ucred = crhold(td->td_ucred); 228 229unlock: 230 ufs_extattr_uepm_unlock(ump, td); 231 232 return (error); 233} 234 235#ifdef UFS_EXTATTR_AUTOSTART 236/* 237 * Helper routine: given a locked parent directory and filename, return 238 * the locked vnode of the inode associated with the name. Will not 239 * follow symlinks, may return any type of vnode. Lock on parent will 240 * be released even in the event of a failure. In the event that the 241 * target is the parent (i.e., "."), there will be two references and 242 * one lock, requiring the caller to possibly special-case. 243 */ 244#define UE_GETDIR_LOCKPARENT 1 245#define UE_GETDIR_LOCKPARENT_DONT 2 246static int 247ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, 248 struct vnode **vp, struct thread *td) 249{ 250 struct vop_cachedlookup_args vargs; 251 struct componentname cnp; 252 struct vnode *target_vp; 253 int error; 254 255 bzero(&cnp, sizeof(cnp)); 256 cnp.cn_nameiop = LOOKUP; 257 cnp.cn_flags = ISLASTCN; 258 if (lockparent == UE_GETDIR_LOCKPARENT) 259 cnp.cn_flags |= LOCKPARENT; 260 cnp.cn_thread = td; 261 cnp.cn_cred = td->td_ucred; 262 cnp.cn_pnbuf = zalloc(namei_zone); 263 cnp.cn_nameptr = cnp.cn_pnbuf; 264 error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, 265 (size_t *) &cnp.cn_namelen); 266 if (error) { 267 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { 268 VOP_UNLOCK(start_dvp, 0, td); 269 } 270 zfree(namei_zone, cnp.cn_pnbuf); 271 printf("ufs_extattr_lookup: copystr failed\n"); 272 return (error); 273 } 274 cnp.cn_namelen--; /* trim nul termination */ 275 vargs.a_desc = NULL; 276 vargs.a_dvp = start_dvp; 277 vargs.a_vpp = &target_vp; 278 vargs.a_cnp = &cnp; 279 error = ufs_lookup(&vargs); 280 zfree(namei_zone, cnp.cn_pnbuf); 281 if (error) { 282 /* 283 * Error condition, may have to release the lock on the parent 284 * if ufs_lookup() didn't. 285 */ 286 if (!(cnp.cn_flags & PDIRUNLOCK) && 287 (lockparent == UE_GETDIR_LOCKPARENT_DONT)) 288 VOP_UNLOCK(start_dvp, 0, td); 289 290 /* 291 * Check that ufs_lookup() didn't release the lock when we 292 * didn't want it to. 293 */ 294 if ((cnp.cn_flags & PDIRUNLOCK) && 295 (lockparent == UE_GETDIR_LOCKPARENT)) 296 panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); 297 298 return (error); 299 } 300/* 301 if (target_vp == start_dvp) 302 panic("ufs_extattr_lookup: target_vp == start_dvp"); 303*/ 304 305 if (target_vp != start_dvp && 306 !(cnp.cn_flags & PDIRUNLOCK) && 307 (lockparent == UE_GETDIR_LOCKPARENT_DONT)) 308 panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK"); 309 310 if ((cnp.cn_flags & PDIRUNLOCK) && 311 (lockparent == UE_GETDIR_LOCKPARENT)) 312 panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); 313 314 /* printf("ufs_extattr_lookup: success\n"); */ 315 *vp = target_vp; 316 return (0); 317} 318#endif /* !UFS_EXTATTR_AUTOSTART */ 319 320/* 321 * Enable an EA using the passed file system, backing vnode, attribute name, 322 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp 323 * to be locked when passed in. The vnode will be returned unlocked, 324 * regardless of success/failure of the function. As a result, the caller 325 * will always need to vrele(), but not vput(). 326 */ 327static int 328ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, 329 int attrnamespace, const char *attrname, struct thread *td) 330{ 331 int error; 332 333 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td); 334 if (error) { 335 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " 336 "with %d\n", error); 337 VOP_UNLOCK(vp, 0, td); 338 return (error); 339 } 340 341 /* 342 * XXX: Note, should VOP_CLOSE() if vfs_object_create() fails, but due 343 * to a similar piece of code in vn_open(), we don't. 344 */ 345 if (vn_canvmio(vp) == TRUE) 346 if ((error = vfs_object_create(vp, td, 347 td->td_ucred)) != 0) { 348 /* 349 * XXX: bug replicated from vn_open(): should 350 * VOP_CLOSE() here. 351 */ 352 VOP_UNLOCK(vp, 0, td); 353 return (error); 354 } 355 356 vp->v_writecount++; 357 358 vref(vp); 359 360 VOP_UNLOCK(vp, 0, td); 361 362 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td); 363 if (error != 0) 364 vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 365 return (error); 366} 367 368#ifdef UFS_EXTATTR_AUTOSTART 369/* 370 * Given a locked directory vnode, iterate over the names in the directory 371 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential 372 * attribute files. Then invoke ufs_extattr_enable_with_open() on each 373 * to attempt to start the attribute. Leaves the directory locked on 374 * exit. 375 */ 376static int 377ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, 378 int attrnamespace, struct thread *td) 379{ 380 struct vop_readdir_args vargs; 381 struct dirent *dp, *edp; 382 struct vnode *attr_vp; 383 struct uio auio; 384 struct iovec aiov; 385 char *dirbuf; 386 int error, eofflag = 0; 387 388 if (dvp->v_type != VDIR) 389 return (ENOTDIR); 390 391 MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK); 392 393 auio.uio_iov = &aiov; 394 auio.uio_iovcnt = 1; 395 auio.uio_rw = UIO_READ; 396 auio.uio_segflg = UIO_SYSSPACE; 397 auio.uio_td = td; 398 auio.uio_offset = 0; 399 400 vargs.a_desc = NULL; 401 vargs.a_vp = dvp; 402 vargs.a_uio = &auio; 403 vargs.a_cred = td->td_ucred; 404 vargs.a_eofflag = &eofflag; 405 vargs.a_ncookies = NULL; 406 vargs.a_cookies = NULL; 407 408 while (!eofflag) { 409 auio.uio_resid = DIRBLKSIZ; 410 aiov.iov_base = dirbuf; 411 aiov.iov_len = DIRBLKSIZ; 412 error = ufs_readdir(&vargs); 413 if (error) { 414 printf("ufs_extattr_iterate_directory: ufs_readdir " 415 "%d\n", error); 416 return (error); 417 } 418 419 edp = (struct dirent *)&dirbuf[DIRBLKSIZ]; 420 for (dp = (struct dirent *)dirbuf; dp < edp; ) { 421#if (BYTE_ORDER == LITTLE_ENDIAN) 422 dp->d_type = dp->d_namlen; 423 dp->d_namlen = 0; 424#else 425 dp->d_type = 0; 426#endif 427 if (dp->d_reclen == 0) 428 break; 429 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, 430 dp->d_name, &attr_vp, td); 431 if (error) { 432 printf("ufs_extattr_iterate_directory: lookup " 433 "%s %d\n", dp->d_name, error); 434 } else if (attr_vp == dvp) { 435 vrele(attr_vp); 436 } else if (attr_vp->v_type != VREG) { 437 vput(attr_vp); 438 } else { 439 error = ufs_extattr_enable_with_open(ump, 440 attr_vp, attrnamespace, dp->d_name, td); 441 vrele(attr_vp); 442 if (error) { 443 printf("ufs_extattr_iterate_directory: " 444 "enable %s %d\n", dp->d_name, 445 error); 446 } else if (bootverbose) { 447 printf("UFS autostarted EA %s\n", 448 dp->d_name); 449 } 450 } 451 dp = (struct dirent *) ((char *)dp + dp->d_reclen); 452 if (dp >= edp) 453 break; 454 } 455 } 456 FREE(dirbuf, M_TEMP); 457 458 return (0); 459} 460 461/* 462 * Auto-start of extended attributes, to be executed (optionally) at 463 * mount-time. 464 */ 465int 466ufs_extattr_autostart(struct mount *mp, struct thread *td) 467{ 468 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; 469 int error; 470 471 /* 472 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the file system root? 473 * If so, automatically start EA's. 474 */ 475 error = VFS_ROOT(mp, &rvp); 476 if (error) { 477 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 478 error); 479 return (error); 480 } 481 482 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 483 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 484 if (error) { 485 /* rvp ref'd but now unlocked */ 486 vrele(rvp); 487 return (error); 488 } 489 if (rvp == attr_dvp) { 490 /* Should never happen. */ 491 vrele(attr_dvp); 492 vput(rvp); 493 return (EINVAL); 494 } 495 vrele(rvp); 496 497 if (attr_dvp->v_type != VDIR) { 498 printf("ufs_extattr_autostart: %s != VDIR\n", 499 UFS_EXTATTR_FSROOTSUBDIR); 500 goto return_vput_attr_dvp; 501 } 502 503 error = ufs_extattr_start(mp, td); 504 if (error) { 505 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 506 error); 507 goto return_vput_attr_dvp; 508 } 509 510 /* 511 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 512 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 513 * and start with appropriate type. Failures in either don't 514 * result in an over-all failure. attr_dvp is left locked to 515 * be cleaned up on exit. 516 */ 517 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 518 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 519 if (!error) { 520 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 521 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 522 if (error) 523 printf("ufs_extattr_iterate_directory returned %d\n", 524 error); 525 vput(attr_system_dvp); 526 } 527 528 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 529 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 530 if (!error) { 531 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 532 attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 533 if (error) 534 printf("ufs_extattr_iterate_directory returned %d\n", 535 error); 536 vput(attr_user_dvp); 537 } 538 539 /* Mask startup failures in sub-directories. */ 540 error = 0; 541 542return_vput_attr_dvp: 543 vput(attr_dvp); 544 545 return (error); 546} 547#endif /* !UFS_EXTATTR_AUTOSTART */ 548 549/* 550 * Stop extended attribute support on an FS. 551 */ 552int 553ufs_extattr_stop(struct mount *mp, struct thread *td) 554{ 555 struct ufs_extattr_list_entry *uele; 556 struct ufsmount *ump = VFSTOUFS(mp); 557 int error = 0; 558 559 ufs_extattr_uepm_lock(ump, td); 560 561 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 562 error = EOPNOTSUPP; 563 goto unlock; 564 } 565 566 while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) { 567 uele = LIST_FIRST(&ump->um_extattr.uepm_list); 568 ufs_extattr_disable(ump, uele->uele_attrnamespace, 569 uele->uele_attrname, td); 570 } 571 572 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 573 574 crfree(ump->um_extattr.uepm_ucred); 575 ump->um_extattr.uepm_ucred = NULL; 576 577unlock: 578 ufs_extattr_uepm_unlock(ump, td); 579 580 return (error); 581} 582 583/* 584 * Enable a named attribute on the specified file system; provide an 585 * unlocked backing vnode to hold the attribute data. 586 */ 587static int 588ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 589 const char *attrname, struct vnode *backing_vnode, struct thread *td) 590{ 591 struct ufs_extattr_list_entry *attribute; 592 struct iovec aiov; 593 struct uio auio; 594 int error = 0; 595 596 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 597 return (EINVAL); 598 if (backing_vnode->v_type != VREG) 599 return (EINVAL); 600 601 MALLOC(attribute, struct ufs_extattr_list_entry *, 602 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); 603 if (attribute == NULL) 604 return (ENOMEM); 605 606 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 607 error = EOPNOTSUPP; 608 goto free_exit; 609 } 610 611 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { 612 error = EEXIST; 613 goto free_exit; 614 } 615 616 strncpy(attribute->uele_attrname, attrname, 617 UFS_EXTATTR_MAXEXTATTRNAME); 618 attribute->uele_attrnamespace = attrnamespace; 619 bzero(&attribute->uele_fileheader, 620 sizeof(struct ufs_extattr_fileheader)); 621 622 attribute->uele_backing_vnode = backing_vnode; 623 624 auio.uio_iov = &aiov; 625 auio.uio_iovcnt = 1; 626 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 627 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 628 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 629 auio.uio_offset = (off_t) 0; 630 auio.uio_segflg = UIO_SYSSPACE; 631 auio.uio_rw = UIO_READ; 632 auio.uio_td = td; 633 634 VOP_LEASE(backing_vnode, td, td->td_ucred, LEASE_WRITE); 635 vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, td); 636 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, 637 ump->um_extattr.uepm_ucred); 638 VOP_UNLOCK(backing_vnode, 0, td); 639 640 if (error) 641 goto free_exit; 642 643 if (auio.uio_resid != 0) { 644 printf("ufs_extattr_enable: malformed attribute header\n"); 645 error = EINVAL; 646 goto free_exit; 647 } 648 649 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 650 printf("ufs_extattr_enable: invalid attribute header magic\n"); 651 error = EINVAL; 652 goto free_exit; 653 } 654 655 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 656 printf("ufs_extattr_enable: incorrect attribute header " 657 "version\n"); 658 error = EINVAL; 659 goto free_exit; 660 } 661 662 backing_vnode->v_flag |= VSYSTEM; 663 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, 664 uele_entries); 665 666 return (0); 667 668free_exit: 669 FREE(attribute, M_UFS_EXTATTR); 670 return (error); 671} 672 673/* 674 * Disable extended attribute support on an FS. 675 */ 676static int 677ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 678 const char *attrname, struct thread *td) 679{ 680 struct ufs_extattr_list_entry *uele; 681 int error = 0; 682 683 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 684 return (EINVAL); 685 686 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); 687 if (!uele) 688 /* XXX: ENOENT here will eventually be ENOATTR. */ 689 return (ENOENT); 690 691 LIST_REMOVE(uele, uele_entries); 692 693 uele->uele_backing_vnode->v_flag &= ~VSYSTEM; 694 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, 695 td->td_ucred, td); 696 697 FREE(uele, M_UFS_EXTATTR); 698 699 return (error); 700} 701 702/* 703 * VFS call to manage extended attributes in UFS. If filename_vp is 704 * non-NULL, it must be passed in locked, and regardless of errors in 705 * processing, will be unlocked. 706 */ 707int 708ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 709 int attrnamespace, const char *attrname, struct thread *td) 710{ 711 struct ufsmount *ump = VFSTOUFS(mp); 712 int error; 713 714 /* 715 * Processes with privilege, but in jail, are not allowed to 716 * configure extended attributes. 717 */ 718 if ((error = suser_xxx(td->td_ucred, td->td_proc, 0))) { 719 if (filename_vp != NULL) 720 VOP_UNLOCK(filename_vp, 0, td); 721 return (error); 722 } 723 724 switch(cmd) { 725 case UFS_EXTATTR_CMD_START: 726 if (filename_vp != NULL) { 727 VOP_UNLOCK(filename_vp, 0, td); 728 return (EINVAL); 729 } 730 if (attrname != NULL) 731 return (EINVAL); 732 733 error = ufs_extattr_start(mp, td); 734 735 return (error); 736 737 case UFS_EXTATTR_CMD_STOP: 738 if (filename_vp != NULL) { 739 VOP_UNLOCK(filename_vp, 0, td); 740 return (EINVAL); 741 } 742 if (attrname != NULL) 743 return (EINVAL); 744 745 error = ufs_extattr_stop(mp, td); 746 747 return (error); 748 749 case UFS_EXTATTR_CMD_ENABLE: 750 751 if (filename_vp == NULL) 752 return (EINVAL); 753 if (attrname == NULL) { 754 VOP_UNLOCK(filename_vp, 0, td); 755 return (EINVAL); 756 } 757 758 /* 759 * ufs_extattr_enable_with_open() will always unlock the 760 * vnode, regardless of failure. 761 */ 762 ufs_extattr_uepm_lock(ump, td); 763 error = ufs_extattr_enable_with_open(ump, filename_vp, 764 attrnamespace, attrname, td); 765 ufs_extattr_uepm_unlock(ump, td); 766 767 return (error); 768 769 case UFS_EXTATTR_CMD_DISABLE: 770 771 if (filename_vp != NULL) { 772 VOP_UNLOCK(filename_vp, 0, td); 773 return (EINVAL); 774 } 775 if (attrname == NULL) 776 return (EINVAL); 777 778 ufs_extattr_uepm_lock(ump, td); 779 error = ufs_extattr_disable(ump, attrnamespace, attrname, 780 td); 781 ufs_extattr_uepm_unlock(ump, td); 782 783 return (error); 784 785 default: 786 return (EINVAL); 787 } 788} 789 790/* 791 * Credential check based on process requesting service, and per-attribute 792 * permissions. 793 */ 794static int 795ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele, 796 struct ucred *cred, struct thread *td, int access) 797{ 798 799 /* 800 * Kernel-invoked always succeeds. 801 */ 802 if (cred == NULL) 803 return (0); 804 805 /* 806 * Do not allow privileged processes in jail to directly 807 * manipulate system attributes. 808 * 809 * XXX What capability should apply here? 810 * Probably CAP_SYS_SETFFLAG. 811 */ 812 switch (uele->uele_attrnamespace) { 813 case EXTATTR_NAMESPACE_SYSTEM: 814 /* Potentially should be: return (EPERM); */ 815 return (suser_xxx(cred, td->td_proc, 0)); 816 case EXTATTR_NAMESPACE_USER: 817 return (VOP_ACCESS(vp, access, cred, td)); 818 default: 819 return (EPERM); 820 } 821} 822 823/* 824 * Vnode operating to retrieve a named extended attribute. 825 */ 826int 827ufs_vop_getextattr(struct vop_getextattr_args *ap) 828/* 829vop_getextattr { 830 IN struct vnode *a_vp; 831 IN int a_attrnamespace; 832 IN const char *a_name; 833 INOUT struct uio *a_uio; 834 OUT struct size_t *a_size; 835 IN struct ucred *a_cred; 836 IN struct thread *a_td; 837}; 838*/ 839{ 840 struct mount *mp = ap->a_vp->v_mount; 841 struct ufsmount *ump = VFSTOUFS(mp); 842 int error; 843 844 ufs_extattr_uepm_lock(ump, ap->a_td); 845 846 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, 847 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td); 848 849 ufs_extattr_uepm_unlock(ump, ap->a_td); 850 851 return (error); 852} 853 854/* 855 * Real work associated with retrieving a named attribute--assumes that 856 * the attribute lock has already been grabbed. 857 */ 858static int 859ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 860 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) 861{ 862 struct ufs_extattr_list_entry *attribute; 863 struct ufs_extattr_header ueh; 864 struct iovec local_aiov; 865 struct uio local_aio; 866 struct mount *mp = vp->v_mount; 867 struct ufsmount *ump = VFSTOUFS(mp); 868 struct inode *ip = VTOI(vp); 869 off_t base_offset; 870 size_t len, old_len; 871 int error = 0; 872 873 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 874 return (EOPNOTSUPP); 875 876 if (strlen(name) == 0) { 877 /* XXX retrieve attribute lists. */ 878 /* XXX should probably be checking for name == NULL? */ 879 return (EINVAL); 880 } 881 882 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 883 if (!attribute) 884 /* XXX: ENOENT here will eventually be ENOATTR. */ 885 return (ENOENT); 886 887 if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IREAD))) 888 return (error); 889 890 /* 891 * Allow only offsets of zero to encourage the read/replace 892 * extended attribute semantic. Otherwise we can't guarantee 893 * atomicity, as we don't provide locks for extended attributes. 894 */ 895 if (uio != NULL && uio->uio_offset != 0) 896 return (ENXIO); 897 898 /* 899 * Find base offset of header in file based on file header size, and 900 * data header size + maximum data size, indexed by inode number. 901 */ 902 base_offset = sizeof(struct ufs_extattr_fileheader) + 903 ip->i_number * (sizeof(struct ufs_extattr_header) + 904 attribute->uele_fileheader.uef_size); 905 906 /* 907 * Read in the data header to see if the data is defined, and if so 908 * how much. 909 */ 910 bzero(&ueh, sizeof(struct ufs_extattr_header)); 911 local_aiov.iov_base = (caddr_t) &ueh; 912 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 913 local_aio.uio_iov = &local_aiov; 914 local_aio.uio_iovcnt = 1; 915 local_aio.uio_rw = UIO_READ; 916 local_aio.uio_segflg = UIO_SYSSPACE; 917 local_aio.uio_td = td; 918 local_aio.uio_offset = base_offset; 919 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 920 921 /* 922 * Acquire locks. 923 */ 924 VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_READ); 925 /* 926 * Don't need to get a lock on the backing file if the getattr is 927 * being applied to the backing file, as the lock is already held. 928 */ 929 if (attribute->uele_backing_vnode != vp) 930 vn_lock(attribute->uele_backing_vnode, LK_SHARED | 931 LK_NOPAUSE | LK_RETRY, td); 932 933 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 934 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 935 if (error) 936 goto vopunlock_exit; 937 938 /* Defined? */ 939 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 940 /* XXX: ENOENT here will eventually be ENOATTR. */ 941 error = ENOENT; 942 goto vopunlock_exit; 943 } 944 945 /* Valid for the current inode generation? */ 946 if (ueh.ueh_i_gen != ip->i_gen) { 947 /* 948 * The inode itself has a different generation number 949 * than the attribute data. For now, the best solution 950 * is to coerce this to undefined, and let it get cleaned 951 * up by the next write or extattrctl clean. 952 */ 953 printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n", 954 ueh.ueh_i_gen, ip->i_gen); 955 /* XXX: ENOENT here will eventually be ENOATTR. */ 956 error = ENOENT; 957 goto vopunlock_exit; 958 } 959 960 /* Local size consistency check. */ 961 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 962 error = ENXIO; 963 goto vopunlock_exit; 964 } 965 966 /* Return full data size if caller requested it. */ 967 if (size != NULL) 968 *size = ueh.ueh_len; 969 970 /* Return data if the caller requested it. */ 971 if (uio != NULL) { 972 /* Allow for offset into the attribute data. */ 973 uio->uio_offset = base_offset + sizeof(struct 974 ufs_extattr_header); 975 976 /* 977 * Figure out maximum to transfer -- use buffer size and 978 * local data limit. 979 */ 980 len = MIN(uio->uio_resid, ueh.ueh_len); 981 old_len = uio->uio_resid; 982 uio->uio_resid = len; 983 984 error = VOP_READ(attribute->uele_backing_vnode, uio, 985 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 986 if (error) 987 goto vopunlock_exit; 988 989 uio->uio_resid = old_len - (len - uio->uio_resid); 990 } 991 992vopunlock_exit: 993 994 if (uio != NULL) 995 uio->uio_offset = 0; 996 997 if (attribute->uele_backing_vnode != vp) 998 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 999 1000 return (error); 1001} 1002 1003/* 1004 * Vnode operation to set a named attribute. 1005 */ 1006int 1007ufs_vop_setextattr(struct vop_setextattr_args *ap) 1008/* 1009vop_setextattr { 1010 IN struct vnode *a_vp; 1011 IN int a_attrnamespace; 1012 IN const char *a_name; 1013 INOUT struct uio *a_uio; 1014 IN struct ucred *a_cred; 1015 IN struct thread *a_td; 1016}; 1017*/ 1018{ 1019 struct mount *mp = ap->a_vp->v_mount; 1020 struct ufsmount *ump = VFSTOUFS(mp); 1021 1022 int error; 1023 1024 ufs_extattr_uepm_lock(ump, ap->a_td); 1025 1026 if (ap->a_uio != NULL) 1027 error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, 1028 ap->a_name, ap->a_uio, ap->a_cred, ap->a_td); 1029 else 1030 error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, 1031 ap->a_name, ap->a_cred, ap->a_td); 1032 1033 ufs_extattr_uepm_unlock(ump, ap->a_td); 1034 1035 return (error); 1036} 1037 1038/* 1039 * Real work associated with setting a vnode's extended attributes; 1040 * assumes that the attribute lock has already been grabbed. 1041 */ 1042static int 1043ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, 1044 struct uio *uio, struct ucred *cred, struct thread *td) 1045{ 1046 struct ufs_extattr_list_entry *attribute; 1047 struct ufs_extattr_header ueh; 1048 struct iovec local_aiov; 1049 struct uio local_aio; 1050 struct mount *mp = vp->v_mount; 1051 struct ufsmount *ump = VFSTOUFS(mp); 1052 struct inode *ip = VTOI(vp); 1053 off_t base_offset; 1054 int error = 0, ioflag; 1055 1056 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1057 return (EROFS); 1058 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1059 return (EOPNOTSUPP); 1060 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1061 return (EINVAL); 1062 1063 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1064 if (!attribute) 1065 /* XXX: ENOENT here will eventually be ENOATTR. */ 1066 return (ENOENT); 1067 1068 if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE))) 1069 return (error); 1070 1071 /* 1072 * Early rejection of invalid offsets/length. 1073 * Reject: any offset but 0 (replace) 1074 * Any size greater than attribute size limit 1075 */ 1076 if (uio->uio_offset != 0 || 1077 uio->uio_resid > attribute->uele_fileheader.uef_size) 1078 return (ENXIO); 1079 1080 /* 1081 * Find base offset of header in file based on file header size, and 1082 * data header size + maximum data size, indexed by inode number. 1083 */ 1084 base_offset = sizeof(struct ufs_extattr_fileheader) + 1085 ip->i_number * (sizeof(struct ufs_extattr_header) + 1086 attribute->uele_fileheader.uef_size); 1087 1088 /* 1089 * Write out a data header for the data. 1090 */ 1091 ueh.ueh_len = uio->uio_resid; 1092 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 1093 ueh.ueh_i_gen = ip->i_gen; 1094 local_aiov.iov_base = (caddr_t) &ueh; 1095 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1096 local_aio.uio_iov = &local_aiov; 1097 local_aio.uio_iovcnt = 1; 1098 local_aio.uio_rw = UIO_WRITE; 1099 local_aio.uio_segflg = UIO_SYSSPACE; 1100 local_aio.uio_td = td; 1101 local_aio.uio_offset = base_offset; 1102 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1103 1104 /* 1105 * Acquire locks. 1106 */ 1107 VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE); 1108 1109 /* 1110 * Don't need to get a lock on the backing file if the setattr is 1111 * being applied to the backing file, as the lock is already held. 1112 */ 1113 if (attribute->uele_backing_vnode != vp) 1114 vn_lock(attribute->uele_backing_vnode, 1115 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td); 1116 1117 ioflag = IO_NODELOCKED; 1118 if (ufs_extattr_sync) 1119 ioflag |= IO_SYNC; 1120 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1121 ump->um_extattr.uepm_ucred); 1122 if (error) 1123 goto vopunlock_exit; 1124 1125 if (local_aio.uio_resid != 0) { 1126 error = ENXIO; 1127 goto vopunlock_exit; 1128 } 1129 1130 /* 1131 * Write out user data. 1132 */ 1133 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 1134 1135 ioflag = IO_NODELOCKED; 1136 if (ufs_extattr_sync) 1137 ioflag |= IO_SYNC; 1138 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, 1139 ump->um_extattr.uepm_ucred); 1140 1141vopunlock_exit: 1142 uio->uio_offset = 0; 1143 1144 if (attribute->uele_backing_vnode != vp) 1145 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1146 1147 return (error); 1148} 1149 1150/* 1151 * Real work associated with removing an extended attribute from a vnode. 1152 * Assumes the attribute lock has already been grabbed. 1153 */ 1154static int 1155ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 1156 struct ucred *cred, struct thread *td) 1157{ 1158 struct ufs_extattr_list_entry *attribute; 1159 struct ufs_extattr_header ueh; 1160 struct iovec local_aiov; 1161 struct uio local_aio; 1162 struct mount *mp = vp->v_mount; 1163 struct ufsmount *ump = VFSTOUFS(mp); 1164 struct inode *ip = VTOI(vp); 1165 off_t base_offset; 1166 int error = 0, ioflag; 1167 1168 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1169 return (EROFS); 1170 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1171 return (EOPNOTSUPP); 1172 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1173 return (EINVAL); 1174 1175 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1176 if (!attribute) 1177 /* XXX: ENOENT here will eventually be ENOATTR. */ 1178 return (ENOENT); 1179 1180 if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE))) 1181 return (error); 1182 1183 /* 1184 * Find base offset of header in file based on file header size, and 1185 * data header size + maximum data size, indexed by inode number. 1186 */ 1187 base_offset = sizeof(struct ufs_extattr_fileheader) + 1188 ip->i_number * (sizeof(struct ufs_extattr_header) + 1189 attribute->uele_fileheader.uef_size); 1190 1191 /* 1192 * Check to see if currently defined. 1193 */ 1194 bzero(&ueh, sizeof(struct ufs_extattr_header)); 1195 1196 local_aiov.iov_base = (caddr_t) &ueh; 1197 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1198 local_aio.uio_iov = &local_aiov; 1199 local_aio.uio_iovcnt = 1; 1200 local_aio.uio_rw = UIO_READ; 1201 local_aio.uio_segflg = UIO_SYSSPACE; 1202 local_aio.uio_td = td; 1203 local_aio.uio_offset = base_offset; 1204 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1205 1206 VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE); 1207 1208 /* 1209 * Don't need to get the lock on the backing vnode if the vnode we're 1210 * modifying is it, as we already hold the lock. 1211 */ 1212 if (attribute->uele_backing_vnode != vp) 1213 vn_lock(attribute->uele_backing_vnode, 1214 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td); 1215 1216 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 1217 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 1218 if (error) 1219 goto vopunlock_exit; 1220 1221 /* Defined? */ 1222 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 1223 /* XXX: ENOENT here will eventually be ENOATTR. */ 1224 error = ENOENT; 1225 goto vopunlock_exit; 1226 } 1227 1228 /* Valid for the current inode generation? */ 1229 if (ueh.ueh_i_gen != ip->i_gen) { 1230 /* 1231 * The inode itself has a different generation number than 1232 * the attribute data. For now, the best solution is to 1233 * coerce this to undefined, and let it get cleaned up by 1234 * the next write or extattrctl clean. 1235 */ 1236 printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n", 1237 ueh.ueh_i_gen, ip->i_gen); 1238 /* XXX: ENOENT here will eventually be ENOATTR. */ 1239 error = ENOENT; 1240 goto vopunlock_exit; 1241 } 1242 1243 /* Flag it as not in use. */ 1244 ueh.ueh_flags = 0; 1245 ueh.ueh_len = 0; 1246 1247 local_aiov.iov_base = (caddr_t) &ueh; 1248 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1249 local_aio.uio_iov = &local_aiov; 1250 local_aio.uio_iovcnt = 1; 1251 local_aio.uio_rw = UIO_WRITE; 1252 local_aio.uio_segflg = UIO_SYSSPACE; 1253 local_aio.uio_td = td; 1254 local_aio.uio_offset = base_offset; 1255 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1256 1257 ioflag = IO_NODELOCKED; 1258 if (ufs_extattr_sync) 1259 ioflag |= IO_SYNC; 1260 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1261 ump->um_extattr.uepm_ucred); 1262 if (error) 1263 goto vopunlock_exit; 1264 1265 if (local_aio.uio_resid != 0) 1266 error = ENXIO; 1267 1268vopunlock_exit: 1269 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1270 1271 return (error); 1272} 1273 1274/* 1275 * Called by UFS when an inode is no longer active and should have its 1276 * attributes stripped. 1277 */ 1278void 1279ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td) 1280{ 1281 struct ufs_extattr_list_entry *uele; 1282 struct mount *mp = vp->v_mount; 1283 struct ufsmount *ump = VFSTOUFS(mp); 1284 1285 /* 1286 * In that case, we cannot lock. We should not have any active vnodes 1287 * on the fs if this is not yet initialized but is going to be, so 1288 * this can go unlocked. 1289 */ 1290 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 1291 return; 1292 1293 ufs_extattr_uepm_lock(ump, td); 1294 1295 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 1296 ufs_extattr_uepm_unlock(ump, td); 1297 return; 1298 } 1299 1300 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) 1301 ufs_extattr_rm(vp, uele->uele_attrnamespace, 1302 uele->uele_attrname, NULL, td); 1303 1304 ufs_extattr_uepm_unlock(ump, td); 1305} 1306 1307#endif /* !UFS_EXTATTR */ 1308