xref: /freebsd/sys/ufs/ufs/ufs_bmap.c (revision bab04ddf1fd4b7a77d1cfae4a67ededf1f35ee0d)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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 
37 #include <sys/systm.h>
38 #include <sys/bio.h>
39 #include <sys/buf.h>
40 #include <sys/proc.h>
41 #include <sys/rwlock.h>
42 #include <sys/vnode.h>
43 #include <sys/mount.h>
44 #include <sys/racct.h>
45 #include <sys/resourcevar.h>
46 #include <sys/sched.h>
47 #include <sys/sf_buf.h>
48 #include <sys/stat.h>
49 
50 #include <vm/vm.h>
51 #include <vm/vm_object.h>
52 #include <vm/vm_page.h>
53 #include <vm/vnode_pager.h>
54 
55 #include <ufs/ufs/extattr.h>
56 #include <ufs/ufs/quota.h>
57 #include <ufs/ufs/inode.h>
58 #include <ufs/ufs/ufsmount.h>
59 #include <ufs/ufs/ufs_extern.h>
60 
61 static ufs_lbn_t lbn_count(struct ufsmount *, int);
62 static int readindir(struct vnode *, ufs_lbn_t, ufs2_daddr_t, struct buf **);
63 
64 static int ufs_bmap_use_unmapped = 1;
65 
66 SYSCTL_INT(_vfs_ufs, OID_AUTO, bmap_use_unmapped, CTLFLAG_RWTUN,
67     &ufs_bmap_use_unmapped, 0, "UFS bmap uses unmapped bufs");
68 
69 /*
70  * Bmap converts the logical block number of a file to its physical block
71  * number on the disk. The conversion is done by using the logical block
72  * number to index into the array of block pointers described by the dinode.
73  */
74 int
ufs_bmap(struct vop_bmap_args * ap)75 ufs_bmap(
76 	struct vop_bmap_args /* {
77 		struct vnode *a_vp;
78 		daddr_t a_bn;
79 		struct bufobj **a_bop;
80 		daddr_t *a_bnp;
81 		int *a_runp;
82 		int *a_runb;
83 	} */ *ap)
84 {
85 	ufs2_daddr_t blkno;
86 	int error;
87 
88 	/*
89 	 * Check for underlying vnode requests and ensure that logical
90 	 * to physical mapping is requested.
91 	 */
92 	if (ap->a_bop != NULL)
93 		*ap->a_bop = &VFSTOUFS(ap->a_vp->v_mount)->um_devvp->v_bufobj;
94 	if (ap->a_bnp == NULL)
95 		return (0);
96 
97 	error = ufs_bmaparray(ap->a_vp, ap->a_bn, &blkno, NULL,
98 	    ap->a_runp, ap->a_runb);
99 	*ap->a_bnp = blkno;
100 	return (error);
101 }
102 
103 static int
readindir(struct vnode * vp,ufs_lbn_t lbn,ufs2_daddr_t daddr,struct buf ** bpp)104 readindir(struct vnode *vp,
105 	ufs_lbn_t lbn,
106 	ufs2_daddr_t daddr,
107 	struct buf **bpp)
108 {
109 	struct buf *bp;
110 	struct mount *mp;
111 	struct ufsmount *ump;
112 	struct inode *ip;
113 	int error, gbflags;
114 
115 	mp = vp->v_mount;
116 	ump = VFSTOUFS(mp);
117 	ip = VTOI(vp);
118 
119 	gbflags = !I_IS_UFS1(ip) && ufs_bmap_use_unmapped ? GB_UNMAPPED : 0;
120 	bp = getblk(vp, lbn, mp->mnt_stat.f_iosize, 0, 0, gbflags);
121 	if ((bp->b_flags & B_CACHE) == 0) {
122 		KASSERT(daddr != 0,
123 		    ("readindir: indirect block not in cache"));
124 
125 		bp->b_blkno = blkptrtodb(ump, daddr);
126 		bp->b_iocmd = BIO_READ;
127 		bp->b_flags &= ~B_INVAL;
128 		bp->b_ioflags &= ~BIO_ERROR;
129 		vfs_busy_pages(bp, 0);
130 		bp->b_iooffset = dbtob(bp->b_blkno);
131 		bstrategy(bp);
132 #ifdef RACCT
133 		if (racct_enable) {
134 			PROC_LOCK(curproc);
135 			racct_add_buf(curproc, bp, 0);
136 			PROC_UNLOCK(curproc);
137 		}
138 #endif
139 		curthread->td_ru.ru_inblock++;
140 		error = bufwait(bp);
141 		if (error != 0) {
142 			brelse(bp);
143 			return (error);
144 		}
145 	}
146 	*bpp = bp;
147 	return (0);
148 }
149 
150 /*
151  * Indirect blocks are now on the vnode for the file.  They are given negative
152  * logical block numbers.  Indirect blocks are addressed by the negative
153  * address of the first data block to which they point.  Double indirect blocks
154  * are addressed by one less than the address of the first indirect block to
155  * which they point.  Triple indirect blocks are addressed by one less than
156  * the address of the first double indirect block to which they point.
157  *
158  * ufs_bmaparray does the bmap conversion, and if requested returns the
159  * array of logical blocks which must be traversed to get to a block.
160  * Each entry contains the offset into that block that gets you to the
161  * next block and the disk address of the block (if it is assigned).
162  */
163 
164 static void *
ufs_bm_sf_get(struct buf * bp,int32_t pgidx,struct sf_buf ** sfp)165 ufs_bm_sf_get(struct buf *bp, int32_t pgidx, struct sf_buf **sfp)
166 {
167 	struct sf_buf *sf;
168 
169 	sched_pin();
170 	sf = sf_buf_alloc(bp->b_pages[pgidx], SFB_CPUPRIVATE);
171 	*sfp = sf;
172 	return (sf_buf_kva(sf));
173 }
174 
175 static void
ufs_bm_sf_put(struct sf_buf * sf)176 ufs_bm_sf_put(struct sf_buf *sf)
177 {
178 	sf_buf_free(sf);
179 	sched_unpin();
180 }
181 
182 int
ufs_bmaparray(struct vnode * vp,ufs2_daddr_t bn,ufs2_daddr_t * bnp,struct buf * nbp,int * runp,int * runb)183 ufs_bmaparray(struct vnode *vp,
184 	ufs2_daddr_t bn,
185 	ufs2_daddr_t *bnp,
186 	struct buf *nbp,
187 	int *runp,
188 	int *runb)
189 {
190 	struct inode *ip;
191 	struct buf *bp;
192 	struct ufsmount *ump;
193 	struct mount *mp;
194 	struct indir a[UFS_NIADDR+1], *ap;
195 	struct sf_buf *sf;
196 	ufs2_daddr_t daddr;
197 	ufs_lbn_t metalbn;
198 	int error, num, maxrun = 0;
199 	int *nump;
200 	ufs1_daddr_t *daddr1p;
201 	ufs2_daddr_t pgbn, daddrppg, prevdaddr, *daddr2p;
202 	int32_t daddrsz, boff, pgidx, pgoff;
203 	void *pgaddr;
204 	bool isseq;
205 
206 	ap = NULL;
207 	ip = VTOI(vp);
208 	mp = vp->v_mount;
209 	ump = VFSTOUFS(mp);
210 
211 	if (runp) {
212 		maxrun = mp->mnt_iosize_max / mp->mnt_stat.f_iosize - 1;
213 		*runp = 0;
214 	}
215 
216 	if (runb) {
217 		*runb = 0;
218 	}
219 
220 	ap = a;
221 	nump = &num;
222 	error = ufs_getlbns(vp, bn, ap, nump);
223 	if (error)
224 		return (error);
225 
226 	num = *nump;
227 	if (num == 0) {
228 		if (bn >= 0 && bn < UFS_NDADDR) {
229 			*bnp = blkptrtodb(ump, DIP(ip, i_db[bn]));
230 		} else if (bn < 0 && bn >= -UFS_NXADDR) {
231 			*bnp = blkptrtodb(ump, ip->i_din2->di_extb[-1 - bn]);
232 			if (*bnp == 0)
233 				*bnp = -1;
234 			if (nbp == NULL) {
235 				/* indirect block not found */
236 				return (EINVAL);
237 			}
238 			nbp->b_xflags |= BX_ALTDATA;
239 			return (0);
240 		} else {
241 			/* blkno out of range */
242 			return (EINVAL);
243 		}
244 		/*
245 		 * Since this is FFS independent code, we are out of
246 		 * scope for the definitions of BLK_NOCOPY and
247 		 * BLK_SNAP, but we do know that they will fall in
248 		 * the range 1..um_seqinc, so we use that test and
249 		 * return a request for a zeroed out buffer if attempts
250 		 * are made to read a BLK_NOCOPY or BLK_SNAP block.
251 		 */
252 		if (IS_SNAPSHOT(ip) && DIP(ip, i_db[bn]) > 0 &&
253 		    DIP(ip, i_db[bn]) < ump->um_seqinc) {
254 			*bnp = -1;
255 		} else if (*bnp == 0) {
256 			*bnp = IS_SNAPSHOT(ip) ? blkptrtodb(ump,
257 			    bn * ump->um_seqinc) : -1;
258 		} else if (runp) {
259 			ufs2_daddr_t bnb = bn;
260 			for (++bn; bn < UFS_NDADDR && *runp < maxrun &&
261 			    is_sequential(ump, DIP(ip, i_db[bn - 1]),
262 			    DIP(ip, i_db[bn]));
263 			    ++bn, ++*runp);
264 			bn = bnb;
265 			if (runb && (bn > 0)) {
266 				for (--bn; (bn >= 0) && (*runb < maxrun) &&
267 					is_sequential(ump, DIP(ip, i_db[bn]),
268 						DIP(ip, i_db[bn+1]));
269 						--bn, ++*runb);
270 			}
271 		}
272 		return (0);
273 	}
274 
275 	/* Get disk address out of indirect block array */
276 	daddr = DIP(ip, i_ib[ap->in_off]);
277 
278 	for (bp = NULL, ++ap; --num; ++ap) {
279 		/*
280 		 * Exit the loop if there is no disk address assigned yet and
281 		 * the indirect block isn't in the cache, or if we were
282 		 * looking for an indirect block and we've found it.
283 		 */
284 
285 		metalbn = ap->in_lbn;
286 		if ((daddr == 0 && !incore(&vp->v_bufobj, metalbn)) || metalbn == bn)
287 			break;
288 		/*
289 		 * If we get here, we've either got the block in the cache
290 		 * or we have a disk address for it, go fetch it.
291 		 */
292 		if (bp)
293 			bqrelse(bp);
294 		error = readindir(vp, metalbn, daddr, &bp);
295 		if (error != 0)
296 			return (error);
297 
298 		daddrsz = I_IS_UFS1(ip) ? sizeof(ufs1_daddr_t) : sizeof(ufs2_daddr_t);
299 		if (!buf_mapped(bp)) {
300 			boff = ap->in_off * daddrsz;
301 			pgidx = boff / PAGE_SIZE;
302 			pgoff = (boff & PAGE_MASK) / daddrsz;
303 			pgaddr = ufs_bm_sf_get(bp, pgidx, &sf);
304 			if (I_IS_UFS1(ip))
305 				daddr = ((ufs1_daddr_t *)pgaddr)[pgoff];
306 			else
307 				daddr = ((ufs2_daddr_t *)pgaddr)[pgoff];
308 			ufs_bm_sf_put(sf);
309 		} else {
310 			if (I_IS_UFS1(ip))
311 				daddr = ((ufs1_daddr_t *)bp->b_data)[ap->in_off];
312 			else
313 				daddr = ((ufs2_daddr_t *)bp->b_data)[ap->in_off];
314 		}
315 
316 		if ((error = UFS_CHECK_BLKNO(mp, ip->i_number, daddr,
317 		     mp->mnt_stat.f_iosize)) != 0) {
318 			bqrelse(bp);
319 			return (error);
320 		}
321 		if (num > 1 || daddr == 0 || runp == NULL)
322 			continue;
323 
324 		daddrppg = PAGE_SIZE / daddrsz;
325 		if (I_IS_UFS1(ip)) {
326 			if (!buf_mapped(bp)) {
327 				prevdaddr = daddr;
328 				isseq = true;
329 				for (bn = ap->in_off + 1;
330 				    bn < MNINDIR(ump) && *runp < maxrun && isseq; ) {
331 					boff = bn * daddrsz;
332 					pgidx = boff / PAGE_SIZE;
333 					pgoff = (boff & PAGE_MASK) / daddrsz;
334 					KASSERT(pgidx >= 0 && pgidx < bp->b_npages,
335 						("pgidx %d vs b_npages %d", pgidx, bp->b_npages));
336 					pgaddr = ufs_bm_sf_get(bp, pgidx, &sf);
337 					daddr1p = (ufs1_daddr_t *)pgaddr;
338 					for (pgbn = pgoff;
339 					     pgbn < daddrppg && *runp < maxrun &&
340 					     (isseq = is_sequential(ump, prevdaddr, daddr1p[pgbn]));
341 					     prevdaddr = daddr1p[pgbn], ++pgbn, ++bn, ++*runp);
342 					ufs_bm_sf_put(sf);
343 				}
344 				prevdaddr = daddr;
345 				bn = ap->in_off;
346 				if (runb && bn) {
347 					isseq = true;
348 					for (--bn; bn >= 0 && *runb < maxrun && isseq; ) {
349 						boff = bn * daddrsz;
350 						pgidx = boff / PAGE_SIZE;
351 						pgoff = (boff & PAGE_MASK) / daddrsz;
352 						KASSERT(pgidx >= 0 && pgidx < bp->b_npages,
353 							("pgidx %d vs b_npages %d", pgidx, bp->b_npages));
354 						pgaddr = ufs_bm_sf_get(bp, pgidx, &sf);
355 						daddr1p = (ufs1_daddr_t *)pgaddr;
356 						for (pgbn = pgoff; pgbn >= 0 && *runb < maxrun &&
357 						     (isseq = is_sequential(ump, daddr1p[pgbn], prevdaddr));
358 						     prevdaddr = daddr1p[pgbn], --pgbn, --bn, ++*runb);
359 						ufs_bm_sf_put(sf);
360 					}
361 				}
362 			} else {
363 				for (bn = ap->in_off + 1;
364 				    bn < MNINDIR(ump) && *runp < maxrun &&
365 				    is_sequential(ump,
366 				    ((ufs1_daddr_t *)bp->b_data)[bn - 1],
367 				    ((ufs1_daddr_t *)bp->b_data)[bn]);
368 				    ++bn, ++*runp);
369 				bn = ap->in_off;
370 				if (runb && bn) {
371 					for (--bn; bn >= 0 && *runb < maxrun &&
372 					    is_sequential(ump,
373 					    ((ufs1_daddr_t *)bp->b_data)[bn],
374 					    ((ufs1_daddr_t *)bp->b_data)[bn+1]);
375 					    --bn, ++*runb);
376 				}
377 			}
378 			continue;
379 		}
380 
381 		if (!buf_mapped(bp)) {
382 			prevdaddr = daddr;
383 			isseq = true;
384 			for (bn = ap->in_off + 1;
385 			    bn < MNINDIR(ump) && *runp < maxrun && isseq; ) {
386 				boff = bn * daddrsz;
387 				pgidx = boff / PAGE_SIZE;
388 				pgoff = (boff & PAGE_MASK) / daddrsz;
389 				KASSERT(pgidx >= 0 && pgidx < bp->b_npages,
390 					("pgidx %d vs b_npages %d", pgidx, bp->b_npages));
391 				pgaddr = ufs_bm_sf_get(bp, pgidx, &sf);
392 				daddr2p = (ufs2_daddr_t *)pgaddr;
393 				for (pgbn = pgoff;
394 				     pgbn < daddrppg && *runp < maxrun &&
395 				     (isseq = is_sequential(ump, prevdaddr, daddr2p[pgbn]));
396 				     prevdaddr = daddr2p[pgbn], ++pgbn, ++bn, ++*runp);
397 				ufs_bm_sf_put(sf);
398 			}
399 			prevdaddr = daddr;
400 			bn = ap->in_off;
401 			if (runb && bn) {
402 				isseq = true;
403 				for (--bn; bn >= 0 && *runb < maxrun && isseq; ) {
404 					boff = bn * daddrsz;
405 					pgidx = boff / PAGE_SIZE;
406 					pgoff = (boff & PAGE_MASK) / daddrsz;
407 					KASSERT(pgidx >= 0 && pgidx < bp->b_npages,
408 						("pgidx %d vs b_npages %d", pgidx, bp->b_npages));
409 					pgaddr = ufs_bm_sf_get(bp, pgidx, &sf);
410 					daddr2p = (ufs2_daddr_t *)pgaddr;
411 					for (pgbn = pgoff; pgbn >= 0 && *runb < maxrun &&
412 					     (isseq = is_sequential(ump, daddr2p[pgbn], prevdaddr));
413 					     prevdaddr = daddr2p[pgbn], --pgbn, --bn, ++*runb);
414 					ufs_bm_sf_put(sf);
415 				}
416 			}
417 		} else {
418 			for (bn = ap->in_off + 1;
419 			    bn < MNINDIR(ump) && *runp < maxrun &&
420 			    is_sequential(ump,
421 			    ((ufs2_daddr_t *)bp->b_data)[bn - 1],
422 			    ((ufs2_daddr_t *)bp->b_data)[bn]);
423 			    ++bn, ++*runp);
424 			bn = ap->in_off;
425 			if (runb && bn) {
426 				for (--bn; bn >= 0 && *runb < maxrun &&
427 				    is_sequential(ump,
428 				    ((ufs2_daddr_t *)bp->b_data)[bn],
429 				    ((ufs2_daddr_t *)bp->b_data)[bn + 1]);
430 				    --bn, ++*runb);
431 			}
432 		}
433 	}
434 	if (bp)
435 		bqrelse(bp);
436 
437 	/*
438 	 * Since this is FFS independent code, we are out of scope for the
439 	 * definitions of BLK_NOCOPY and BLK_SNAP, but we do know that they
440 	 * will fall in the range 1..um_seqinc, so we use that test and
441 	 * return a request for a zeroed out buffer if attempts are made
442 	 * to read a BLK_NOCOPY or BLK_SNAP block.
443 	 */
444 	if (IS_SNAPSHOT(ip) && daddr > 0 && daddr < ump->um_seqinc){
445 		*bnp = -1;
446 		return (0);
447 	}
448 	*bnp = blkptrtodb(ump, daddr);
449 	if (*bnp == 0) {
450 		if (IS_SNAPSHOT(ip))
451 			*bnp = blkptrtodb(ump, bn * ump->um_seqinc);
452 		else
453 			*bnp = -1;
454 	}
455 	return (0);
456 }
457 
458 static ufs_lbn_t
lbn_count(struct ufsmount * ump,int level)459 lbn_count(struct ufsmount *ump, int level)
460 {
461 	ufs_lbn_t blockcnt;
462 
463 	for (blockcnt = 1; level > 0; level--)
464 		blockcnt *= MNINDIR(ump);
465 	return (blockcnt);
466 }
467 
468 int
ufs_bmap_seekdata(struct vnode * vp,off_t * offp)469 ufs_bmap_seekdata(struct vnode *vp, off_t *offp)
470 {
471 	struct buf *bp;
472 	struct indir a[UFS_NIADDR + 1], *ap;
473 	struct inode *ip;
474 	struct mount *mp;
475 	struct ufsmount *ump;
476 	ufs2_daddr_t bn, daddr, nextbn;
477 	uint64_t bsize;
478 	off_t numblks;
479 	int error, num, num1, off;
480 
481 	bp = NULL;
482 	error = 0;
483 	ip = VTOI(vp);
484 	mp = vp->v_mount;
485 	ump = VFSTOUFS(mp);
486 
487 	if (vp->v_type != VREG || IS_SNAPSHOT(ip))
488 		return (EINVAL);
489 	if (*offp < 0 || *offp >= ip->i_size)
490 		return (ENXIO);
491 
492 	/*
493 	 * We could have pages on the vnode' object queue which still
494 	 * do not have the data blocks allocated.  Convert all dirty
495 	 * pages into buffer writes to ensure that we see all
496 	 * allocated data.
497 	 */
498 	vnode_pager_clean_sync(vp);
499 
500 	bsize = mp->mnt_stat.f_iosize;
501 	for (bn = *offp / bsize, numblks = howmany(ip->i_size, bsize);
502 	    bn < numblks; bn = nextbn) {
503 		if (bn < UFS_NDADDR) {
504 			daddr = DIP(ip, i_db[bn]);
505 			if (daddr != 0)
506 				break;
507 			nextbn = bn + 1;
508 			continue;
509 		}
510 
511 		ap = a;
512 		error = ufs_getlbns(vp, bn, ap, &num);
513 		if (error != 0)
514 			break;
515 		MPASS(num >= 2);
516 		daddr = DIP(ip, i_ib[ap->in_off]);
517 		ap++, num--;
518 		for (nextbn = UFS_NDADDR, num1 = num - 1; num1 > 0; num1--)
519 			nextbn += lbn_count(ump, num1);
520 		if (daddr == 0) {
521 			nextbn += lbn_count(ump, num);
522 			continue;
523 		}
524 
525 		for (; daddr != 0 && num > 0; ap++, num--) {
526 			if (bp != NULL)
527 				bqrelse(bp);
528 			error = readindir(vp, ap->in_lbn, daddr, &bp);
529 			if (error != 0)
530 				return (error);
531 
532 			/*
533 			 * Scan the indirect block until we find a non-zero
534 			 * pointer.
535 			 */
536 			off = ap->in_off;
537 			do {
538 				daddr = I_IS_UFS1(ip) ?
539 				    ((ufs1_daddr_t *)bp->b_data)[off] :
540 				    ((ufs2_daddr_t *)bp->b_data)[off];
541 			} while (daddr == 0 && ++off < MNINDIR(ump));
542 			nextbn += off * lbn_count(ump, num - 1);
543 
544 			/*
545 			 * We need to recompute the LBNs of indirect
546 			 * blocks, so restart with the updated block offset.
547 			 */
548 			if (off != ap->in_off)
549 				break;
550 		}
551 		if (num == 0) {
552 			/*
553 			 * We found a data block.
554 			 */
555 			bn = nextbn;
556 			break;
557 		}
558 	}
559 	if (bp != NULL)
560 		bqrelse(bp);
561 	if (bn >= numblks)
562 		error = ENXIO;
563 	if (error == 0 && *offp < bn * bsize)
564 		*offp = bn * bsize;
565 	return (error);
566 }
567 
568 /*
569  * Create an array of logical block number/offset pairs which represent the
570  * path of indirect blocks required to access a data block.  The first "pair"
571  * contains the logical block number of the appropriate single, double or
572  * triple indirect block and the offset into the inode indirect block array.
573  * Note, the logical block number of the inode single/double/triple indirect
574  * block appears twice in the array, once with the offset into the i_ib and
575  * once with the offset into the page itself.
576  */
577 int
ufs_getlbns(struct vnode * vp,ufs2_daddr_t bn,struct indir * ap,int * nump)578 ufs_getlbns(struct vnode *vp,
579 	ufs2_daddr_t bn,
580 	struct indir *ap,
581 	int *nump)
582 {
583 	ufs2_daddr_t blockcnt;
584 	ufs_lbn_t metalbn, realbn;
585 	struct ufsmount *ump;
586 	int i, numlevels, off;
587 
588 	ump = VFSTOUFS(vp->v_mount);
589 	if (nump)
590 		*nump = 0;
591 	numlevels = 0;
592 	realbn = bn;
593 	if (bn < 0)
594 		bn = -bn;
595 
596 	/* The first UFS_NDADDR blocks are direct blocks. */
597 	if (bn < UFS_NDADDR)
598 		return (0);
599 
600 	/*
601 	 * Determine the number of levels of indirection.  After this loop
602 	 * is done, blockcnt indicates the number of data blocks possible
603 	 * at the previous level of indirection, and UFS_NIADDR - i is the
604 	 * number of levels of indirection needed to locate the requested block.
605 	 */
606 	for (blockcnt = 1, i = UFS_NIADDR, bn -= UFS_NDADDR; ;
607 	    i--, bn -= blockcnt) {
608 		if (i == 0)
609 			return (EFBIG);
610 		blockcnt *= MNINDIR(ump);
611 		if (bn < blockcnt)
612 			break;
613 	}
614 
615 	/* Calculate the address of the first meta-block. */
616 	if (realbn >= 0)
617 		metalbn = -(realbn - bn + UFS_NIADDR - i);
618 	else
619 		metalbn = -(-realbn - bn + UFS_NIADDR - i);
620 
621 	/*
622 	 * At each iteration, off is the offset into the bap array which is
623 	 * an array of disk addresses at the current level of indirection.
624 	 * The logical block number and the offset in that block are stored
625 	 * into the argument array.
626 	 */
627 	ap->in_lbn = metalbn;
628 	ap->in_off = off = UFS_NIADDR - i;
629 	ap++;
630 	for (++numlevels; i <= UFS_NIADDR; i++) {
631 		/* If searching for a meta-data block, quit when found. */
632 		if (metalbn == realbn)
633 			break;
634 
635 		blockcnt /= MNINDIR(ump);
636 		off = (bn / blockcnt) % MNINDIR(ump);
637 
638 		++numlevels;
639 		ap->in_lbn = metalbn;
640 		ap->in_off = off;
641 		++ap;
642 
643 		metalbn -= -1 + off * blockcnt;
644 	}
645 	if (nump)
646 		*nump = numlevels;
647 	return (0);
648 }
649