xref: /csrg-svn/sys/ufs/ffs/ufs_readwrite.c (revision 65798)
164412Smckusick /*-
2*65798Sbostic  * Copyright (c) 1993
3*65798Sbostic  *	The Regents of the University of California.  All rights reserved.
464412Smckusick  *
564412Smckusick  * %sccs.include.redist.c%
664412Smckusick  *
7*65798Sbostic  *	@(#)ufs_readwrite.c	8.7 (Berkeley) 01/21/94
864412Smckusick  */
964412Smckusick 
1064412Smckusick #ifdef LFS_READWRITE
1164412Smckusick #define	BLKSIZE(a, b, c)	blksize(a)
1264412Smckusick #define	FS			struct lfs
1364412Smckusick #define	I_FS			i_lfs
1464412Smckusick #define	READ			lfs_read
1565602Smckusick #define	READ_S			"lfs_read"
1664412Smckusick #define	WRITE			lfs_write
1765602Smckusick #define	WRITE_S			"lfs_write"
1864412Smckusick #define	fs_bsize		lfs_bsize
1964412Smckusick #define	fs_maxfilesize		lfs_maxfilesize
2064412Smckusick #else
2164412Smckusick #define	BLKSIZE(a, b, c)	blksize(a, b, c)
2264412Smckusick #define	FS			struct fs
2364412Smckusick #define	I_FS			i_fs
2464412Smckusick #define	READ			ffs_read
2565602Smckusick #define	READ_S			"ffs_read"
2664412Smckusick #define	WRITE			ffs_write
2765602Smckusick #define	WRITE_S			"ffs_write"
2864412Smckusick #endif
2964412Smckusick 
3064412Smckusick /*
3164412Smckusick  * Vnode op for reading.
3264412Smckusick  */
3364412Smckusick /* ARGSUSED */
3464412Smckusick READ(ap)
3564412Smckusick 	struct vop_read_args /* {
3664412Smckusick 		struct vnode *a_vp;
3764412Smckusick 		struct uio *a_uio;
3864412Smckusick 		int a_ioflag;
3964412Smckusick 		struct ucred *a_cred;
4064412Smckusick 	} */ *ap;
4164412Smckusick {
4264412Smckusick 	register struct vnode *vp;
4364412Smckusick 	register struct inode *ip;
4464412Smckusick 	register struct uio *uio;
4564412Smckusick 	register FS *fs;
4664412Smckusick 	struct buf *bp;
4764412Smckusick 	daddr_t lbn, nextlbn;
4864412Smckusick 	off_t bytesinfile;
4964412Smckusick 	long size, xfersize, blkoffset;
5065720Sbostic 	int error;
5164412Smckusick 	u_short mode;
5264412Smckusick 
5364412Smckusick 	vp = ap->a_vp;
5464412Smckusick 	ip = VTOI(vp);
5564412Smckusick 	mode = ip->i_mode;
5664412Smckusick 	uio = ap->a_uio;
5764412Smckusick 
5864412Smckusick #ifdef DIAGNOSTIC
5964412Smckusick 	if (uio->uio_rw != UIO_READ)
6065602Smckusick 		panic("%s: mode", READ_S);
6164412Smckusick 
6264412Smckusick 	if (vp->v_type == VLNK) {
6364412Smckusick 		if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
6465602Smckusick 			panic("%s: short symlink", READ_S);
6564412Smckusick 	} else if (vp->v_type != VREG && vp->v_type != VDIR)
6665602Smckusick 		panic("%s: type %d", READ_S, vp->v_type);
6764412Smckusick #endif
6864412Smckusick 	fs = ip->I_FS;
6964412Smckusick 	if ((u_quad_t)uio->uio_offset > fs->fs_maxfilesize)
7064412Smckusick 		return (EFBIG);
7164412Smckusick 
7264412Smckusick 	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
7364412Smckusick 		if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
7464412Smckusick 			break;
7564412Smckusick 		lbn = lblkno(fs, uio->uio_offset);
7664412Smckusick 		nextlbn = lbn + 1;
7764412Smckusick 		size = BLKSIZE(fs, ip, lbn);
7864412Smckusick 		blkoffset = blkoff(fs, uio->uio_offset);
7964412Smckusick 		xfersize = fs->fs_bsize - blkoffset;
8064412Smckusick 		if (uio->uio_resid < xfersize)
8164412Smckusick 			xfersize = uio->uio_resid;
8264412Smckusick 		if (bytesinfile < xfersize)
8364412Smckusick 			xfersize = bytesinfile;
8464412Smckusick 
8564412Smckusick #ifdef LFS_READWRITE
8664412Smckusick 		(void)lfs_check(vp, lbn);
8764412Smckusick 		error = cluster_read(vp, ip->i_size, lbn, size, NOCRED, &bp);
8864412Smckusick #else
8965720Sbostic 		if (lblktosize(fs, nextlbn) > ip->i_size)
9064412Smckusick 			error = bread(vp, lbn, size, NOCRED, &bp);
9165720Sbostic 		else if (doclusterread)
9265720Sbostic 			error = cluster_read(vp,
9365720Sbostic 			    ip->i_size, lbn, size, NOCRED, &bp);
9465720Sbostic 		else if (lbn - 1 == vp->v_lastr) {
9565720Sbostic 			int nextsize = BLKSIZE(fs, ip, nextlbn);
9665720Sbostic 			error = breadn(vp, lbn,
9765720Sbostic 			    size, &nextlbn, &nextsize, 1, NOCRED, &bp);
9865720Sbostic 		} else
9965720Sbostic 			error = bread(vp, lbn, size, NOCRED, &bp);
10064412Smckusick #endif
10164412Smckusick 		if (error)
10264412Smckusick 			break;
10364412Smckusick 		vp->v_lastr = lbn;
10464412Smckusick 
10564412Smckusick 		/*
10664412Smckusick 		 * We should only get non-zero b_resid when an I/O error
10764412Smckusick 		 * has occurred, which should cause us to break above.
10864412Smckusick 		 * However, if the short read did not cause an error,
10964412Smckusick 		 * then we want to ensure that we do not uiomove bad
11064412Smckusick 		 * or uninitialized data.
11164412Smckusick 		 */
11264412Smckusick 		size -= bp->b_resid;
11364412Smckusick 		if (size < xfersize) {
11464412Smckusick 			if (size == 0)
11564412Smckusick 				break;
11664412Smckusick 			xfersize = size;
11764412Smckusick 		}
11864412Smckusick 		if (error =
11964519Sbostic 		    uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio))
12064412Smckusick 			break;
12164412Smckusick 
12264412Smckusick 		if (S_ISREG(mode) && (xfersize + blkoffset == fs->fs_bsize ||
12364412Smckusick 		    uio->uio_offset == ip->i_size))
12464412Smckusick 			bp->b_flags |= B_AGE;
12564412Smckusick 		brelse(bp);
12664412Smckusick 	}
12764412Smckusick 	if (bp != NULL)
12864412Smckusick 		brelse(bp);
12964601Sbostic 	ip->i_flag |= IN_ACCESS;
13064412Smckusick 	return (error);
13164412Smckusick }
13264412Smckusick 
13364412Smckusick /*
13464412Smckusick  * Vnode op for writing.
13564412Smckusick  */
13664412Smckusick WRITE(ap)
13764412Smckusick 	struct vop_write_args /* {
13864412Smckusick 		struct vnode *a_vp;
13964412Smckusick 		struct uio *a_uio;
14064412Smckusick 		int a_ioflag;
14164412Smckusick 		struct ucred *a_cred;
14264412Smckusick 	} */ *ap;
14364412Smckusick {
14464412Smckusick 	register struct vnode *vp;
14564412Smckusick 	register struct uio *uio;
14664412Smckusick 	register struct inode *ip;
14764412Smckusick 	register FS *fs;
14864412Smckusick 	struct buf *bp;
14964412Smckusick 	struct proc *p;
15064412Smckusick 	daddr_t lbn;
15164412Smckusick 	off_t osize;
15265470Sbostic 	int blkoffset, error, flags, ioflag, resid, size, xfersize;
15364412Smckusick 
15464412Smckusick 	ioflag = ap->a_ioflag;
15564412Smckusick 	uio = ap->a_uio;
15664412Smckusick 	vp = ap->a_vp;
15764412Smckusick 	ip = VTOI(vp);
15864412Smckusick 
15964412Smckusick #ifdef DIAGNOSTIC
16064412Smckusick 	if (uio->uio_rw != UIO_WRITE)
16165602Smckusick 		panic("%s: mode", WRITE_S);
16264412Smckusick #endif
16364412Smckusick 
16464412Smckusick 	switch (vp->v_type) {
16564412Smckusick 	case VREG:
16664412Smckusick 		if (ioflag & IO_APPEND)
16764412Smckusick 			uio->uio_offset = ip->i_size;
16864412Smckusick 		if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size)
16964412Smckusick 			return (EPERM);
17064412Smckusick 		/* FALLTHROUGH */
17164412Smckusick 	case VLNK:
17264412Smckusick 		break;
17364412Smckusick 	case VDIR:
17464412Smckusick 		if ((ioflag & IO_SYNC) == 0)
17565602Smckusick 			panic("%s: nonsync dir write", WRITE_S);
17664412Smckusick 		break;
17764412Smckusick 	default:
17865602Smckusick 		panic("%s: type", WRITE_S);
17964412Smckusick 	}
18064412Smckusick 
18164412Smckusick 	fs = ip->I_FS;
18264412Smckusick 	if (uio->uio_offset < 0 ||
18364412Smckusick 	    (u_quad_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize)
18464412Smckusick 		return (EFBIG);
18564412Smckusick 	/*
18664412Smckusick 	 * Maybe this should be above the vnode op call, but so long as
18764412Smckusick 	 * file servers have no limits, I don't think it matters.
18864412Smckusick 	 */
18964412Smckusick 	p = uio->uio_procp;
19064412Smckusick 	if (vp->v_type == VREG && p &&
19164412Smckusick 	    uio->uio_offset + uio->uio_resid >
19264412Smckusick 	    p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
19364412Smckusick 		psignal(p, SIGXFSZ);
19464412Smckusick 		return (EFBIG);
19564412Smckusick 	}
19664412Smckusick 
19764412Smckusick 	resid = uio->uio_resid;
19864412Smckusick 	osize = ip->i_size;
19964412Smckusick 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
20064412Smckusick 
20164412Smckusick 	for (error = 0; uio->uio_resid > 0;) {
20264412Smckusick 		lbn = lblkno(fs, uio->uio_offset);
20364412Smckusick 		blkoffset = blkoff(fs, uio->uio_offset);
20464412Smckusick 		xfersize = fs->fs_bsize - blkoffset;
20564412Smckusick 		if (uio->uio_resid < xfersize)
20664412Smckusick 			xfersize = uio->uio_resid;
20764412Smckusick #ifdef LFS_READWRITE
20864412Smckusick 		(void)lfs_check(vp, lbn);
20964412Smckusick 		error = lfs_balloc(vp, xfersize, lbn, &bp);
21064412Smckusick #else
21164412Smckusick 		if (fs->fs_bsize > xfersize)
21264412Smckusick 			flags |= B_CLRBUF;
21364412Smckusick 		else
21464412Smckusick 			flags &= ~B_CLRBUF;
21564412Smckusick 
21664412Smckusick 		error = ffs_balloc(ip,
21764412Smckusick 		    lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
21864412Smckusick #endif
21964412Smckusick 		if (error)
22064412Smckusick 			break;
22164412Smckusick 		if (uio->uio_offset + xfersize > ip->i_size) {
22264412Smckusick 			ip->i_size = uio->uio_offset + xfersize;
22364412Smckusick 			vnode_pager_setsize(vp, (u_long)ip->i_size);
22464412Smckusick 		}
22564412Smckusick 		(void)vnode_pager_uncache(vp);
22664412Smckusick 
22764412Smckusick 		size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
22864412Smckusick 		if (size < xfersize)
22964412Smckusick 			xfersize = size;
23064412Smckusick 
23164412Smckusick 		error =
23264519Sbostic 		    uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
23364412Smckusick #ifdef LFS_READWRITE
23464412Smckusick 		(void)VOP_BWRITE(bp);
23564412Smckusick #else
23664412Smckusick 		if (ioflag & IO_SYNC)
23764412Smckusick 			(void)bwrite(bp);
23864412Smckusick 		else if (xfersize + blkoffset == fs->fs_bsize)
23964412Smckusick 			if (doclusterwrite)
24064412Smckusick 				cluster_write(bp, ip->i_size);
24164412Smckusick 			else {
24264412Smckusick 				bp->b_flags |= B_AGE;
24364412Smckusick 				bawrite(bp);
24464412Smckusick 			}
24564412Smckusick 		else
24664412Smckusick 			bdwrite(bp);
24764412Smckusick #endif
24864412Smckusick 		if (error || xfersize == 0)
24964412Smckusick 			break;
25064601Sbostic 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
25164412Smckusick 	}
25264412Smckusick 	/*
25364412Smckusick 	 * If we successfully wrote any data, and we are not the superuser
25464412Smckusick 	 * we clear the setuid and setgid bits as a precaution against
25564412Smckusick 	 * tampering.
25664412Smckusick 	 */
25764412Smckusick 	if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
25864412Smckusick 		ip->i_mode &= ~(ISUID | ISGID);
25964412Smckusick 	if (error) {
26064412Smckusick 		if (ioflag & IO_UNIT) {
26164412Smckusick 			(void)VOP_TRUNCATE(vp, osize,
26264412Smckusick 			    ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
26364412Smckusick 			uio->uio_offset -= resid - uio->uio_resid;
26464412Smckusick 			uio->uio_resid = resid;
26564412Smckusick 		}
26664412Smckusick 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
26764412Smckusick 		error = VOP_UPDATE(vp, &time, &time, 1);
26864412Smckusick 	return (error);
26964412Smckusick }
270