xref: /original-bsd/sys/ufs/ufs/ufs_readwrite.c (revision b3c06cab)
1 /*-
2  * Copyright (c) 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)ufs_readwrite.c	8.11 (Berkeley) 05/08/95
8  */
9 
10 #ifdef LFS_READWRITE
11 #define	BLKSIZE(a, b, c)	blksize(a, b, c)
12 #define	FS			struct lfs
13 #define	I_FS			i_lfs
14 #define	READ			lfs_read
15 #define	READ_S			"lfs_read"
16 #define	WRITE			lfs_write
17 #define	WRITE_S			"lfs_write"
18 #define	fs_bsize		lfs_bsize
19 #define	fs_maxfilesize		lfs_maxfilesize
20 #else
21 #define	BLKSIZE(a, b, c)	blksize(a, b, c)
22 #define	FS			struct fs
23 #define	I_FS			i_fs
24 #define	READ			ffs_read
25 #define	READ_S			"ffs_read"
26 #define	WRITE			ffs_write
27 #define	WRITE_S			"ffs_write"
28 #endif
29 
30 /*
31  * Vnode op for reading.
32  */
33 /* ARGSUSED */
34 READ(ap)
35 	struct vop_read_args /* {
36 		struct vnode *a_vp;
37 		struct uio *a_uio;
38 		int a_ioflag;
39 		struct ucred *a_cred;
40 	} */ *ap;
41 {
42 	register struct vnode *vp;
43 	register struct inode *ip;
44 	register struct uio *uio;
45 	register FS *fs;
46 	struct buf *bp;
47 	ufs_daddr_t lbn, nextlbn;
48 	off_t bytesinfile;
49 	long size, xfersize, blkoffset;
50 	int error;
51 	u_short mode;
52 
53 	vp = ap->a_vp;
54 	ip = VTOI(vp);
55 	mode = ip->i_mode;
56 	uio = ap->a_uio;
57 
58 #ifdef DIAGNOSTIC
59 	if (uio->uio_rw != UIO_READ)
60 		panic("%s: mode", READ_S);
61 
62 	if (vp->v_type == VLNK) {
63 		if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen)
64 			panic("%s: short symlink", READ_S);
65 	} else if (vp->v_type != VREG && vp->v_type != VDIR)
66 		panic("%s: type %d", READ_S, vp->v_type);
67 #endif
68 	fs = ip->I_FS;
69 	if ((u_int64_t)uio->uio_offset > fs->fs_maxfilesize)
70 		return (EFBIG);
71 
72 	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
73 		if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0)
74 			break;
75 		lbn = lblkno(fs, uio->uio_offset);
76 		nextlbn = lbn + 1;
77 		size = BLKSIZE(fs, ip, lbn);
78 		blkoffset = blkoff(fs, uio->uio_offset);
79 		xfersize = fs->fs_bsize - blkoffset;
80 		if (uio->uio_resid < xfersize)
81 			xfersize = uio->uio_resid;
82 		if (bytesinfile < xfersize)
83 			xfersize = bytesinfile;
84 
85 #ifdef LFS_READWRITE
86 		(void)lfs_check(vp, lbn);
87 		error = cluster_read(vp, ip->i_size, lbn, size, NOCRED, &bp);
88 #else
89 		if (lblktosize(fs, nextlbn) >= ip->i_size)
90 			error = bread(vp, lbn, size, NOCRED, &bp);
91 		else if (doclusterread)
92 			error = cluster_read(vp,
93 			    ip->i_size, lbn, size, NOCRED, &bp);
94 		else if (lbn - 1 == vp->v_lastr) {
95 			int nextsize = BLKSIZE(fs, ip, nextlbn);
96 			error = breadn(vp, lbn,
97 			    size, &nextlbn, &nextsize, 1, NOCRED, &bp);
98 		} else
99 			error = bread(vp, lbn, size, NOCRED, &bp);
100 #endif
101 		if (error)
102 			break;
103 		vp->v_lastr = lbn;
104 
105 		/*
106 		 * We should only get non-zero b_resid when an I/O error
107 		 * has occurred, which should cause us to break above.
108 		 * However, if the short read did not cause an error,
109 		 * then we want to ensure that we do not uiomove bad
110 		 * or uninitialized data.
111 		 */
112 		size -= bp->b_resid;
113 		if (size < xfersize) {
114 			if (size == 0)
115 				break;
116 			xfersize = size;
117 		}
118 		if (error =
119 		    uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio))
120 			break;
121 
122 		if (S_ISREG(mode) && (xfersize + blkoffset == fs->fs_bsize ||
123 		    uio->uio_offset == ip->i_size))
124 			bp->b_flags |= B_AGE;
125 		brelse(bp);
126 	}
127 	if (bp != NULL)
128 		brelse(bp);
129 	ip->i_flag |= IN_ACCESS;
130 	return (error);
131 }
132 
133 /*
134  * Vnode op for writing.
135  */
136 WRITE(ap)
137 	struct vop_write_args /* {
138 		struct vnode *a_vp;
139 		struct uio *a_uio;
140 		int a_ioflag;
141 		struct ucred *a_cred;
142 	} */ *ap;
143 {
144 	register struct vnode *vp;
145 	register struct uio *uio;
146 	register struct inode *ip;
147 	register FS *fs;
148 	struct buf *bp;
149 	struct proc *p;
150 	ufs_daddr_t lbn;
151 	off_t osize;
152 	int blkoffset, error, flags, ioflag, resid, size, xfersize;
153 
154 	ioflag = ap->a_ioflag;
155 	uio = ap->a_uio;
156 	vp = ap->a_vp;
157 	ip = VTOI(vp);
158 
159 #ifdef DIAGNOSTIC
160 	if (uio->uio_rw != UIO_WRITE)
161 		panic("%s: mode", WRITE_S);
162 #endif
163 
164 	switch (vp->v_type) {
165 	case VREG:
166 		if (ioflag & IO_APPEND)
167 			uio->uio_offset = ip->i_size;
168 		if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size)
169 			return (EPERM);
170 		/* FALLTHROUGH */
171 	case VLNK:
172 		break;
173 	case VDIR:
174 		if ((ioflag & IO_SYNC) == 0)
175 			panic("%s: nonsync dir write", WRITE_S);
176 		break;
177 	default:
178 		panic("%s: type", WRITE_S);
179 	}
180 
181 	fs = ip->I_FS;
182 	if (uio->uio_offset < 0 ||
183 	    (u_int64_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize)
184 		return (EFBIG);
185 	/*
186 	 * Maybe this should be above the vnode op call, but so long as
187 	 * file servers have no limits, I don't think it matters.
188 	 */
189 	p = uio->uio_procp;
190 	if (vp->v_type == VREG && p &&
191 	    uio->uio_offset + uio->uio_resid >
192 	    p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
193 		psignal(p, SIGXFSZ);
194 		return (EFBIG);
195 	}
196 
197 	resid = uio->uio_resid;
198 	osize = ip->i_size;
199 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
200 
201 	for (error = 0; uio->uio_resid > 0;) {
202 		lbn = lblkno(fs, uio->uio_offset);
203 		blkoffset = blkoff(fs, uio->uio_offset);
204 		xfersize = fs->fs_bsize - blkoffset;
205 		if (uio->uio_resid < xfersize)
206 			xfersize = uio->uio_resid;
207 #ifdef LFS_READWRITE
208 		(void)lfs_check(vp, lbn);
209 		error = lfs_balloc(vp, blkoffset, xfersize, lbn, &bp);
210 #else
211 		if (fs->fs_bsize > xfersize)
212 			flags |= B_CLRBUF;
213 		else
214 			flags &= ~B_CLRBUF;
215 
216 		error = ffs_balloc(ip,
217 		    lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
218 #endif
219 		if (error)
220 			break;
221 		if (uio->uio_offset + xfersize > ip->i_size) {
222 			ip->i_size = uio->uio_offset + xfersize;
223 			vnode_pager_setsize(vp, (u_long)ip->i_size);
224 		}
225 		(void)vnode_pager_uncache(vp);
226 
227 		size = BLKSIZE(fs, ip, lbn) - bp->b_resid;
228 		if (size < xfersize)
229 			xfersize = size;
230 
231 		error =
232 		    uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
233 #ifdef LFS_READWRITE
234 		(void)VOP_BWRITE(bp);
235 #else
236 		if (ioflag & IO_SYNC)
237 			(void)bwrite(bp);
238 		else if (xfersize + blkoffset == fs->fs_bsize)
239 			if (doclusterwrite)
240 				cluster_write(bp, ip->i_size);
241 			else {
242 				bp->b_flags |= B_AGE;
243 				bawrite(bp);
244 			}
245 		else
246 			bdwrite(bp);
247 #endif
248 		if (error || xfersize == 0)
249 			break;
250 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
251 	}
252 	/*
253 	 * If we successfully wrote any data, and we are not the superuser
254 	 * we clear the setuid and setgid bits as a precaution against
255 	 * tampering.
256 	 */
257 	if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
258 		ip->i_mode &= ~(ISUID | ISGID);
259 	if (error) {
260 		if (ioflag & IO_UNIT) {
261 			(void)VOP_TRUNCATE(vp, osize,
262 			    ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
263 			uio->uio_offset -= resid - uio->uio_resid;
264 			uio->uio_resid = resid;
265 		}
266 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC))
267 		error = VOP_UPDATE(vp, &time, &time, 1);
268 	return (error);
269 }
270