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