1 /*	$OpenBSD: ufs_bmap.c,v 1.37 2021/12/12 09:14:59 visa Exp $	*/
2 /*	$NetBSD: ufs_bmap.c,v 1.3 1996/02/09 22:36:00 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1989, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * (c) UNIX System Laboratories, Inc.
8  * All or some portions of this file are derived from material licensed
9  * to the University of California by American Telephone and Telegraph
10  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11  * the permission of UNIX System Laboratories, Inc.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)ufs_bmap.c	8.6 (Berkeley) 1/21/94
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/buf.h>
43 #include <sys/proc.h>
44 #include <sys/vnode.h>
45 #include <sys/mount.h>
46 #include <sys/specdev.h>
47 
48 #include <ufs/ufs/quota.h>
49 #include <ufs/ufs/inode.h>
50 #include <ufs/ufs/ufsmount.h>
51 #include <ufs/ufs/ufs_extern.h>
52 
53 /*
54  * Bmap converts a the logical block number of a file to its physical block
55  * number on the disk. The conversion is done by using the logical block
56  * number to index into the array of block pointers described by the dinode.
57  */
58 int
ufs_bmap(void * v)59 ufs_bmap(void *v)
60 {
61 	struct vop_bmap_args *ap = v;
62 
63 	/*
64 	 * Check for underlying vnode requests and ensure that logical
65 	 * to physical mapping is requested.
66 	 */
67 	if (ap->a_vpp != NULL)
68 		*ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
69 	if (ap->a_bnp == NULL)
70 		return (0);
71 
72 	return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
73 	    ap->a_runp));
74 }
75 
76 /*
77  * Indirect blocks are now on the vnode for the file.  They are given negative
78  * logical block numbers.  Indirect blocks are addressed by the negative
79  * address of the first data block to which they point.  Double indirect blocks
80  * are addressed by one less than the address of the first indirect block to
81  * which they point.  Triple indirect blocks are addressed by one less than
82  * the address of the first double indirect block to which they point.
83  *
84  * ufs_bmaparray does the bmap conversion, and if requested returns the
85  * array of logical blocks which must be traversed to get to a block.
86  * Each entry contains the offset into that block that gets you to the
87  * next block and the disk address of the block (if it is assigned).
88  */
89 int
ufs_bmaparray(struct vnode * vp,daddr_t bn,daddr_t * bnp,struct indir * ap,int * nump,int * runp)90 ufs_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, struct indir *ap,
91     int *nump, int *runp)
92 {
93 	struct inode *ip;
94 	struct buf *bp;
95 	struct ufsmount *ump;
96 	struct mount *mp;
97 	struct vnode *devvp;
98 	struct indir a[NIADDR+1], *xap;
99 	daddr_t daddr, metalbn;
100 	int error, maxrun = 0, num;
101 
102 	ip = VTOI(vp);
103 	mp = vp->v_mount;
104 	ump = VFSTOUFS(mp);
105 #ifdef DIAGNOSTIC
106 	if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL))
107 		panic("ufs_bmaparray: invalid arguments");
108 #endif
109 
110 	if (runp) {
111 		/*
112 		 * XXX
113 		 * If MAXBSIZE is the largest transfer the disks can handle,
114 		 * we probably want maxrun to be 1 block less so that we
115 		 * don't create a block larger than the device can handle.
116 		 */
117 		*runp = 0;
118 		maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1;
119 	}
120 
121 	xap = ap == NULL ? a : ap;
122 	if (!nump)
123 		nump = &num;
124 	if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0)
125 		return (error);
126 
127 	num = *nump;
128 	if (num == 0) {
129 		*bnp = blkptrtodb(ump, DIP(ip, db[bn]));
130 		if (*bnp == 0)
131 			*bnp = -1;
132 		else if (runp)
133 			for (++bn; bn < NDADDR && *runp < maxrun &&
134 			    is_sequential(ump, DIP(ip, db[bn - 1]),
135 			        DIP(ip, db[bn]));
136 			    ++bn, ++*runp);
137 		return (0);
138 	}
139 
140 
141 	/* Get disk address out of indirect block array */
142 	daddr = DIP(ip, ib[xap->in_off]);
143 
144 	devvp = VFSTOUFS(vp->v_mount)->um_devvp;
145 	for (bp = NULL, ++xap; --num; ++xap) {
146 		/*
147 		 * Exit the loop if there is no disk address assigned yet and
148 		 * the indirect block isn't in the cache, or if we were
149 		 * looking for an indirect block and we've found it.
150 		 */
151 
152 		metalbn = xap->in_lbn;
153 		if ((daddr == 0 && !incore(vp, metalbn)) || metalbn == bn)
154 			break;
155 		/*
156 		 * If we get here, we've either got the block in the cache
157 		 * or we have a disk address for it, go fetch it.
158 		 */
159 		if (bp)
160 			brelse(bp);
161 
162 		xap->in_exists = 1;
163 		bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, INFSLP);
164 		if (bp->b_flags & (B_DONE | B_DELWRI)) {
165 			;
166 		}
167 #ifdef DIAGNOSTIC
168 		else if (!daddr)
169 			panic("ufs_bmaparray: indirect block not in cache");
170 #endif
171 		else {
172 			bp->b_blkno = blkptrtodb(ump, daddr);
173 			bp->b_flags |= B_READ;
174 			bcstats.pendingreads++;
175 			bcstats.numreads++;
176 			VOP_STRATEGY(bp->b_vp, bp);
177 			curproc->p_ru.ru_inblock++;		/* XXX */
178 			if ((error = biowait(bp)) != 0) {
179 				brelse(bp);
180 				return (error);
181 			}
182 		}
183 
184 #ifdef FFS2
185 		if (ip->i_ump->um_fstype == UM_UFS2) {
186 			daddr = ((int64_t *)bp->b_data)[xap->in_off];
187 			if (num == 1 && daddr && runp)
188 				for (bn = xap->in_off + 1;
189 				    bn < MNINDIR(ump) && *runp < maxrun &&
190 				    is_sequential(ump,
191 					((int64_t *)bp->b_data)[bn - 1],
192 					((int64_t *)bp->b_data)[bn]);
193 				    ++bn, ++*runp);
194 
195                         continue;
196 		}
197 
198 #endif /* FFS2 */
199 
200 		daddr = ((int32_t *)bp->b_data)[xap->in_off];
201 		if (num == 1 && daddr && runp)
202 			for (bn = xap->in_off + 1;
203 			    bn < MNINDIR(ump) && *runp < maxrun &&
204 			    is_sequential(ump,
205 				((int32_t *)bp->b_data)[bn - 1],
206 				((int32_t *)bp->b_data)[bn]);
207 			    ++bn, ++*runp);
208 	}
209 	if (bp)
210 		brelse(bp);
211 
212 	daddr = blkptrtodb(ump, daddr);
213 	*bnp = daddr == 0 ? -1 : daddr;
214 	return (0);
215 }
216 
217 /*
218  * Create an array of logical block number/offset pairs which represent the
219  * path of indirect blocks required to access a data block.  The first "pair"
220  * contains the logical block number of the appropriate single, double or
221  * triple indirect block and the offset into the inode indirect block array.
222  * Note, the logical block number of the inode single/double/triple indirect
223  * block appears twice in the array, once with the offset into the i_ffs_ib and
224  * once with the offset into the page itself.
225  */
226 int
ufs_getlbns(struct vnode * vp,daddr_t bn,struct indir * ap,int * nump)227 ufs_getlbns(struct vnode *vp, daddr_t bn, struct indir *ap, int *nump)
228 {
229 	daddr_t metalbn, realbn;
230 	struct ufsmount *ump;
231 	int64_t blockcnt;
232 	int i, numlevels, off;
233 
234 	ump = VFSTOUFS(vp->v_mount);
235 	if (nump)
236 		*nump = 0;
237 	numlevels = 0;
238 	realbn = bn;
239 	if (bn < 0)
240 		bn = -bn;
241 
242 #ifdef DIAGNOSTIC
243 	if (realbn < 0 && realbn > -NDADDR) {
244 		panic ("ufs_getlbns: Invalid indirect block %lld specified",
245 		    (long long)realbn);
246 	}
247 #endif
248 
249 	/* The first NDADDR blocks are direct blocks. */
250 	if (bn < NDADDR)
251 		return (0);
252 
253 	/*
254 	 * Determine the number of levels of indirection.  After this loop
255 	 * is done, blockcnt indicates the number of data blocks possible
256 	 * at the given level of indirection, and NIADDR - i is the number
257 	 * of levels of indirection needed to locate the requested block.
258 	 */
259 	for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
260 		if (i == 0)
261 			return (EFBIG);
262 		blockcnt *= MNINDIR(ump);
263 		if (bn < blockcnt)
264 			break;
265 	}
266 
267 	/* Calculate the address of the first meta-block. */
268 	if (realbn >= 0)
269 		metalbn = -(realbn - bn + NIADDR - i);
270 	else
271 		metalbn = -(-realbn - bn + NIADDR - i);
272 
273 	/*
274 	 * At each iteration, off is the offset into the bap array which is
275 	 * an array of disk addresses at the current level of indirection.
276 	 * The logical block number and the offset in that block are stored
277 	 * into the argument array.
278 	 */
279 	ap->in_lbn = metalbn;
280 	ap->in_off = off = NIADDR - i;
281 	ap->in_exists = 0;
282 	ap++;
283 	for (++numlevels; i <= NIADDR; i++) {
284 		/* If searching for a meta-data block, quit when found. */
285 		if (metalbn == realbn)
286 			break;
287 
288 		blockcnt /= MNINDIR(ump);
289 		off = (bn / blockcnt) % MNINDIR(ump);
290 
291 		++numlevels;
292 		ap->in_lbn = metalbn;
293 		ap->in_off = off;
294 		ap->in_exists = 0;
295 		++ap;
296 
297 		metalbn -= -1 + off * blockcnt;
298 	}
299 #ifdef DIAGNOSTIC
300 	if (realbn < 0 && metalbn != realbn) {
301 		panic("ufs_getlbns: indirect block %lld not found",
302 		    (long long)realbn);
303 	}
304 #endif
305 	if (nump)
306 		*nump = numlevels;
307 	return (0);
308 }
309