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