1 | /* $NetBSD: ext2fs_balloc.c,v 1.41 2016/08/13 07:40:10 christos Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1982, 1986, 1989, 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 | * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93 |
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 | * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93 |
59 | * Modified for ext2fs by Manuel Bouyer. |
60 | */ |
61 | |
62 | #include <sys/cdefs.h> |
63 | __KERNEL_RCSID(0, "$NetBSD: ext2fs_balloc.c,v 1.41 2016/08/13 07:40:10 christos Exp $" ); |
64 | |
65 | #if defined(_KERNEL_OPT) |
66 | #include "opt_uvmhist.h" |
67 | #endif |
68 | |
69 | #include <sys/param.h> |
70 | #include <sys/systm.h> |
71 | #include <sys/buf.h> |
72 | #include <sys/proc.h> |
73 | #include <sys/file.h> |
74 | #include <sys/vnode.h> |
75 | #include <sys/mount.h> |
76 | #include <sys/kauth.h> |
77 | |
78 | #include <uvm/uvm.h> |
79 | |
80 | #include <ufs/ufs/inode.h> |
81 | #include <ufs/ufs/ufs_extern.h> |
82 | |
83 | #include <ufs/ext2fs/ext2fs.h> |
84 | #include <ufs/ext2fs/ext2fs_extern.h> |
85 | |
86 | /* |
87 | * Balloc defines the structure of file system storage |
88 | * by allocating the physical blocks on a device given |
89 | * the inode and the logical block number in a file. |
90 | */ |
91 | int |
92 | ext2fs_balloc(struct inode *ip, daddr_t bn, int size, |
93 | kauth_cred_t cred, struct buf **bpp, int flags) |
94 | { |
95 | struct m_ext2fs *fs; |
96 | daddr_t nb; |
97 | struct buf *bp, *nbp; |
98 | struct vnode *vp = ITOV(ip); |
99 | struct indir indirs[EXT2FS_NIADDR + 2]; |
100 | daddr_t newb, lbn, pref; |
101 | int32_t *bap; /* XXX ondisk32 */ |
102 | int num, i, error; |
103 | u_int deallocated; |
104 | daddr_t *blkp, *allocblk, allociblk[EXT2FS_NIADDR + 1]; |
105 | int32_t *allocib; /* XXX ondisk32 */ |
106 | int unwindidx = -1; |
107 | UVMHIST_FUNC("ext2fs_balloc" ); UVMHIST_CALLED(ubchist); |
108 | |
109 | UVMHIST_LOG(ubchist, "bn 0x%x" , bn, 0, 0, 0); |
110 | |
111 | if (bpp != NULL) { |
112 | *bpp = NULL; |
113 | } |
114 | if (bn < 0) |
115 | return EFBIG; |
116 | fs = ip->i_e2fs; |
117 | lbn = bn; |
118 | |
119 | /* |
120 | * The first EXT2FS_NDADDR blocks are direct blocks |
121 | */ |
122 | if (bn < EXT2FS_NDADDR) { |
123 | /* XXX ondisk32 */ |
124 | nb = fs2h32(ip->i_e2fs_blocks[bn]); |
125 | if (nb != 0) { |
126 | |
127 | /* |
128 | * the block is already allocated, just read it. |
129 | */ |
130 | |
131 | if (bpp != NULL) { |
132 | error = bread(vp, bn, fs->e2fs_bsize, |
133 | B_MODIFY, &bp); |
134 | if (error) { |
135 | return error; |
136 | } |
137 | *bpp = bp; |
138 | } |
139 | return 0; |
140 | } |
141 | |
142 | /* |
143 | * allocate a new direct block. |
144 | */ |
145 | |
146 | error = ext2fs_alloc(ip, bn, |
147 | ext2fs_blkpref(ip, bn, bn, &ip->i_e2fs_blocks[0]), |
148 | cred, &newb); |
149 | if (error) |
150 | return error; |
151 | ip->i_e2fs_last_lblk = lbn; |
152 | ip->i_e2fs_last_blk = newb; |
153 | /* XXX ondisk32 */ |
154 | ip->i_e2fs_blocks[bn] = h2fs32((int32_t)newb); |
155 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
156 | if (bpp != NULL) { |
157 | bp = getblk(vp, bn, fs->e2fs_bsize, 0, 0); |
158 | bp->b_blkno = EXT2_FSBTODB(fs, newb); |
159 | if (flags & B_CLRBUF) |
160 | clrbuf(bp); |
161 | *bpp = bp; |
162 | } |
163 | return 0; |
164 | } |
165 | /* |
166 | * Determine the number of levels of indirection. |
167 | */ |
168 | pref = 0; |
169 | if ((error = ufs_getlbns(vp, bn, indirs, &num)) != 0) |
170 | return error; |
171 | #ifdef DIAGNOSTIC |
172 | if (num < 1) |
173 | panic("%s: ufs_getlbns returned indirect block\n" , __func__); |
174 | #endif |
175 | /* |
176 | * Fetch the first indirect block allocating if necessary. |
177 | */ |
178 | --num; |
179 | /* XXX ondisk32 */ |
180 | nb = fs2h32(ip->i_e2fs_blocks[EXT2FS_NDADDR + indirs[0].in_off]); |
181 | allocib = NULL; |
182 | allocblk = allociblk; |
183 | if (nb == 0) { |
184 | pref = ext2fs_blkpref(ip, lbn, 0, (int32_t *)0); |
185 | error = ext2fs_alloc(ip, lbn, pref, cred, &newb); |
186 | if (error) |
187 | return error; |
188 | nb = newb; |
189 | *allocblk++ = nb; |
190 | ip->i_e2fs_last_blk = newb; |
191 | bp = getblk(vp, indirs[1].in_lbn, fs->e2fs_bsize, 0, 0); |
192 | bp->b_blkno = EXT2_FSBTODB(fs, newb); |
193 | clrbuf(bp); |
194 | /* |
195 | * Write synchronously so that indirect blocks |
196 | * never point at garbage. |
197 | */ |
198 | if ((error = bwrite(bp)) != 0) |
199 | goto fail; |
200 | unwindidx = 0; |
201 | allocib = &ip->i_e2fs_blocks[EXT2FS_NDADDR + indirs[0].in_off]; |
202 | /* XXX ondisk32 */ |
203 | *allocib = h2fs32((int32_t)newb); |
204 | ip->i_flag |= IN_CHANGE | IN_UPDATE; |
205 | } |
206 | /* |
207 | * Fetch through the indirect blocks, allocating as necessary. |
208 | */ |
209 | for (i = 1;;) { |
210 | error = bread(vp, |
211 | indirs[i].in_lbn, (int)fs->e2fs_bsize, 0, &bp); |
212 | if (error) { |
213 | goto fail; |
214 | } |
215 | bap = (int32_t *)bp->b_data; /* XXX ondisk32 */ |
216 | nb = fs2h32(bap[indirs[i].in_off]); |
217 | if (i == num) |
218 | break; |
219 | i++; |
220 | if (nb != 0) { |
221 | brelse(bp, 0); |
222 | continue; |
223 | } |
224 | pref = ext2fs_blkpref(ip, lbn, 0, (int32_t *)0); |
225 | error = ext2fs_alloc(ip, lbn, pref, cred, &newb); |
226 | if (error) { |
227 | brelse(bp, 0); |
228 | goto fail; |
229 | } |
230 | nb = newb; |
231 | *allocblk++ = nb; |
232 | ip->i_e2fs_last_blk = newb; |
233 | nbp = getblk(vp, indirs[i].in_lbn, fs->e2fs_bsize, 0, 0); |
234 | nbp->b_blkno = EXT2_FSBTODB(fs, nb); |
235 | clrbuf(nbp); |
236 | /* |
237 | * Write synchronously so that indirect blocks |
238 | * never point at garbage. |
239 | */ |
240 | if ((error = bwrite(nbp)) != 0) { |
241 | brelse(bp, 0); |
242 | goto fail; |
243 | } |
244 | if (unwindidx < 0) |
245 | unwindidx = i - 1; |
246 | /* XXX ondisk32 */ |
247 | bap[indirs[i - 1].in_off] = h2fs32((int32_t)nb); |
248 | /* |
249 | * If required, write synchronously, otherwise use |
250 | * delayed write. |
251 | */ |
252 | if (flags & B_SYNC) { |
253 | bwrite(bp); |
254 | } else { |
255 | bdwrite(bp); |
256 | } |
257 | } |
258 | /* |
259 | * Get the data block, allocating if necessary. |
260 | */ |
261 | if (nb == 0) { |
262 | pref = ext2fs_blkpref(ip, lbn, indirs[num].in_off, &bap[0]); |
263 | error = ext2fs_alloc(ip, lbn, pref, cred, &newb); |
264 | if (error) { |
265 | brelse(bp, 0); |
266 | goto fail; |
267 | } |
268 | nb = newb; |
269 | *allocblk++ = nb; |
270 | ip->i_e2fs_last_lblk = lbn; |
271 | ip->i_e2fs_last_blk = newb; |
272 | /* XXX ondisk32 */ |
273 | bap[indirs[num].in_off] = h2fs32((int32_t)nb); |
274 | /* |
275 | * If required, write synchronously, otherwise use |
276 | * delayed write. |
277 | */ |
278 | if (flags & B_SYNC) { |
279 | bwrite(bp); |
280 | } else { |
281 | bdwrite(bp); |
282 | } |
283 | if (bpp != NULL) { |
284 | nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0); |
285 | nbp->b_blkno = EXT2_FSBTODB(fs, nb); |
286 | if (flags & B_CLRBUF) |
287 | clrbuf(nbp); |
288 | *bpp = nbp; |
289 | } |
290 | return 0; |
291 | } |
292 | brelse(bp, 0); |
293 | if (bpp != NULL) { |
294 | if (flags & B_CLRBUF) { |
295 | error = bread(vp, lbn, (int)fs->e2fs_bsize, |
296 | B_MODIFY, &nbp); |
297 | if (error) { |
298 | goto fail; |
299 | } |
300 | } else { |
301 | nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0); |
302 | nbp->b_blkno = EXT2_FSBTODB(fs, nb); |
303 | } |
304 | *bpp = nbp; |
305 | } |
306 | return 0; |
307 | fail: |
308 | /* |
309 | * If we have failed part way through block allocation, we |
310 | * have to deallocate any indirect blocks that we have allocated. |
311 | */ |
312 | for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) { |
313 | ext2fs_blkfree(ip, *blkp); |
314 | deallocated += fs->e2fs_bsize; |
315 | } |
316 | if (unwindidx >= 0) { |
317 | if (unwindidx == 0) { |
318 | *allocib = 0; |
319 | } else { |
320 | int r; |
321 | |
322 | r = bread(vp, indirs[unwindidx].in_lbn, |
323 | (int)fs->e2fs_bsize, B_MODIFY, &bp); |
324 | if (r) { |
325 | panic("%s: Could not unwind indirect block, " |
326 | "error %d" , __func__, r); |
327 | } else { |
328 | bap = (int32_t *)bp->b_data; /* XXX ondisk32 */ |
329 | bap[indirs[unwindidx].in_off] = 0; |
330 | if (flags & B_SYNC) |
331 | bwrite(bp); |
332 | else |
333 | bdwrite(bp); |
334 | } |
335 | } |
336 | for (i = unwindidx + 1; i <= num; i++) { |
337 | bp = getblk(vp, indirs[i].in_lbn, (int)fs->e2fs_bsize, |
338 | 0, 0); |
339 | brelse(bp, BC_INVAL); |
340 | } |
341 | } |
342 | if (deallocated) { |
343 | ext2fs_setnblock(ip, ext2fs_nblock(ip) - btodb(deallocated)); |
344 | ip->i_e2fs_flags |= IN_CHANGE | IN_UPDATE; |
345 | } |
346 | return error; |
347 | } |
348 | |
349 | int |
350 | ext2fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, |
351 | kauth_cred_t cred) |
352 | { |
353 | struct inode *ip = VTOI(vp); |
354 | struct m_ext2fs *fs = ip->i_e2fs; |
355 | int error, delta, bshift, bsize; |
356 | UVMHIST_FUNC("ext2fs_gop_alloc" ); UVMHIST_CALLED(ubchist); |
357 | |
358 | bshift = fs->e2fs_bshift; |
359 | bsize = 1 << bshift; |
360 | |
361 | delta = off & (bsize - 1); |
362 | off -= delta; |
363 | len += delta; |
364 | |
365 | while (len > 0) { |
366 | bsize = min(bsize, len); |
367 | UVMHIST_LOG(ubchist, "off 0x%x len 0x%x bsize 0x%x" , |
368 | off, len, bsize, 0); |
369 | |
370 | error = ext2fs_balloc(ip, ext2_lblkno(fs, off), bsize, cred, |
371 | NULL, flags); |
372 | if (error) { |
373 | UVMHIST_LOG(ubchist, "error %d" , error, 0, 0, 0); |
374 | return error; |
375 | } |
376 | |
377 | /* |
378 | * increase file size now, ext2fs_balloc() requires that |
379 | * EOF be up-to-date before each call. |
380 | */ |
381 | |
382 | if (ext2fs_size(ip) < off + bsize) { |
383 | UVMHIST_LOG(ubchist, "old 0x%lx%8lx new 0x%lx%8lx" , |
384 | /* Note that arguments are always cast to u_long. */ |
385 | ext2fs_size(ip) >> 32, |
386 | ext2fs_size(ip) & 0xffffffff, |
387 | (off + bsize) >> 32, |
388 | (off + bsize) & 0xffffffff); |
389 | error = ext2fs_setsize(ip, off + bsize); |
390 | if (error) { |
391 | UVMHIST_LOG(ubchist, "error %d" , |
392 | error, 0, 0, 0); |
393 | return error; |
394 | } |
395 | } |
396 | |
397 | off += bsize; |
398 | len -= bsize; |
399 | } |
400 | return 0; |
401 | } |
402 | |