xref: /netbsd-src/sys/ufs/ext2fs/ext2fs_readwrite.c (revision 27578b9aac214cc7796ead81dcc5427e79d5f2a0)
1 /*	$NetBSD: ext2fs_readwrite.c,v 1.17 2001/09/15 20:36:41 chs Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997 Manuel Bouyer.
5  * Copyright (c) 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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  *	@(#)ufs_readwrite.c	8.8 (Berkeley) 8/4/94
37  * Modified for ext2fs by Manuel Bouyer.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/resourcevar.h>
43 #include <sys/kernel.h>
44 #include <sys/file.h>
45 #include <sys/stat.h>
46 #include <sys/buf.h>
47 #include <sys/proc.h>
48 #include <sys/conf.h>
49 #include <sys/mount.h>
50 #include <sys/vnode.h>
51 #include <sys/malloc.h>
52 #include <sys/signalvar.h>
53 
54 #include <ufs/ufs/quota.h>
55 #include <ufs/ufs/inode.h>
56 #include <ufs/ext2fs/ext2fs.h>
57 #include <ufs/ext2fs/ext2fs_extern.h>
58 
59 
60 #define doclusterread 0 /* XXX underway */
61 #define doclusterwrite 0
62 
63 /*
64  * Vnode op for reading.
65  */
66 /* ARGSUSED */
67 int
68 ext2fs_read(v)
69 	void *v;
70 {
71 	struct vop_read_args /* {
72 		struct vnode *a_vp;
73 		struct uio *a_uio;
74 		int a_ioflag;
75 		struct ucred *a_cred;
76 	} */ *ap = v;
77 	struct vnode *vp;
78 	struct inode *ip;
79 	struct uio *uio;
80 	struct m_ext2fs *fs;
81 	struct buf *bp;
82 	void *win;
83 	vsize_t bytelen;
84 	ufs_daddr_t lbn, nextlbn;
85 	off_t bytesinfile;
86 	long size, xfersize, blkoffset;
87 	int error;
88 
89 	vp = ap->a_vp;
90 	ip = VTOI(vp);
91 	uio = ap->a_uio;
92 
93 #ifdef DIAGNOSTIC
94 	if (uio->uio_rw != UIO_READ)
95 		panic("%s: mode", "ext2fs_read");
96 
97 	if (vp->v_type == VLNK) {
98 		if ((int)ip->i_e2fs_size < vp->v_mount->mnt_maxsymlinklen ||
99 			(vp->v_mount->mnt_maxsymlinklen == 0 &&
100 			 ip->i_e2fs_nblock == 0))
101 			panic("%s: short symlink", "ext2fs_read");
102 	} else if (vp->v_type != VREG && vp->v_type != VDIR)
103 		panic("%s: type %d", "ext2fs_read", vp->v_type);
104 #endif
105 	fs = ip->i_e2fs;
106 	if ((u_int64_t)uio->uio_offset >
107 		((u_int64_t)0x80000000 * fs->e2fs_bsize - 1))
108 		return (EFBIG);
109 	if (uio->uio_resid == 0)
110 		return (0);
111 
112 	if (vp->v_type == VREG) {
113 		error = 0;
114 		while (uio->uio_resid > 0) {
115 			bytelen = MIN(ip->i_e2fs_size - uio->uio_offset,
116 			    uio->uio_resid);
117 
118 			if (bytelen == 0) {
119 				break;
120 			}
121 			win = ubc_alloc(&vp->v_uobj, uio->uio_offset,
122 			    &bytelen, UBC_READ);
123 			error = uiomove(win, bytelen, uio);
124 			ubc_release(win, 0);
125 			if (error) {
126 				break;
127 			}
128 		}
129 		goto out;
130 	}
131 
132 	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
133 		if ((bytesinfile = ip->i_e2fs_size - uio->uio_offset) <= 0)
134 			break;
135 		lbn = lblkno(fs, uio->uio_offset);
136 		nextlbn = lbn + 1;
137 		size = fs->e2fs_bsize;
138 		blkoffset = blkoff(fs, uio->uio_offset);
139 		xfersize = fs->e2fs_bsize - blkoffset;
140 		if (uio->uio_resid < xfersize)
141 			xfersize = uio->uio_resid;
142 		if (bytesinfile < xfersize)
143 			xfersize = bytesinfile;
144 
145 		if (lblktosize(fs, nextlbn) >= ip->i_e2fs_size)
146 			error = bread(vp, lbn, size, NOCRED, &bp);
147 		else {
148 			int nextsize = fs->e2fs_bsize;
149 			error = breadn(vp, lbn,
150 				size, &nextlbn, &nextsize, 1, NOCRED, &bp);
151 		}
152 		if (error)
153 			break;
154 
155 		/*
156 		 * We should only get non-zero b_resid when an I/O error
157 		 * has occurred, which should cause us to break above.
158 		 * However, if the short read did not cause an error,
159 		 * then we want to ensure that we do not uiomove bad
160 		 * or uninitialized data.
161 		 */
162 		size -= bp->b_resid;
163 		if (size < xfersize) {
164 			if (size == 0)
165 				break;
166 			xfersize = size;
167 		}
168 		error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio);
169 		if (error)
170 			break;
171 		brelse(bp);
172 	}
173 	if (bp != NULL)
174 		brelse(bp);
175 
176 out:
177 	if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) {
178 		ip->i_flag |= IN_ACCESS;
179 		if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)
180 			error = VOP_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
181 	}
182 	return (error);
183 }
184 
185 /*
186  * Vnode op for writing.
187  */
188 int
189 ext2fs_write(v)
190 	void *v;
191 {
192 	struct vop_write_args /* {
193 		struct vnode *a_vp;
194 		struct uio *a_uio;
195 		int a_ioflag;
196 		struct ucred *a_cred;
197 	} */ *ap = v;
198 	struct vnode *vp;
199 	struct uio *uio;
200 	struct inode *ip;
201 	struct m_ext2fs *fs;
202 	struct buf *bp;
203 	struct proc *p;
204 	ufs_daddr_t lbn;
205 	off_t osize;
206 	int blkoffset, error, flags, ioflag, resid, xfersize;
207 	vsize_t bytelen;
208 	void *win;
209 	off_t oldoff;
210 
211 	ioflag = ap->a_ioflag;
212 	uio = ap->a_uio;
213 	vp = ap->a_vp;
214 	ip = VTOI(vp);
215 	error = 0;
216 
217 #ifdef DIAGNOSTIC
218 	if (uio->uio_rw != UIO_WRITE)
219 		panic("%s: mode", "ext2fs_write");
220 #endif
221 
222 	switch (vp->v_type) {
223 	case VREG:
224 		if (ioflag & IO_APPEND)
225 			uio->uio_offset = ip->i_e2fs_size;
226 		if ((ip->i_e2fs_flags & EXT2_APPEND) &&
227 			uio->uio_offset != ip->i_e2fs_size)
228 			return (EPERM);
229 		/* FALLTHROUGH */
230 	case VLNK:
231 		break;
232 	case VDIR:
233 		if ((ioflag & IO_SYNC) == 0)
234 			panic("%s: nonsync dir write", "ext2fs_write");
235 		break;
236 	default:
237 		panic("%s: type", "ext2fs_write");
238 	}
239 
240 	fs = ip->i_e2fs;
241 	if (uio->uio_offset < 0 ||
242 		(u_int64_t)uio->uio_offset + uio->uio_resid >
243 		((u_int64_t)0x80000000 * fs->e2fs_bsize - 1))
244 		return (EFBIG);
245 	/*
246 	 * Maybe this should be above the vnode op call, but so long as
247 	 * file servers have no limits, I don't think it matters.
248 	 */
249 	p = uio->uio_procp;
250 	if (vp->v_type == VREG && p &&
251 		uio->uio_offset + uio->uio_resid >
252 		p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
253 		psignal(p, SIGXFSZ);
254 		return (EFBIG);
255 	}
256 
257 	resid = uio->uio_resid;
258 	osize = ip->i_e2fs_size;
259 
260 	if (vp->v_type == VREG) {
261 		while (uio->uio_resid > 0) {
262 			oldoff = uio->uio_offset;
263 			blkoffset = blkoff(fs, uio->uio_offset);
264 			bytelen = MIN(fs->e2fs_bsize - blkoffset,
265 			    uio->uio_resid);
266 
267 			error = ext2fs_balloc_range(vp, uio->uio_offset,
268 			    bytelen, ap->a_cred, 0);
269 			if (error) {
270 				break;
271 			}
272 			win = ubc_alloc(&vp->v_uobj, uio->uio_offset,
273 			    &bytelen, UBC_WRITE);
274 			error = uiomove(win, bytelen, uio);
275 			ubc_release(win, 0);
276 			if (error) {
277 				break;
278 			}
279 
280 			/*
281 			 * flush what we just wrote if necessary.
282 			 * XXXUBC simplistic async flushing.
283 			 */
284 
285 			if (oldoff >> 16 != uio->uio_offset >> 16) {
286 				simple_lock(&vp->v_uobj.vmobjlock);
287 				error = vp->v_uobj.pgops->pgo_put(
288 				    &vp->v_uobj, (oldoff >> 16) << 16,
289 				    (uio->uio_offset >> 16) << 16, PGO_CLEANIT);
290 			}
291 		}
292 		if (error == 0 && ioflag & IO_SYNC) {
293 			simple_lock(&vp->v_uobj.vmobjlock);
294 			error = vp->v_uobj.pgops->pgo_put(&vp->v_uobj, oldoff,
295 			    oldoff + bytelen, PGO_CLEANIT|PGO_SYNCIO);
296 		}
297 
298 		goto out;
299 	}
300 
301 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
302 	for (error = 0; uio->uio_resid > 0;) {
303 		lbn = lblkno(fs, uio->uio_offset);
304 		blkoffset = blkoff(fs, uio->uio_offset);
305 		xfersize = MIN(fs->e2fs_bsize - blkoffset, uio->uio_resid);
306 		if (xfersize < fs->e2fs_bsize)
307 			flags |= B_CLRBUF;
308 		else
309 			flags &= ~B_CLRBUF;
310 		error = ext2fs_balloc(ip,
311 		    lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
312 		if (error)
313 			break;
314 		if (ip->i_e2fs_size < uio->uio_offset + xfersize) {
315 			ip->i_e2fs_size = uio->uio_offset + xfersize;
316 		}
317 		error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio);
318 		if (ioflag & IO_SYNC)
319 			(void)bwrite(bp);
320 		else if (xfersize + blkoffset == fs->e2fs_bsize)
321 			bawrite(bp);
322 		else
323 			bdwrite(bp);
324 		if (error || xfersize == 0)
325 			break;
326 	}
327 	/*
328 	 * If we successfully wrote any data, and we are not the superuser
329 	 * we clear the setuid and setgid bits as a precaution against
330 	 * tampering.
331 	 */
332 out:
333 	ip->i_flag |= IN_CHANGE | IN_UPDATE;
334 	if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
335 		ip->i_e2fs_mode &= ~(ISUID | ISGID);
336 	if (error) {
337 		(void) VOP_TRUNCATE(vp, osize, ioflag & IO_SYNC, ap->a_cred,
338 		    uio->uio_procp);
339 		uio->uio_offset -= resid - uio->uio_resid;
340 		uio->uio_resid = resid;
341 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC)
342 		error = VOP_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
343 	KASSERT(vp->v_size == ip->i_e2fs_size);
344 	return (error);
345 }
346