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