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