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