Home | History | Annotate | Line # | Download | only in ffs
      1 /*	$NetBSD: ffs_extattr.c,v 1.10 2022/11/28 04:52:04 chs Exp $	*/
      2 
      3 /*-
      4  * SPDX-License-Identifier: (BSD-2-Clause-FreeBSD AND BSD-3-Clause)
      5  *
      6  * Copyright (c) 2002, 2003 Networks Associates Technology, Inc.
      7  * All rights reserved.
      8  *
      9  * This software was developed for the FreeBSD Project by Marshall
     10  * Kirk McKusick and Network Associates Laboratories, the Security
     11  * Research Division of Network Associates, Inc. under DARPA/SPAWAR
     12  * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
     13  * research program
     14  *
     15  * Redistribution and use in source and binary forms, with or without
     16  * modification, are permitted provided that the following conditions
     17  * are met:
     18  * 1. Redistributions of source code must retain the above copyright
     19  *    notice, this list of conditions and the following disclaimer.
     20  * 2. Redistributions in binary form must reproduce the above copyright
     21  *    notice, this list of conditions and the following disclaimer in the
     22  *    documentation and/or other materials provided with the distribution.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  *
     36  * Copyright (c) 1982, 1986, 1989, 1993
     37  *	The Regents of the University of California.  All rights reserved.
     38  *
     39  * Redistribution and use in source and binary forms, with or without
     40  * modification, are permitted provided that the following conditions
     41  * are met:
     42  * 1. Redistributions of source code must retain the above copyright
     43  *    notice, this list of conditions and the following disclaimer.
     44  * 2. Redistributions in binary form must reproduce the above copyright
     45  *    notice, this list of conditions and the following disclaimer in the
     46  *    documentation and/or other materials provided with the distribution.
     47  * 3. Neither the name of the University nor the names of its contributors
     48  *    may be used to endorse or promote products derived from this software
     49  *    without specific prior written permission.
     50  *
     51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     61  * SUCH DAMAGE.
     62  *
     63  *	from: @(#)ufs_readwrite.c	8.11 (Berkeley) 5/8/95
     64  * from: $FreeBSD: .../ufs/ufs_readwrite.c,v 1.96 2002/08/12 09:22:11 phk ...
     65  *	@(#)ffs_vnops.c	8.15 (Berkeley) 5/14/95
     66  */
     67 
     68 #include <sys/cdefs.h>
     69 __KERNEL_RCSID(0, "$NetBSD: ffs_extattr.c,v 1.10 2022/11/28 04:52:04 chs Exp $");
     70 
     71 #if defined(_KERNEL_OPT)
     72 #include "opt_ffs.h"
     73 #include "opt_wapbl.h"
     74 #endif
     75 
     76 #include <sys/param.h>
     77 #include <sys/systm.h>
     78 #include <sys/resourcevar.h>
     79 #include <sys/kernel.h>
     80 #include <sys/file.h>
     81 #include <sys/stat.h>
     82 #include <sys/buf.h>
     83 #include <sys/event.h>
     84 #include <sys/extattr.h>
     85 #include <sys/kauth.h>
     86 #include <sys/proc.h>
     87 #include <sys/mount.h>
     88 #include <sys/vnode.h>
     89 #include <sys/malloc.h>
     90 #include <sys/pool.h>
     91 #include <sys/signalvar.h>
     92 #include <sys/kauth.h>
     93 #include <sys/wapbl.h>
     94 
     95 #include <miscfs/fifofs/fifo.h>
     96 #include <miscfs/genfs/genfs.h>
     97 #include <miscfs/specfs/specdev.h>
     98 
     99 #include <ufs/ufs/inode.h>
    100 #include <ufs/ufs/dir.h>
    101 #include <ufs/ufs/ufs_extern.h>
    102 #include <ufs/ufs/ufsmount.h>
    103 #include <ufs/ufs/ufs_wapbl.h>
    104 
    105 #include <ufs/ffs/fs.h>
    106 #include <ufs/ffs/ffs_extern.h>
    107 
    108 #define ALIGNED_TO(ptr, s)  \
    109     (((uintptr_t)(ptr) & (_Alignof(s) - 1)) == 0)
    110 #define uoff_t uintmax_t
    111 #define ITOFS(ip) (ip)->i_fs
    112 #define i_din2 i_din.ffs2_din
    113 #define VI_LOCK(vp)		mutex_enter((vp)->v_interlock)
    114 #define VI_UNLOCK(vp)		mutex_exit((vp)->v_interlock)
    115 #define UFS_INODE_SET_FLAG(ip, f)	((ip)->i_flag |= (f))
    116 #define ASSERT_VOP_ELOCKED(vp, m)	KASSERT(VOP_ISLOCKED(vp))
    117 #define I_IS_UFS2(ip)		((ip)->i_ump->um_fstype == UFS2)
    118 #define	lblktosize(fs, o)	ffs_lblktosize(fs, o)
    119 #define	lblkno(fs, o)		ffs_lblkno(fs, o)
    120 #define	blkoff(fs, o)		ffs_blkoff(fs, o)
    121 #define	sblksize(fs, o, lbn)	ffs_sblksize(fs, o, lbn)
    122 typedef daddr_t ufs_lbn_t;
    123 #define msleep(chan, mtx, pri, wmesg, timeo) \
    124     mtsleep((chan), (pri), (wmesg), (timeo), *(mtx))
    125 #define vm_page_count_severe()		0
    126 #define buf_dirty_count_severe()	0
    127 #define BA_CLRBUF B_CLRBUF
    128 #define IO_ASYNC 0
    129 #define vfs_bio_brelse(bp, ioflag) 	brelse(bp, 0)
    130 #define vfs_bio_clrbuf(bp) 		clrbuf(bp)
    131 #define vfs_bio_set_flags(bp, ioflag) 	__nothing
    132 
    133 /*
    134  * Extended attribute area reading.
    135  */
    136 static int
    137 ffs_extread(struct vnode *vp, struct uio *uio, int ioflag)
    138 {
    139 	struct inode *ip;
    140 	struct ufs2_dinode *dp;
    141 	struct fs *fs;
    142 	struct buf *bp;
    143 	ufs_lbn_t lbn, nextlbn;
    144 	off_t bytesinfile;
    145 	long size, xfersize, blkoffset;
    146 	ssize_t orig_resid;
    147 	int error;
    148 
    149 	ip = VTOI(vp);
    150 	fs = ITOFS(ip);
    151 	dp = ip->i_din2;
    152 
    153 #ifdef INVARIANTS
    154 	if (uio->uio_rw != UIO_READ || ip->i_ump->um_fstype != UFS2)
    155 		panic("ffs_extread: mode");
    156 
    157 #endif
    158 	orig_resid = uio->uio_resid;
    159 	KASSERT(orig_resid >= 0);
    160 	if (orig_resid == 0)
    161 		return (0);
    162 	KASSERT(uio->uio_offset >= 0);
    163 
    164 	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
    165 		if ((bytesinfile = dp->di_extsize - uio->uio_offset) <= 0)
    166 			break;
    167 		lbn = lblkno(fs, uio->uio_offset);
    168 		nextlbn = lbn + 1;
    169 
    170 		/*
    171 		 * size of buffer.  The buffer representing the
    172 		 * end of the file is rounded up to the size of
    173 		 * the block type ( fragment or full block,
    174 		 * depending ).
    175 		 */
    176 		size = sblksize(fs, dp->di_extsize, lbn);
    177 		blkoffset = blkoff(fs, uio->uio_offset);
    178 
    179 		/*
    180 		 * The amount we want to transfer in this iteration is
    181 		 * one FS block less the amount of the data before
    182 		 * our startpoint (duh!)
    183 		 */
    184 		xfersize = fs->fs_bsize - blkoffset;
    185 
    186 		/*
    187 		 * But if we actually want less than the block,
    188 		 * or the file doesn't have a whole block more of data,
    189 		 * then use the lesser number.
    190 		 */
    191 		if (uio->uio_resid < xfersize)
    192 			xfersize = uio->uio_resid;
    193 		if (bytesinfile < xfersize)
    194 			xfersize = bytesinfile;
    195 
    196 		if (lblktosize(fs, nextlbn) >= dp->di_extsize) {
    197 			/*
    198 			 * Don't do readahead if this is the end of the info.
    199 			 */
    200 			error = bread(vp, -1 - lbn, size, 0, &bp);
    201 		} else {
    202 			/*
    203 			 * If we have a second block, then
    204 			 * fire off a request for a readahead
    205 			 * as well as a read. Note that the 4th and 5th
    206 			 * arguments point to arrays of the size specified in
    207 			 * the 6th argument.
    208 			 */
    209 			u_int nextsize = sblksize(fs, dp->di_extsize, nextlbn);
    210 
    211 			nextlbn = -1 - nextlbn;
    212 			error = breadn(vp, -1 - lbn,
    213 			    size, &nextlbn, &nextsize, 1, 0, &bp);
    214 		}
    215 		if (error) {
    216 			brelse(bp, 0);
    217 			bp = NULL;
    218 			break;
    219 		}
    220 
    221 		/*
    222 		 * We should only get non-zero b_resid when an I/O error
    223 		 * has occurred, which should cause us to break above.
    224 		 * However, if the short read did not cause an error,
    225 		 * then we want to ensure that we do not uiomove bad
    226 		 * or uninitialized data.
    227 		 */
    228 		size -= bp->b_resid;
    229 		if (size < xfersize) {
    230 			if (size == 0)
    231 				break;
    232 			xfersize = size;
    233 		}
    234 
    235 		error = uiomove((char *)bp->b_data + blkoffset,
    236 					(int)xfersize, uio);
    237 		if (error)
    238 			break;
    239 		vfs_bio_brelse(bp, ioflag);
    240 	}
    241 
    242 	/*
    243 	 * This can only happen in the case of an error
    244 	 * because the loop above resets bp to NULL on each iteration
    245 	 * and on normal completion has not set a new value into it.
    246 	 * so it must have come from a 'break' statement
    247 	 */
    248 	if (bp != NULL)
    249 		vfs_bio_brelse(bp, ioflag);
    250 	return (error);
    251 }
    252 /*
    253  * Extended attribute area writing.
    254  */
    255 static int
    256 ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t ucred)
    257 {
    258 	struct inode *ip;
    259 	struct ufs2_dinode *dp;
    260 	struct fs *fs;
    261 	struct buf *bp;
    262 	ufs_lbn_t lbn;
    263 	off_t osize;
    264 	ssize_t resid;
    265 	int blkoffset, error, flags, size, xfersize;
    266 
    267 	ip = VTOI(vp);
    268 	fs = ITOFS(ip);
    269 	dp = ip->i_din2;
    270 
    271 #ifdef INVARIANTS
    272 	if (uio->uio_rw != UIO_WRITE || ip->i_ump->um_fstype != UFS2)
    273 		panic("ffs_extwrite: mode");
    274 #endif
    275 
    276 	if (ioflag & IO_APPEND)
    277 		uio->uio_offset = dp->di_extsize;
    278 	KASSERT(uio->uio_offset >= 0);
    279 	if ((uoff_t)uio->uio_offset + uio->uio_resid >
    280 	    UFS_NXADDR * fs->fs_bsize)
    281 		return (EFBIG);
    282 
    283 	resid = uio->uio_resid;
    284 	osize = dp->di_extsize;
    285 	flags = IO_EXT;
    286 	if (ioflag & IO_SYNC)
    287 		flags |= IO_SYNC;
    288 
    289 	if ((error = UFS_WAPBL_BEGIN(vp->v_mount)) != 0)
    290 		return error;
    291 
    292 	for (error = 0; uio->uio_resid > 0;) {
    293 		lbn = lblkno(fs, uio->uio_offset);
    294 		blkoffset = blkoff(fs, uio->uio_offset);
    295 		xfersize = fs->fs_bsize - blkoffset;
    296 		if (uio->uio_resid < xfersize)
    297 			xfersize = uio->uio_resid;
    298 
    299 		/*
    300 		 * We must perform a read-before-write if the transfer size
    301 		 * does not cover the entire buffer.
    302 		 */
    303 		if (fs->fs_bsize > xfersize)
    304 			flags |= BA_CLRBUF;
    305 		else
    306 			flags &= ~BA_CLRBUF;
    307 		error = UFS_BALLOC(vp, uio->uio_offset, xfersize,
    308 		    ucred, flags, &bp);
    309 		if (error != 0)
    310 			break;
    311 		/*
    312 		 * If the buffer is not valid we have to clear out any
    313 		 * garbage data from the pages instantiated for the buffer.
    314 		 * If we do not, a failed uiomove() during a write can leave
    315 		 * the prior contents of the pages exposed to a userland
    316 		 * mmap().  XXX deal with uiomove() errors a better way.
    317 		 */
    318 		if ((bp->b_flags & BC_NOCACHE) && fs->fs_bsize <= xfersize)
    319 			vfs_bio_clrbuf(bp);
    320 
    321 		if (uio->uio_offset + xfersize > dp->di_extsize)
    322 			dp->di_extsize = uio->uio_offset + xfersize;
    323 
    324 		size = sblksize(fs, dp->di_extsize, lbn) - bp->b_resid;
    325 		if (size < xfersize)
    326 			xfersize = size;
    327 
    328 		error =
    329 		    uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
    330 
    331 		vfs_bio_set_flags(bp, ioflag);
    332 
    333 		/*
    334 		 * If IO_SYNC each buffer is written synchronously.  Otherwise
    335 		 * if we have a severe page deficiency write the buffer
    336 		 * asynchronously.  Otherwise try to cluster, and if that
    337 		 * doesn't do it then either do an async write (if O_DIRECT),
    338 		 * or a delayed write (if not).
    339 		 */
    340 		if (ioflag & IO_SYNC) {
    341 			(void)bwrite(bp);
    342 		} else if (vm_page_count_severe() ||
    343 			    buf_dirty_count_severe() ||
    344 			    xfersize + blkoffset == fs->fs_bsize ||
    345 			    (ioflag & (IO_ASYNC | IO_DIRECT)))
    346 			bawrite(bp);
    347 		else
    348 			bdwrite(bp);
    349 		if (error || xfersize == 0)
    350 			break;
    351 		UFS_INODE_SET_FLAG(ip, IN_CHANGE);
    352 	}
    353 	/*
    354 	 * If we successfully wrote any data, and we are not the superuser
    355 	 * we clear the setuid and setgid bits as a precaution against
    356 	 * tampering.
    357 	 */
    358 	if ((ip->i_mode & (ISUID | ISGID)) && resid > uio->uio_resid && ucred) {
    359 		ip->i_mode &= ~(ISUID | ISGID);
    360 		dp->di_mode = ip->i_mode;
    361 	}
    362 	if (error) {
    363 		if (ioflag & IO_UNIT) {
    364 			(void)ffs_truncate(vp, osize,
    365 			    IO_EXT | (ioflag&IO_SYNC), ucred);
    366 			uio->uio_offset -= resid - uio->uio_resid;
    367 			uio->uio_resid = resid;
    368 		}
    369 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
    370 		error = ffs_update(vp, NULL, NULL, UPDATE_WAIT);
    371 	UFS_WAPBL_END(vp->v_mount);
    372 	return (error);
    373 }
    374 
    375 /*
    376  * Vnode operating to retrieve a named extended attribute.
    377  *
    378  * Locate a particular EA (nspace:name) in the area (ptr:length), and return
    379  * the length of the EA, and possibly the pointer to the entry and to the data.
    380  */
    381 static int
    382 ffs_findextattr(u_char *ptr, u_int length, int nspace, const char *name,
    383     struct extattr **eapp, u_char **eac)
    384 {
    385 	struct extattr *eap, *eaend;
    386 	size_t nlen;
    387 
    388 	nlen = strlen(name);
    389 	KASSERT(ALIGNED_TO(ptr, struct extattr));
    390 	eap = (struct extattr *)ptr;
    391 	eaend = (struct extattr *)(ptr + length);
    392 	for (; eap < eaend; eap = EXTATTR_NEXT(eap)) {
    393 		/* make sure this entry is complete */
    394 		if (EXTATTR_NEXT(eap) > eaend)
    395 			break;
    396 		if (eap->ea_namespace != nspace || eap->ea_namelength != nlen
    397 		    || memcmp(eap->ea_name, name, nlen) != 0)
    398 			continue;
    399 		if (eapp != NULL)
    400 			*eapp = eap;
    401 		if (eac != NULL)
    402 			*eac = EXTATTR_CONTENT(eap);
    403 		return (EXTATTR_CONTENT_SIZE(eap));
    404 	}
    405 	return (-1);
    406 }
    407 
    408 static int
    409 ffs_rdextattr(u_char **p, struct vnode *vp, int extra)
    410 {
    411 	struct inode *ip;
    412 	struct ufs2_dinode *dp;
    413 	struct fs *fs;
    414 	struct uio luio;
    415 	struct iovec liovec;
    416 	u_int easize;
    417 	int error;
    418 	u_char *eae;
    419 
    420 	ip = VTOI(vp);
    421 	fs = ITOFS(ip);
    422 	dp = ip->i_din2;
    423 	easize = dp->di_extsize;
    424 	if ((uoff_t)easize + extra > UFS_NXADDR * fs->fs_bsize)
    425 		return (EFBIG);
    426 
    427 	eae = malloc(easize + extra, M_TEMP, M_WAITOK);
    428 
    429 	liovec.iov_base = eae;
    430 	liovec.iov_len = easize;
    431 	luio.uio_iov = &liovec;
    432 	luio.uio_iovcnt = 1;
    433 	luio.uio_offset = 0;
    434 	luio.uio_resid = easize;
    435 	luio.uio_vmspace = vmspace_kernel();
    436 	luio.uio_rw = UIO_READ;
    437 
    438 	error = ffs_extread(vp, &luio, IO_EXT | IO_SYNC);
    439 	if (error) {
    440 		free(eae, M_TEMP);
    441 		return(error);
    442 	}
    443 	*p = eae;
    444 	return (0);
    445 }
    446 
    447 static void
    448 ffs_lock_ea(struct vnode *vp)
    449 {
    450 	genfs_node_wrlock(vp);
    451 }
    452 
    453 static void
    454 ffs_unlock_ea(struct vnode *vp)
    455 {
    456 	genfs_node_unlock(vp);
    457 }
    458 
    459 static int
    460 ffs_open_ea(struct vnode *vp, kauth_cred_t cred)
    461 {
    462 	struct inode *ip;
    463 	struct ufs2_dinode *dp;
    464 	int error;
    465 
    466 	ip = VTOI(vp);
    467 	if ((ip->i_ump->um_flags & UFS_EA) == 0) {
    468 		return EOPNOTSUPP;
    469 	}
    470 
    471 	ffs_lock_ea(vp);
    472 	if (ip->i_ea_area != NULL) {
    473 		ip->i_ea_refs++;
    474 		ffs_unlock_ea(vp);
    475 		return (0);
    476 	}
    477 	dp = ip->i_din2;
    478 	error = ffs_rdextattr(&ip->i_ea_area, vp, 0);
    479 	if (error) {
    480 		ffs_unlock_ea(vp);
    481 		return (error);
    482 	}
    483 	ip->i_ea_len = dp->di_extsize;
    484 	ip->i_ea_error = 0;
    485 	ip->i_ea_refs++;
    486 	ffs_unlock_ea(vp);
    487 	return (0);
    488 }
    489 
    490 /*
    491  * Vnode extattr transaction commit/abort
    492  */
    493 static int
    494 ffs_close_ea(struct vnode *vp, int commit, kauth_cred_t cred)
    495 {
    496 	struct inode *ip;
    497 	struct uio luio;
    498 	struct iovec liovec;
    499 	int error;
    500 	struct ufs2_dinode *dp;
    501 
    502 	ip = VTOI(vp);
    503 	KASSERT((ip->i_ump->um_flags & UFS_EA) != 0);
    504 
    505 	if (commit)
    506 		KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    507 	else
    508 		KASSERT(VOP_ISLOCKED(vp));
    509 	ffs_lock_ea(vp);
    510 	if (ip->i_ea_area == NULL) {
    511 		ffs_unlock_ea(vp);
    512 		return (EINVAL);
    513 	}
    514 	dp = ip->i_din2;
    515 	error = ip->i_ea_error;
    516 	if (commit && error == 0) {
    517 		ASSERT_VOP_ELOCKED(vp, "ffs_close_ea commit");
    518 		if (cred == NOCRED)
    519 			cred =  lwp0.l_cred;
    520 		liovec.iov_base = ip->i_ea_area;
    521 		liovec.iov_len = ip->i_ea_len;
    522 		luio.uio_iov = &liovec;
    523 		luio.uio_iovcnt = 1;
    524 		luio.uio_offset = 0;
    525 		luio.uio_resid = ip->i_ea_len;
    526 		luio.uio_vmspace = vmspace_kernel();
    527 		luio.uio_rw = UIO_WRITE;
    528 
    529 		/* XXX: I'm not happy about truncating to zero size */
    530 		if (ip->i_ea_len < dp->di_extsize) {
    531 			if ((error = UFS_WAPBL_BEGIN(vp->v_mount)) != 0) {
    532 				ffs_unlock_ea(vp);
    533 				return error;
    534 			}
    535 			error = ffs_truncate(vp, 0, IO_EXT, cred);
    536 			UFS_WAPBL_END(vp->v_mount);
    537 		}
    538 		error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred);
    539 	}
    540 	if (--ip->i_ea_refs == 0) {
    541 		free(ip->i_ea_area, M_TEMP);
    542 		ip->i_ea_area = NULL;
    543 		ip->i_ea_len = 0;
    544 		ip->i_ea_error = 0;
    545 	}
    546 	ffs_unlock_ea(vp);
    547 	return (error);
    548 }
    549 
    550 /*
    551  * Vnode extattr strategy routine for fifos.
    552  *
    553  * We need to check for a read or write of the external attributes.
    554  * Otherwise we just fall through and do the usual thing.
    555  */
    556 int
    557 ffsext_strategy(void *v)
    558 {
    559 	struct vop_strategy_args /* {
    560 		struct vnodeop_desc *a_desc;
    561 		struct vnode *a_vp;
    562 		struct buf *a_bp;
    563 	} */ *ap = v;
    564 	struct vnode *vp;
    565 	daddr_t lbn;
    566 
    567 	vp = ap->a_vp;
    568 	lbn = ap->a_bp->b_lblkno;
    569 	if (I_IS_UFS2(VTOI(vp)) && lbn < 0 && lbn >= -UFS_NXADDR)
    570 		return ufs_strategy(ap);
    571 	if (vp->v_type == VFIFO)
    572 		return vn_fifo_bypass(ap);
    573 	panic("spec nodes went here");
    574 }
    575 
    576 /*
    577  * Vnode extattr transaction commit/abort
    578  */
    579 int
    580 ffs_openextattr(void *v)
    581 {
    582 	struct vop_openextattr_args /* {
    583 		struct vnode *a_vp;
    584 		kauth_cred_t a_cred;
    585 		struct proc *a_p;
    586 	} */ *ap = v;
    587 	struct inode *ip = VTOI(ap->a_vp);
    588 
    589 	/* Not supported for UFS1 file systems. */
    590 	if (ip->i_ump->um_fstype == UFS1)
    591 		return (EOPNOTSUPP);
    592 
    593 #ifdef __FreeBSD__
    594 	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
    595 		return (EOPNOTSUPP);
    596 #endif
    597 
    598 	return (ffs_open_ea(ap->a_vp, ap->a_cred));
    599 }
    600 
    601 /*
    602  * Vnode extattr transaction commit/abort
    603  */
    604 int
    605 ffs_closeextattr(void *v)
    606 {
    607 	struct vop_closeextattr_args /* {
    608 		struct vnode *a_vp;
    609 		int a_commit;
    610 		kauth_cred_t a_cred;
    611 		struct proc *a_p;
    612 	} */ *ap = v;
    613 	struct inode *ip = VTOI(ap->a_vp);
    614 
    615 	/* Not supported for UFS1 file systems. */
    616 	if (ip->i_ump->um_fstype == UFS1)
    617 		return (EOPNOTSUPP);
    618 
    619 #ifdef __FreeBSD__
    620 	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
    621 		return (EOPNOTSUPP);
    622 #endif
    623 
    624 	if (ap->a_commit && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY))
    625 		return (EROFS);
    626 
    627 	return (ffs_close_ea(ap->a_vp, ap->a_commit, ap->a_cred));
    628 }
    629 
    630 /*
    631  * Vnode operation to retrieve a named extended attribute.
    632  */
    633 int
    634 ffs_getextattr(void *v)
    635 {
    636 	struct vop_getextattr_args /* {
    637 		struct vnode *a_vp;
    638 		int a_attrnamespace;
    639 		const char *a_name;
    640 		struct uio *a_uio;
    641 		size_t *a_size;
    642 		kauth_cred_t a_cred;
    643 		struct proc *a_p;
    644 	} */ *ap = v;
    645 	struct vnode *vp = ap->a_vp;
    646 	struct inode *ip = VTOI(vp);
    647 
    648 	KASSERT(VOP_ISLOCKED(vp));
    649 
    650 	if (ip->i_ump->um_fstype == UFS1) {
    651 #ifdef UFS_EXTATTR
    652 		return ufs_getextattr(ap);
    653 #else
    654 		return EOPNOTSUPP;
    655 #endif
    656 	}
    657 
    658 	u_char *eae, *p;
    659 	unsigned easize;
    660 	int error, ealen;
    661 
    662 #ifdef __FreeBSD__
    663 	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
    664 		return (EOPNOTSUPP);
    665 #endif
    666 
    667 	error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
    668 	    ap->a_cred, VREAD);
    669 	if (error)
    670 		return (error);
    671 
    672 	error = ffs_open_ea(ap->a_vp, ap->a_cred);
    673 	if (error)
    674 		return (error);
    675 
    676 	eae = ip->i_ea_area;
    677 	easize = ip->i_ea_len;
    678 
    679 	ealen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name,
    680 	    NULL, &p);
    681 	if (ealen >= 0) {
    682 		error = 0;
    683 		if (ap->a_size != NULL)
    684 			*ap->a_size = ealen;
    685 		else if (ap->a_uio != NULL)
    686 			error = uiomove(p, ealen, ap->a_uio);
    687 	} else
    688 		error = ENOATTR;
    689 
    690 	ffs_close_ea(ap->a_vp, 0, ap->a_cred);
    691 	return (error);
    692 }
    693 
    694 /*
    695  * Vnode operation to set a named attribute.
    696  */
    697 int
    698 ffs_setextattr(void *v)
    699 {
    700 	struct vop_setextattr_args /* {
    701 		struct vnode *a_vp;
    702 		int a_attrnamespace;
    703 		const char *a_name;
    704 		struct uio *a_uio;
    705 		kauth_cred_t a_cred;
    706 		struct proc *a_p;
    707 	} */ *ap = v;
    708 	struct vnode *vp = ap->a_vp;
    709 	struct inode *ip = VTOI(vp);
    710 	struct fs *fs = ip->i_fs;
    711 
    712 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    713 	if (ip->i_ump->um_fstype == UFS1) {
    714 #ifdef UFS_EXTATTR
    715 		return ufs_setextattr(ap);
    716 #else
    717 		return EOPNOTSUPP;
    718 #endif
    719 	}
    720 
    721 	struct extattr *eap;
    722 	uint32_t ealength, ul;
    723 	ssize_t ealen;
    724 	int olen, eapad1, eapad2, error, i, easize;
    725 	u_char *eae;
    726 	void *tmp;
    727 
    728 	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
    729 		return (EOPNOTSUPP);
    730 
    731 	if (strlen(ap->a_name) == 0)
    732 		return (EINVAL);
    733 
    734 	/* XXX Now unsupported API to delete EAs using NULL uio. */
    735 	if (ap->a_uio == NULL)
    736 		return (EOPNOTSUPP);
    737 
    738 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
    739 		return (EROFS);
    740 
    741 	ealen = ap->a_uio->uio_resid;
    742 	if (ealen < 0 || ealen > lblktosize(fs, UFS_NXADDR))
    743 		return (EINVAL);
    744 
    745 	error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
    746 	    ap->a_cred, VWRITE);
    747 	if (error) {
    748 
    749 		/*
    750 		 * ffs_lock_ea is not needed there, because the vnode
    751 		 * must be exclusively locked.
    752 		 */
    753 		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
    754 			ip->i_ea_error = error;
    755 		return (error);
    756 	}
    757 
    758 	error = ffs_open_ea(ap->a_vp, ap->a_cred);
    759 	if (error)
    760 		return (error);
    761 
    762 	ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name);
    763 	eapad1 = roundup2(ealength, 8) - ealength;
    764 	eapad2 = roundup2(ealen, 8) - ealen;
    765 	ealength += eapad1 + ealen + eapad2;
    766 
    767 	/*
    768 	 * CEM: rewrites of the same size or smaller could be done in-place
    769 	 * instead.  (We don't acquire any fine-grained locks in here either,
    770 	 * so we could also do bigger writes in-place.)
    771 	 */
    772 	eae = malloc(ip->i_ea_len + ealength, M_TEMP, M_WAITOK);
    773 	bcopy(ip->i_ea_area, eae, ip->i_ea_len);
    774 	easize = ip->i_ea_len;
    775 
    776 	olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name,
    777 	    &eap, NULL);
    778         if (olen == -1) {
    779 		/* new, append at end */
    780 		KASSERT(ALIGNED_TO(eae + easize, struct extattr));
    781 		eap = (struct extattr *)(eae + easize);
    782 		easize += ealength;
    783 	} else {
    784 		ul = eap->ea_length;
    785 		i = (u_char *)EXTATTR_NEXT(eap) - eae;
    786 		if (ul != ealength) {
    787 			bcopy(EXTATTR_NEXT(eap), (u_char *)eap + ealength,
    788 			    easize - i);
    789 			easize += (ealength - ul);
    790 		}
    791 	}
    792 	if (easize > lblktosize(fs, UFS_NXADDR)) {
    793 		free(eae, M_TEMP);
    794 		ffs_close_ea(ap->a_vp, 0, ap->a_cred);
    795 		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
    796 			ip->i_ea_error = ENOSPC;
    797 		return (ENOSPC);
    798 	}
    799 	eap->ea_length = ealength;
    800 	eap->ea_namespace = ap->a_attrnamespace;
    801 	eap->ea_contentpadlen = eapad2;
    802 	eap->ea_namelength = strlen(ap->a_name);
    803 	memcpy(eap->ea_name, ap->a_name, strlen(ap->a_name));
    804 	bzero(&eap->ea_name[strlen(ap->a_name)], eapad1);
    805 	error = uiomove(EXTATTR_CONTENT(eap), ealen, ap->a_uio);
    806 	if (error) {
    807 		free(eae, M_TEMP);
    808 		ffs_close_ea(ap->a_vp, 0, ap->a_cred);
    809 		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
    810 			ip->i_ea_error = error;
    811 		return (error);
    812 	}
    813 	bzero((u_char *)EXTATTR_CONTENT(eap) + ealen, eapad2);
    814 
    815 	tmp = ip->i_ea_area;
    816 	ip->i_ea_area = eae;
    817 	ip->i_ea_len = easize;
    818 	free(tmp, M_TEMP);
    819 	error = ffs_close_ea(ap->a_vp, 1, ap->a_cred);
    820 	return (error);
    821 }
    822 
    823 /*
    824  * Vnode operation to retrieve extended attributes on a vnode.
    825  */
    826 int
    827 ffs_listextattr(void *v)
    828 {
    829 	struct vop_listextattr_args /* {
    830 		struct vnode *a_vp;
    831 		int a_attrnamespace;
    832 		struct uio *a_uio;
    833 		size_t *a_size;
    834 		kauth_cred_t a_cred;
    835 		struct proc *a_p;
    836 	} */ *ap = v;
    837 	struct inode *ip = VTOI(ap->a_vp);
    838 
    839 	if (ip->i_ump->um_fstype == UFS1) {
    840 #ifdef UFS_EXTATTR
    841 		return ufs_listextattr(ap);
    842 #else
    843 		return EOPNOTSUPP;
    844 #endif
    845 	}
    846 
    847 	struct extattr *eap, *eaend;
    848 	int error, ealen;
    849 
    850 	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
    851 		return (EOPNOTSUPP);
    852 
    853 	error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
    854 	    ap->a_cred, VREAD);
    855 	if (error)
    856 		return (error);
    857 
    858 	error = ffs_open_ea(ap->a_vp, ap->a_cred);
    859 	if (error)
    860 		return (error);
    861 
    862 	error = 0;
    863 	if (ap->a_size != NULL)
    864 		*ap->a_size = 0;
    865 
    866 	KASSERT(ALIGNED_TO(ip->i_ea_area, struct extattr));
    867 	eap = (struct extattr *)ip->i_ea_area;
    868 	eaend = (struct extattr *)(ip->i_ea_area + ip->i_ea_len);
    869 	for (; error == 0 && eap < eaend; eap = EXTATTR_NEXT(eap)) {
    870 		/* make sure this entry is complete */
    871 		if (EXTATTR_NEXT(eap) > eaend)
    872 			break;
    873 		if (eap->ea_namespace != ap->a_attrnamespace)
    874 			continue;
    875 
    876 		ealen = eap->ea_namelength;
    877 		if (ap->a_size != NULL)
    878 			*ap->a_size += ealen + 1;
    879 		else if (ap->a_uio != NULL)
    880 			error = uiomove(&eap->ea_namelength, ealen + 1,
    881 			    ap->a_uio);
    882 	}
    883 
    884 	ffs_close_ea(ap->a_vp, 0, ap->a_cred);
    885 	return (error);
    886 }
    887 
    888 /*
    889  * Vnode operation to remove a named attribute.
    890  */
    891 int
    892 ffs_deleteextattr(void *v)
    893 {
    894 	struct vop_deleteextattr_args /* {
    895 		struct vnode *a_vp;
    896 		int a_attrnamespace;
    897 		kauth_cred_t a_cred;
    898 		struct proc *a_p;
    899 	} */ *ap = v;
    900 	struct vnode *vp = ap->a_vp;
    901 	struct inode *ip = VTOI(vp);
    902 
    903 	if (ip->i_ump->um_fstype == UFS1) {
    904 #ifdef UFS_EXTATTR
    905 		return ufs_deleteextattr(ap);
    906 #else
    907 		return EOPNOTSUPP;
    908 #endif
    909 	}
    910 
    911 	struct extattr *eap;
    912 	uint32_t ul;
    913 	int olen, error, i, easize;
    914 	u_char *eae;
    915 	void *tmp;
    916 
    917 #ifdef __FreeBSD__
    918 	if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
    919 		return (EOPNOTSUPP);
    920 #endif
    921 
    922 	if (strlen(ap->a_name) == 0)
    923 		return (EINVAL);
    924 
    925 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
    926 		return (EROFS);
    927 
    928 	error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
    929 	    ap->a_cred, VWRITE);
    930 	if (error) {
    931 		/*
    932 		 * ffs_lock_ea is not needed there, because the vnode
    933 		 * must be exclusively locked.
    934 		 */
    935 		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
    936 			ip->i_ea_error = error;
    937 		return (error);
    938 	}
    939 
    940 	error = ffs_open_ea(ap->a_vp, ap->a_cred);
    941 	if (error)
    942 		return (error);
    943 
    944 	/* CEM: delete could be done in-place instead */
    945 	eae = malloc(ip->i_ea_len, M_TEMP, M_WAITOK);
    946 	bcopy(ip->i_ea_area, eae, ip->i_ea_len);
    947 	easize = ip->i_ea_len;
    948 
    949 	olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name,
    950 	    &eap, NULL);
    951 	if (olen == -1) {
    952 		/* delete but nonexistent */
    953 		free(eae, M_TEMP);
    954 		ffs_close_ea(ap->a_vp, 0, ap->a_cred);
    955 		return (ENOATTR);
    956 	}
    957 	ul = eap->ea_length;
    958 	i = (u_char *)EXTATTR_NEXT(eap) - eae;
    959 	bcopy(EXTATTR_NEXT(eap), eap, easize - i);
    960 	easize -= ul;
    961 
    962 	tmp = ip->i_ea_area;
    963 	ip->i_ea_area = eae;
    964 	ip->i_ea_len = easize;
    965 	free(tmp, M_TEMP);
    966 	error = ffs_close_ea(ap->a_vp, 1, ap->a_cred);
    967 	return error;
    968 }
    969