1 | /* $NetBSD: ext2fs_bmap.c,v 1.30 2016/08/14 11:26:35 jdolecek Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1989, 1991, 1993 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * (c) UNIX System Laboratories, Inc. |
7 | * All or some portions of this file are derived from material licensed |
8 | * to the University of California by American Telephone and Telegraph |
9 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
10 | * the permission of UNIX System Laboratories, Inc. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * 3. 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_bmap.c 8.6 (Berkeley) 1/21/94 |
37 | * Modified for ext2fs by Manuel Bouyer. |
38 | */ |
39 | |
40 | /* |
41 | * Copyright (c) 1997 Manuel Bouyer. |
42 | * |
43 | * Redistribution and use in source and binary forms, with or without |
44 | * modification, are permitted provided that the following conditions |
45 | * are met: |
46 | * 1. Redistributions of source code must retain the above copyright |
47 | * notice, this list of conditions and the following disclaimer. |
48 | * 2. Redistributions in binary form must reproduce the above copyright |
49 | * notice, this list of conditions and the following disclaimer in the |
50 | * documentation and/or other materials provided with the distribution. |
51 | * |
52 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
53 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
54 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
55 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
56 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
57 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
58 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
59 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
60 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
61 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
62 | * |
63 | * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94 |
64 | * Modified for ext2fs by Manuel Bouyer. |
65 | */ |
66 | |
67 | #include <sys/cdefs.h> |
68 | __KERNEL_RCSID(0, "$NetBSD: ext2fs_bmap.c,v 1.30 2016/08/14 11:26:35 jdolecek Exp $" ); |
69 | |
70 | #include <sys/param.h> |
71 | #include <sys/systm.h> |
72 | #include <sys/buf.h> |
73 | #include <sys/proc.h> |
74 | #include <sys/vnode.h> |
75 | #include <sys/mount.h> |
76 | #include <sys/resourcevar.h> |
77 | #include <sys/trace.h> |
78 | |
79 | #include <miscfs/specfs/specdev.h> |
80 | |
81 | #include <ufs/ufs/inode.h> |
82 | #include <ufs/ufs/ufsmount.h> |
83 | #include <ufs/ufs/ufs_extern.h> |
84 | #include <ufs/ext2fs/ext2fs.h> |
85 | #include <ufs/ext2fs/ext2fs_extern.h> |
86 | |
87 | |
88 | static int ext4_bmapext(struct vnode *, int32_t, int64_t *, int *, int *); |
89 | static int ext2fs_bmaparray(struct vnode *, daddr_t, daddr_t *, struct indir *, |
90 | int *, int *); |
91 | |
92 | #define is_sequential(ump, a, b) ((b) == (a) + ump->um_seqinc) |
93 | |
94 | /* |
95 | * Bmap converts a the logical block number of a file to its physical block |
96 | * number on the disk. The conversion is done by using the logical block |
97 | * number to index into the array of block pointers described by the dinode. |
98 | */ |
99 | int |
100 | ext2fs_bmap(void *v) |
101 | { |
102 | struct vop_bmap_args /* { |
103 | struct vnode *a_vp; |
104 | daddr_t a_bn; |
105 | struct vnode **a_vpp; |
106 | daddr_t *a_bnp; |
107 | int *a_runp; |
108 | } */ *ap = v; |
109 | |
110 | /* |
111 | * Check for underlying vnode requests and ensure that logical |
112 | * to physical mapping is requested. |
113 | */ |
114 | if (ap->a_vpp != NULL) |
115 | *ap->a_vpp = VTOI(ap->a_vp)->i_devvp; |
116 | if (ap->a_bnp == NULL) |
117 | return 0; |
118 | |
119 | if (VTOI(ap->a_vp)->i_din.e2fs_din->e2di_flags & EXT2_EXTENTS) |
120 | return ext4_bmapext(ap->a_vp, ap->a_bn, ap->a_bnp, |
121 | ap->a_runp, NULL); |
122 | else |
123 | return ext2fs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, |
124 | NULL, ap->a_runp); |
125 | } |
126 | |
127 | /* |
128 | * Convert the logical block number of a file to its physical block number |
129 | * on the disk within ext4 extents. |
130 | */ |
131 | static int |
132 | ext4_bmapext(struct vnode *vp, int32_t bn, int64_t *bnp, int *runp, int *runb) |
133 | { |
134 | struct inode *ip; |
135 | struct m_ext2fs *fs; |
136 | struct ext4_extent *ep; |
137 | struct ext4_extent_path path = { .ep_bp = NULL }; |
138 | daddr_t lbn; |
139 | int error = 0; |
140 | |
141 | ip = VTOI(vp); |
142 | fs = ip->i_e2fs; |
143 | lbn = bn; |
144 | |
145 | /* XXX: Should not initialize on error? */ |
146 | if (runp != NULL) |
147 | *runp = 0; |
148 | |
149 | if (runb != NULL) |
150 | *runb = 0; |
151 | |
152 | ext4_ext_find_extent(fs, ip, lbn, &path); |
153 | if (path.ep_is_sparse) { |
154 | *bnp = -1; |
155 | if (runp != NULL) |
156 | *runp = path.ep_sparse_ext.e_len - |
157 | (lbn - path.ep_sparse_ext.e_blk) - 1; |
158 | if (runb != NULL) |
159 | *runb = lbn - path.ep_sparse_ext.e_blk; |
160 | } else { |
161 | if (path.ep_ext == NULL) { |
162 | error = EIO; |
163 | goto out; |
164 | } |
165 | ep = path.ep_ext; |
166 | *bnp = fsbtodb(fs, lbn - ep->e_blk |
167 | + (ep->e_start_lo | (daddr_t)ep->e_start_hi << 32)); |
168 | |
169 | if (*bnp == 0) |
170 | *bnp = -1; |
171 | |
172 | if (runp != NULL) |
173 | *runp = ep->e_len - (lbn - ep->e_blk) - 1; |
174 | if (runb != NULL) |
175 | *runb = lbn - ep->e_blk; |
176 | } |
177 | |
178 | out: |
179 | if (path.ep_bp != NULL) { |
180 | brelse(path.ep_bp, 0); |
181 | } |
182 | |
183 | return error; |
184 | } |
185 | |
186 | |
187 | |
188 | /* |
189 | * Indirect blocks are now on the vnode for the file. They are given negative |
190 | * logical block numbers. Indirect blocks are addressed by the negative |
191 | * address of the first data block to which they point. Double indirect blocks |
192 | * are addressed by one less than the address of the first indirect block to |
193 | * which they point. Triple indirect blocks are addressed by one less than |
194 | * the address of the first double indirect block to which they point. |
195 | * |
196 | * ext2fs_bmaparray does the bmap conversion, and if requested returns the |
197 | * array of logical blocks which must be traversed to get to a block. |
198 | * Each entry contains the offset into that block that gets you to the |
199 | * next block and the disk address of the block (if it is assigned). |
200 | */ |
201 | |
202 | int |
203 | ext2fs_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, struct indir *ap, |
204 | int *nump, int *runp) |
205 | { |
206 | struct inode *ip; |
207 | struct buf *bp, *cbp; |
208 | struct ufsmount *ump; |
209 | struct mount *mp; |
210 | struct indir a[EXT2FS_NIADDR+1], *xap; |
211 | daddr_t daddr; |
212 | daddr_t metalbn; |
213 | int error, maxrun = 0, num; |
214 | |
215 | ip = VTOI(vp); |
216 | mp = vp->v_mount; |
217 | ump = ip->i_ump; |
218 | #ifdef DIAGNOSTIC |
219 | if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL)) |
220 | panic("ext2fs_bmaparray: invalid arguments" ); |
221 | #endif |
222 | |
223 | if (runp) { |
224 | /* |
225 | * XXX |
226 | * If MAXBSIZE is the largest transfer the disks can handle, |
227 | * we probably want maxrun to be 1 block less so that we |
228 | * don't create a block larger than the device can handle. |
229 | */ |
230 | *runp = 0; |
231 | maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1; |
232 | } |
233 | |
234 | if (bn >= 0 && bn < EXT2FS_NDADDR) { |
235 | /* XXX ondisk32 */ |
236 | *bnp = blkptrtodb(ump, fs2h32(ip->i_e2fs_blocks[bn])); |
237 | if (*bnp == 0) |
238 | *bnp = -1; |
239 | else if (runp) |
240 | /* XXX ondisk32 */ |
241 | for (++bn; bn < EXT2FS_NDADDR && *runp < maxrun && |
242 | is_sequential(ump, (daddr_t)fs2h32(ip->i_e2fs_blocks[bn - 1]), |
243 | (daddr_t)fs2h32(ip->i_e2fs_blocks[bn])); |
244 | ++bn, ++*runp); |
245 | return 0; |
246 | } |
247 | |
248 | xap = ap == NULL ? a : ap; |
249 | if (!nump) |
250 | nump = # |
251 | if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0) |
252 | return error; |
253 | |
254 | num = *nump; |
255 | |
256 | /* Get disk address out of indirect block array */ |
257 | /* XXX ondisk32 */ |
258 | daddr = fs2h32(ip->i_e2fs_blocks[EXT2FS_NDADDR + xap->in_off]); |
259 | |
260 | #ifdef DIAGNOSTIC |
261 | if (num > EXT2FS_NIADDR + 1 || num < 1) { |
262 | printf("ext2fs_bmaparray: num=%d\n" , num); |
263 | panic("ext2fs_bmaparray: num" ); |
264 | } |
265 | #endif |
266 | for (bp = NULL, ++xap; --num; ++xap) { |
267 | /* |
268 | * Exit the loop if there is no disk address assigned yet and |
269 | * the indirect block isn't in the cache, or if we were |
270 | * looking for an indirect block and we've found it. |
271 | */ |
272 | |
273 | metalbn = xap->in_lbn; |
274 | if (metalbn == bn) |
275 | break; |
276 | if (daddr == 0) { |
277 | mutex_enter(&bufcache_lock); |
278 | cbp = incore(vp, metalbn); |
279 | mutex_exit(&bufcache_lock); |
280 | if (cbp == NULL) |
281 | break; |
282 | } |
283 | /* |
284 | * If we get here, we've either got the block in the cache |
285 | * or we have a disk address for it, go fetch it. |
286 | */ |
287 | if (bp) |
288 | brelse(bp, 0); |
289 | |
290 | xap->in_exists = 1; |
291 | bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0); |
292 | if (bp == NULL) { |
293 | |
294 | /* |
295 | * getblk() above returns NULL only iff we are |
296 | * pagedaemon. See the implementation of getblk |
297 | * for detail. |
298 | */ |
299 | |
300 | return ENOMEM; |
301 | } |
302 | if (bp->b_oflags & (BO_DONE | BO_DELWRI)) { |
303 | trace(TR_BREADHIT, pack(vp, size), metalbn); |
304 | } |
305 | #ifdef DIAGNOSTIC |
306 | else if (!daddr) |
307 | panic("ext2fs_bmaparry: indirect block not in cache" ); |
308 | #endif |
309 | else { |
310 | trace(TR_BREADMISS, pack(vp, size), metalbn); |
311 | bp->b_blkno = blkptrtodb(ump, daddr); |
312 | bp->b_flags |= B_READ; |
313 | VOP_STRATEGY(vp, bp); |
314 | curlwp->l_ru.ru_inblock++; /* XXX */ |
315 | if ((error = biowait(bp)) != 0) { |
316 | brelse(bp, 0); |
317 | return error; |
318 | } |
319 | } |
320 | |
321 | /* XXX ondisk32 */ |
322 | daddr = fs2h32(((int32_t *)bp->b_data)[xap->in_off]); |
323 | if (num == 1 && daddr && runp) |
324 | /* XXX ondisk32 */ |
325 | for (bn = xap->in_off + 1; |
326 | bn < MNINDIR(ump) && *runp < maxrun && |
327 | is_sequential(ump, ((int32_t *)bp->b_data)[bn - 1], |
328 | ((int32_t *)bp->b_data)[bn]); |
329 | ++bn, ++*runp); |
330 | } |
331 | if (bp) |
332 | brelse(bp, 0); |
333 | |
334 | daddr = blkptrtodb(ump, daddr); |
335 | *bnp = daddr == 0 ? -1 : daddr; |
336 | return 0; |
337 | } |
338 | |