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