xref: /openbsd-src/sys/ufs/ext2fs/ext2fs_readwrite.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: ext2fs_readwrite.c,v 1.32 2014/07/13 13:28:26 pelikan Exp $	*/
2 /*	$NetBSD: ext2fs_readwrite.c,v 1.16 2001/02/27 04:37:47 chs Exp $	*/
3 
4 /*-
5  * Copyright (c) 1997 Manuel Bouyer.
6  * Copyright (c) 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *	@(#)ufs_readwrite.c	8.8 (Berkeley) 8/4/94
34  * Modified for ext2fs by Manuel Bouyer.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/resourcevar.h>
40 #include <sys/kernel.h>
41 #include <sys/file.h>
42 #include <sys/stat.h>
43 #include <sys/buf.h>
44 #include <sys/proc.h>
45 #include <sys/conf.h>
46 #include <sys/mount.h>
47 #include <sys/vnode.h>
48 #include <sys/malloc.h>
49 #include <sys/signalvar.h>
50 
51 #include <ufs/ufs/quota.h>
52 #include <ufs/ufs/ufsmount.h>
53 #include <ufs/ufs/inode.h>
54 #include <ufs/ext2fs/ext2fs.h>
55 #include <ufs/ext2fs/ext2fs_extern.h>
56 
57 
58 static int	ext2_ind_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *);
59 static int	ext4_ext_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *);
60 
61 #define doclusterread 0 /* XXX underway */
62 #define doclusterwrite 0
63 
64 /*
65  * Vnode op for reading.
66  */
67 /* ARGSUSED */
68 int
69 ext2fs_read(void *v)
70 {
71 	struct vop_read_args *ap = v;
72 	struct vnode *vp;
73 	struct inode *ip;
74 	struct uio *uio;
75 	struct m_ext2fs *fs;
76 
77 	vp = ap->a_vp;
78 	ip = VTOI(vp);
79 	uio = ap->a_uio;
80 	fs = ip->i_e2fs;
81 
82 	if (ip->i_e2fs_flags & EXT4_EXTENTS)
83 		return ext4_ext_read(vp, ip, fs, uio);
84 	else
85 		return ext2_ind_read(vp, ip, fs, uio);
86 }
87 
88 static int
89 ext2_ind_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs,
90     struct uio *uio)
91 {
92 	struct buf *bp;
93 	daddr_t lbn, nextlbn;
94 	off_t bytesinfile;
95 	long size, xfersize, blkoffset;
96 	int error;
97 
98 #ifdef DIAGNOSTIC
99 	if (uio->uio_rw != UIO_READ)
100 		panic("%s: mode", "ext2fs_read");
101 
102 	if (vp->v_type == VLNK) {
103 		if ((int)ext2fs_size(ip) < vp->v_mount->mnt_maxsymlinklen ||
104 		    (vp->v_mount->mnt_maxsymlinklen == 0 &&
105 		    ip->i_e2fs_nblock == 0))
106 			panic("%s: short symlink", "ext2fs_read");
107 	} else if (vp->v_type != VREG && vp->v_type != VDIR)
108 		panic("%s: type %d", "ext2fs_read", vp->v_type);
109 #endif
110 	if (e2fs_overflow(fs, 0, uio->uio_offset))
111 		return (EFBIG);
112 	if (uio->uio_resid == 0)
113 		return (0);
114 
115 	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
116 		if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0)
117 			break;
118 		lbn = lblkno(fs, uio->uio_offset);
119 		nextlbn = lbn + 1;
120 		size = fs->e2fs_bsize;
121 		blkoffset = blkoff(fs, uio->uio_offset);
122 		xfersize = fs->e2fs_bsize - blkoffset;
123 		if (uio->uio_resid < xfersize)
124 			xfersize = uio->uio_resid;
125 		if (bytesinfile < xfersize)
126 			xfersize = bytesinfile;
127 
128 		if (lblktosize(fs, nextlbn) >= ext2fs_size(ip))
129 			error = bread(vp, lbn, size, &bp);
130 		else if (lbn - 1 == ip->i_ci.ci_lastr) {
131 			int nextsize = fs->e2fs_bsize;
132 			error = breadn(vp, lbn, size, &nextlbn, &nextsize,
133 			    1, &bp);
134 		} else
135 			error = bread(vp, lbn, size, &bp);
136 		if (error)
137 			break;
138 		ip->i_ci.ci_lastr = lbn;
139 
140 		/*
141 		 * We should only get non-zero b_resid when an I/O error
142 		 * has occurred, which should cause us to break above.
143 		 * However, if the short read did not cause an error,
144 		 * then we want to ensure that we do not uiomove bad
145 		 * or uninitialized data.
146 		 */
147 		size -= bp->b_resid;
148 		if (size < xfersize) {
149 			if (size == 0)
150 				break;
151 			xfersize = size;
152 		}
153 		error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio);
154 		if (error)
155 			break;
156 		brelse(bp);
157 	}
158 	if (bp != NULL)
159 		brelse(bp);
160 
161 	if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) {
162 		ip->i_flag |= IN_ACCESS;
163 	}
164 	return (error);
165 }
166 
167 int
168 ext4_ext_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, struct uio *uio)
169 {
170 	struct ext4_extent_path path;
171 	struct ext4_extent nex, *ep;
172 	struct buf *bp;
173 	size_t orig_resid;
174 	daddr_t lbn, pos;
175 	off_t bytesinfile;
176 	long size, xfersize, blkoffset;
177 	int error, cache_type;
178 
179 	memset(&path, 0, sizeof path);
180 
181 	orig_resid = uio->uio_resid;
182 	if (orig_resid == 0)
183 		return (0);
184 
185 	if (e2fs_overflow(fs, 0, uio->uio_offset))
186 		return (EFBIG);
187 
188 	while (uio->uio_resid > 0) {
189 		if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0)
190 			break;
191 		lbn = lblkno(fs, uio->uio_offset);
192 		size = fs->e2fs_bsize;
193 		blkoffset = blkoff(fs, uio->uio_offset);
194 
195 		xfersize = fs->e2fs_fsize - blkoffset;
196 		xfersize = MIN(xfersize, uio->uio_resid);
197 		xfersize = MIN(xfersize, bytesinfile);
198 
199 		cache_type = ext4_ext_in_cache(ip, lbn, &nex);
200 		switch (cache_type) {
201 		case EXT4_EXT_CACHE_NO:
202 			ext4_ext_find_extent(fs, ip, lbn, &path);
203 			if ((ep = path.ep_ext) == NULL)
204 				return (EIO);
205 			ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN);
206 
207 			pos = lbn - ep->e_blk + (((daddr_t) ep->e_start_hi << 32) | ep->e_start_lo);
208 			if (path.ep_bp != NULL) {
209 				brelse(path.ep_bp);
210 				path.ep_bp = NULL;
211 			}
212 			break;
213 		case EXT4_EXT_CACHE_GAP:
214 			/* block has not been allocated yet */
215 			return (0);
216 		case EXT4_EXT_CACHE_IN:
217 			pos = lbn - nex.e_blk + (((daddr_t) nex.e_start_hi << 32) | nex.e_start_lo);
218 			break;
219 		}
220 		error = bread(ip->i_devvp, fsbtodb(fs, pos), size, &bp);
221 		if (error) {
222 			brelse(bp);
223 			return (error);
224 		}
225 		size -= bp->b_resid;
226 		if (size < xfersize) {
227 			if (size == 0) {
228 				brelse(bp);
229 				break;
230 			}
231 			xfersize = size;
232 		}
233 		error = uiomove(bp->b_data + blkoffset, xfersize, uio);
234 		brelse(bp);
235 		if (error)
236 			return (error);
237 	}
238 	return (0);
239 }
240 
241 /*
242  * Vnode op for writing.
243  */
244 int
245 ext2fs_write(void *v)
246 {
247 	struct vop_write_args *ap = v;
248 	struct vnode *vp;
249 	struct uio *uio;
250 	struct inode *ip;
251 	struct m_ext2fs *fs;
252 	struct buf *bp;
253 	int32_t lbn;
254 	off_t osize;
255 	int blkoffset, error, flags, ioflag, size, xfersize;
256 	ssize_t resid, overrun;
257 
258 	ioflag = ap->a_ioflag;
259 	uio = ap->a_uio;
260 	vp = ap->a_vp;
261 	ip = VTOI(vp);
262 
263 #ifdef DIAGNOSTIC
264 	if (uio->uio_rw != UIO_WRITE)
265 		panic("%s: mode", "ext2fs_write");
266 #endif
267 
268 	/*
269 	 * If writing 0 bytes, succeed and do not change
270 	 * update time or file offset (standards compliance)
271 	 */
272 	if (uio->uio_resid == 0)
273 		return (0);
274 
275 	switch (vp->v_type) {
276 	case VREG:
277 		if (ioflag & IO_APPEND)
278 			uio->uio_offset = ext2fs_size(ip);
279 		if ((ip->i_e2fs_flags & EXT2_APPEND) &&
280 			uio->uio_offset != ext2fs_size(ip))
281 			return (EPERM);
282 		/* FALLTHROUGH */
283 	case VLNK:
284 		break;
285 	case VDIR:
286 		if ((ioflag & IO_SYNC) == 0)
287 			panic("%s: nonsync dir write", "ext2fs_write");
288 		break;
289 	default:
290 		panic("%s: type", "ext2fs_write");
291 	}
292 
293 	fs = ip->i_e2fs;
294 	if (e2fs_overflow(fs, uio->uio_resid, uio->uio_offset + uio->uio_resid))
295 		return (EFBIG);
296 
297 	/* do the filesize rlimit check */
298 	if ((error = vn_fsizechk(vp, uio, ioflag, &overrun)))
299 		return (error);
300 
301 	resid = uio->uio_resid;
302 	osize = ext2fs_size(ip);
303 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
304 
305 	for (error = 0; uio->uio_resid > 0;) {
306 		lbn = lblkno(fs, uio->uio_offset);
307 		blkoffset = blkoff(fs, uio->uio_offset);
308 		xfersize = fs->e2fs_bsize - blkoffset;
309 		if (uio->uio_resid < xfersize)
310 			xfersize = uio->uio_resid;
311 		if (fs->e2fs_bsize > xfersize)
312 			flags |= B_CLRBUF;
313 		else
314 			flags &= ~B_CLRBUF;
315 
316 		error = ext2fs_buf_alloc(ip,
317 			lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
318 		if (error)
319 			break;
320 		if (uio->uio_offset + xfersize > ext2fs_size(ip)) {
321 			error = ext2fs_setsize(ip, uio->uio_offset + xfersize);
322 			if (error)
323 				break;
324 			uvm_vnp_setsize(vp, ip->i_e2fs_size);
325 		}
326 		uvm_vnp_uncache(vp);
327 
328 		size = fs->e2fs_bsize - bp->b_resid;
329 		if (size < xfersize)
330 			xfersize = size;
331 
332 		error =
333 			uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
334 #if 0
335 		if (ioflag & IO_NOCACHE)
336 			bp->b_flags |= B_NOCACHE;
337 #endif
338 		if (ioflag & IO_SYNC)
339 			(void)bwrite(bp);
340 		else if (xfersize + blkoffset == fs->e2fs_bsize) {
341 			if (doclusterwrite)
342 				cluster_write(bp, &ip->i_ci, ext2fs_size(ip));
343 			else
344 				bawrite(bp);
345 		} else
346 			bdwrite(bp);
347 		if (error || xfersize == 0)
348 			break;
349 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
350 	}
351 	/*
352 	 * If we successfully wrote any data, and we are not the superuser
353 	 * we clear the setuid and setgid bits as a precaution against
354 	 * tampering.
355 	 */
356 	if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
357 		ip->i_e2fs_mode &= ~(ISUID | ISGID);
358 	if (error) {
359 		if (ioflag & IO_UNIT) {
360 			(void)ext2fs_truncate(ip, osize,
361 				ioflag & IO_SYNC, ap->a_cred);
362 			uio->uio_offset -= resid - uio->uio_resid;
363 			uio->uio_resid = resid;
364 		}
365 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
366 		error = ext2fs_update(ip, 1);
367 	}
368 	/* correct the result for writes clamped by vn_fsizechk() */
369 	uio->uio_resid += overrun;
370 	return (error);
371 }
372