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