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