ext2fs_readwrite.c revision 1.64.8.2 1 /* $NetBSD: ext2fs_readwrite.c,v 1.64.8.2 2015/01/17 12:10:55 martin Exp $ */
2
3 /*-
4 * Copyright (c) 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94
32 * Modified for ext2fs by Manuel Bouyer.
33 */
34
35 /*-
36 * Copyright (c) 1997 Manuel Bouyer.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 *
58 * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94
59 * Modified for ext2fs by Manuel Bouyer.
60 */
61
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: ext2fs_readwrite.c,v 1.64.8.2 2015/01/17 12:10:55 martin Exp $");
64
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/resourcevar.h>
68 #include <sys/kernel.h>
69 #include <sys/file.h>
70 #include <sys/stat.h>
71 #include <sys/buf.h>
72 #include <sys/proc.h>
73 #include <sys/mount.h>
74 #include <sys/vnode.h>
75 #include <sys/signalvar.h>
76 #include <sys/kauth.h>
77
78 #include <ufs/ufs/inode.h>
79 #include <ufs/ufs/ufsmount.h>
80 #include <ufs/ufs/ufs_extern.h>
81 #include <ufs/ext2fs/ext2fs.h>
82 #include <ufs/ext2fs/ext2fs_extern.h>
83
84 /*
85 * Vnode op for reading.
86 */
87 /* ARGSUSED */
88 int
89 ext2fs_read(void *v)
90 {
91 struct vop_read_args /* {
92 struct vnode *a_vp;
93 struct uio *a_uio;
94 int a_ioflag;
95 kauth_cred_t a_cred;
96 } */ *ap = v;
97 struct vnode *vp;
98 struct inode *ip;
99 struct uio *uio;
100 struct m_ext2fs *fs;
101 struct buf *bp;
102 struct ufsmount *ump;
103 vsize_t bytelen;
104 daddr_t lbn, nextlbn;
105 off_t bytesinfile;
106 long size, xfersize, blkoffset;
107 int error;
108
109 vp = ap->a_vp;
110 ip = VTOI(vp);
111 ump = ip->i_ump;
112 uio = ap->a_uio;
113 error = 0;
114
115 #ifdef DIAGNOSTIC
116 if (uio->uio_rw != UIO_READ)
117 panic("%s: mode", "ext2fs_read");
118
119 if (vp->v_type == VLNK) {
120 if (ext2fs_size(ip) < ump->um_maxsymlinklen ||
121 (ump->um_maxsymlinklen == 0 && ext2fs_nblock(ip) == 0))
122 panic("%s: short symlink", "ext2fs_read");
123 } else if (vp->v_type != VREG && vp->v_type != VDIR)
124 panic("%s: type %d", "ext2fs_read", vp->v_type);
125 #endif
126 fs = ip->i_e2fs;
127 if ((uint64_t)uio->uio_offset > ump->um_maxfilesize)
128 return (EFBIG);
129 if (uio->uio_resid == 0)
130 return (0);
131 if (uio->uio_offset >= ext2fs_size(ip))
132 goto out;
133
134 if (vp->v_type == VREG) {
135 const int advice = IO_ADV_DECODE(ap->a_ioflag);
136
137 while (uio->uio_resid > 0) {
138 bytelen = MIN(ext2fs_size(ip) - uio->uio_offset,
139 uio->uio_resid);
140 if (bytelen == 0)
141 break;
142
143 error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
144 UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp));
145 if (error)
146 break;
147 }
148 goto out;
149 }
150
151 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
152 bytesinfile = ext2fs_size(ip) - uio->uio_offset;
153 if (bytesinfile <= 0)
154 break;
155 lbn = ext2_lblkno(fs, uio->uio_offset);
156 nextlbn = lbn + 1;
157 size = fs->e2fs_bsize;
158 blkoffset = ext2_blkoff(fs, uio->uio_offset);
159 xfersize = fs->e2fs_bsize - blkoffset;
160 if (uio->uio_resid < xfersize)
161 xfersize = uio->uio_resid;
162 if (bytesinfile < xfersize)
163 xfersize = bytesinfile;
164
165 if (ext2_lblktosize(fs, nextlbn) >= ext2fs_size(ip))
166 error = bread(vp, lbn, size, NOCRED, 0, &bp);
167 else {
168 int nextsize = fs->e2fs_bsize;
169 error = breadn(vp, lbn,
170 size, &nextlbn, &nextsize, 1, NOCRED, 0, &bp);
171 }
172 if (error)
173 break;
174
175 /*
176 * We should only get non-zero b_resid when an I/O error
177 * has occurred, which should cause us to break above.
178 * However, if the short read did not cause an error,
179 * then we want to ensure that we do not uiomove bad
180 * or uninitialized data.
181 */
182 size -= bp->b_resid;
183 if (size < xfersize) {
184 if (size == 0)
185 break;
186 xfersize = size;
187 }
188 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio);
189 if (error)
190 break;
191 brelse(bp, 0);
192 }
193 if (bp != NULL)
194 brelse(bp, 0);
195
196 out:
197 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) {
198 ip->i_flag |= IN_ACCESS;
199 if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)
200 error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT);
201 }
202 return (error);
203 }
204
205 /*
206 * Vnode op for writing.
207 */
208 int
209 ext2fs_write(void *v)
210 {
211 struct vop_write_args /* {
212 struct vnode *a_vp;
213 struct uio *a_uio;
214 int a_ioflag;
215 kauth_cred_t a_cred;
216 } */ *ap = v;
217 struct vnode *vp;
218 struct uio *uio;
219 struct inode *ip;
220 struct m_ext2fs *fs;
221 struct buf *bp;
222 struct ufsmount *ump;
223 daddr_t lbn;
224 off_t osize;
225 int blkoffset, error, flags, ioflag, resid, xfersize;
226 vsize_t bytelen;
227 off_t oldoff = 0; /* XXX */
228 bool async;
229 int extended = 0;
230 int advice;
231
232 ioflag = ap->a_ioflag;
233 advice = IO_ADV_DECODE(ioflag);
234 uio = ap->a_uio;
235 vp = ap->a_vp;
236 ip = VTOI(vp);
237 ump = ip->i_ump;
238 error = 0;
239
240 #ifdef DIAGNOSTIC
241 if (uio->uio_rw != UIO_WRITE)
242 panic("%s: mode", "ext2fs_write");
243 #endif
244
245 switch (vp->v_type) {
246 case VREG:
247 if (ioflag & IO_APPEND)
248 uio->uio_offset = ext2fs_size(ip);
249 if ((ip->i_e2fs_flags & EXT2_APPEND) &&
250 uio->uio_offset != ext2fs_size(ip))
251 return (EPERM);
252 /* FALLTHROUGH */
253 case VLNK:
254 break;
255 case VDIR:
256 if ((ioflag & IO_SYNC) == 0)
257 panic("%s: nonsync dir write", "ext2fs_write");
258 break;
259 default:
260 panic("%s: type", "ext2fs_write");
261 }
262
263 fs = ip->i_e2fs;
264 if (uio->uio_offset < 0 ||
265 (uint64_t)uio->uio_offset + uio->uio_resid > ump->um_maxfilesize)
266 return (EFBIG);
267 if (uio->uio_resid == 0)
268 return (0);
269
270 async = vp->v_mount->mnt_flag & MNT_ASYNC;
271 resid = uio->uio_resid;
272 osize = ext2fs_size(ip);
273
274 if (vp->v_type == VREG) {
275 while (uio->uio_resid > 0) {
276 oldoff = uio->uio_offset;
277 blkoffset = ext2_blkoff(fs, uio->uio_offset);
278 bytelen = MIN(fs->e2fs_bsize - blkoffset,
279 uio->uio_resid);
280
281 if (vp->v_size < oldoff + bytelen) {
282 uvm_vnp_setwritesize(vp, oldoff + bytelen);
283 }
284 error = ufs_balloc_range(vp, uio->uio_offset,
285 bytelen, ap->a_cred, 0);
286 if (error)
287 break;
288 error = ubc_uiomove(&vp->v_uobj, uio, bytelen, advice,
289 UBC_WRITE | UBC_UNMAP_FLAG(vp));
290 if (error)
291 break;
292
293 /*
294 * update UVM's notion of the size now that we've
295 * copied the data into the vnode's pages.
296 */
297
298 if (vp->v_size < uio->uio_offset) {
299 uvm_vnp_setsize(vp, uio->uio_offset);
300 extended = 1;
301 }
302
303 /*
304 * flush what we just wrote if necessary.
305 * XXXUBC simplistic async flushing.
306 */
307
308 if (!async && oldoff >> 16 != uio->uio_offset >> 16) {
309 mutex_enter(vp->v_interlock);
310 error = VOP_PUTPAGES(vp, (oldoff >> 16) << 16,
311 (uio->uio_offset >> 16) << 16,
312 PGO_CLEANIT | PGO_LAZY);
313 }
314 }
315 if (error == 0 && ioflag & IO_SYNC) {
316 mutex_enter(vp->v_interlock);
317 error = VOP_PUTPAGES(vp, trunc_page(oldoff),
318 round_page(ext2_blkroundup(fs, uio->uio_offset)),
319 PGO_CLEANIT | PGO_SYNCIO);
320 }
321
322 goto out;
323 }
324
325 flags = ioflag & IO_SYNC ? B_SYNC : 0;
326 for (error = 0; uio->uio_resid > 0;) {
327 lbn = ext2_lblkno(fs, uio->uio_offset);
328 blkoffset = ext2_blkoff(fs, uio->uio_offset);
329 xfersize = MIN(fs->e2fs_bsize - blkoffset, uio->uio_resid);
330 if (xfersize < fs->e2fs_bsize)
331 flags |= B_CLRBUF;
332 else
333 flags &= ~B_CLRBUF;
334 error = ext2fs_balloc(ip,
335 lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
336 if (error)
337 break;
338 if (ext2fs_size(ip) < uio->uio_offset + xfersize) {
339 error = ext2fs_setsize(ip, uio->uio_offset + xfersize);
340 if (error)
341 break;
342 }
343 error = uiomove((char *)bp->b_data + blkoffset, xfersize, uio);
344
345 /*
346 * update UVM's notion of the size now that we've
347 * copied the data into the vnode's pages.
348 */
349
350 if (vp->v_size < uio->uio_offset) {
351 uvm_vnp_setsize(vp, uio->uio_offset);
352 extended = 1;
353 }
354
355 if (ioflag & IO_SYNC)
356 (void)bwrite(bp);
357 else if (xfersize + blkoffset == fs->e2fs_bsize)
358 bawrite(bp);
359 else
360 bdwrite(bp);
361 if (error || xfersize == 0)
362 break;
363 }
364
365 /*
366 * If we successfully wrote any data, and we are not the superuser
367 * we clear the setuid and setgid bits as a precaution against
368 * tampering.
369 */
370
371 out:
372 ip->i_flag |= IN_CHANGE | IN_UPDATE;
373 if (vp->v_mount->mnt_flag & MNT_RELATIME)
374 ip->i_flag |= IN_ACCESS;
375 if (resid > uio->uio_resid && ap->a_cred) {
376 if (ip->i_e2fs_mode & ISUID) {
377 if (kauth_authorize_vnode(ap->a_cred,
378 KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0)
379 ip->i_e2fs_mode &= ISUID;
380 }
381
382 if (ip->i_e2fs_mode & ISGID) {
383 if (kauth_authorize_vnode(ap->a_cred,
384 KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0)
385 ip->i_e2fs_mode &= ~ISGID;
386 }
387 }
388 if (resid > uio->uio_resid)
389 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
390 if (error) {
391 (void) ext2fs_truncate(vp, osize, ioflag & IO_SYNC, ap->a_cred);
392 uio->uio_offset -= resid - uio->uio_resid;
393 uio->uio_resid = resid;
394 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC)
395 error = ext2fs_update(vp, NULL, NULL, UPDATE_WAIT);
396 KASSERT(vp->v_size == ext2fs_size(ip));
397 return (error);
398 }
399