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