1235537Sgber/*-
2235537Sgber * Copyright (c) 2012 Semihalf
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber *
14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15235537Sgber * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16235537Sgber * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17235537Sgber * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18235537Sgber * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19235537Sgber * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20235537Sgber * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21235537Sgber * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22235537Sgber * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23235537Sgber * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24235537Sgber */
25235537Sgber
26235537Sgber#include <sys/cdefs.h>
27235537Sgber__FBSDID("$FreeBSD$");
28235537Sgber
29235537Sgber#include <sys/param.h>
30235537Sgber#include <sys/systm.h>
31235537Sgber#include <sys/namei.h>
32235537Sgber#include <sys/kernel.h>
33235537Sgber#include <sys/stat.h>
34235537Sgber#include <sys/buf.h>
35235537Sgber#include <sys/bio.h>
36235537Sgber#include <sys/proc.h>
37235537Sgber#include <sys/mount.h>
38235537Sgber#include <sys/vnode.h>
39235537Sgber#include <sys/signalvar.h>
40235537Sgber#include <sys/malloc.h>
41235537Sgber#include <sys/dirent.h>
42235537Sgber#include <sys/lockf.h>
43235537Sgber#include <sys/ktr.h>
44235537Sgber#include <sys/kdb.h>
45235537Sgber
46235537Sgber#include <vm/vm.h>
47235537Sgber#include <vm/vm_extern.h>
48235537Sgber#include <vm/vm_object.h>
49235537Sgber#include <vm/vnode_pager.h>
50235537Sgber
51235537Sgber#include <machine/_inttypes.h>
52235537Sgber
53235537Sgber#include <vm/vm.h>
54235537Sgber#include <vm/vm_extern.h>
55235537Sgber#include <vm/vm_object.h>
56235537Sgber#include <vm/vnode_pager.h>
57235537Sgber
58235537Sgber#include "nandfs_mount.h"
59235537Sgber#include "nandfs.h"
60235537Sgber#include "nandfs_subr.h"
61235537Sgber#include "bmap.h"
62235537Sgber
63235537Sgberstatic int bmap_getlbns(struct nandfs_node *, nandfs_lbn_t,
64235537Sgber    struct nandfs_indir *, int *);
65235537Sgber
66235537Sgberint
67235537Sgberbmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk, nandfs_daddr_t *vblk)
68235537Sgber{
69235537Sgber	struct nandfs_inode *ip;
70235537Sgber	struct nandfs_indir a[NIADDR + 1], *ap;
71235537Sgber	nandfs_daddr_t daddr;
72235537Sgber	struct buf *bp;
73235537Sgber	int error;
74235537Sgber	int num, *nump;
75235537Sgber
76235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk %jx enter\n", __func__, node, lblk));
77235537Sgber	ip = &node->nn_inode;
78235537Sgber
79235537Sgber	ap = a;
80235537Sgber	nump = &num;
81235537Sgber
82235537Sgber	error = bmap_getlbns(node, lblk, ap, nump);
83235537Sgber	if (error)
84235537Sgber		return (error);
85235537Sgber
86235537Sgber	if (num == 0) {
87235537Sgber		*vblk = ip->i_db[lblk];
88235537Sgber		return (0);
89235537Sgber	}
90235537Sgber
91235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk=%jx trying ip->i_ib[%x]\n", __func__,
92235537Sgber	    node, lblk, ap->in_off));
93235537Sgber	daddr = ip->i_ib[ap->in_off];
94235537Sgber	for (bp = NULL, ++ap; --num; ap++) {
95235537Sgber		if (daddr == 0) {
96235537Sgber			DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with "
97235537Sgber			    "vblk 0\n", __func__, node, lblk));
98235537Sgber			*vblk = 0;
99235537Sgber			return (0);
100235537Sgber		}
101235537Sgber		if (ap->in_lbn == lblk) {
102235537Sgber			DPRINTF(BMAP, ("%s: node %p lblk=%jx ap->in_lbn=%jx "
103235537Sgber			    "returning address of indirect block (%jx)\n",
104235537Sgber			    __func__, node, lblk, ap->in_lbn, daddr));
105235537Sgber			*vblk = daddr;
106235537Sgber			return (0);
107235537Sgber		}
108235537Sgber
109235537Sgber		DPRINTF(BMAP, ("%s: node %p lblk=%jx reading block "
110235537Sgber		    "ap->in_lbn=%jx\n", __func__, node, lblk, ap->in_lbn));
111235537Sgber
112235537Sgber		error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp);
113235537Sgber		if (error) {
114235537Sgber			brelse(bp);
115235537Sgber			return (error);
116235537Sgber		}
117235537Sgber
118235537Sgber		daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off];
119235537Sgber		brelse(bp);
120235537Sgber	}
121235537Sgber
122235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with %jx\n", __func__,
123235537Sgber	    node, lblk, daddr));
124235537Sgber	*vblk = daddr;
125235537Sgber
126235537Sgber	return (0);
127235537Sgber}
128235537Sgber
129235537Sgberint
130235537Sgberbmap_dirty_meta(struct nandfs_node *node, nandfs_lbn_t lblk, int force)
131235537Sgber{
132235537Sgber	struct nandfs_indir a[NIADDR+1], *ap;
133235537Sgber#ifdef DEBUG
134235537Sgber	nandfs_daddr_t daddr;
135235537Sgber#endif
136235537Sgber	struct buf *bp;
137235537Sgber	int error;
138235537Sgber	int num, *nump;
139235537Sgber
140235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk=%jx\n", __func__, node, lblk));
141235537Sgber
142235537Sgber	ap = a;
143235537Sgber	nump = &num;
144235537Sgber
145235537Sgber	error = bmap_getlbns(node, lblk, ap, nump);
146235537Sgber	if (error)
147235537Sgber		return (error);
148235537Sgber
149235537Sgber	/*
150235537Sgber	 * Direct block, nothing to do
151235537Sgber	 */
152235537Sgber	if (num == 0)
153235537Sgber		return (0);
154235537Sgber
155235537Sgber	DPRINTF(BMAP, ("%s: node %p reading blocks\n", __func__, node));
156235537Sgber
157235537Sgber	for (bp = NULL, ++ap; --num; ap++) {
158235537Sgber		error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp);
159235537Sgber		if (error) {
160235537Sgber			brelse(bp);
161235537Sgber			return (error);
162235537Sgber		}
163235537Sgber
164235537Sgber#ifdef DEBUG
165235537Sgber		daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off];
166235537Sgber		MPASS(daddr != 0 || node->nn_ino == 3);
167235537Sgber#endif
168235537Sgber
169235537Sgber		error = nandfs_dirty_buf_meta(bp, force);
170235537Sgber		if (error)
171235537Sgber			return (error);
172235537Sgber	}
173235537Sgber
174235537Sgber	return (0);
175235537Sgber}
176235537Sgber
177235537Sgberint
178235537Sgberbmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk,
179235537Sgber    nandfs_daddr_t vblk)
180235537Sgber{
181235537Sgber	struct nandfs_inode *ip;
182235537Sgber	struct nandfs_indir a[NIADDR+1], *ap;
183235537Sgber	struct buf *bp;
184235537Sgber	nandfs_daddr_t daddr;
185235537Sgber	int error;
186235537Sgber	int num, *nump, i;
187235537Sgber
188235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx\n", __func__, node, lblk,
189235537Sgber	    vblk));
190235537Sgber
191235537Sgber	ip = &node->nn_inode;
192235537Sgber
193235537Sgber	ap = a;
194235537Sgber	nump = &num;
195235537Sgber
196235537Sgber	error = bmap_getlbns(node, lblk, ap, nump);
197235537Sgber	if (error)
198235537Sgber		return (error);
199235537Sgber
200235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx got num=%d\n", __func__,
201235537Sgber	    node, lblk, vblk, num));
202235537Sgber
203235537Sgber	if (num == 0) {
204235537Sgber		DPRINTF(BMAP, ("%s: node %p lblk=%jx direct block\n", __func__,
205235537Sgber		    node, lblk));
206235537Sgber		ip->i_db[lblk] = vblk;
207235537Sgber		return (0);
208235537Sgber	}
209235537Sgber
210235537Sgber	DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block level %d\n",
211235537Sgber	    __func__, node, lblk, ap->in_off));
212235537Sgber
213235537Sgber	if (num == 1) {
214235537Sgber		DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block: inserting "
215235537Sgber		    "%jx as vblk for indirect block %d\n", __func__, node,
216235537Sgber		    lblk, vblk, ap->in_off));
217235537Sgber		ip->i_ib[ap->in_off] = vblk;
218235537Sgber		return (0);
219235537Sgber	}
220235537Sgber
221235537Sgber	bp = NULL;
222235537Sgber	daddr = ip->i_ib[a[0].in_off];
223235537Sgber	for (i = 1; i < num; i++) {
224235537Sgber		if (bp)
225235537Sgber			brelse(bp);
226235537Sgber		if (daddr == 0) {
227235537Sgber			DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx create "
228235537Sgber			    "block %jx %d\n", __func__, node, lblk, vblk,
229235537Sgber			    a[i].in_lbn, a[i].in_off));
230235537Sgber			error = nandfs_bcreate_meta(node, a[i].in_lbn, NOCRED,
231235537Sgber			    0, &bp);
232235537Sgber			if (error)
233235537Sgber				return (error);
234235537Sgber		} else {
235235537Sgber			DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx read "
236235537Sgber			    "block %jx %d\n", __func__, node, daddr, vblk,
237235537Sgber			    a[i].in_lbn, a[i].in_off));
238235537Sgber			error = nandfs_bread_meta(node, a[i].in_lbn, NOCRED, 0, &bp);
239235537Sgber			if (error) {
240235537Sgber				brelse(bp);
241235537Sgber				return (error);
242235537Sgber			}
243235537Sgber		}
244235537Sgber		daddr = ((nandfs_daddr_t *)bp->b_data)[a[i].in_off];
245235537Sgber	}
246235537Sgber	i--;
247235537Sgber
248235537Sgber	DPRINTF(BMAP,
249235537Sgber	    ("%s: bmap node %p lblk=%jx vblk=%jx inserting vblk level %d at "
250235537Sgber	    "offset %d at %jx\n", __func__, node, lblk, vblk, i, a[i].in_off,
251235537Sgber	    daddr));
252235537Sgber
253235537Sgber	if (!bp) {
254235537Sgber		nandfs_error("%s: cannot find indirect block\n", __func__);
255235537Sgber		return (-1);
256235537Sgber	}
257235537Sgber	((nandfs_daddr_t *)bp->b_data)[a[i].in_off] = vblk;
258235537Sgber
259235537Sgber	error = nandfs_dirty_buf_meta(bp, 0);
260235537Sgber	if (error) {
261235537Sgber		nandfs_warning("%s: dirty failed buf: %p\n", __func__, bp);
262235537Sgber		return (error);
263235537Sgber	}
264235537Sgber	DPRINTF(BMAP, ("%s: exiting node %p lblk=%jx vblk=%jx\n", __func__,
265235537Sgber	    node, lblk, vblk));
266235537Sgber
267235537Sgber	return (error);
268235537Sgber}
269235537Sgber
270235537SgberCTASSERT(NIADDR <= 3);
271235537Sgber#define SINGLE	0	/* index of single indirect block */
272235537Sgber#define DOUBLE	1	/* index of double indirect block */
273235537Sgber#define TRIPLE	2	/* index of triple indirect block */
274235537Sgber
275235537Sgberstatic __inline nandfs_lbn_t
276235537Sgberlbn_offset(struct nandfs_device *fsdev, int level)
277235537Sgber{
278235537Sgber	nandfs_lbn_t res;
279235537Sgber
280235537Sgber	for (res = 1; level > 0; level--)
281235537Sgber		res *= MNINDIR(fsdev);
282235537Sgber	return (res);
283235537Sgber}
284235537Sgber
285235537Sgberstatic nandfs_lbn_t
286235537Sgberblocks_inside(struct nandfs_device *fsdev, int level, struct nandfs_indir *nip)
287235537Sgber{
288235537Sgber	nandfs_lbn_t blocks;
289235537Sgber
290235537Sgber	for (blocks = 1; level >= SINGLE; level--, nip++) {
291235537Sgber		MPASS(nip->in_off >= 0 && nip->in_off < MNINDIR(fsdev));
292235537Sgber		blocks += nip->in_off * lbn_offset(fsdev, level);
293235537Sgber	}
294235537Sgber
295235537Sgber	return (blocks);
296235537Sgber}
297235537Sgber
298235537Sgberstatic int
299235537Sgberbmap_truncate_indirect(struct nandfs_node *node, int level, nandfs_lbn_t *left,
300235537Sgber    int *cleaned, struct nandfs_indir *ap, struct nandfs_indir *fp,
301235537Sgber    nandfs_daddr_t *copy)
302235537Sgber{
303235537Sgber	struct buf *bp;
304235537Sgber	nandfs_lbn_t i, lbn, nlbn, factor, tosub;
305235537Sgber	struct nandfs_device *fsdev;
306235537Sgber	int error, lcleaned, modified;
307235537Sgber
308235537Sgber	DPRINTF(BMAP, ("%s: node %p level %d left %jx\n", __func__,
309235537Sgber	    node, level, *left));
310235537Sgber
311235537Sgber	fsdev = node->nn_nandfsdev;
312235537Sgber
313235537Sgber	MPASS(ap->in_off >= 0 && ap->in_off < MNINDIR(fsdev));
314235537Sgber
315235537Sgber	factor = lbn_offset(fsdev, level);
316235537Sgber	lbn = ap->in_lbn;
317235537Sgber
318235537Sgber	error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp);
319235537Sgber	if (error) {
320235537Sgber		brelse(bp);
321235537Sgber		return (error);
322235537Sgber	}
323235537Sgber
324235537Sgber	bcopy(bp->b_data, copy, fsdev->nd_blocksize);
325235537Sgber	bqrelse(bp);
326235537Sgber
327235537Sgber	modified = 0;
328235537Sgber
329235537Sgber	i = ap->in_off;
330235537Sgber
331235537Sgber	if (ap != fp)
332235537Sgber		ap++;
333235537Sgber	for (nlbn = lbn + 1 - i * factor; i >= 0 && *left > 0; i--,
334235537Sgber	    nlbn += factor) {
335235537Sgber		lcleaned = 0;
336235537Sgber
337235537Sgber		DPRINTF(BMAP,
338235537Sgber		    ("%s: node %p i=%jx nlbn=%jx left=%jx ap=%p vblk %jx\n",
339235537Sgber		    __func__, node, i, nlbn, *left, ap, copy[i]));
340235537Sgber
341235537Sgber		if (copy[i] == 0) {
342235537Sgber			tosub = blocks_inside(fsdev, level - 1, ap);
343235537Sgber			if (tosub > *left)
344235537Sgber				tosub = 0;
345235537Sgber
346235537Sgber			*left -= tosub;
347235537Sgber		} else {
348235537Sgber			if (level > SINGLE) {
349235537Sgber				if (ap == fp)
350235537Sgber					ap->in_lbn = nlbn;
351235537Sgber
352235537Sgber				error = bmap_truncate_indirect(node, level - 1,
353235537Sgber				    left, &lcleaned, ap, fp,
354235537Sgber				    copy + MNINDIR(fsdev));
355235537Sgber				if (error)
356235537Sgber					return (error);
357235537Sgber			} else {
358235537Sgber				error = nandfs_bdestroy(node, copy[i]);
359235537Sgber				if (error)
360235537Sgber					return (error);
361235537Sgber				lcleaned = 1;
362235537Sgber				*left -= 1;
363235537Sgber			}
364235537Sgber		}
365235537Sgber
366235537Sgber		if (lcleaned) {
367235537Sgber			if (level > SINGLE) {
368235537Sgber				error = nandfs_vblock_end(fsdev, copy[i]);
369235537Sgber				if (error)
370235537Sgber					return (error);
371235537Sgber			}
372235537Sgber			copy[i] = 0;
373235537Sgber			modified++;
374235537Sgber		}
375235537Sgber
376235537Sgber		ap = fp;
377235537Sgber	}
378235537Sgber
379235537Sgber	if (i == -1)
380235537Sgber		*cleaned = 1;
381235537Sgber
382235537Sgber	error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp);
383235537Sgber	if (error) {
384235537Sgber		brelse(bp);
385235537Sgber		return (error);
386235537Sgber	}
387235537Sgber	if (modified)
388235537Sgber		bcopy(copy, bp->b_data, fsdev->nd_blocksize);
389235537Sgber
390235537Sgber	error = nandfs_dirty_buf_meta(bp, 0);
391235537Sgber	if (error)
392235537Sgber		return (error);
393235537Sgber
394235537Sgber	return (error);
395235537Sgber}
396235537Sgber
397235537Sgberint
398235537Sgberbmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t lastblk,
399235537Sgber    nandfs_lbn_t todo)
400235537Sgber{
401235537Sgber	struct nandfs_inode *ip;
402235537Sgber	struct nandfs_indir a[NIADDR + 1], f[NIADDR], *ap;
403235537Sgber	nandfs_daddr_t indir_lbn[NIADDR];
404235537Sgber	nandfs_daddr_t *copy;
405235537Sgber	int error, level;
406235537Sgber	nandfs_lbn_t left, tosub;
407235537Sgber	struct nandfs_device *fsdev;
408235537Sgber	int cleaned, i;
409235537Sgber	int num, *nump;
410235537Sgber
411235537Sgber	DPRINTF(BMAP, ("%s: node %p lastblk %jx truncating by %jx\n", __func__,
412235537Sgber	    node, lastblk, todo));
413235537Sgber
414235537Sgber	ip = &node->nn_inode;
415235537Sgber	fsdev = node->nn_nandfsdev;
416235537Sgber
417235537Sgber	ap = a;
418235537Sgber	nump = &num;
419235537Sgber
420235537Sgber	error = bmap_getlbns(node, lastblk, ap, nump);
421235537Sgber	if (error)
422235537Sgber		return (error);
423235537Sgber
424235537Sgber	indir_lbn[SINGLE] = -NDADDR;
425235537Sgber	indir_lbn[DOUBLE] = indir_lbn[SINGLE] - MNINDIR(fsdev) - 1;
426235537Sgber	indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - MNINDIR(fsdev)
427235537Sgber	    * MNINDIR(fsdev) - 1;
428235537Sgber
429235537Sgber	for (i = 0; i < NIADDR; i++) {
430235537Sgber		f[i].in_off = MNINDIR(fsdev) - 1;
431235537Sgber		f[i].in_lbn = 0xdeadbeef;
432235537Sgber	}
433235537Sgber
434235537Sgber	left = todo;
435235537Sgber
436235537Sgber#ifdef DEBUG
437235537Sgber	a[num].in_off = -1;
438235537Sgber#endif
439235537Sgber
440235537Sgber	ap++;
441235537Sgber	num -= 2;
442235537Sgber
443235537Sgber	if (num < 0)
444235537Sgber		goto direct;
445235537Sgber
446235537Sgber	copy = malloc(MNINDIR(fsdev) * sizeof(nandfs_daddr_t) * (num + 1),
447235537Sgber	    M_NANDFSTEMP, M_WAITOK);
448235537Sgber
449235537Sgber	for (level = num; level >= SINGLE && left > 0; level--) {
450235537Sgber		cleaned = 0;
451235537Sgber
452235537Sgber		if (ip->i_ib[level] == 0) {
453235537Sgber			tosub = blocks_inside(fsdev, level, ap);
454235537Sgber			if (tosub > left)
455235537Sgber				left = 0;
456235537Sgber			else
457235537Sgber				left -= tosub;
458235537Sgber		} else {
459235537Sgber			if (ap == f)
460235537Sgber				ap->in_lbn = indir_lbn[level];
461235537Sgber			error = bmap_truncate_indirect(node, level, &left,
462235537Sgber			    &cleaned, ap, f, copy);
463235537Sgber			if (error) {
464235537Sgber				nandfs_error("%s: error %d when truncate "
465235537Sgber				    "at level %d\n", __func__, error, level);
466235537Sgber				return (error);
467235537Sgber			}
468235537Sgber		}
469235537Sgber
470235537Sgber		if (cleaned) {
471235537Sgber			nandfs_vblock_end(fsdev, ip->i_ib[level]);
472235537Sgber			ip->i_ib[level] = 0;
473235537Sgber		}
474235537Sgber
475235537Sgber		ap = f;
476235537Sgber	}
477235537Sgber
478235537Sgber	free(copy, M_NANDFSTEMP);
479235537Sgber
480235537Sgberdirect:
481235537Sgber	if (num < 0)
482235537Sgber		i = lastblk;
483235537Sgber	else
484235537Sgber		i = NDADDR - 1;
485235537Sgber
486235537Sgber	for (; i >= 0 && left > 0; i--) {
487235537Sgber		if (ip->i_db[i] != 0) {
488235537Sgber			error = nandfs_bdestroy(node, ip->i_db[i]);
489235537Sgber			if (error) {
490235537Sgber				nandfs_error("%s: cannot destroy "
491235537Sgber				    "block %jx, error %d\n", __func__,
492235537Sgber				    (uintmax_t)ip->i_db[i], error);
493235537Sgber				return (error);
494235537Sgber			}
495235537Sgber			ip->i_db[i] = 0;
496235537Sgber		}
497235537Sgber
498235537Sgber		left--;
499235537Sgber	}
500235537Sgber
501235537Sgber	KASSERT(left == 0,
502235537Sgber	    ("truncated wrong number of blocks (%jd should be 0)", left));
503235537Sgber
504235537Sgber	return (error);
505235537Sgber}
506235537Sgber
507235537Sgbernandfs_lbn_t
508235537Sgberget_maxfilesize(struct nandfs_device *fsdev)
509235537Sgber{
510235537Sgber	struct nandfs_indir f[NIADDR];
511235537Sgber	nandfs_lbn_t max;
512235537Sgber	int i;
513235537Sgber
514235537Sgber	max = NDADDR;
515235537Sgber
516235537Sgber	for (i = 0; i < NIADDR; i++) {
517235537Sgber		f[i].in_off = MNINDIR(fsdev) - 1;
518235537Sgber		max += blocks_inside(fsdev, i, f);
519235537Sgber	}
520235537Sgber
521235537Sgber	max *= fsdev->nd_blocksize;
522235537Sgber
523235537Sgber	return (max);
524235537Sgber}
525235537Sgber
526235537Sgber/*
527235537Sgber * This is ufs_getlbns with minor modifications.
528235537Sgber */
529235537Sgber/*
530235537Sgber * Create an array of logical block number/offset pairs which represent the
531235537Sgber * path of indirect blocks required to access a data block.  The first "pair"
532235537Sgber * contains the logical block number of the appropriate single, double or
533235537Sgber * triple indirect block and the offset into the inode indirect block array.
534235537Sgber * Note, the logical block number of the inode single/double/triple indirect
535235537Sgber * block appears twice in the array, once with the offset into the i_ib and
536235537Sgber * once with the offset into the page itself.
537235537Sgber */
538235537Sgberstatic int
539235537Sgberbmap_getlbns(struct nandfs_node *node, nandfs_lbn_t bn, struct nandfs_indir *ap, int *nump)
540235537Sgber{
541235537Sgber	nandfs_daddr_t blockcnt;
542235537Sgber	nandfs_lbn_t metalbn, realbn;
543235537Sgber	struct nandfs_device *fsdev;
544235537Sgber	int i, numlevels, off;
545235537Sgber
546235537Sgber	fsdev = node->nn_nandfsdev;
547235537Sgber
548235537Sgber	DPRINTF(BMAP, ("%s: node %p bn=%jx mnindir=%zd enter\n", __func__,
549235537Sgber	    node, bn, MNINDIR(fsdev)));
550235537Sgber
551240358Skevlo	if (nump)
552240358Skevlo		*nump = 0;
553235537Sgber	numlevels = 0;
554235537Sgber	realbn = bn;
555235537Sgber
556235537Sgber	if (bn < 0)
557235537Sgber		bn = -bn;
558235537Sgber
559235537Sgber	/* The first NDADDR blocks are direct blocks. */
560235537Sgber	if (bn < NDADDR)
561235537Sgber		return (0);
562235537Sgber
563235537Sgber	/*
564235537Sgber	 * Determine the number of levels of indirection.  After this loop
565235537Sgber	 * is done, blockcnt indicates the number of data blocks possible
566235537Sgber	 * at the previous level of indirection, and NIADDR - i is the number
567235537Sgber	 * of levels of indirection needed to locate the requested block.
568235537Sgber	 */
569235537Sgber	for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
570235537Sgber		DPRINTF(BMAP, ("%s: blockcnt=%jd i=%d bn=%jd\n", __func__,
571235537Sgber		    blockcnt, i, bn));
572235537Sgber		if (i == 0)
573235537Sgber			return (EFBIG);
574235537Sgber		blockcnt *= MNINDIR(fsdev);
575235537Sgber		if (bn < blockcnt)
576235537Sgber			break;
577235537Sgber	}
578235537Sgber
579235537Sgber	/* Calculate the address of the first meta-block. */
580235537Sgber	if (realbn >= 0)
581235537Sgber		metalbn = -(realbn - bn + NIADDR - i);
582235537Sgber	else
583235537Sgber		metalbn = -(-realbn - bn + NIADDR - i);
584235537Sgber
585235537Sgber	/*
586235537Sgber	 * At each iteration, off is the offset into the bap array which is
587235537Sgber	 * an array of disk addresses at the current level of indirection.
588235537Sgber	 * The logical block number and the offset in that block are stored
589235537Sgber	 * into the argument array.
590235537Sgber	 */
591235537Sgber	ap->in_lbn = metalbn;
592235537Sgber	ap->in_off = off = NIADDR - i;
593235537Sgber
594235537Sgber	DPRINTF(BMAP, ("%s: initial: ap->in_lbn=%jx ap->in_off=%d\n", __func__,
595235537Sgber	    metalbn, off));
596235537Sgber
597235537Sgber	ap++;
598235537Sgber	for (++numlevels; i <= NIADDR; i++) {
599235537Sgber		/* If searching for a meta-data block, quit when found. */
600235537Sgber		if (metalbn == realbn)
601235537Sgber			break;
602235537Sgber
603235537Sgber		blockcnt /= MNINDIR(fsdev);
604235537Sgber		off = (bn / blockcnt) % MNINDIR(fsdev);
605235537Sgber
606235537Sgber		++numlevels;
607235537Sgber		ap->in_lbn = metalbn;
608235537Sgber		ap->in_off = off;
609235537Sgber
610235537Sgber		DPRINTF(BMAP, ("%s: in_lbn=%jx in_off=%d\n", __func__,
611235537Sgber		    ap->in_lbn, ap->in_off));
612235537Sgber		++ap;
613235537Sgber
614235537Sgber		metalbn -= -1 + off * blockcnt;
615235537Sgber	}
616235537Sgber	if (nump)
617235537Sgber		*nump = numlevels;
618235537Sgber
619235537Sgber	DPRINTF(BMAP, ("%s: numlevels=%d\n", __func__, numlevels));
620235537Sgber
621235537Sgber	return (0);
622235537Sgber}
623