xref: /openbsd-src/sys/ufs/ext2fs/ext2fs_readwrite.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1 /*	$OpenBSD: ext2fs_readwrite.c,v 1.46 2023/03/08 04:43:09 guenther 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/stat.h>
42 #include <sys/buf.h>
43 #include <sys/mount.h>
44 #include <sys/vnode.h>
45 #include <sys/malloc.h>
46 #include <sys/signalvar.h>
47 #include <sys/event.h>
48 
49 #include <ufs/ufs/quota.h>
50 #include <ufs/ufs/ufsmount.h>
51 #include <ufs/ufs/inode.h>
52 #include <ufs/ext2fs/ext2fs.h>
53 #include <ufs/ext2fs/ext2fs_extern.h>
54 
55 
56 static int	ext2_ind_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *);
57 static int	ext4_ext_read(struct vnode *, struct inode *, struct m_ext2fs *, struct uio *);
58 
59 /*
60  * Vnode op for reading.
61  */
62 int
ext2fs_read(void * v)63 ext2fs_read(void *v)
64 {
65 	struct vop_read_args *ap = v;
66 	struct vnode *vp;
67 	struct inode *ip;
68 	struct uio *uio;
69 	struct m_ext2fs *fs;
70 
71 	vp = ap->a_vp;
72 	ip = VTOI(vp);
73 	uio = ap->a_uio;
74 	fs = ip->i_e2fs;
75 
76 	if (ip->i_e2fs_flags & EXT4_EXTENTS)
77 		return ext4_ext_read(vp, ip, fs, uio);
78 	else
79 		return ext2_ind_read(vp, ip, fs, uio);
80 }
81 
82 static int
ext2_ind_read(struct vnode * vp,struct inode * ip,struct m_ext2fs * fs,struct uio * uio)83 ext2_ind_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs,
84     struct uio *uio)
85 {
86 	struct buf *bp;
87 	daddr_t lbn, nextlbn;
88 	off_t bytesinfile;
89 	int size, xfersize, blkoffset;
90 	int error;
91 
92 #ifdef DIAGNOSTIC
93 	if (uio->uio_rw != UIO_READ)
94 		panic("%s: mode", "ext2fs_read");
95 
96 	if (vp->v_type == VLNK) {
97 		if (ext2fs_size(ip) < EXT2_MAXSYMLINKLEN)
98 			panic("%s: short symlink", "ext2fs_read");
99 	} else if (vp->v_type != VREG && vp->v_type != VDIR)
100 		panic("%s: type %d", "ext2fs_read", vp->v_type);
101 #endif
102 	if (uio->uio_offset < 0)
103 		return (EINVAL);
104 	if (uio->uio_resid == 0)
105 		return (0);
106 
107 	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
108 		if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0)
109 			break;
110 		lbn = lblkno(fs, uio->uio_offset);
111 		nextlbn = lbn + 1;
112 		size = fs->e2fs_bsize;
113 		blkoffset = blkoff(fs, uio->uio_offset);
114 		xfersize = fs->e2fs_bsize - blkoffset;
115 		if (uio->uio_resid < xfersize)
116 			xfersize = uio->uio_resid;
117 		if (bytesinfile < xfersize)
118 			xfersize = bytesinfile;
119 
120 		if (lblktosize(fs, nextlbn) >= ext2fs_size(ip))
121 			error = bread(vp, lbn, size, &bp);
122 		else if (lbn - 1 == ip->i_ci.ci_lastr) {
123 			int nextsize = fs->e2fs_bsize;
124 			error = breadn(vp, lbn, size, &nextlbn, &nextsize,
125 			    1, &bp);
126 		} else
127 			error = bread(vp, lbn, size, &bp);
128 		if (error)
129 			break;
130 		ip->i_ci.ci_lastr = lbn;
131 
132 		/*
133 		 * We should only get non-zero b_resid when an I/O error
134 		 * has occurred, which should cause us to break above.
135 		 * However, if the short read did not cause an error,
136 		 * then we want to ensure that we do not uiomove bad
137 		 * or uninitialized data.
138 		 */
139 		size -= bp->b_resid;
140 		if (size < xfersize) {
141 			if (size == 0)
142 				break;
143 			xfersize = size;
144 		}
145 		error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio);
146 		if (error)
147 			break;
148 		brelse(bp);
149 	}
150 	if (bp != NULL)
151 		brelse(bp);
152 
153 	if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) {
154 		ip->i_flag |= IN_ACCESS;
155 	}
156 	return (error);
157 }
158 
159 int
ext4_ext_read(struct vnode * vp,struct inode * ip,struct m_ext2fs * fs,struct uio * uio)160 ext4_ext_read(struct vnode *vp, struct inode *ip, struct m_ext2fs *fs, struct uio *uio)
161 {
162 	struct ext4_extent_path path;
163 	struct ext4_extent nex, *ep;
164 	struct buf *bp;
165 	daddr_t lbn, pos;
166 	off_t bytesinfile;
167 	int size, xfersize, blkoffset;
168 	int error, cache_type;
169 
170 	memset(&path, 0, sizeof path);
171 
172 	if (uio->uio_offset < 0)
173 		return (EINVAL);
174 	if (uio->uio_resid == 0)
175 		return (0);
176 
177 	while (uio->uio_resid > 0) {
178 		if ((bytesinfile = ext2fs_size(ip) - uio->uio_offset) <= 0)
179 			break;
180 		lbn = lblkno(fs, uio->uio_offset);
181 		size = fs->e2fs_bsize;
182 		blkoffset = blkoff(fs, uio->uio_offset);
183 
184 		xfersize = fs->e2fs_fsize - blkoffset;
185 		xfersize = MIN(xfersize, uio->uio_resid);
186 		xfersize = MIN(xfersize, bytesinfile);
187 
188 		cache_type = ext4_ext_in_cache(ip, lbn, &nex);
189 		switch (cache_type) {
190 		case EXT4_EXT_CACHE_NO:
191 			ext4_ext_find_extent(fs, ip, lbn, &path);
192 			if ((ep = path.ep_ext) == NULL)
193 				return (EIO);
194 			ext4_ext_put_cache(ip, ep, EXT4_EXT_CACHE_IN);
195 
196 			pos = lbn - ep->e_blk + (((daddr_t) ep->e_start_hi << 32) | ep->e_start_lo);
197 			if (path.ep_bp != NULL) {
198 				brelse(path.ep_bp);
199 				path.ep_bp = NULL;
200 			}
201 			break;
202 		case EXT4_EXT_CACHE_GAP:
203 			/* block has not been allocated yet */
204 			return (0);
205 		case EXT4_EXT_CACHE_IN:
206 			pos = lbn - nex.e_blk + (((daddr_t) nex.e_start_hi << 32) | nex.e_start_lo);
207 			break;
208 		}
209 		error = bread(ip->i_devvp, fsbtodb(fs, pos), size, &bp);
210 		if (error) {
211 			brelse(bp);
212 			return (error);
213 		}
214 		size -= bp->b_resid;
215 		if (size < xfersize) {
216 			if (size == 0) {
217 				brelse(bp);
218 				break;
219 			}
220 			xfersize = size;
221 		}
222 		error = uiomove(bp->b_data + blkoffset, xfersize, uio);
223 		brelse(bp);
224 		if (error)
225 			return (error);
226 	}
227 	return (0);
228 }
229 
230 /*
231  * Vnode op for writing.
232  */
233 int
ext2fs_write(void * v)234 ext2fs_write(void *v)
235 {
236 	struct vop_write_args *ap = v;
237 	struct vnode *vp;
238 	struct uio *uio;
239 	struct inode *ip;
240 	struct m_ext2fs *fs;
241 	struct buf *bp;
242 	int32_t lbn;
243 	off_t osize;
244 	int blkoffset, error, extended, flags, ioflag, size, xfersize;
245 	size_t resid;
246 	ssize_t overrun;
247 
248 	extended = 0;
249 	ioflag = ap->a_ioflag;
250 	uio = ap->a_uio;
251 	vp = ap->a_vp;
252 	ip = VTOI(vp);
253 
254 #ifdef DIAGNOSTIC
255 	if (uio->uio_rw != UIO_WRITE)
256 		panic("%s: mode", "ext2fs_write");
257 #endif
258 
259 	/*
260 	 * If writing 0 bytes, succeed and do not change
261 	 * update time or file offset (standards compliance)
262 	 */
263 	if (uio->uio_resid == 0)
264 		return (0);
265 
266 	switch (vp->v_type) {
267 	case VREG:
268 		if (ioflag & IO_APPEND)
269 			uio->uio_offset = ext2fs_size(ip);
270 		if ((ip->i_e2fs_flags & EXT2_APPEND) &&
271 			uio->uio_offset != ext2fs_size(ip))
272 			return (EPERM);
273 		/* FALLTHROUGH */
274 	case VLNK:
275 		break;
276 	case VDIR:
277 		if ((ioflag & IO_SYNC) == 0)
278 			panic("%s: nonsync dir write", "ext2fs_write");
279 		break;
280 	default:
281 		panic("%s: type", "ext2fs_write");
282 	}
283 
284 	fs = ip->i_e2fs;
285 	if (e2fs_overflow(fs, uio->uio_resid, uio->uio_offset + uio->uio_resid))
286 		return (EFBIG);
287 
288 	/* do the filesize rlimit check */
289 	if ((error = vn_fsizechk(vp, uio, ioflag, &overrun)))
290 		return (error);
291 
292 	resid = uio->uio_resid;
293 	osize = ext2fs_size(ip);
294 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
295 
296 	for (error = 0; uio->uio_resid > 0;) {
297 		lbn = lblkno(fs, uio->uio_offset);
298 		blkoffset = blkoff(fs, uio->uio_offset);
299 		xfersize = fs->e2fs_bsize - blkoffset;
300 		if (uio->uio_resid < xfersize)
301 			xfersize = uio->uio_resid;
302 		if (fs->e2fs_bsize > xfersize)
303 			flags |= B_CLRBUF;
304 		else
305 			flags &= ~B_CLRBUF;
306 
307 		error = ext2fs_buf_alloc(ip,
308 			lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
309 		if (error)
310 			break;
311 		if (uio->uio_offset + xfersize > ext2fs_size(ip)) {
312 			error = ext2fs_setsize(ip, uio->uio_offset + xfersize);
313 			if (error)
314 				break;
315 			uvm_vnp_setsize(vp, ext2fs_size(ip));
316 			extended = 1;
317 		}
318 		uvm_vnp_uncache(vp);
319 
320 		size = fs->e2fs_bsize - bp->b_resid;
321 		if (size < xfersize)
322 			xfersize = size;
323 
324 		error = uiomove(bp->b_data + blkoffset, xfersize, uio);
325 		/*
326 		 * If the buffer is not already filled and we encounter an
327 		 * error while trying to fill it, we have to clear out any
328 		 * garbage data from the pages instantiated for the buffer.
329 		 * If we do not, a failed uiomove() during a write can leave
330 		 * the prior contents of the pages exposed to a userland mmap.
331 		 *
332 		 * Note that we don't need to clear buffers that were
333 		 * allocated with the B_CLRBUF flag set.
334 		 */
335 		if (error != 0 && !(flags & B_CLRBUF))
336 			memset(bp->b_data + blkoffset, 0, xfersize);
337 
338 		if (ioflag & IO_NOCACHE)
339 			bp->b_flags |= B_NOCACHE;
340 
341 		if (ioflag & IO_SYNC)
342 			(void)bwrite(bp);
343 		else if (xfersize + blkoffset == fs->e2fs_bsize)
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 (resid > uio->uio_resid)
359 		VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
360 	if (error) {
361 		if (ioflag & IO_UNIT) {
362 			(void)ext2fs_truncate(ip, osize,
363 				ioflag & IO_SYNC, ap->a_cred);
364 			uio->uio_offset -= resid - uio->uio_resid;
365 			uio->uio_resid = resid;
366 		}
367 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
368 		error = ext2fs_update(ip, 1);
369 	}
370 	/* correct the result for writes clamped by vn_fsizechk() */
371 	uio->uio_resid += overrun;
372 	return (error);
373 }
374