159241Srwatson/*- 2126097Srwatson * Copyright (c) 1999-2002 Robert N. M. Watson 3126097Srwatson * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 459241Srwatson * All rights reserved. 559241Srwatson * 685845Srwatson * This software was developed by Robert Watson for the TrustedBSD Project. 785845Srwatson * 8106394Srwatson * This software was developed for the FreeBSD Project in part by Network 9106394Srwatson * Associates Laboratories, the Security Research Division of Network 10106394Srwatson * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 11106394Srwatson * as part of the DARPA CHATS research program. 1290452Srwatson * 1359241Srwatson * Redistribution and use in source and binary forms, with or without 1459241Srwatson * modification, are permitted provided that the following conditions 1559241Srwatson * are met: 1659241Srwatson * 1. Redistributions of source code must retain the above copyright 1759241Srwatson * notice, this list of conditions and the following disclaimer. 1859241Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1959241Srwatson * notice, this list of conditions and the following disclaimer in the 2059241Srwatson * documentation and/or other materials provided with the distribution. 2159241Srwatson * 2259241Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2359241Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2459241Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2559241Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2659241Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2759241Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2859241Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2959241Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3059241Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3159241Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3259241Srwatson * SUCH DAMAGE. 3359241Srwatson * 3459241Srwatson */ 35116192Sobrien 3659241Srwatson/* 3796755Strhodes * Support for filesystem extended attribute: UFS-specific support functions. 3859241Srwatson */ 3959241Srwatson 40116192Sobrien#include <sys/cdefs.h> 41116192Sobrien__FBSDID("$FreeBSD: releng/10.3/sys/ufs/ufs/ufs_extattr.c 252438 2013-07-01 04:06:40Z gleb $"); 42116192Sobrien 4390453Srwatson#include "opt_ufs.h" 4490453Srwatson 4559241Srwatson#include <sys/param.h> 4659241Srwatson#include <sys/systm.h> 4759241Srwatson#include <sys/kernel.h> 4859241Srwatson#include <sys/namei.h> 4959241Srwatson#include <sys/malloc.h> 5059241Srwatson#include <sys/fcntl.h> 51164033Srwatson#include <sys/priv.h> 5259241Srwatson#include <sys/proc.h> 5359241Srwatson#include <sys/vnode.h> 5459241Srwatson#include <sys/mount.h> 5559241Srwatson#include <sys/lock.h> 5674234Srwatson#include <sys/dirent.h> 5774273Srwatson#include <sys/extattr.h> 58176797Srwatson#include <sys/sx.h> 5975106Srwatson#include <sys/sysctl.h> 6059241Srwatson 6192768Sjeff#include <vm/uma.h> 6274234Srwatson 6374234Srwatson#include <ufs/ufs/dir.h> 6459241Srwatson#include <ufs/ufs/extattr.h> 6559241Srwatson#include <ufs/ufs/quota.h> 6659241Srwatson#include <ufs/ufs/ufsmount.h> 6759241Srwatson#include <ufs/ufs/inode.h> 6874234Srwatson#include <ufs/ufs/ufs_extern.h> 6959241Srwatson 7074433Srwatson#ifdef UFS_EXTATTR 7174273Srwatson 7259241Srwatsonstatic MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); 7359241Srwatson 7475106Srwatsonstatic int ufs_extattr_sync = 0; 7575106SrwatsonSYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync, 7675106Srwatson 0, ""); 7775106Srwatson 7885577Srwatsonstatic int ufs_extattr_valid_attrname(int attrnamespace, 7990453Srwatson const char *attrname); 8074273Srwatsonstatic int ufs_extattr_enable_with_open(struct ufsmount *ump, 8190453Srwatson struct vnode *vp, int attrnamespace, const char *attrname, 8290453Srwatson struct thread *td); 8374437Srwatsonstatic int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 8490453Srwatson const char *attrname, struct vnode *backing_vnode, 8590453Srwatson struct thread *td); 8674437Srwatsonstatic int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 8790453Srwatson const char *attrname, struct thread *td); 8874437Srwatsonstatic int ufs_extattr_get(struct vnode *vp, int attrnamespace, 8990453Srwatson const char *name, struct uio *uio, size_t *size, 9090453Srwatson struct ucred *cred, struct thread *td); 9174437Srwatsonstatic int ufs_extattr_set(struct vnode *vp, int attrnamespace, 9290453Srwatson const char *name, struct uio *uio, struct ucred *cred, 9390453Srwatson struct thread *td); 9474437Srwatsonstatic int ufs_extattr_rm(struct vnode *vp, int attrnamespace, 9590453Srwatson const char *name, struct ucred *cred, struct thread *td); 96191990Sattilio#ifdef UFS_EXTATTR_AUTOSTART 97186898Skibstatic int ufs_extattr_autostart_locked(struct mount *mp, 98186898Skib struct thread *td); 99191990Sattilio#endif 100186898Skibstatic int ufs_extattr_start_locked(struct ufsmount *ump, 101186898Skib struct thread *td); 10259241Srwatson 10359241Srwatson/* 10470776Srwatson * Per-FS attribute lock protecting attribute operations. 105176797Srwatson * 106176797Srwatson * XXXRW: Perhaps something more fine-grained would be appropriate, but at 107176797Srwatson * the end of the day we're going to contend on the vnode lock for the 108176797Srwatson * backing file anyway. 10959241Srwatson */ 11059241Srwatsonstatic void 111234613Straszufs_extattr_uepm_lock(struct ufsmount *ump) 11259241Srwatson{ 11359241Srwatson 114176797Srwatson sx_xlock(&ump->um_extattr.uepm_lock); 11559241Srwatson} 11659241Srwatson 11759241Srwatsonstatic void 118234613Straszufs_extattr_uepm_unlock(struct ufsmount *ump) 11959241Srwatson{ 12059241Srwatson 121176797Srwatson sx_xunlock(&ump->um_extattr.uepm_lock); 12259241Srwatson} 12359241Srwatson 124131067Srwatson/*- 12565377Srwatson * Determine whether the name passed is a valid name for an actual 12665377Srwatson * attribute. 12765377Srwatson * 12865377Srwatson * Invalid currently consists of: 12970776Srwatson * NULL pointer for attrname 13070776Srwatson * zero-length attrname (used to retrieve application attribute list) 13165377Srwatson */ 13265377Srwatsonstatic int 13385577Srwatsonufs_extattr_valid_attrname(int attrnamespace, const char *attrname) 13465377Srwatson{ 13565377Srwatson 13665377Srwatson if (attrname == NULL) 13765377Srwatson return (0); 13865377Srwatson if (strlen(attrname) == 0) 13965377Srwatson return (0); 14065377Srwatson return (1); 14165377Srwatson} 14265377Srwatson 14365377Srwatson/* 14459241Srwatson * Locate an attribute given a name and mountpoint. 14559241Srwatson * Must be holding uepm lock for the mount point. 14659241Srwatson */ 14759241Srwatsonstatic struct ufs_extattr_list_entry * 14874437Srwatsonufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, 14974273Srwatson const char *attrname) 15059241Srwatson{ 151131066Srwatson struct ufs_extattr_list_entry *search_attribute; 15259241Srwatson 153176797Srwatson sx_assert(&ump->um_extattr.uepm_lock, SA_XLOCKED); 154176797Srwatson 15571999Sphk for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); 156131066Srwatson search_attribute != NULL; 15771999Sphk search_attribute = LIST_NEXT(search_attribute, uele_entries)) { 15870776Srwatson if (!(strncmp(attrname, search_attribute->uele_attrname, 15974273Srwatson UFS_EXTATTR_MAXEXTATTRNAME)) && 16074437Srwatson (attrnamespace == search_attribute->uele_attrnamespace)) { 16170776Srwatson return (search_attribute); 16270776Srwatson } 16370776Srwatson } 16459241Srwatson 16570776Srwatson return (0); 16659241Srwatson} 16759241Srwatson 16859241Srwatson/* 16959241Srwatson * Initialize per-FS structures supporting extended attributes. Do not 17059241Srwatson * start extended attributes yet. 17159241Srwatson */ 17259241Srwatsonvoid 17359241Srwatsonufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) 17459241Srwatson{ 17559241Srwatson 17659241Srwatson uepm->uepm_flags = 0; 17759241Srwatson LIST_INIT(&uepm->uepm_list); 178176797Srwatson sx_init(&uepm->uepm_lock, "ufs_extattr_sx"); 17959241Srwatson uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; 18059241Srwatson} 18159241Srwatson 18259241Srwatson/* 18366616Srwatson * Destroy per-FS structures supporting extended attributes. Assumes 18466616Srwatson * that EAs have already been stopped, and will panic if not. 18566616Srwatson */ 18666616Srwatsonvoid 18766616Srwatsonufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) 18866616Srwatson{ 18966616Srwatson 19066616Srwatson if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 19166616Srwatson panic("ufs_extattr_uepm_destroy: not initialized"); 19266616Srwatson 19366893Srwatson if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 19466616Srwatson panic("ufs_extattr_uepm_destroy: called while still started"); 19566616Srwatson 19666616Srwatson /* 19785579Srwatson * It's not clear that either order for the next two lines is 19866616Srwatson * ideal, and it should never be a problem if this is only called 19966616Srwatson * during unmount, and with vfs_busy(). 20066616Srwatson */ 20166616Srwatson uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; 202176797Srwatson sx_destroy(&uepm->uepm_lock); 20366616Srwatson} 20466616Srwatson 20566616Srwatson/* 20670776Srwatson * Start extended attribute support on an FS. 20759241Srwatson */ 20859241Srwatsonint 20983366Sjulianufs_extattr_start(struct mount *mp, struct thread *td) 21059241Srwatson{ 211131066Srwatson struct ufsmount *ump; 212131066Srwatson int error = 0; 21359241Srwatson 21459241Srwatson ump = VFSTOUFS(mp); 21559241Srwatson 216234613Strasz ufs_extattr_uepm_lock(ump); 217186898Skib error = ufs_extattr_start_locked(ump, td); 218234613Strasz ufs_extattr_uepm_unlock(ump); 219186898Skib return (error); 220186898Skib} 22159241Srwatson 222186898Skibstatic int 223186898Skibufs_extattr_start_locked(struct ufsmount *ump, struct thread *td) 224186898Skib{ 225186898Skib if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 226186898Skib return (EOPNOTSUPP); 227186898Skib if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) 228186898Skib return (EBUSY); 22959241Srwatson 23059241Srwatson ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; 23191406Sjhb ump->um_extattr.uepm_ucred = crhold(td->td_ucred); 232186898Skib return (0); 23359241Srwatson} 23459241Srwatson 23574433Srwatson#ifdef UFS_EXTATTR_AUTOSTART 23659241Srwatson/* 23774234Srwatson * Helper routine: given a locked parent directory and filename, return 23874234Srwatson * the locked vnode of the inode associated with the name. Will not 23974234Srwatson * follow symlinks, may return any type of vnode. Lock on parent will 24074234Srwatson * be released even in the event of a failure. In the event that the 24174234Srwatson * target is the parent (i.e., "."), there will be two references and 24274234Srwatson * one lock, requiring the caller to possibly special-case. 24374234Srwatson */ 24474234Srwatson#define UE_GETDIR_LOCKPARENT 1 24574234Srwatson#define UE_GETDIR_LOCKPARENT_DONT 2 24674234Srwatsonstatic int 24774234Srwatsonufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, 24883366Sjulian struct vnode **vp, struct thread *td) 24974234Srwatson{ 25074234Srwatson struct vop_cachedlookup_args vargs; 25174234Srwatson struct componentname cnp; 25274234Srwatson struct vnode *target_vp; 25374234Srwatson int error; 25474234Srwatson 25574234Srwatson bzero(&cnp, sizeof(cnp)); 25674234Srwatson cnp.cn_nameiop = LOOKUP; 25774234Srwatson cnp.cn_flags = ISLASTCN; 25874234Srwatson if (lockparent == UE_GETDIR_LOCKPARENT) 25974234Srwatson cnp.cn_flags |= LOCKPARENT; 260151258Srwatson cnp.cn_lkflags = LK_EXCLUSIVE; 26183366Sjulian cnp.cn_thread = td; 26291406Sjhb cnp.cn_cred = td->td_ucred; 263111119Simp cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 26474234Srwatson cnp.cn_nameptr = cnp.cn_pnbuf; 26574234Srwatson error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, 26674234Srwatson (size_t *) &cnp.cn_namelen); 26774234Srwatson if (error) { 26874234Srwatson if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { 269175294Sattilio VOP_UNLOCK(start_dvp, 0); 27074234Srwatson } 27192768Sjeff uma_zfree(namei_zone, cnp.cn_pnbuf); 27274234Srwatson printf("ufs_extattr_lookup: copystr failed\n"); 27374234Srwatson return (error); 27474234Srwatson } 27574234Srwatson cnp.cn_namelen--; /* trim nul termination */ 276138814Simp vargs.a_gen.a_desc = NULL; 27774234Srwatson vargs.a_dvp = start_dvp; 27874234Srwatson vargs.a_vpp = &target_vp; 27974234Srwatson vargs.a_cnp = &cnp; 28074234Srwatson error = ufs_lookup(&vargs); 28192768Sjeff uma_zfree(namei_zone, cnp.cn_pnbuf); 28274234Srwatson if (error) { 28374234Srwatson /* 28474234Srwatson * Error condition, may have to release the lock on the parent 28574234Srwatson * if ufs_lookup() didn't. 28674234Srwatson */ 287144209Sjeff if (lockparent == UE_GETDIR_LOCKPARENT_DONT) 288175294Sattilio VOP_UNLOCK(start_dvp, 0); 28974234Srwatson 29074234Srwatson /* 29174234Srwatson * Check that ufs_lookup() didn't release the lock when we 29274234Srwatson * didn't want it to. 29374234Srwatson */ 294144209Sjeff if (lockparent == UE_GETDIR_LOCKPARENT) 295144209Sjeff ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 29674234Srwatson 29774234Srwatson return (error); 29874234Srwatson } 29974234Srwatson/* 30074234Srwatson if (target_vp == start_dvp) 30174234Srwatson panic("ufs_extattr_lookup: target_vp == start_dvp"); 30274234Srwatson*/ 30374234Srwatson 304144209Sjeff if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT) 305175294Sattilio VOP_UNLOCK(start_dvp, 0); 30674234Srwatson 307144209Sjeff if (lockparent == UE_GETDIR_LOCKPARENT) 308144209Sjeff ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 30974234Srwatson 31074234Srwatson /* printf("ufs_extattr_lookup: success\n"); */ 31174234Srwatson *vp = target_vp; 31274234Srwatson return (0); 31374234Srwatson} 31474433Srwatson#endif /* !UFS_EXTATTR_AUTOSTART */ 31574234Srwatson 31674234Srwatson/* 31796755Strhodes * Enable an EA using the passed filesystem, backing vnode, attribute name, 31874273Srwatson * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp 31985580Srwatson * to be locked when passed in. The vnode will be returned unlocked, 32085580Srwatson * regardless of success/failure of the function. As a result, the caller 32185580Srwatson * will always need to vrele(), but not vput(). 32274234Srwatson */ 32374234Srwatsonstatic int 32474234Srwatsonufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, 32583366Sjulian int attrnamespace, const char *attrname, struct thread *td) 32674234Srwatson{ 32774234Srwatson int error; 32874234Srwatson 329170183Skib error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, NULL); 33074234Srwatson if (error) { 33174234Srwatson printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " 33274234Srwatson "with %d\n", error); 333175294Sattilio VOP_UNLOCK(vp, 0); 33474234Srwatson return (error); 33574234Srwatson } 33674234Srwatson 337242476Skib VOP_ADD_WRITECOUNT(vp, 1); 338232701Sjhb CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d", __func__, vp, 339232701Sjhb vp->v_writecount); 34074234Srwatson 34174234Srwatson vref(vp); 34274234Srwatson 343175294Sattilio VOP_UNLOCK(vp, 0); 34474234Srwatson 34583366Sjulian error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td); 34677847Stmm if (error != 0) 34791406Sjhb vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 34877847Stmm return (error); 34974234Srwatson} 35074234Srwatson 35174433Srwatson#ifdef UFS_EXTATTR_AUTOSTART 35274234Srwatson/* 35374234Srwatson * Given a locked directory vnode, iterate over the names in the directory 35474234Srwatson * and use ufs_extattr_lookup() to retrieve locked vnodes of potential 35574234Srwatson * attribute files. Then invoke ufs_extattr_enable_with_open() on each 35674234Srwatson * to attempt to start the attribute. Leaves the directory locked on 35774234Srwatson * exit. 35874234Srwatson */ 35974234Srwatsonstatic int 36074234Srwatsonufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, 36183366Sjulian int attrnamespace, struct thread *td) 36274234Srwatson{ 36374234Srwatson struct vop_readdir_args vargs; 36474234Srwatson struct dirent *dp, *edp; 36574234Srwatson struct vnode *attr_vp; 36674234Srwatson struct uio auio; 36774234Srwatson struct iovec aiov; 36874234Srwatson char *dirbuf; 36974234Srwatson int error, eofflag = 0; 37074234Srwatson 37174234Srwatson if (dvp->v_type != VDIR) 37274234Srwatson return (ENOTDIR); 37374234Srwatson 374184205Sdes dirbuf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK); 37574234Srwatson 37674234Srwatson auio.uio_iov = &aiov; 37774234Srwatson auio.uio_iovcnt = 1; 37874234Srwatson auio.uio_rw = UIO_READ; 37974234Srwatson auio.uio_segflg = UIO_SYSSPACE; 38083366Sjulian auio.uio_td = td; 38174234Srwatson auio.uio_offset = 0; 38274234Srwatson 383138814Simp vargs.a_gen.a_desc = NULL; 38474234Srwatson vargs.a_vp = dvp; 38574234Srwatson vargs.a_uio = &auio; 38691406Sjhb vargs.a_cred = td->td_ucred; 38774234Srwatson vargs.a_eofflag = &eofflag; 38874234Srwatson vargs.a_ncookies = NULL; 38974234Srwatson vargs.a_cookies = NULL; 39074234Srwatson 39174234Srwatson while (!eofflag) { 39274234Srwatson auio.uio_resid = DIRBLKSIZ; 39374234Srwatson aiov.iov_base = dirbuf; 39474234Srwatson aiov.iov_len = DIRBLKSIZ; 39574234Srwatson error = ufs_readdir(&vargs); 39674234Srwatson if (error) { 39774234Srwatson printf("ufs_extattr_iterate_directory: ufs_readdir " 39874234Srwatson "%d\n", error); 39974234Srwatson return (error); 40074234Srwatson } 40174234Srwatson 402252438Sgleb edp = (struct dirent *)&dirbuf[DIRBLKSIZ - auio.uio_resid]; 40374234Srwatson for (dp = (struct dirent *)dirbuf; dp < edp; ) { 40474234Srwatson if (dp->d_reclen == 0) 40574234Srwatson break; 40674234Srwatson error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, 40783366Sjulian dp->d_name, &attr_vp, td); 40874234Srwatson if (error) { 40974234Srwatson printf("ufs_extattr_iterate_directory: lookup " 41074234Srwatson "%s %d\n", dp->d_name, error); 41174234Srwatson } else if (attr_vp == dvp) { 41274234Srwatson vrele(attr_vp); 41374234Srwatson } else if (attr_vp->v_type != VREG) { 41474234Srwatson vput(attr_vp); 41574234Srwatson } else { 41674234Srwatson error = ufs_extattr_enable_with_open(ump, 41783366Sjulian attr_vp, attrnamespace, dp->d_name, td); 41874273Srwatson vrele(attr_vp); 41974234Srwatson if (error) { 42074234Srwatson printf("ufs_extattr_iterate_directory: " 42174234Srwatson "enable %s %d\n", dp->d_name, 42274234Srwatson error); 42385580Srwatson } else if (bootverbose) { 42485580Srwatson printf("UFS autostarted EA %s\n", 42585580Srwatson dp->d_name); 42674234Srwatson } 42774234Srwatson } 42874234Srwatson dp = (struct dirent *) ((char *)dp + dp->d_reclen); 42974234Srwatson if (dp >= edp) 43074234Srwatson break; 43174234Srwatson } 43274234Srwatson } 433184205Sdes free(dirbuf, M_TEMP); 43474234Srwatson 43574234Srwatson return (0); 43674234Srwatson} 43774234Srwatson 43874234Srwatson/* 43974234Srwatson * Auto-start of extended attributes, to be executed (optionally) at 44074234Srwatson * mount-time. 44174234Srwatson */ 44274234Srwatsonint 44383366Sjulianufs_extattr_autostart(struct mount *mp, struct thread *td) 44474234Srwatson{ 445186898Skib struct ufsmount *ump; 446186898Skib int error; 447186898Skib 448186898Skib ump = VFSTOUFS(mp); 449234613Strasz ufs_extattr_uepm_lock(ump); 450186898Skib error = ufs_extattr_autostart_locked(mp, td); 451234613Strasz ufs_extattr_uepm_unlock(ump); 452186898Skib return (error); 453186898Skib} 454186898Skib 455186898Skibstatic int 456186898Skibufs_extattr_autostart_locked(struct mount *mp, struct thread *td) 457186898Skib{ 45874404Srwatson struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; 459176752Srwatson struct ufsmount *ump = VFSTOUFS(mp); 46074234Srwatson int error; 46174234Srwatson 46274234Srwatson /* 463176752Srwatson * UFS_EXTATTR applies only to UFS1, as UFS2 uses native extended 464176752Srwatson * attributes, so don't autostart. 465176752Srwatson */ 466176752Srwatson if (ump->um_fstype != UFS1) 467176752Srwatson return (0); 468176752Srwatson 469176752Srwatson /* 47096755Strhodes * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root? 47174273Srwatson * If so, automatically start EA's. 47274234Srwatson */ 473191990Sattilio error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp); 47474234Srwatson if (error) { 47585578Srwatson printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 47685578Srwatson error); 47774234Srwatson return (error); 47874234Srwatson } 47974234Srwatson 48074234Srwatson error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 48183366Sjulian UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 48274234Srwatson if (error) { 48374234Srwatson /* rvp ref'd but now unlocked */ 48474234Srwatson vrele(rvp); 48574234Srwatson return (error); 48674234Srwatson } 48774234Srwatson if (rvp == attr_dvp) { 48874234Srwatson /* Should never happen. */ 489155160Sjeff vput(rvp); 49074234Srwatson vrele(attr_dvp); 49174234Srwatson return (EINVAL); 49274234Srwatson } 49374234Srwatson vrele(rvp); 49474234Srwatson 49574234Srwatson if (attr_dvp->v_type != VDIR) { 49674273Srwatson printf("ufs_extattr_autostart: %s != VDIR\n", 49774273Srwatson UFS_EXTATTR_FSROOTSUBDIR); 49874404Srwatson goto return_vput_attr_dvp; 49974234Srwatson } 50074234Srwatson 501186898Skib error = ufs_extattr_start_locked(ump, td); 50274234Srwatson if (error) { 50374234Srwatson printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 50474234Srwatson error); 50574404Srwatson goto return_vput_attr_dvp; 50674234Srwatson } 50774234Srwatson 50874234Srwatson /* 50974404Srwatson * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 51074404Srwatson * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 51174404Srwatson * and start with appropriate type. Failures in either don't 51274404Srwatson * result in an over-all failure. attr_dvp is left locked to 51374404Srwatson * be cleaned up on exit. 51474234Srwatson */ 51574404Srwatson error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 51683366Sjulian UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 51774404Srwatson if (!error) { 51874404Srwatson error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 51983366Sjulian attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 52074404Srwatson if (error) 52174404Srwatson printf("ufs_extattr_iterate_directory returned %d\n", 52274404Srwatson error); 52374404Srwatson vput(attr_system_dvp); 52474404Srwatson } 52574234Srwatson 52674404Srwatson error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 52783366Sjulian UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 52874404Srwatson if (!error) { 52974404Srwatson error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 53083366Sjulian attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 53174404Srwatson if (error) 53274404Srwatson printf("ufs_extattr_iterate_directory returned %d\n", 53374404Srwatson error); 53474404Srwatson vput(attr_user_dvp); 53574404Srwatson } 53674404Srwatson 53774404Srwatson /* Mask startup failures in sub-directories. */ 53874234Srwatson error = 0; 53974234Srwatson 54074404Srwatsonreturn_vput_attr_dvp: 54174234Srwatson vput(attr_dvp); 54274234Srwatson 54374234Srwatson return (error); 54474234Srwatson} 54574433Srwatson#endif /* !UFS_EXTATTR_AUTOSTART */ 54674234Srwatson 54774234Srwatson/* 54870776Srwatson * Stop extended attribute support on an FS. 54959241Srwatson */ 55059241Srwatsonint 55183366Sjulianufs_extattr_stop(struct mount *mp, struct thread *td) 55259241Srwatson{ 553131066Srwatson struct ufs_extattr_list_entry *uele; 554131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 555131066Srwatson int error = 0; 55659241Srwatson 557234613Strasz ufs_extattr_uepm_lock(ump); 55859241Srwatson 55959241Srwatson if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 56059241Srwatson error = EOPNOTSUPP; 56159241Srwatson goto unlock; 56259241Srwatson } 56359241Srwatson 564152163Sdelphij while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) { 56574437Srwatson ufs_extattr_disable(ump, uele->uele_attrnamespace, 56683366Sjulian uele->uele_attrname, td); 56770776Srwatson } 56859241Srwatson 56959241Srwatson ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 57059241Srwatson 57159241Srwatson crfree(ump->um_extattr.uepm_ucred); 57259241Srwatson ump->um_extattr.uepm_ucred = NULL; 57359241Srwatson 57459241Srwatsonunlock: 575234613Strasz ufs_extattr_uepm_unlock(ump); 57659241Srwatson 57759241Srwatson return (error); 57859241Srwatson} 57959241Srwatson 58059241Srwatson/* 58196755Strhodes * Enable a named attribute on the specified filesystem; provide an 58274273Srwatson * unlocked backing vnode to hold the attribute data. 58359241Srwatson */ 58459241Srwatsonstatic int 58574437Srwatsonufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 58683366Sjulian const char *attrname, struct vnode *backing_vnode, struct thread *td) 58759241Srwatson{ 588131066Srwatson struct ufs_extattr_list_entry *attribute; 589131066Srwatson struct iovec aiov; 590131066Srwatson struct uio auio; 591131066Srwatson int error = 0; 59259241Srwatson 59385577Srwatson if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 59465377Srwatson return (EINVAL); 59559241Srwatson if (backing_vnode->v_type != VREG) 59659241Srwatson return (EINVAL); 59759241Srwatson 598184214Sdes attribute = malloc(sizeof(struct ufs_extattr_list_entry), 599184214Sdes M_UFS_EXTATTR, M_WAITOK); 60059241Srwatson if (attribute == NULL) 60159241Srwatson return (ENOMEM); 60259241Srwatson 60359241Srwatson if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 60459241Srwatson error = EOPNOTSUPP; 60559241Srwatson goto free_exit; 60659241Srwatson } 60759241Srwatson 60874437Srwatson if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { 60965768Srwatson error = EEXIST; 61059241Srwatson goto free_exit; 61159241Srwatson } 61259241Srwatson 61385578Srwatson strncpy(attribute->uele_attrname, attrname, 61485578Srwatson UFS_EXTATTR_MAXEXTATTRNAME); 61574437Srwatson attribute->uele_attrnamespace = attrnamespace; 61659241Srwatson bzero(&attribute->uele_fileheader, 61759241Srwatson sizeof(struct ufs_extattr_fileheader)); 61859241Srwatson 61959241Srwatson attribute->uele_backing_vnode = backing_vnode; 62059241Srwatson 62159241Srwatson auio.uio_iov = &aiov; 62259241Srwatson auio.uio_iovcnt = 1; 62359241Srwatson aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 62459241Srwatson aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 62559241Srwatson auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 62659241Srwatson auio.uio_offset = (off_t) 0; 62759241Srwatson auio.uio_segflg = UIO_SYSSPACE; 62859241Srwatson auio.uio_rw = UIO_READ; 62983366Sjulian auio.uio_td = td; 63059241Srwatson 631175202Sattilio vn_lock(backing_vnode, LK_SHARED | LK_RETRY); 63265768Srwatson error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, 63365768Srwatson ump->um_extattr.uepm_ucred); 63459241Srwatson 63574273Srwatson if (error) 636115040Srwatson goto unlock_free_exit; 63759241Srwatson 63859241Srwatson if (auio.uio_resid != 0) { 63959241Srwatson printf("ufs_extattr_enable: malformed attribute header\n"); 64059241Srwatson error = EINVAL; 641115040Srwatson goto unlock_free_exit; 64259241Srwatson } 64359241Srwatson 64459400Srwatson if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 64559400Srwatson printf("ufs_extattr_enable: invalid attribute header magic\n"); 64659400Srwatson error = EINVAL; 647115040Srwatson goto unlock_free_exit; 64859400Srwatson } 64959400Srwatson 65059400Srwatson if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 65159400Srwatson printf("ufs_extattr_enable: incorrect attribute header " 65259400Srwatson "version\n"); 65359400Srwatson error = EINVAL; 654115040Srwatson goto unlock_free_exit; 65559400Srwatson } 65659400Srwatson 657101308Sjeff ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable"); 65885578Srwatson LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, 65985578Srwatson uele_entries); 66059241Srwatson 661175294Sattilio VOP_UNLOCK(backing_vnode, 0); 66259241Srwatson return (0); 66359241Srwatson 664115040Srwatsonunlock_free_exit: 665175294Sattilio VOP_UNLOCK(backing_vnode, 0); 666115040Srwatson 66759241Srwatsonfree_exit: 668184205Sdes free(attribute, M_UFS_EXTATTR); 66959241Srwatson return (error); 67059241Srwatson} 67159241Srwatson 67259241Srwatson/* 67370776Srwatson * Disable extended attribute support on an FS. 67459241Srwatson */ 67559241Srwatsonstatic int 67674437Srwatsonufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 67783366Sjulian const char *attrname, struct thread *td) 67859241Srwatson{ 679131066Srwatson struct ufs_extattr_list_entry *uele; 680131066Srwatson int error = 0; 68159241Srwatson 68285577Srwatson if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 68365377Srwatson return (EINVAL); 68465377Srwatson 68574437Srwatson uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); 68659241Srwatson if (!uele) 68791814Sgreen return (ENOATTR); 68859241Srwatson 68959241Srwatson LIST_REMOVE(uele, uele_entries); 69059241Srwatson 691175202Sattilio vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY); 692101308Sjeff ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable"); 693175294Sattilio VOP_UNLOCK(uele->uele_backing_vnode, 0); 69485578Srwatson error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, 69591406Sjhb td->td_ucred, td); 69659241Srwatson 697184205Sdes free(uele, M_UFS_EXTATTR); 69859241Srwatson 69959241Srwatson return (error); 70059241Srwatson} 70159241Srwatson 70259241Srwatson/* 70374273Srwatson * VFS call to manage extended attributes in UFS. If filename_vp is 70474273Srwatson * non-NULL, it must be passed in locked, and regardless of errors in 70574273Srwatson * processing, will be unlocked. 70659241Srwatson */ 70759241Srwatsonint 70874273Srwatsonufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 709191990Sattilio int attrnamespace, const char *attrname) 71059241Srwatson{ 711131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 712191990Sattilio struct thread *td = curthread; 713131066Srwatson int error; 71459241Srwatson 71566041Srwatson /* 71666041Srwatson * Processes with privilege, but in jail, are not allowed to 71766041Srwatson * configure extended attributes. 71866041Srwatson */ 719164033Srwatson error = priv_check(td, PRIV_UFS_EXTATTRCTL); 720164033Srwatson if (error) { 72174273Srwatson if (filename_vp != NULL) 722175294Sattilio VOP_UNLOCK(filename_vp, 0); 72359241Srwatson return (error); 72474273Srwatson } 72559241Srwatson 726176752Srwatson /* 727176752Srwatson * We only allow extattrctl(2) on UFS1 file systems, as UFS2 uses 728176752Srwatson * native extended attributes. 729176752Srwatson */ 730176752Srwatson if (ump->um_fstype != UFS1) { 731176752Srwatson if (filename_vp != NULL) 732176752Srwatson VOP_UNLOCK(filename_vp, 0); 733176752Srwatson return (EOPNOTSUPP); 734176752Srwatson } 735176752Srwatson 73659241Srwatson switch(cmd) { 73759241Srwatson case UFS_EXTATTR_CMD_START: 73874273Srwatson if (filename_vp != NULL) { 739175294Sattilio VOP_UNLOCK(filename_vp, 0); 74074273Srwatson return (EINVAL); 74174273Srwatson } 74274273Srwatson if (attrname != NULL) 74374273Srwatson return (EINVAL); 74474273Srwatson 74583366Sjulian error = ufs_extattr_start(mp, td); 74659241Srwatson 74759241Srwatson return (error); 74859241Srwatson 74959241Srwatson case UFS_EXTATTR_CMD_STOP: 75074273Srwatson if (filename_vp != NULL) { 751175294Sattilio VOP_UNLOCK(filename_vp, 0); 75274273Srwatson return (EINVAL); 75374273Srwatson } 75474273Srwatson if (attrname != NULL) 75574273Srwatson return (EINVAL); 75659241Srwatson 75783366Sjulian error = ufs_extattr_stop(mp, td); 75874273Srwatson 75974273Srwatson return (error); 76074273Srwatson 76159241Srwatson case UFS_EXTATTR_CMD_ENABLE: 76259241Srwatson 76374273Srwatson if (filename_vp == NULL) 76474273Srwatson return (EINVAL); 76574273Srwatson if (attrname == NULL) { 766175294Sattilio VOP_UNLOCK(filename_vp, 0); 76774273Srwatson return (EINVAL); 76874273Srwatson } 76959241Srwatson 77074273Srwatson /* 77174273Srwatson * ufs_extattr_enable_with_open() will always unlock the 77274273Srwatson * vnode, regardless of failure. 77374273Srwatson */ 774234613Strasz ufs_extattr_uepm_lock(ump); 77574273Srwatson error = ufs_extattr_enable_with_open(ump, filename_vp, 77683366Sjulian attrnamespace, attrname, td); 777234613Strasz ufs_extattr_uepm_unlock(ump); 77859241Srwatson 77959241Srwatson return (error); 78059241Srwatson 78159241Srwatson case UFS_EXTATTR_CMD_DISABLE: 78259241Srwatson 78374273Srwatson if (filename_vp != NULL) { 784175294Sattilio VOP_UNLOCK(filename_vp, 0); 78574273Srwatson return (EINVAL); 78674273Srwatson } 78774273Srwatson if (attrname == NULL) 78874273Srwatson return (EINVAL); 78974273Srwatson 790234613Strasz ufs_extattr_uepm_lock(ump); 79185578Srwatson error = ufs_extattr_disable(ump, attrnamespace, attrname, 79285578Srwatson td); 793234613Strasz ufs_extattr_uepm_unlock(ump); 79459241Srwatson 79559241Srwatson return (error); 79659241Srwatson 79759241Srwatson default: 79859241Srwatson return (EINVAL); 79959241Srwatson } 80059241Srwatson} 80159241Srwatson 80259241Srwatson/* 80370776Srwatson * Vnode operating to retrieve a named extended attribute. 80459241Srwatson */ 80559241Srwatsonint 80695974Sphkufs_getextattr(struct vop_getextattr_args *ap) 80759241Srwatson/* 80859241Srwatsonvop_getextattr { 80970776Srwatson IN struct vnode *a_vp; 81074437Srwatson IN int a_attrnamespace; 81170776Srwatson IN const char *a_name; 81270776Srwatson INOUT struct uio *a_uio; 813104346Sdd OUT size_t *a_size; 81470776Srwatson IN struct ucred *a_cred; 81583366Sjulian IN struct thread *a_td; 81659241Srwatson}; 81759241Srwatson*/ 81859241Srwatson{ 819131066Srwatson struct mount *mp = ap->a_vp->v_mount; 820131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 821131066Srwatson int error; 82259241Srwatson 823234613Strasz ufs_extattr_uepm_lock(ump); 82459241Srwatson 82574437Srwatson error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, 82690448Srwatson ap->a_uio, ap->a_size, ap->a_cred, ap->a_td); 82759241Srwatson 828234613Strasz ufs_extattr_uepm_unlock(ump); 82959241Srwatson 83059241Srwatson return (error); 83159241Srwatson} 83259241Srwatson 83359241Srwatson/* 83459241Srwatson * Real work associated with retrieving a named attribute--assumes that 83559241Srwatson * the attribute lock has already been grabbed. 83659241Srwatson */ 83759241Srwatsonstatic int 83874437Srwatsonufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 83990448Srwatson struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) 84059241Srwatson{ 841131066Srwatson struct ufs_extattr_list_entry *attribute; 842131066Srwatson struct ufs_extattr_header ueh; 843131066Srwatson struct iovec local_aiov; 844131066Srwatson struct uio local_aio; 845131066Srwatson struct mount *mp = vp->v_mount; 846131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 847131066Srwatson struct inode *ip = VTOI(vp); 848131066Srwatson off_t base_offset; 849131066Srwatson size_t len, old_len; 850131066Srwatson int error = 0; 85159241Srwatson 85259241Srwatson if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 85359241Srwatson return (EOPNOTSUPP); 85459241Srwatson 855115865Srwatson if (strlen(name) == 0) 85665377Srwatson return (EINVAL); 85765377Srwatson 858182721Strasz error = extattr_check_cred(vp, attrnamespace, cred, td, VREAD); 859102985Sphk if (error) 860102985Sphk return (error); 861102985Sphk 86274437Srwatson attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 86359241Srwatson if (!attribute) 86491814Sgreen return (ENOATTR); 86559241Srwatson 86659241Srwatson /* 86759913Srwatson * Allow only offsets of zero to encourage the read/replace 86859913Srwatson * extended attribute semantic. Otherwise we can't guarantee 86970776Srwatson * atomicity, as we don't provide locks for extended attributes. 87059241Srwatson */ 87190448Srwatson if (uio != NULL && uio->uio_offset != 0) 87259241Srwatson return (ENXIO); 87359241Srwatson 87459241Srwatson /* 87559241Srwatson * Find base offset of header in file based on file header size, and 87670776Srwatson * data header size + maximum data size, indexed by inode number. 87759241Srwatson */ 87859241Srwatson base_offset = sizeof(struct ufs_extattr_fileheader) + 87959241Srwatson ip->i_number * (sizeof(struct ufs_extattr_header) + 88059241Srwatson attribute->uele_fileheader.uef_size); 88159241Srwatson 88259241Srwatson /* 88359241Srwatson * Read in the data header to see if the data is defined, and if so 88459241Srwatson * how much. 88559241Srwatson */ 88659241Srwatson bzero(&ueh, sizeof(struct ufs_extattr_header)); 88759241Srwatson local_aiov.iov_base = (caddr_t) &ueh; 88859241Srwatson local_aiov.iov_len = sizeof(struct ufs_extattr_header); 88959241Srwatson local_aio.uio_iov = &local_aiov; 89059241Srwatson local_aio.uio_iovcnt = 1; 89159241Srwatson local_aio.uio_rw = UIO_READ; 89259241Srwatson local_aio.uio_segflg = UIO_SYSSPACE; 89383366Sjulian local_aio.uio_td = td; 89459241Srwatson local_aio.uio_offset = base_offset; 89559241Srwatson local_aio.uio_resid = sizeof(struct ufs_extattr_header); 89659241Srwatson 89765768Srwatson /* 89865768Srwatson * Acquire locks. 899141523Srwatson * 90065768Srwatson * Don't need to get a lock on the backing file if the getattr is 90165768Srwatson * being applied to the backing file, as the lock is already held. 90265768Srwatson */ 90365768Srwatson if (attribute->uele_backing_vnode != vp) 904175202Sattilio vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY); 90559241Srwatson 90665768Srwatson error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 90765768Srwatson IO_NODELOCKED, ump->um_extattr.uepm_ucred); 90859241Srwatson if (error) 90959241Srwatson goto vopunlock_exit; 91059241Srwatson 91170776Srwatson /* Defined? */ 91259241Srwatson if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 91391814Sgreen error = ENOATTR; 91459241Srwatson goto vopunlock_exit; 91559241Srwatson } 91659241Srwatson 91770776Srwatson /* Valid for the current inode generation? */ 91859388Srwatson if (ueh.ueh_i_gen != ip->i_gen) { 91959388Srwatson /* 92059388Srwatson * The inode itself has a different generation number 92159388Srwatson * than the attribute data. For now, the best solution 92259388Srwatson * is to coerce this to undefined, and let it get cleaned 92359388Srwatson * up by the next write or extattrctl clean. 92459388Srwatson */ 925252437Spfg printf("ufs_extattr_get (%s): inode number inconsistency (%d, %ju)\n", 926252435Spfg mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (uintmax_t)ip->i_gen); 92791814Sgreen error = ENOATTR; 92859388Srwatson goto vopunlock_exit; 92959388Srwatson } 93059388Srwatson 93170776Srwatson /* Local size consistency check. */ 93259241Srwatson if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 93359241Srwatson error = ENXIO; 93459241Srwatson goto vopunlock_exit; 93559241Srwatson } 93659241Srwatson 93790448Srwatson /* Return full data size if caller requested it. */ 93890448Srwatson if (size != NULL) 93990448Srwatson *size = ueh.ueh_len; 94059241Srwatson 94190448Srwatson /* Return data if the caller requested it. */ 94290448Srwatson if (uio != NULL) { 94390448Srwatson /* Allow for offset into the attribute data. */ 94490448Srwatson uio->uio_offset = base_offset + sizeof(struct 94590448Srwatson ufs_extattr_header); 94659241Srwatson 94790448Srwatson /* 94890448Srwatson * Figure out maximum to transfer -- use buffer size and 94990448Srwatson * local data limit. 95090448Srwatson */ 95190448Srwatson len = MIN(uio->uio_resid, ueh.ueh_len); 95290448Srwatson old_len = uio->uio_resid; 95390448Srwatson uio->uio_resid = len; 95459241Srwatson 95590448Srwatson error = VOP_READ(attribute->uele_backing_vnode, uio, 95690448Srwatson IO_NODELOCKED, ump->um_extattr.uepm_ucred); 95790448Srwatson if (error) 95890448Srwatson goto vopunlock_exit; 95959241Srwatson 96090448Srwatson uio->uio_resid = old_len - (len - uio->uio_resid); 96190448Srwatson } 96290448Srwatson 96359241Srwatsonvopunlock_exit: 96459241Srwatson 96590448Srwatson if (uio != NULL) 96690448Srwatson uio->uio_offset = 0; 96765768Srwatson 96865768Srwatson if (attribute->uele_backing_vnode != vp) 969175294Sattilio VOP_UNLOCK(attribute->uele_backing_vnode, 0); 97065768Srwatson 97159241Srwatson return (error); 97259241Srwatson} 97359241Srwatson 97459241Srwatson/* 975118131Srwatson * Vnode operation to remove a named attribute. 976118131Srwatson */ 977118131Srwatsonint 978118131Srwatsonufs_deleteextattr(struct vop_deleteextattr_args *ap) 979118131Srwatson/* 980118131Srwatsonvop_deleteextattr { 981118131Srwatson IN struct vnode *a_vp; 982118131Srwatson IN int a_attrnamespace; 983118131Srwatson IN const char *a_name; 984118131Srwatson IN struct ucred *a_cred; 985118131Srwatson IN struct thread *a_td; 986118131Srwatson}; 987118131Srwatson*/ 988118131Srwatson{ 989131066Srwatson struct mount *mp = ap->a_vp->v_mount; 990131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 991131066Srwatson int error; 992118131Srwatson 993234613Strasz ufs_extattr_uepm_lock(ump); 994118131Srwatson 995118131Srwatson error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name, 996118131Srwatson ap->a_cred, ap->a_td); 997118131Srwatson 998118131Srwatson 999234613Strasz ufs_extattr_uepm_unlock(ump); 1000118131Srwatson 1001118131Srwatson return (error); 1002118131Srwatson} 1003118131Srwatson 1004118131Srwatson/* 100570776Srwatson * Vnode operation to set a named attribute. 100659241Srwatson */ 100759241Srwatsonint 100895974Sphkufs_setextattr(struct vop_setextattr_args *ap) 100959241Srwatson/* 101059241Srwatsonvop_setextattr { 101170776Srwatson IN struct vnode *a_vp; 101274437Srwatson IN int a_attrnamespace; 101370776Srwatson IN const char *a_name; 101470776Srwatson INOUT struct uio *a_uio; 101570776Srwatson IN struct ucred *a_cred; 101683366Sjulian IN struct thread *a_td; 101759241Srwatson}; 101859241Srwatson*/ 101959241Srwatson{ 1020131066Srwatson struct mount *mp = ap->a_vp->v_mount; 1021131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 1022131066Srwatson int error; 102359241Srwatson 1024118131Srwatson /* 1025118131Srwatson * XXX: No longer a supported way to delete extended attributes. 1026118131Srwatson */ 1027118131Srwatson if (ap->a_uio == NULL) 1028118131Srwatson return (EINVAL); 102959241Srwatson 1030234613Strasz ufs_extattr_uepm_lock(ump); 1031225104Sae 1032118131Srwatson error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name, 1033118131Srwatson ap->a_uio, ap->a_cred, ap->a_td); 1034118131Srwatson 1035234613Strasz ufs_extattr_uepm_unlock(ump); 103659241Srwatson 103759241Srwatson return (error); 103859241Srwatson} 103959241Srwatson 104059241Srwatson/* 104159241Srwatson * Real work associated with setting a vnode's extended attributes; 104259241Srwatson * assumes that the attribute lock has already been grabbed. 104359241Srwatson */ 104459241Srwatsonstatic int 104574437Srwatsonufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, 104683366Sjulian struct uio *uio, struct ucred *cred, struct thread *td) 104759241Srwatson{ 1048131066Srwatson struct ufs_extattr_list_entry *attribute; 1049131066Srwatson struct ufs_extattr_header ueh; 1050131066Srwatson struct iovec local_aiov; 1051131066Srwatson struct uio local_aio; 1052131066Srwatson struct mount *mp = vp->v_mount; 1053131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 1054131066Srwatson struct inode *ip = VTOI(vp); 1055131066Srwatson off_t base_offset; 1056131066Srwatson int error = 0, ioflag; 105759241Srwatson 105859241Srwatson if (vp->v_mount->mnt_flag & MNT_RDONLY) 105959241Srwatson return (EROFS); 106059241Srwatson if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 106159241Srwatson return (EOPNOTSUPP); 106285577Srwatson if (!ufs_extattr_valid_attrname(attrnamespace, name)) 106365377Srwatson return (EINVAL); 106459241Srwatson 1065182721Strasz error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE); 1066102985Sphk if (error) 1067102985Sphk return (error); 1068102985Sphk 106974437Srwatson attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 107059241Srwatson if (!attribute) 107191814Sgreen return (ENOATTR); 107259241Srwatson 107359241Srwatson /* 107470776Srwatson * Early rejection of invalid offsets/length. 107559241Srwatson * Reject: any offset but 0 (replace) 107670776Srwatson * Any size greater than attribute size limit 107759241Srwatson */ 107859241Srwatson if (uio->uio_offset != 0 || 107959241Srwatson uio->uio_resid > attribute->uele_fileheader.uef_size) 108059241Srwatson return (ENXIO); 108159241Srwatson 108259241Srwatson /* 108359241Srwatson * Find base offset of header in file based on file header size, and 108470776Srwatson * data header size + maximum data size, indexed by inode number. 108559241Srwatson */ 108659241Srwatson base_offset = sizeof(struct ufs_extattr_fileheader) + 108759241Srwatson ip->i_number * (sizeof(struct ufs_extattr_header) + 108859241Srwatson attribute->uele_fileheader.uef_size); 108959241Srwatson 109059241Srwatson /* 109170776Srwatson * Write out a data header for the data. 109259241Srwatson */ 109359241Srwatson ueh.ueh_len = uio->uio_resid; 109459241Srwatson ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 109559388Srwatson ueh.ueh_i_gen = ip->i_gen; 109659241Srwatson local_aiov.iov_base = (caddr_t) &ueh; 109759241Srwatson local_aiov.iov_len = sizeof(struct ufs_extattr_header); 109859241Srwatson local_aio.uio_iov = &local_aiov; 109959241Srwatson local_aio.uio_iovcnt = 1; 110059241Srwatson local_aio.uio_rw = UIO_WRITE; 110159241Srwatson local_aio.uio_segflg = UIO_SYSSPACE; 110283366Sjulian local_aio.uio_td = td; 110359241Srwatson local_aio.uio_offset = base_offset; 110459241Srwatson local_aio.uio_resid = sizeof(struct ufs_extattr_header); 110559241Srwatson 110659241Srwatson /* 110765768Srwatson * Acquire locks. 1108141523Srwatson * 110959241Srwatson * Don't need to get a lock on the backing file if the setattr is 111065768Srwatson * being applied to the backing file, as the lock is already held. 111159241Srwatson */ 111259241Srwatson if (attribute->uele_backing_vnode != vp) 1113175202Sattilio vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); 111459241Srwatson 111575106Srwatson ioflag = IO_NODELOCKED; 111675106Srwatson if (ufs_extattr_sync) 111775106Srwatson ioflag |= IO_SYNC; 111875106Srwatson error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 111975106Srwatson ump->um_extattr.uepm_ucred); 112059241Srwatson if (error) 112159241Srwatson goto vopunlock_exit; 112259241Srwatson 112359268Srwatson if (local_aio.uio_resid != 0) { 112459241Srwatson error = ENXIO; 112559241Srwatson goto vopunlock_exit; 112659268Srwatson } 112759241Srwatson 112859241Srwatson /* 112970776Srwatson * Write out user data. 113059241Srwatson */ 113159241Srwatson uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 113259241Srwatson 113375106Srwatson ioflag = IO_NODELOCKED; 113475106Srwatson if (ufs_extattr_sync) 113575106Srwatson ioflag |= IO_SYNC; 113675106Srwatson error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, 113775106Srwatson ump->um_extattr.uepm_ucred); 113875106Srwatson 113959241Srwatsonvopunlock_exit: 114059241Srwatson uio->uio_offset = 0; 114159241Srwatson 114259268Srwatson if (attribute->uele_backing_vnode != vp) 1143175294Sattilio VOP_UNLOCK(attribute->uele_backing_vnode, 0); 114459268Srwatson 114559241Srwatson return (error); 114659241Srwatson} 114759241Srwatson 114859241Srwatson/* 114959241Srwatson * Real work associated with removing an extended attribute from a vnode. 115059241Srwatson * Assumes the attribute lock has already been grabbed. 115159241Srwatson */ 115259241Srwatsonstatic int 115374437Srwatsonufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 115483366Sjulian struct ucred *cred, struct thread *td) 115559241Srwatson{ 1156131066Srwatson struct ufs_extattr_list_entry *attribute; 1157131066Srwatson struct ufs_extattr_header ueh; 1158131066Srwatson struct iovec local_aiov; 1159131066Srwatson struct uio local_aio; 1160131066Srwatson struct mount *mp = vp->v_mount; 1161131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 1162131066Srwatson struct inode *ip = VTOI(vp); 1163131066Srwatson off_t base_offset; 1164131066Srwatson int error = 0, ioflag; 116559241Srwatson 116659241Srwatson if (vp->v_mount->mnt_flag & MNT_RDONLY) 116759241Srwatson return (EROFS); 116859241Srwatson if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 116959241Srwatson return (EOPNOTSUPP); 117085577Srwatson if (!ufs_extattr_valid_attrname(attrnamespace, name)) 117165377Srwatson return (EINVAL); 117259241Srwatson 1173182721Strasz error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE); 1174102985Sphk if (error) 1175102985Sphk return (error); 1176102985Sphk 117774437Srwatson attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 117859241Srwatson if (!attribute) 117991814Sgreen return (ENOATTR); 118059241Srwatson 118159241Srwatson /* 118259241Srwatson * Find base offset of header in file based on file header size, and 118370776Srwatson * data header size + maximum data size, indexed by inode number. 118459241Srwatson */ 118559241Srwatson base_offset = sizeof(struct ufs_extattr_fileheader) + 118659241Srwatson ip->i_number * (sizeof(struct ufs_extattr_header) + 118759241Srwatson attribute->uele_fileheader.uef_size); 118859241Srwatson 118959241Srwatson /* 119065768Srwatson * Check to see if currently defined. 119159241Srwatson */ 119259241Srwatson bzero(&ueh, sizeof(struct ufs_extattr_header)); 119359241Srwatson 119459241Srwatson local_aiov.iov_base = (caddr_t) &ueh; 119559241Srwatson local_aiov.iov_len = sizeof(struct ufs_extattr_header); 119659241Srwatson local_aio.uio_iov = &local_aiov; 119759241Srwatson local_aio.uio_iovcnt = 1; 119859241Srwatson local_aio.uio_rw = UIO_READ; 119959241Srwatson local_aio.uio_segflg = UIO_SYSSPACE; 120083366Sjulian local_aio.uio_td = td; 120159241Srwatson local_aio.uio_offset = base_offset; 120259241Srwatson local_aio.uio_resid = sizeof(struct ufs_extattr_header); 120359241Srwatson 120459241Srwatson /* 120559241Srwatson * Don't need to get the lock on the backing vnode if the vnode we're 120659241Srwatson * modifying is it, as we already hold the lock. 120759241Srwatson */ 120859241Srwatson if (attribute->uele_backing_vnode != vp) 1209175202Sattilio vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); 121059241Srwatson 121165768Srwatson error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 121265768Srwatson IO_NODELOCKED, ump->um_extattr.uepm_ucred); 121359241Srwatson if (error) 121459241Srwatson goto vopunlock_exit; 121559241Srwatson 121670776Srwatson /* Defined? */ 121759241Srwatson if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 121891814Sgreen error = ENOATTR; 121959241Srwatson goto vopunlock_exit; 122059241Srwatson } 122159241Srwatson 122270773Srwatson /* Valid for the current inode generation? */ 122370773Srwatson if (ueh.ueh_i_gen != ip->i_gen) { 122470773Srwatson /* 122570773Srwatson * The inode itself has a different generation number than 122670776Srwatson * the attribute data. For now, the best solution is to 122770773Srwatson * coerce this to undefined, and let it get cleaned up by 122870773Srwatson * the next write or extattrctl clean. 122970773Srwatson */ 1230106673Sjhb printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n", 1231106673Sjhb mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 123291814Sgreen error = ENOATTR; 123370773Srwatson goto vopunlock_exit; 123470773Srwatson } 123570773Srwatson 123670776Srwatson /* Flag it as not in use. */ 123759241Srwatson ueh.ueh_flags = 0; 123870774Srwatson ueh.ueh_len = 0; 123959241Srwatson 124070774Srwatson local_aiov.iov_base = (caddr_t) &ueh; 124170774Srwatson local_aiov.iov_len = sizeof(struct ufs_extattr_header); 124270774Srwatson local_aio.uio_iov = &local_aiov; 124370774Srwatson local_aio.uio_iovcnt = 1; 124470774Srwatson local_aio.uio_rw = UIO_WRITE; 124570774Srwatson local_aio.uio_segflg = UIO_SYSSPACE; 124683366Sjulian local_aio.uio_td = td; 124765768Srwatson local_aio.uio_offset = base_offset; 124865768Srwatson local_aio.uio_resid = sizeof(struct ufs_extattr_header); 124965768Srwatson 125075106Srwatson ioflag = IO_NODELOCKED; 125175106Srwatson if (ufs_extattr_sync) 125275106Srwatson ioflag |= IO_SYNC; 125375106Srwatson error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 125475106Srwatson ump->um_extattr.uepm_ucred); 125559241Srwatson if (error) 125659241Srwatson goto vopunlock_exit; 125759241Srwatson 125859241Srwatson if (local_aio.uio_resid != 0) 125959241Srwatson error = ENXIO; 126059241Srwatson 126159241Srwatsonvopunlock_exit: 1262175294Sattilio VOP_UNLOCK(attribute->uele_backing_vnode, 0); 126359241Srwatson 126459241Srwatson return (error); 126559241Srwatson} 126659241Srwatson 126759241Srwatson/* 126859241Srwatson * Called by UFS when an inode is no longer active and should have its 126959241Srwatson * attributes stripped. 127059241Srwatson */ 127159241Srwatsonvoid 127283366Sjulianufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td) 127359241Srwatson{ 1274131066Srwatson struct ufs_extattr_list_entry *uele; 1275131066Srwatson struct mount *mp = vp->v_mount; 1276131066Srwatson struct ufsmount *ump = VFSTOUFS(mp); 127759241Srwatson 127877190Stmm /* 127977190Stmm * In that case, we cannot lock. We should not have any active vnodes 128077190Stmm * on the fs if this is not yet initialized but is going to be, so 128177190Stmm * this can go unlocked. 128277190Stmm */ 128377190Stmm if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 128477190Stmm return; 128577190Stmm 1286234613Strasz ufs_extattr_uepm_lock(ump); 128770776Srwatson 128859241Srwatson if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 1289234613Strasz ufs_extattr_uepm_unlock(ump); 129059241Srwatson return; 129159241Srwatson } 129259241Srwatson 129372012Sphk LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) 129474437Srwatson ufs_extattr_rm(vp, uele->uele_attrnamespace, 129583366Sjulian uele->uele_attrname, NULL, td); 129659241Srwatson 1297234613Strasz ufs_extattr_uepm_unlock(ump); 129859241Srwatson} 129974273Srwatson 130074433Srwatson#endif /* !UFS_EXTATTR */ 1301