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