1235537Sgber/*-
2235537Sgber * Copyright (c) 2010-2012 Semihalf
3235537Sgber * Copyright (c) 2008, 2009 Reinoud Zandijk
4235537Sgber * All rights reserved.
5235537Sgber *
6235537Sgber * Redistribution and use in source and binary forms, with or without
7235537Sgber * modification, are permitted provided that the following conditions
8235537Sgber * are met:
9235537Sgber * 1. Redistributions of source code must retain the above copyright
10235537Sgber *    notice, this list of conditions and the following disclaimer.
11235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
12235537Sgber *    notice, this list of conditions and the following disclaimer in the
13235537Sgber *    documentation and/or other materials provided with the distribution.
14235537Sgber *
15235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16235537Sgber * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17235537Sgber * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18235537Sgber * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19235537Sgber * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20235537Sgber * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21235537Sgber * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22235537Sgber * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23235537Sgber * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24235537Sgber * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25235537Sgber *
26235537Sgber * From: NetBSD: nilfs_vfsops.c,v 1.1 2009/07/18 16:31:42 reinoud Exp
27235537Sgber */
28235537Sgber
29235537Sgber#include <sys/cdefs.h>
30235537Sgber__FBSDID("$FreeBSD$");
31235537Sgber
32235537Sgber#include <sys/param.h>
33235537Sgber#include <sys/systm.h>
34235537Sgber#include <sys/fcntl.h>
35235537Sgber#include <sys/kernel.h>
36235537Sgber#include <sys/lock.h>
37235537Sgber#include <sys/malloc.h>
38235537Sgber#include <sys/mount.h>
39235537Sgber#include <sys/namei.h>
40235537Sgber#include <sys/proc.h>
41235537Sgber#include <sys/priv.h>
42235537Sgber#include <sys/vnode.h>
43235537Sgber#include <sys/buf.h>
44235537Sgber#include <sys/sysctl.h>
45235537Sgber#include <sys/libkern.h>
46235537Sgber
47235537Sgber#include <geom/geom.h>
48235537Sgber#include <geom/geom_vfs.h>
49235537Sgber
50235537Sgber#include <machine/_inttypes.h>
51235537Sgber
52235537Sgber#include <fs/nandfs/nandfs_mount.h>
53235537Sgber#include <fs/nandfs/nandfs.h>
54235537Sgber#include <fs/nandfs/nandfs_subr.h>
55235537Sgber
56235537Sgberstatic MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount structure");
57235537Sgber
58235537Sgber#define	NANDFS_SET_SYSTEMFILE(vp) {	\
59235537Sgber	(vp)->v_vflag |= VV_SYSTEM;	\
60235537Sgber	vref(vp);			\
61235537Sgber	vput(vp); }
62235537Sgber
63235537Sgber#define	NANDFS_UNSET_SYSTEMFILE(vp) {	\
64235537Sgber	VOP_LOCK(vp, LK_EXCLUSIVE);	\
65235537Sgber	MPASS(vp->v_bufobj.bo_dirty.bv_cnt == 0); \
66235537Sgber	(vp)->v_vflag &= ~VV_SYSTEM;	\
67235537Sgber	vgone(vp);			\
68235537Sgber	vput(vp); }
69235537Sgber
70235537Sgber/* Globals */
71235537Sgberstruct _nandfs_devices nandfs_devices;
72235537Sgber
73235537Sgber/* Parameters */
74235537Sgberint nandfs_verbose = 0;
75235537Sgber
76235537Sgberstatic void
77235537Sgbernandfs_tunable_init(void *arg)
78235537Sgber{
79235537Sgber
80235537Sgber	TUNABLE_INT_FETCH("vfs.nandfs.verbose", &nandfs_verbose);
81235537Sgber}
82235537SgberSYSINIT(nandfs_tunables, SI_SUB_VFS, SI_ORDER_ANY, nandfs_tunable_init, NULL);
83235537Sgber
84235537Sgberstatic SYSCTL_NODE(_vfs, OID_AUTO, nandfs, CTLFLAG_RD, 0, "NAND filesystem");
85235537Sgberstatic SYSCTL_NODE(_vfs_nandfs, OID_AUTO, mount, CTLFLAG_RD, 0,
86235537Sgber    "NANDFS mountpoints");
87235537SgberSYSCTL_INT(_vfs_nandfs, OID_AUTO, verbose, CTLFLAG_RW, &nandfs_verbose, 0, "");
88235537Sgber
89235537Sgber#define NANDFS_CONSTR_INTERVAL	5
90235537Sgberint nandfs_sync_interval = NANDFS_CONSTR_INTERVAL; /* sync every 5 seconds */
91235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, sync_interval, CTLFLAG_RW,
92235537Sgber    &nandfs_sync_interval, 0, "");
93235537Sgber
94235537Sgber#define NANDFS_MAX_DIRTY_SEGS	5
95235537Sgberint nandfs_max_dirty_segs = NANDFS_MAX_DIRTY_SEGS; /* sync when 5 dirty seg */
96235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, max_dirty_segs, CTLFLAG_RW,
97235537Sgber    &nandfs_max_dirty_segs, 0, "");
98235537Sgber
99235537Sgber#define NANDFS_CPS_BETWEEN_SBLOCKS 5
100235537Sgberint nandfs_cps_between_sblocks = NANDFS_CPS_BETWEEN_SBLOCKS; /* write superblock every 5 checkpoints */
101235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cps_between_sblocks, CTLFLAG_RW,
102235537Sgber    &nandfs_cps_between_sblocks, 0, "");
103235537Sgber
104235537Sgber#define NANDFS_CLEANER_ENABLE 1
105235537Sgberint nandfs_cleaner_enable = NANDFS_CLEANER_ENABLE;
106235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_enable, CTLFLAG_RW,
107235537Sgber    &nandfs_cleaner_enable, 0, "");
108235537Sgber
109235537Sgber#define NANDFS_CLEANER_INTERVAL 5
110235537Sgberint nandfs_cleaner_interval = NANDFS_CLEANER_INTERVAL;
111235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_interval, CTLFLAG_RW,
112235537Sgber    &nandfs_cleaner_interval, 0, "");
113235537Sgber
114235537Sgber#define NANDFS_CLEANER_SEGMENTS 5
115235537Sgberint nandfs_cleaner_segments = NANDFS_CLEANER_SEGMENTS;
116235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_segments, CTLFLAG_RW,
117235537Sgber    &nandfs_cleaner_segments, 0, "");
118235537Sgber
119235537Sgberstatic int nandfs_mountfs(struct vnode *devvp, struct mount *mp);
120235537Sgberstatic vfs_mount_t	nandfs_mount;
121235537Sgberstatic vfs_root_t	nandfs_root;
122235537Sgberstatic vfs_statfs_t	nandfs_statfs;
123235537Sgberstatic vfs_unmount_t	nandfs_unmount;
124235537Sgberstatic vfs_vget_t	nandfs_vget;
125235537Sgberstatic vfs_sync_t	nandfs_sync;
126235537Sgberstatic const char *nandfs_opts[] = {
127235537Sgber	"snap", "from", "noatime", NULL
128235537Sgber};
129235537Sgber
130235537Sgber/* System nodes */
131235537Sgberstatic int
132235537Sgbernandfs_create_system_nodes(struct nandfs_device *nandfsdev)
133235537Sgber{
134235537Sgber	int error;
135235537Sgber
136235537Sgber	error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_DAT_INO,
137235537Sgber	    &nandfsdev->nd_super_root.sr_dat, &nandfsdev->nd_dat_node);
138235537Sgber	if (error)
139235537Sgber		goto errorout;
140235537Sgber
141235537Sgber	error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_CPFILE_INO,
142235537Sgber	    &nandfsdev->nd_super_root.sr_cpfile, &nandfsdev->nd_cp_node);
143235537Sgber	if (error)
144235537Sgber		goto errorout;
145235537Sgber
146235537Sgber	error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_SUFILE_INO,
147235537Sgber	    &nandfsdev->nd_super_root.sr_sufile, &nandfsdev->nd_su_node);
148235537Sgber	if (error)
149235537Sgber		goto errorout;
150235537Sgber
151235537Sgber	error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_GC_INO,
152235537Sgber	    NULL, &nandfsdev->nd_gc_node);
153235537Sgber	if (error)
154235537Sgber		goto errorout;
155235537Sgber
156235537Sgber	NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node));
157235537Sgber	NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node));
158235537Sgber	NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node));
159235537Sgber	NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node));
160235537Sgber
161235537Sgber	DPRINTF(VOLUMES, ("System vnodes: dat: %p cp: %p su: %p\n",
162235537Sgber	    NTOV(nandfsdev->nd_dat_node), NTOV(nandfsdev->nd_cp_node),
163235537Sgber	    NTOV(nandfsdev->nd_su_node)));
164235537Sgber	return (0);
165235537Sgber
166235537Sgbererrorout:
167235537Sgber	nandfs_dispose_node(&nandfsdev->nd_gc_node);
168235537Sgber	nandfs_dispose_node(&nandfsdev->nd_dat_node);
169235537Sgber	nandfs_dispose_node(&nandfsdev->nd_cp_node);
170235537Sgber	nandfs_dispose_node(&nandfsdev->nd_su_node);
171235537Sgber
172235537Sgber	return (error);
173235537Sgber}
174235537Sgber
175235537Sgberstatic void
176235537Sgbernandfs_release_system_nodes(struct nandfs_device *nandfsdev)
177235537Sgber{
178235537Sgber
179235537Sgber	if (!nandfsdev)
180235537Sgber		return;
181235537Sgber	if (nandfsdev->nd_refcnt > 0)
182235537Sgber		return;
183235537Sgber
184235537Sgber	if (nandfsdev->nd_gc_node)
185235537Sgber		NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node));
186235537Sgber	if (nandfsdev->nd_dat_node)
187235537Sgber		NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node));
188235537Sgber	if (nandfsdev->nd_cp_node)
189235537Sgber		NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node));
190235537Sgber	if (nandfsdev->nd_su_node)
191235537Sgber		NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node));
192235537Sgber}
193235537Sgber
194235537Sgberstatic int
195235537Sgbernandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
196235537Sgber{
197235537Sgber	uint32_t fsdata_crc, comp_crc;
198235537Sgber
199235537Sgber	if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
200235537Sgber		return (0);
201235537Sgber
202235537Sgber	/* Preserve CRC */
203235537Sgber	fsdata_crc = fsdata->f_sum;
204235537Sgber
205235537Sgber	/* Calculate */
206235537Sgber	fsdata->f_sum = (0);
207235537Sgber	comp_crc = crc32((uint8_t *)fsdata, fsdata->f_bytes);
208235537Sgber
209235537Sgber	/* Restore */
210235537Sgber	fsdata->f_sum = fsdata_crc;
211235537Sgber
212235537Sgber	/* Check CRC */
213235537Sgber	return (fsdata_crc == comp_crc);
214235537Sgber}
215235537Sgber
216235537Sgberstatic int
217235537Sgbernandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
218235537Sgber    struct nandfs_super_block *super)
219235537Sgber{
220235537Sgber	uint32_t super_crc, comp_crc;
221235537Sgber
222235537Sgber	/* Check super block magic */
223235537Sgber	if (super->s_magic != NANDFS_SUPER_MAGIC)
224235537Sgber		return (0);
225235537Sgber
226235537Sgber	/* Preserve CRC */
227235537Sgber	super_crc = super->s_sum;
228235537Sgber
229235537Sgber	/* Calculate */
230235537Sgber	super->s_sum = (0);
231235537Sgber	comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes);
232235537Sgber
233235537Sgber	/* Restore */
234235537Sgber	super->s_sum = super_crc;
235235537Sgber
236235537Sgber	/* Check CRC */
237235537Sgber	return (super_crc == comp_crc);
238235537Sgber}
239235537Sgber
240235537Sgberstatic void
241235537Sgbernandfs_calc_superblock_crc(struct nandfs_fsdata *fsdata,
242235537Sgber    struct nandfs_super_block *super)
243235537Sgber{
244235537Sgber	uint32_t comp_crc;
245235537Sgber
246235537Sgber	/* Calculate */
247235537Sgber	super->s_sum = 0;
248235537Sgber	comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes);
249235537Sgber
250235537Sgber	/* Restore */
251235537Sgber	super->s_sum = comp_crc;
252235537Sgber}
253235537Sgber
254235537Sgberstatic int
255235537Sgbernandfs_is_empty(u_char *area, int size)
256235537Sgber{
257235537Sgber	int i;
258235537Sgber
259235537Sgber	for (i = 0; i < size; i++)
260235537Sgber		if (area[i] != 0xff)
261235537Sgber			return (0);
262235537Sgber
263235537Sgber	return (1);
264235537Sgber}
265235537Sgber
266235537Sgberstatic __inline int
267235537Sgbernandfs_sblocks_in_esize(struct nandfs_device *fsdev)
268235537Sgber{
269235537Sgber
270235537Sgber	return ((fsdev->nd_erasesize - NANDFS_SBLOCK_OFFSET_BYTES) /
271235537Sgber	    sizeof(struct nandfs_super_block));
272235537Sgber}
273235537Sgber
274235537Sgberstatic __inline int
275235537Sgbernandfs_max_sblocks(struct nandfs_device *fsdev)
276235537Sgber{
277235537Sgber
278235537Sgber	return (NANDFS_NFSAREAS * nandfs_sblocks_in_esize(fsdev));
279235537Sgber}
280235537Sgber
281235537Sgberstatic __inline int
282235537Sgbernandfs_sblocks_in_block(struct nandfs_device *fsdev)
283235537Sgber{
284235537Sgber
285235537Sgber	return (fsdev->nd_devblocksize / sizeof(struct nandfs_super_block));
286235537Sgber}
287235537Sgber
288235537Sgberstatic __inline int
289235537Sgbernandfs_sblocks_in_first_block(struct nandfs_device *fsdev)
290235537Sgber{
291235537Sgber	int n;
292235537Sgber
293235537Sgber	n = nandfs_sblocks_in_block(fsdev) -
294235537Sgber	    NANDFS_SBLOCK_OFFSET_BYTES / sizeof(struct nandfs_super_block);
295235537Sgber	if (n < 0)
296235537Sgber		n = 0;
297235537Sgber
298235537Sgber	return (n);
299235537Sgber}
300235537Sgber
301235537Sgberstatic int
302235537Sgbernandfs_write_superblock_at(struct nandfs_device *fsdev,
303235537Sgber    struct nandfs_fsarea *fstp)
304235537Sgber{
305235537Sgber	struct nandfs_super_block *super, *supert;
306235537Sgber	struct buf *bp;
307235537Sgber	int sb_per_sector, sbs_in_fsd, read_block;
308235537Sgber	int index, pos, error;
309235537Sgber	off_t offset;
310235537Sgber
311235537Sgber	DPRINTF(SYNC, ("%s: last_used %d nandfs_sblocks_in_esize %d\n",
312235537Sgber	    __func__, fstp->last_used, nandfs_sblocks_in_esize(fsdev)));
313235537Sgber	if (fstp->last_used == nandfs_sblocks_in_esize(fsdev) - 1)
314235537Sgber		index = 0;
315235537Sgber	else
316235537Sgber		index = fstp->last_used + 1;
317235537Sgber
318235537Sgber	super = &fsdev->nd_super;
319235537Sgber	supert = NULL;
320235537Sgber
321235537Sgber	sb_per_sector = nandfs_sblocks_in_block(fsdev);
322235537Sgber	sbs_in_fsd = sizeof(struct nandfs_fsdata) /
323235537Sgber	    sizeof(struct nandfs_super_block);
324235537Sgber	index += sbs_in_fsd;
325235537Sgber	offset = fstp->offset;
326235537Sgber
327235537Sgber	DPRINTF(SYNC, ("%s: offset %#jx s_last_pseg %#jx s_last_cno %#jx "
328235537Sgber	    "s_last_seq %#jx wtime %jd index %d\n", __func__, offset,
329235537Sgber	    super->s_last_pseg, super->s_last_cno, super->s_last_seq,
330235537Sgber	    super->s_wtime, index));
331235537Sgber
332235537Sgber	read_block = btodb(offset + ((index / sb_per_sector) * sb_per_sector)
333235537Sgber	    * sizeof(struct nandfs_super_block));
334235537Sgber
335235537Sgber	DPRINTF(SYNC, ("%s: read_block %#x\n", __func__, read_block));
336235537Sgber
337235537Sgber	if (index == sbs_in_fsd) {
338235537Sgber		error = nandfs_erase(fsdev, offset, fsdev->nd_erasesize);
339235537Sgber		if (error)
340235537Sgber			return (error);
341235537Sgber
342235537Sgber		error = bread(fsdev->nd_devvp, btodb(offset),
343235537Sgber		    fsdev->nd_devblocksize, NOCRED, &bp);
344235537Sgber		if (error) {
345235537Sgber			printf("NANDFS: couldn't read initial data: %d\n",
346235537Sgber			    error);
347235537Sgber			brelse(bp);
348235537Sgber			return (error);
349235537Sgber		}
350235537Sgber		memcpy(bp->b_data, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata));
351235537Sgber		/*
352235537Sgber		 * 0xff-out the rest. This bp could be cached, so potentially
353235537Sgber		 * b_data contains stale super blocks.
354235537Sgber		 *
355235537Sgber		 * We don't mind cached bp since most of the time we just add
356235537Sgber		 * super blocks to already 0xff-out b_data and don't need to
357235537Sgber		 * perform actual read.
358235537Sgber		 */
359235537Sgber		if (fsdev->nd_devblocksize > sizeof(fsdev->nd_fsdata))
360235537Sgber			memset(bp->b_data + sizeof(fsdev->nd_fsdata), 0xff,
361235537Sgber			    fsdev->nd_devblocksize - sizeof(fsdev->nd_fsdata));
362235537Sgber		error = bwrite(bp);
363235537Sgber		if (error) {
364235537Sgber			printf("NANDFS: cannot rewrite initial data at %jx\n",
365235537Sgber			    offset);
366235537Sgber			return (error);
367235537Sgber		}
368235537Sgber	}
369235537Sgber
370235537Sgber	error = bread(fsdev->nd_devvp, read_block, fsdev->nd_devblocksize,
371235537Sgber	    NOCRED, &bp);
372235537Sgber	if (error) {
373235537Sgber		brelse(bp);
374235537Sgber		return (error);
375235537Sgber	}
376235537Sgber
377235537Sgber	supert = (struct nandfs_super_block *)(bp->b_data);
378235537Sgber	pos = index % sb_per_sector;
379235537Sgber
380235537Sgber	DPRINTF(SYNC, ("%s: storing at %d\n", __func__, pos));
381235537Sgber	memcpy(&supert[pos], super, sizeof(struct nandfs_super_block));
382235537Sgber
383235537Sgber	/*
384235537Sgber	 * See comment above in code that performs erase.
385235537Sgber	 */
386235537Sgber	if (pos == 0)
387235537Sgber		memset(&supert[1], 0xff,
388235537Sgber		    (sb_per_sector - 1) * sizeof(struct nandfs_super_block));
389235537Sgber
390235537Sgber	error = bwrite(bp);
391235537Sgber	if (error) {
392235537Sgber		printf("NANDFS: cannot update superblock at %jx\n", offset);
393235537Sgber		return (error);
394235537Sgber	}
395235537Sgber
396235537Sgber	DPRINTF(SYNC, ("%s: fstp->last_used %d -> %d\n", __func__,
397235537Sgber	    fstp->last_used, index - sbs_in_fsd));
398235537Sgber	fstp->last_used = index - sbs_in_fsd;
399235537Sgber
400235537Sgber	return (0);
401235537Sgber}
402235537Sgber
403235537Sgberint
404235537Sgbernandfs_write_superblock(struct nandfs_device *fsdev)
405235537Sgber{
406235537Sgber	struct nandfs_super_block *super;
407235537Sgber	struct timespec ts;
408235537Sgber	int error;
409235537Sgber	int i, j;
410235537Sgber
411235537Sgber	vfs_timestamp(&ts);
412235537Sgber
413235537Sgber	super = &fsdev->nd_super;
414235537Sgber
415235537Sgber	super->s_last_pseg = fsdev->nd_last_pseg;
416235537Sgber	super->s_last_cno = fsdev->nd_last_cno;
417235537Sgber	super->s_last_seq = fsdev->nd_seg_sequence;
418235537Sgber	super->s_wtime = ts.tv_sec;
419235537Sgber
420235537Sgber	nandfs_calc_superblock_crc(&fsdev->nd_fsdata, super);
421235537Sgber
422235537Sgber	error = 0;
423235537Sgber	for (i = 0, j = fsdev->nd_last_fsarea; i < NANDFS_NFSAREAS;
424235537Sgber	    i++, j = (j + 1 % NANDFS_NFSAREAS)) {
425235537Sgber		if (fsdev->nd_fsarea[j].flags & NANDFS_FSSTOR_FAILED) {
426235537Sgber			DPRINTF(SYNC, ("%s: skipping %d\n", __func__, j));
427235537Sgber			continue;
428235537Sgber		}
429235537Sgber		error = nandfs_write_superblock_at(fsdev, &fsdev->nd_fsarea[j]);
430235537Sgber		if (error) {
431235537Sgber			printf("NANDFS: writing superblock at offset %d failed:"
432235537Sgber			    "%d\n", j * fsdev->nd_erasesize, error);
433235537Sgber			fsdev->nd_fsarea[j].flags |= NANDFS_FSSTOR_FAILED;
434235537Sgber		} else
435235537Sgber			break;
436235537Sgber	}
437235537Sgber
438235537Sgber	if (i == NANDFS_NFSAREAS) {
439235537Sgber		printf("NANDFS: superblock was not written\n");
440235537Sgber		/*
441235537Sgber		 * TODO: switch to read-only?
442235537Sgber		 */
443235537Sgber		return (error);
444235537Sgber	} else
445235537Sgber		fsdev->nd_last_fsarea = (j + 1) % NANDFS_NFSAREAS;
446235537Sgber
447235537Sgber	return (0);
448235537Sgber}
449235537Sgber
450235537Sgberstatic int
451235537Sgbernandfs_select_fsdata(struct nandfs_device *fsdev,
452235537Sgber    struct nandfs_fsdata *fsdatat, struct nandfs_fsdata **fsdata, int nfsds)
453235537Sgber{
454235537Sgber	int i;
455235537Sgber
456235537Sgber	*fsdata = NULL;
457235537Sgber	for (i = 0; i < nfsds; i++) {
458235537Sgber		DPRINTF(VOLUMES, ("%s: i %d f_magic %x f_crc %x\n", __func__,
459235537Sgber		    i, fsdatat[i].f_magic, fsdatat[i].f_sum));
460235537Sgber		if (!nandfs_check_fsdata_crc(&fsdatat[i]))
461235537Sgber			continue;
462235537Sgber		*fsdata = &fsdatat[i];
463235537Sgber		break;
464235537Sgber	}
465235537Sgber
466235537Sgber	return (*fsdata != NULL ? 0 : EINVAL);
467235537Sgber}
468235537Sgber
469235537Sgberstatic int
470235537Sgbernandfs_select_sb(struct nandfs_device *fsdev,
471235537Sgber    struct nandfs_super_block *supert, struct nandfs_super_block **super,
472235537Sgber    int nsbs)
473235537Sgber{
474235537Sgber	int i;
475235537Sgber
476235537Sgber	*super = NULL;
477235537Sgber	for (i = 0; i < nsbs; i++) {
478235537Sgber		if (!nandfs_check_superblock_crc(&fsdev->nd_fsdata, &supert[i]))
479235537Sgber			continue;
480235537Sgber		DPRINTF(SYNC, ("%s: i %d s_last_cno %jx s_magic %x "
481235537Sgber		    "s_wtime %jd\n", __func__, i, supert[i].s_last_cno,
482235537Sgber		    supert[i].s_magic, supert[i].s_wtime));
483235537Sgber		if (*super == NULL || supert[i].s_last_cno >
484235537Sgber		    (*super)->s_last_cno)
485235537Sgber			*super = &supert[i];
486235537Sgber	}
487235537Sgber
488235537Sgber	return (*super != NULL ? 0 : EINVAL);
489235537Sgber}
490235537Sgber
491235537Sgberstatic int
492235537Sgbernandfs_read_structures_at(struct nandfs_device *fsdev,
493235537Sgber    struct nandfs_fsarea *fstp, struct nandfs_fsdata *fsdata,
494235537Sgber    struct nandfs_super_block *super)
495235537Sgber{
496235537Sgber	struct nandfs_super_block *tsuper, *tsuperd;
497235537Sgber	struct buf *bp;
498235537Sgber	int error, read_size;
499235537Sgber	int i;
500235537Sgber	int offset;
501235537Sgber
502235537Sgber	offset = fstp->offset;
503235537Sgber
504235537Sgber	if (fsdev->nd_erasesize > MAXBSIZE)
505235537Sgber		read_size = MAXBSIZE;
506235537Sgber	else
507235537Sgber		read_size = fsdev->nd_erasesize;
508235537Sgber
509235537Sgber	error = bread(fsdev->nd_devvp, btodb(offset), read_size, NOCRED, &bp);
510235537Sgber	if (error) {
511235537Sgber		printf("couldn't read: %d\n", error);
512235537Sgber		brelse(bp);
513235537Sgber		fstp->flags |= NANDFS_FSSTOR_FAILED;
514235537Sgber		return (error);
515235537Sgber	}
516235537Sgber
517235537Sgber	tsuper = super;
518235537Sgber
519235537Sgber	memcpy(fsdata, bp->b_data, sizeof(struct nandfs_fsdata));
520235537Sgber	memcpy(tsuper, (bp->b_data + sizeof(struct nandfs_fsdata)),
521235537Sgber	    read_size - sizeof(struct nandfs_fsdata));
522235537Sgber	brelse(bp);
523235537Sgber
524235537Sgber	tsuper += (read_size - sizeof(struct nandfs_fsdata)) /
525235537Sgber	    sizeof(struct nandfs_super_block);
526235537Sgber
527235537Sgber	for (i = 1; i < fsdev->nd_erasesize / read_size; i++) {
528235537Sgber		error = bread(fsdev->nd_devvp, btodb(offset + i * read_size),
529235537Sgber		    read_size, NOCRED, &bp);
530235537Sgber		if (error) {
531235537Sgber			printf("couldn't read: %d\n", error);
532235537Sgber			brelse(bp);
533235537Sgber			fstp->flags |= NANDFS_FSSTOR_FAILED;
534235537Sgber			return (error);
535235537Sgber		}
536235537Sgber		memcpy(tsuper, bp->b_data, read_size);
537235537Sgber		tsuper += read_size / sizeof(struct nandfs_super_block);
538235537Sgber		brelse(bp);
539235537Sgber	}
540235537Sgber
541235537Sgber	tsuper -= 1;
542235537Sgber	fstp->last_used = nandfs_sblocks_in_esize(fsdev) - 1;
543235537Sgber	for (tsuperd = super - 1; (tsuper != tsuperd); tsuper -= 1) {
544235537Sgber		if (nandfs_is_empty((u_char *)tsuper, sizeof(*tsuper)))
545235537Sgber			fstp->last_used--;
546235537Sgber		else
547235537Sgber			break;
548235537Sgber	}
549235537Sgber
550235537Sgber	DPRINTF(VOLUMES, ("%s: last_used %d\n", __func__, fstp->last_used));
551235537Sgber
552235537Sgber	return (0);
553235537Sgber}
554235537Sgber
555235537Sgberstatic int
556235537Sgbernandfs_read_structures(struct nandfs_device *fsdev)
557235537Sgber{
558235537Sgber	struct nandfs_fsdata *fsdata, *fsdatat;
559235537Sgber	struct nandfs_super_block *sblocks, *ssblock;
560235537Sgber	int nsbs, nfsds, i;
561235537Sgber	int error = 0;
562235537Sgber	int nrsbs;
563235537Sgber
564235537Sgber	nfsds = NANDFS_NFSAREAS;
565235537Sgber	nsbs = nandfs_max_sblocks(fsdev);
566235537Sgber
567235537Sgber	fsdatat = malloc(sizeof(struct nandfs_fsdata) * nfsds, M_NANDFSTEMP,
568235537Sgber	    M_WAITOK | M_ZERO);
569235537Sgber	sblocks = malloc(sizeof(struct nandfs_super_block) * nsbs, M_NANDFSTEMP,
570235537Sgber	    M_WAITOK | M_ZERO);
571235537Sgber
572235537Sgber	nrsbs = 0;
573235537Sgber	for (i = 0; i < NANDFS_NFSAREAS; i++) {
574235537Sgber		fsdev->nd_fsarea[i].offset = i * fsdev->nd_erasesize;
575235537Sgber		error = nandfs_read_structures_at(fsdev, &fsdev->nd_fsarea[i],
576235537Sgber		    &fsdatat[i], sblocks + nrsbs);
577235537Sgber		if (error)
578235537Sgber			continue;
579235537Sgber		nrsbs += (fsdev->nd_fsarea[i].last_used + 1);
580235537Sgber		if (fsdev->nd_fsarea[fsdev->nd_last_fsarea].last_used >
581235537Sgber		    fsdev->nd_fsarea[i].last_used)
582235537Sgber			fsdev->nd_last_fsarea = i;
583235537Sgber	}
584235537Sgber
585235537Sgber	if (nrsbs == 0) {
586235537Sgber		printf("nandfs: no valid superblocks found\n");
587235537Sgber		error = EINVAL;
588235537Sgber		goto out;
589235537Sgber	}
590235537Sgber
591235537Sgber	error = nandfs_select_fsdata(fsdev, fsdatat, &fsdata, nfsds);
592235537Sgber	if (error)
593235537Sgber		goto out;
594235537Sgber	memcpy(&fsdev->nd_fsdata, fsdata, sizeof(struct nandfs_fsdata));
595235537Sgber
596235537Sgber	error = nandfs_select_sb(fsdev, sblocks, &ssblock, nsbs);
597235537Sgber	if (error)
598235537Sgber		goto out;
599235537Sgber
600235537Sgber	memcpy(&fsdev->nd_super, ssblock, sizeof(struct nandfs_super_block));
601235537Sgberout:
602235537Sgber	free(fsdatat, M_NANDFSTEMP);
603235537Sgber	free(sblocks, M_NANDFSTEMP);
604235537Sgber
605235537Sgber	if (error == 0)
606235537Sgber		DPRINTF(VOLUMES, ("%s: selected sb with w_time %jd "
607235537Sgber		    "last_pseg %#jx\n", __func__, fsdev->nd_super.s_wtime,
608235537Sgber		    fsdev->nd_super.s_last_pseg));
609235537Sgber
610235537Sgber	return (error);
611235537Sgber}
612235537Sgber
613235537Sgberstatic void
614235537Sgbernandfs_unmount_base(struct nandfs_device *nandfsdev)
615235537Sgber{
616235537Sgber	int error;
617235537Sgber
618235537Sgber	if (!nandfsdev)
619235537Sgber		return;
620235537Sgber
621235537Sgber	/* Remove all our information */
622235537Sgber	error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0);
623235537Sgber	if (error) {
624235537Sgber		/*
625235537Sgber		 * Flushing buffers failed when fs was umounting, can't do
626235537Sgber		 * much now, just printf error and continue with umount.
627235537Sgber		 */
628235537Sgber		nandfs_error("%s(): error:%d when umounting FS\n",
629235537Sgber		    __func__, error);
630235537Sgber	}
631235537Sgber
632235537Sgber	/* Release the device's system nodes */
633235537Sgber	nandfs_release_system_nodes(nandfsdev);
634235537Sgber}
635235537Sgber
636235537Sgberstatic void
637235537Sgbernandfs_get_ncleanseg(struct nandfs_device *nandfsdev)
638235537Sgber{
639235537Sgber	struct nandfs_seg_stat nss;
640235537Sgber
641235537Sgber	nandfs_get_seg_stat(nandfsdev, &nss);
642235537Sgber	nandfsdev->nd_clean_segs = nss.nss_ncleansegs;
643235537Sgber	DPRINTF(VOLUMES, ("nandfs_mount: clean segs: %jx\n",
644235537Sgber	    (uintmax_t)nandfsdev->nd_clean_segs));
645235537Sgber}
646235537Sgber
647235537Sgber
648235537Sgberstatic int
649235537Sgbernandfs_mount_base(struct nandfs_device *nandfsdev, struct mount *mp,
650235537Sgber    struct nandfs_args *args)
651235537Sgber{
652235537Sgber	uint32_t log_blocksize;
653235537Sgber	int error;
654235537Sgber
655235537Sgber	/* Flush out any old buffers remaining from a previous use. */
656235537Sgber	if ((error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0)))
657235537Sgber		return (error);
658235537Sgber
659235537Sgber	error = nandfs_read_structures(nandfsdev);
660235537Sgber	if (error) {
661235537Sgber		printf("nandfs: could not get valid filesystem structures\n");
662235537Sgber		return (error);
663235537Sgber	}
664235537Sgber
665235537Sgber	if (nandfsdev->nd_fsdata.f_rev_level != NANDFS_CURRENT_REV) {
666235537Sgber		printf("nandfs: unsupported file system revision: %d "
667235537Sgber		    "(supported is %d).\n", nandfsdev->nd_fsdata.f_rev_level,
668235537Sgber		    NANDFS_CURRENT_REV);
669235537Sgber		return (EINVAL);
670235537Sgber	}
671235537Sgber
672235537Sgber	if (nandfsdev->nd_fsdata.f_erasesize != nandfsdev->nd_erasesize) {
673235537Sgber		printf("nandfs: erasesize mismatch (device %#x, fs %#x)\n",
674235537Sgber		    nandfsdev->nd_erasesize, nandfsdev->nd_fsdata.f_erasesize);
675235537Sgber		return (EINVAL);
676235537Sgber	}
677235537Sgber
678235537Sgber	/* Get our blocksize */
679235537Sgber	log_blocksize = nandfsdev->nd_fsdata.f_log_block_size;
680235537Sgber	nandfsdev->nd_blocksize = (uint64_t) 1 << (log_blocksize + 10);
681235537Sgber	DPRINTF(VOLUMES, ("%s: blocksize:%x\n", __func__,
682235537Sgber	    nandfsdev->nd_blocksize));
683235537Sgber
684235537Sgber	DPRINTF(VOLUMES, ("%s: accepted super block with cp %#jx\n", __func__,
685235537Sgber	    (uintmax_t)nandfsdev->nd_super.s_last_cno));
686235537Sgber
687235537Sgber	/* Calculate dat structure parameters */
688235537Sgber	nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_dat_mdt,
689235537Sgber	    nandfsdev->nd_fsdata.f_dat_entry_size);
690235537Sgber	nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_ifile_mdt,
691235537Sgber	    nandfsdev->nd_fsdata.f_inode_size);
692235537Sgber
693235537Sgber	/* Search for the super root and roll forward when needed */
694235537Sgber	if (nandfs_search_super_root(nandfsdev)) {
695235537Sgber		printf("Cannot find valid SuperRoot\n");
696235537Sgber		return (EINVAL);
697235537Sgber	}
698235537Sgber
699235537Sgber	nandfsdev->nd_mount_state = nandfsdev->nd_super.s_state;
700235537Sgber	if (nandfsdev->nd_mount_state != NANDFS_VALID_FS) {
701235537Sgber		printf("FS is seriously damaged, needs repairing\n");
702235537Sgber		printf("aborting mount\n");
703235537Sgber		return (EINVAL);
704235537Sgber	}
705235537Sgber
706235537Sgber	/*
707235537Sgber	 * FS should be ok now. The superblock and the last segsum could be
708235537Sgber	 * updated from the repair so extract running values again.
709235537Sgber	 */
710235537Sgber	nandfsdev->nd_last_pseg = nandfsdev->nd_super.s_last_pseg;
711235537Sgber	nandfsdev->nd_seg_sequence = nandfsdev->nd_super.s_last_seq;
712235537Sgber	nandfsdev->nd_seg_num = nandfs_get_segnum_of_block(nandfsdev,
713235537Sgber	    nandfsdev->nd_last_pseg);
714235537Sgber	nandfsdev->nd_next_seg_num = nandfs_get_segnum_of_block(nandfsdev,
715235537Sgber	    nandfsdev->nd_last_segsum.ss_next);
716235537Sgber	nandfsdev->nd_ts.tv_sec = nandfsdev->nd_last_segsum.ss_create;
717235537Sgber	nandfsdev->nd_last_cno = nandfsdev->nd_super.s_last_cno;
718235537Sgber	nandfsdev->nd_fakevblk = 1;
719235537Sgber	nandfsdev->nd_last_ino  = NANDFS_USER_INO;
720235537Sgber	DPRINTF(VOLUMES, ("%s: last_pseg %#jx last_cno %#jx last_seq %#jx\n"
721235537Sgber	    "fsdev: last_seg: seq %#jx num %#jx, next_seg_num %#jx\n",
722235537Sgber	    __func__, (uintmax_t)nandfsdev->nd_last_pseg,
723235537Sgber	    (uintmax_t)nandfsdev->nd_last_cno,
724235537Sgber	    (uintmax_t)nandfsdev->nd_seg_sequence,
725235537Sgber	    (uintmax_t)nandfsdev->nd_seg_sequence,
726235537Sgber	    (uintmax_t)nandfsdev->nd_seg_num,
727235537Sgber	    (uintmax_t)nandfsdev->nd_next_seg_num));
728235537Sgber
729235537Sgber	DPRINTF(VOLUMES, ("nandfs_mount: accepted super root\n"));
730235537Sgber
731235537Sgber	/* Create system vnodes for DAT, CP and SEGSUM */
732235537Sgber	error = nandfs_create_system_nodes(nandfsdev);
733235537Sgber	if (error)
734235537Sgber		nandfs_unmount_base(nandfsdev);
735235537Sgber
736235537Sgber	nandfs_get_ncleanseg(nandfsdev);
737235537Sgber
738235537Sgber	return (error);
739235537Sgber}
740235537Sgber
741235537Sgberstatic void
742235537Sgbernandfs_unmount_device(struct nandfs_device *nandfsdev)
743235537Sgber{
744235537Sgber
745235537Sgber	/* Is there anything? */
746235537Sgber	if (nandfsdev == NULL)
747235537Sgber		return;
748235537Sgber
749235537Sgber	/* Remove the device only if we're the last reference */
750235537Sgber	nandfsdev->nd_refcnt--;
751235537Sgber	if (nandfsdev->nd_refcnt >= 1)
752235537Sgber		return;
753235537Sgber
754235537Sgber	MPASS(nandfsdev->nd_syncer == NULL);
755235537Sgber	MPASS(nandfsdev->nd_cleaner == NULL);
756235537Sgber	MPASS(nandfsdev->nd_free_base == NULL);
757235537Sgber
758235537Sgber	/* Unmount our base */
759235537Sgber	nandfs_unmount_base(nandfsdev);
760235537Sgber
761235537Sgber	/* Remove from our device list */
762235537Sgber	SLIST_REMOVE(&nandfs_devices, nandfsdev, nandfs_device, nd_next_device);
763235537Sgber
764235537Sgber	DROP_GIANT();
765235537Sgber	g_topology_lock();
766235537Sgber	g_vfs_close(nandfsdev->nd_gconsumer);
767235537Sgber	g_topology_unlock();
768235537Sgber	PICKUP_GIANT();
769235537Sgber
770235537Sgber	DPRINTF(VOLUMES, ("closing device\n"));
771235537Sgber
772235537Sgber	/* Clear our mount reference and release device node */
773235537Sgber	vrele(nandfsdev->nd_devvp);
774235537Sgber
775235537Sgber	dev_rel(nandfsdev->nd_devvp->v_rdev);
776235537Sgber
777235537Sgber	/* Free our device info */
778235537Sgber	cv_destroy(&nandfsdev->nd_sync_cv);
779235537Sgber	mtx_destroy(&nandfsdev->nd_sync_mtx);
780235537Sgber	cv_destroy(&nandfsdev->nd_clean_cv);
781235537Sgber	mtx_destroy(&nandfsdev->nd_clean_mtx);
782235537Sgber	mtx_destroy(&nandfsdev->nd_mutex);
783235537Sgber	lockdestroy(&nandfsdev->nd_seg_const);
784235537Sgber	free(nandfsdev, M_NANDFSMNT);
785235537Sgber}
786235537Sgber
787235537Sgberstatic int
788235537Sgbernandfs_check_mounts(struct nandfs_device *nandfsdev, struct mount *mp,
789235537Sgber    struct nandfs_args *args)
790235537Sgber{
791235537Sgber	struct nandfsmount *nmp;
792235537Sgber	uint64_t last_cno;
793235537Sgber
794235537Sgber	/* no double-mounting of the same checkpoint */
795235537Sgber	STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
796235537Sgber		if (nmp->nm_mount_args.cpno == args->cpno)
797235537Sgber			return (EBUSY);
798235537Sgber	}
799235537Sgber
800235537Sgber	/* Allow readonly mounts without questioning here */
801235537Sgber	if (mp->mnt_flag & MNT_RDONLY)
802235537Sgber		return (0);
803235537Sgber
804235537Sgber	/* Read/write mount */
805235537Sgber	STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
806235537Sgber		/* Only one RW mount on this device! */
807235537Sgber		if ((nmp->nm_vfs_mountp->mnt_flag & MNT_RDONLY)==0)
808235537Sgber			return (EROFS);
809235537Sgber		/* RDONLY on last mountpoint is device busy */
810235537Sgber		last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno;
811235537Sgber		if (nmp->nm_mount_args.cpno == last_cno)
812235537Sgber			return (EBUSY);
813235537Sgber	}
814235537Sgber
815235537Sgber	/* OK for now */
816235537Sgber	return (0);
817235537Sgber}
818235537Sgber
819235537Sgberstatic int
820235537Sgbernandfs_mount_device(struct vnode *devvp, struct mount *mp,
821235537Sgber    struct nandfs_args *args, struct nandfs_device **nandfsdev_p)
822235537Sgber{
823235537Sgber	struct nandfs_device *nandfsdev;
824235537Sgber	struct g_provider *pp;
825235537Sgber	struct g_consumer *cp;
826235537Sgber	struct cdev *dev;
827235537Sgber	uint32_t erasesize;
828235537Sgber	int error, size;
829235537Sgber	int ronly;
830235537Sgber
831235537Sgber	DPRINTF(VOLUMES, ("Mounting NANDFS device\n"));
832235537Sgber
833235537Sgber	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
834235537Sgber
835235537Sgber	/* Look up device in our nandfs_mountpoints */
836235537Sgber	*nandfsdev_p = NULL;
837235537Sgber	SLIST_FOREACH(nandfsdev, &nandfs_devices, nd_next_device)
838235537Sgber		if (nandfsdev->nd_devvp == devvp)
839235537Sgber			break;
840235537Sgber
841235537Sgber	if (nandfsdev) {
842235537Sgber		DPRINTF(VOLUMES, ("device already mounted\n"));
843235537Sgber		error = nandfs_check_mounts(nandfsdev, mp, args);
844235537Sgber		if (error)
845235537Sgber			return error;
846235537Sgber		nandfsdev->nd_refcnt++;
847235537Sgber		*nandfsdev_p = nandfsdev;
848235537Sgber
849235537Sgber		if (!ronly) {
850235537Sgber			DROP_GIANT();
851235537Sgber			g_topology_lock();
852235537Sgber			error = g_access(nandfsdev->nd_gconsumer, 0, 1, 0);
853235537Sgber			g_topology_unlock();
854235537Sgber			PICKUP_GIANT();
855235537Sgber		}
856235537Sgber		return (error);
857235537Sgber	}
858235537Sgber
859235537Sgber	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
860235537Sgber	dev = devvp->v_rdev;
861235537Sgber	dev_ref(dev);
862235537Sgber	DROP_GIANT();
863235537Sgber	g_topology_lock();
864235537Sgber	error = g_vfs_open(devvp, &cp, "nandfs", ronly ? 0 : 1);
865235537Sgber	pp = g_dev_getprovider(dev);
866235537Sgber	g_topology_unlock();
867235537Sgber	PICKUP_GIANT();
868235537Sgber	VOP_UNLOCK(devvp, 0);
869235537Sgber	if (error) {
870235537Sgber		dev_rel(dev);
871235537Sgber		return (error);
872235537Sgber	}
873235537Sgber
874235537Sgber	nandfsdev = malloc(sizeof(struct nandfs_device), M_NANDFSMNT, M_WAITOK | M_ZERO);
875235537Sgber
876235537Sgber	/* Initialise */
877235537Sgber	nandfsdev->nd_refcnt = 1;
878235537Sgber	nandfsdev->nd_devvp = devvp;
879235537Sgber	nandfsdev->nd_syncing = 0;
880235537Sgber	nandfsdev->nd_cleaning = 0;
881235537Sgber	nandfsdev->nd_gconsumer = cp;
882235537Sgber	cv_init(&nandfsdev->nd_sync_cv, "nandfssync");
883235537Sgber	mtx_init(&nandfsdev->nd_sync_mtx, "nffssyncmtx", NULL, MTX_DEF);
884235537Sgber	cv_init(&nandfsdev->nd_clean_cv, "nandfsclean");
885235537Sgber	mtx_init(&nandfsdev->nd_clean_mtx, "nffscleanmtx", NULL, MTX_DEF);
886235537Sgber	mtx_init(&nandfsdev->nd_mutex, "nandfsdev lock", NULL, MTX_DEF);
887235537Sgber	lockinit(&nandfsdev->nd_seg_const, PVFS, "nffssegcon", VLKTIMEOUT,
888235537Sgber	    LK_CANRECURSE);
889235537Sgber	STAILQ_INIT(&nandfsdev->nd_mounts);
890235537Sgber
891235537Sgber	nandfsdev->nd_devsize = pp->mediasize;
892235537Sgber	nandfsdev->nd_devblocksize = pp->sectorsize;
893235537Sgber
894235537Sgber	size = sizeof(erasesize);
895235537Sgber	error = g_io_getattr("NAND::blocksize", nandfsdev->nd_gconsumer, &size,
896235537Sgber	    &erasesize);
897235537Sgber	if (error) {
898235537Sgber		DPRINTF(VOLUMES, ("couldn't get erasesize: %d\n", error));
899235537Sgber
900235537Sgber		if (error == ENOIOCTL || error == EOPNOTSUPP) {
901235537Sgber			/*
902235537Sgber			 * We conclude that this is not NAND storage
903235537Sgber			 */
904235537Sgber			nandfsdev->nd_erasesize = NANDFS_DEF_ERASESIZE;
905235537Sgber			nandfsdev->nd_is_nand = 0;
906235537Sgber		} else {
907235537Sgber			DROP_GIANT();
908235537Sgber			g_topology_lock();
909235537Sgber			g_vfs_close(nandfsdev->nd_gconsumer);
910235537Sgber			g_topology_unlock();
911235537Sgber			PICKUP_GIANT();
912235537Sgber			dev_rel(dev);
913235537Sgber			free(nandfsdev, M_NANDFSMNT);
914235537Sgber			return (error);
915235537Sgber		}
916235537Sgber	} else {
917235537Sgber		nandfsdev->nd_erasesize = erasesize;
918235537Sgber		nandfsdev->nd_is_nand = 1;
919235537Sgber	}
920235537Sgber
921235537Sgber	DPRINTF(VOLUMES, ("%s: erasesize %x\n", __func__,
922235537Sgber	    nandfsdev->nd_erasesize));
923235537Sgber
924235537Sgber	/* Register nandfs_device in list */
925235537Sgber	SLIST_INSERT_HEAD(&nandfs_devices, nandfsdev, nd_next_device);
926235537Sgber
927235537Sgber	error = nandfs_mount_base(nandfsdev, mp, args);
928235537Sgber	if (error) {
929235537Sgber		/* Remove all our information */
930235537Sgber		nandfs_unmount_device(nandfsdev);
931235537Sgber		return (EINVAL);
932235537Sgber	}
933235537Sgber
934235537Sgber	nandfsdev->nd_maxfilesize = nandfs_get_maxfilesize(nandfsdev);
935235537Sgber
936235537Sgber	*nandfsdev_p = nandfsdev;
937235537Sgber	DPRINTF(VOLUMES, ("NANDFS device mounted ok\n"));
938235537Sgber
939235537Sgber	return (0);
940235537Sgber}
941235537Sgber
942235537Sgberstatic int
943235537Sgbernandfs_mount_checkpoint(struct nandfsmount *nmp)
944235537Sgber{
945235537Sgber	struct nandfs_cpfile_header *cphdr;
946235537Sgber	struct nandfs_checkpoint *cp;
947235537Sgber	struct nandfs_inode ifile_inode;
948235537Sgber	struct nandfs_node *cp_node;
949235537Sgber	struct buf *bp;
950235537Sgber	uint64_t ncp, nsn, cpno, fcpno, blocknr, last_cno;
951235537Sgber	uint32_t off, dlen;
952235537Sgber	int cp_per_block, error;
953235537Sgber
954235537Sgber	cpno = nmp->nm_mount_args.cpno;
955235537Sgber	if (cpno == 0)
956235537Sgber		cpno = nmp->nm_nandfsdev->nd_super.s_last_cno;
957235537Sgber
958235537Sgber	DPRINTF(VOLUMES, ("%s: trying to mount checkpoint number %"PRIu64"\n",
959235537Sgber	    __func__, cpno));
960235537Sgber
961235537Sgber	cp_node = nmp->nm_nandfsdev->nd_cp_node;
962235537Sgber
963235537Sgber	VOP_LOCK(NTOV(cp_node), LK_SHARED);
964235537Sgber	/* Get cpfile header from 1st block of cp file */
965235537Sgber	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
966235537Sgber	if (error) {
967235537Sgber		brelse(bp);
968235537Sgber		VOP_UNLOCK(NTOV(cp_node), 0);
969235537Sgber		return (error);
970235537Sgber	}
971235537Sgber
972235537Sgber	cphdr = (struct nandfs_cpfile_header *) bp->b_data;
973235537Sgber	ncp = cphdr->ch_ncheckpoints;
974235537Sgber	nsn = cphdr->ch_nsnapshots;
975235537Sgber
976235537Sgber	brelse(bp);
977235537Sgber
978235537Sgber	DPRINTF(VOLUMES, ("mount_nandfs: checkpoint header read in\n"));
979235537Sgber	DPRINTF(VOLUMES, ("\tNumber of checkpoints %"PRIu64"\n", ncp));
980235537Sgber	DPRINTF(VOLUMES, ("\tNumber of snapshots %"PRIu64"\n", nsn));
981235537Sgber
982235537Sgber	/* Read in our specified checkpoint */
983235537Sgber	dlen = nmp->nm_nandfsdev->nd_fsdata.f_checkpoint_size;
984235537Sgber	cp_per_block = nmp->nm_nandfsdev->nd_blocksize / dlen;
985235537Sgber
986235537Sgber	fcpno = cpno + NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
987235537Sgber	blocknr = fcpno / cp_per_block;
988235537Sgber	off = (fcpno % cp_per_block) * dlen;
989235537Sgber	error = nandfs_bread(cp_node, blocknr, NOCRED, 0, &bp);
990235537Sgber	if (error) {
991235537Sgber		brelse(bp);
992235537Sgber		VOP_UNLOCK(NTOV(cp_node), 0);
993235537Sgber		printf("mount_nandfs: couldn't read cp block %"PRIu64"\n",
994235537Sgber		    fcpno);
995235537Sgber		return (EINVAL);
996235537Sgber	}
997235537Sgber
998235537Sgber	/* Needs to be a valid checkpoint */
999235537Sgber	cp = (struct nandfs_checkpoint *) ((uint8_t *) bp->b_data + off);
1000235537Sgber	if (cp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
1001235537Sgber		printf("mount_nandfs: checkpoint marked invalid\n");
1002235537Sgber		brelse(bp);
1003235537Sgber		VOP_UNLOCK(NTOV(cp_node), 0);
1004235537Sgber		return (EINVAL);
1005235537Sgber	}
1006235537Sgber
1007235537Sgber	/* Is this really the checkpoint we want? */
1008235537Sgber	if (cp->cp_cno != cpno) {
1009235537Sgber		printf("mount_nandfs: checkpoint file corrupt? "
1010235537Sgber		    "expected cpno %"PRIu64", found cpno %"PRIu64"\n",
1011235537Sgber		    cpno, cp->cp_cno);
1012235537Sgber		brelse(bp);
1013235537Sgber		VOP_UNLOCK(NTOV(cp_node), 0);
1014235537Sgber		return (EINVAL);
1015235537Sgber	}
1016235537Sgber
1017235537Sgber	/* Check if it's a snapshot ! */
1018235537Sgber	last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno;
1019235537Sgber	if (cpno != last_cno) {
1020235537Sgber		/* Only allow snapshots if not mounting on the last cp */
1021235537Sgber		if ((cp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) == 0) {
1022235537Sgber			printf( "mount_nandfs: checkpoint %"PRIu64" is not a "
1023235537Sgber			    "snapshot\n", cpno);
1024235537Sgber			brelse(bp);
1025235537Sgber			VOP_UNLOCK(NTOV(cp_node), 0);
1026235537Sgber			return (EINVAL);
1027235537Sgber		}
1028235537Sgber	}
1029235537Sgber
1030235537Sgber	ifile_inode = cp->cp_ifile_inode;
1031235537Sgber	brelse(bp);
1032235537Sgber
1033235537Sgber	/* Get ifile inode */
1034235537Sgber	error = nandfs_get_node_raw(nmp->nm_nandfsdev, NULL, NANDFS_IFILE_INO,
1035235537Sgber	    &ifile_inode, &nmp->nm_ifile_node);
1036235537Sgber	if (error) {
1037235537Sgber		printf("mount_nandfs: can't read ifile node\n");
1038235537Sgber		VOP_UNLOCK(NTOV(cp_node), 0);
1039235537Sgber		return (EINVAL);
1040235537Sgber	}
1041235537Sgber
1042235537Sgber	NANDFS_SET_SYSTEMFILE(NTOV(nmp->nm_ifile_node));
1043235537Sgber	VOP_UNLOCK(NTOV(cp_node), 0);
1044235537Sgber	/* Get root node? */
1045235537Sgber
1046235537Sgber	return (0);
1047235537Sgber}
1048235537Sgber
1049235537Sgberstatic void
1050235537Sgberfree_nandfs_mountinfo(struct mount *mp)
1051235537Sgber{
1052235537Sgber	struct nandfsmount *nmp = VFSTONANDFS(mp);
1053235537Sgber
1054235537Sgber	if (nmp == NULL)
1055235537Sgber		return;
1056235537Sgber
1057235537Sgber	free(nmp, M_NANDFSMNT);
1058235537Sgber}
1059235537Sgber
1060235537Sgbervoid
1061235537Sgbernandfs_wakeup_wait_sync(struct nandfs_device *nffsdev, int reason)
1062235537Sgber{
1063235537Sgber	char *reasons[] = {
1064235537Sgber	    "umount",
1065235537Sgber	    "vfssync",
1066235537Sgber	    "bdflush",
1067235537Sgber	    "fforce",
1068235537Sgber	    "fsync",
1069235537Sgber	    "ro_upd"
1070235537Sgber	};
1071235537Sgber
1072235537Sgber	DPRINTF(SYNC, ("%s: %s\n", __func__, reasons[reason]));
1073235537Sgber	mtx_lock(&nffsdev->nd_sync_mtx);
1074235537Sgber	if (nffsdev->nd_syncing)
1075235537Sgber		cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx);
1076235537Sgber	if (reason == SYNCER_UMOUNT)
1077235537Sgber		nffsdev->nd_syncer_exit = 1;
1078235537Sgber	nffsdev->nd_syncing = 1;
1079235537Sgber	wakeup(&nffsdev->nd_syncing);
1080235537Sgber	cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx);
1081235537Sgber
1082235537Sgber	mtx_unlock(&nffsdev->nd_sync_mtx);
1083235537Sgber}
1084235537Sgber
1085235537Sgberstatic void
1086235537Sgbernandfs_gc_finished(struct nandfs_device *nffsdev, int exit)
1087235537Sgber{
1088235537Sgber	int error;
1089235537Sgber
1090235537Sgber	mtx_lock(&nffsdev->nd_sync_mtx);
1091235537Sgber	nffsdev->nd_syncing = 0;
1092235537Sgber	DPRINTF(SYNC, ("%s: cleaner finish\n", __func__));
1093235537Sgber	cv_broadcast(&nffsdev->nd_sync_cv);
1094235537Sgber	mtx_unlock(&nffsdev->nd_sync_mtx);
1095235537Sgber	if (!exit) {
1096235537Sgber		error = tsleep(&nffsdev->nd_syncing, PRIBIO, "-",
1097235537Sgber		    hz * nandfs_sync_interval);
1098235537Sgber		DPRINTF(SYNC, ("%s: cleaner waked up: %d\n",
1099235537Sgber		    __func__, error));
1100235537Sgber	}
1101235537Sgber}
1102235537Sgber
1103235537Sgberstatic void
1104235537Sgbernandfs_syncer(struct nandfsmount *nmp)
1105235537Sgber{
1106235537Sgber	struct nandfs_device *nffsdev;
1107235537Sgber	struct mount *mp;
1108235537Sgber	int flags, error;
1109235537Sgber
1110235537Sgber	mp = nmp->nm_vfs_mountp;
1111235537Sgber	nffsdev = nmp->nm_nandfsdev;
1112235537Sgber	tsleep(&nffsdev->nd_syncing, PRIBIO, "-", hz * nandfs_sync_interval);
1113235537Sgber
1114235537Sgber	while (!nffsdev->nd_syncer_exit) {
1115235537Sgber		DPRINTF(SYNC, ("%s: syncer run\n", __func__));
1116235537Sgber		nffsdev->nd_syncing = 1;
1117235537Sgber
1118235537Sgber		flags = (nmp->nm_flags & (NANDFS_FORCE_SYNCER | NANDFS_UMOUNT));
1119235537Sgber
1120235537Sgber		error = nandfs_segment_constructor(nmp, flags);
1121235537Sgber		if (error)
1122235537Sgber			nandfs_error("%s: error:%d when creating segments\n",
1123235537Sgber			    __func__, error);
1124235537Sgber
1125235537Sgber		nmp->nm_flags &= ~flags;
1126235537Sgber
1127235537Sgber		nandfs_gc_finished(nffsdev, 0);
1128235537Sgber	}
1129235537Sgber
1130235537Sgber	MPASS(nffsdev->nd_cleaner == NULL);
1131235537Sgber	error = nandfs_segment_constructor(nmp,
1132235537Sgber	    NANDFS_FORCE_SYNCER | NANDFS_UMOUNT);
1133235537Sgber	if (error)
1134235537Sgber		nandfs_error("%s: error:%d when creating segments\n",
1135235537Sgber		    __func__, error);
1136235537Sgber	nandfs_gc_finished(nffsdev, 1);
1137235537Sgber	nffsdev->nd_syncer = NULL;
1138235537Sgber	MPASS(nffsdev->nd_free_base == NULL);
1139235537Sgber
1140235537Sgber	DPRINTF(SYNC, ("%s: exiting\n", __func__));
1141235537Sgber	kthread_exit();
1142235537Sgber}
1143235537Sgber
1144235537Sgberstatic int
1145235537Sgberstart_syncer(struct nandfsmount *nmp)
1146235537Sgber{
1147235537Sgber	int error;
1148235537Sgber
1149235537Sgber	MPASS(nmp->nm_nandfsdev->nd_syncer == NULL);
1150235537Sgber
1151235537Sgber	DPRINTF(SYNC, ("%s: start syncer\n", __func__));
1152235537Sgber
1153235537Sgber	nmp->nm_nandfsdev->nd_syncer_exit = 0;
1154235537Sgber
1155235537Sgber	error = kthread_add((void(*)(void *))nandfs_syncer, nmp, NULL,
1156235537Sgber	    &nmp->nm_nandfsdev->nd_syncer, 0, 0, "nandfs_syncer");
1157235537Sgber
1158235537Sgber	if (error)
1159235537Sgber		printf("nandfs: could not start syncer: %d\n", error);
1160235537Sgber
1161235537Sgber	return (error);
1162235537Sgber}
1163235537Sgber
1164235537Sgberstatic int
1165235537Sgberstop_syncer(struct nandfsmount *nmp)
1166235537Sgber{
1167235537Sgber
1168235537Sgber	MPASS(nmp->nm_nandfsdev->nd_syncer != NULL);
1169235537Sgber
1170235537Sgber	nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_UMOUNT);
1171235537Sgber
1172235537Sgber	DPRINTF(SYNC, ("%s: stop syncer\n", __func__));
1173235537Sgber	return (0);
1174235537Sgber}
1175235537Sgber
1176235537Sgber/*
1177235537Sgber * Mount null layer
1178235537Sgber */
1179235537Sgberstatic int
1180235537Sgbernandfs_mount(struct mount *mp)
1181235537Sgber{
1182235537Sgber	struct nandfsmount *nmp;
1183235537Sgber	struct vnode *devvp;
1184235537Sgber	struct nameidata nd;
1185235537Sgber	struct vfsoptlist *opts;
1186235537Sgber	struct thread *td;
1187235537Sgber	char *from;
1188235537Sgber	int error = 0, flags;
1189235537Sgber
1190235537Sgber	DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp));
1191235537Sgber
1192235537Sgber	td = curthread;
1193235537Sgber	opts = mp->mnt_optnew;
1194235537Sgber
1195235537Sgber	if (vfs_filteropt(opts, nandfs_opts))
1196235537Sgber		return (EINVAL);
1197235537Sgber
1198235537Sgber	/*
1199235537Sgber	 * Update is a no-op
1200235537Sgber	 */
1201235537Sgber	if (mp->mnt_flag & MNT_UPDATE) {
1202235537Sgber		nmp = VFSTONANDFS(mp);
1203235537Sgber		if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) {
1204235537Sgber			return (error);
1205235537Sgber		}
1206235537Sgber		if (!(nmp->nm_ronly) && vfs_flagopt(opts, "ro", NULL, 0)) {
1207235537Sgber			vn_start_write(NULL, &mp, V_WAIT);
1208235537Sgber			error = VFS_SYNC(mp, MNT_WAIT);
1209235537Sgber			if (error)
1210235537Sgber				return (error);
1211235537Sgber			vn_finished_write(mp);
1212235537Sgber
1213235537Sgber			flags = WRITECLOSE;
1214235537Sgber			if (mp->mnt_flag & MNT_FORCE)
1215235537Sgber				flags |= FORCECLOSE;
1216235537Sgber
1217235537Sgber			nandfs_wakeup_wait_sync(nmp->nm_nandfsdev,
1218235537Sgber			    SYNCER_ROUPD);
1219235537Sgber			error = vflush(mp, 0, flags, td);
1220235537Sgber			if (error)
1221235537Sgber				return (error);
1222235537Sgber
1223235537Sgber			nandfs_stop_cleaner(nmp->nm_nandfsdev);
1224235537Sgber			stop_syncer(nmp);
1225235537Sgber			DROP_GIANT();
1226235537Sgber			g_topology_lock();
1227235537Sgber			g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1, 0);
1228235537Sgber			g_topology_unlock();
1229235537Sgber			PICKUP_GIANT();
1230235537Sgber			MNT_ILOCK(mp);
1231235537Sgber			mp->mnt_flag |= MNT_RDONLY;
1232235537Sgber			MNT_IUNLOCK(mp);
1233235537Sgber			nmp->nm_ronly = 1;
1234235537Sgber
1235235537Sgber		} else if ((nmp->nm_ronly) &&
1236235537Sgber		    !vfs_flagopt(opts, "ro", NULL, 0)) {
1237235537Sgber			/*
1238235537Sgber			 * Don't allow read-write snapshots.
1239235537Sgber			 */
1240235537Sgber			if (nmp->nm_mount_args.cpno != 0)
1241235537Sgber				return (EROFS);
1242235537Sgber			/*
1243235537Sgber			 * If upgrade to read-write by non-root, then verify
1244235537Sgber			 * that user has necessary permissions on the device.
1245235537Sgber			 */
1246235537Sgber			devvp = nmp->nm_nandfsdev->nd_devvp;
1247235537Sgber			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
1248235537Sgber			error = VOP_ACCESS(devvp, VREAD | VWRITE,
1249235537Sgber			    td->td_ucred, td);
1250235537Sgber			if (error) {
1251235537Sgber				error = priv_check(td, PRIV_VFS_MOUNT_PERM);
1252235537Sgber				if (error) {
1253235537Sgber					VOP_UNLOCK(devvp, 0);
1254235537Sgber					return (error);
1255235537Sgber				}
1256235537Sgber			}
1257235537Sgber
1258235537Sgber			VOP_UNLOCK(devvp, 0);
1259235537Sgber			DROP_GIANT();
1260235537Sgber			g_topology_lock();
1261235537Sgber			error = g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, 1,
1262235537Sgber			    0);
1263235537Sgber			g_topology_unlock();
1264235537Sgber			PICKUP_GIANT();
1265235537Sgber			if (error)
1266235537Sgber				return (error);
1267235537Sgber
1268235537Sgber			MNT_ILOCK(mp);
1269235537Sgber			mp->mnt_flag &= ~MNT_RDONLY;
1270235537Sgber			MNT_IUNLOCK(mp);
1271235537Sgber			error = start_syncer(nmp);
1272235537Sgber			if (error == 0)
1273235537Sgber				error = nandfs_start_cleaner(nmp->nm_nandfsdev);
1274235537Sgber			if (error) {
1275235537Sgber				DROP_GIANT();
1276235537Sgber				g_topology_lock();
1277235537Sgber				g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1,
1278235537Sgber				    0);
1279235537Sgber				g_topology_unlock();
1280235537Sgber				PICKUP_GIANT();
1281235537Sgber				return (error);
1282235537Sgber			}
1283235537Sgber
1284235537Sgber			nmp->nm_ronly = 0;
1285235537Sgber		}
1286235537Sgber		return (0);
1287235537Sgber	}
1288235537Sgber
1289235537Sgber	from = vfs_getopts(opts, "from", &error);
1290235537Sgber	if (error)
1291235537Sgber		return (error);
1292235537Sgber
1293235537Sgber	/*
1294235537Sgber	 * Find device node
1295235537Sgber	 */
1296235537Sgber	NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, from, curthread);
1297235537Sgber	error = namei(&nd);
1298235537Sgber	if (error)
1299235537Sgber		return (error);
1300235537Sgber	NDFREE(&nd, NDF_ONLY_PNBUF);
1301235537Sgber
1302235537Sgber	devvp = nd.ni_vp;
1303235537Sgber
1304235537Sgber	if (!vn_isdisk(devvp, &error)) {
1305235537Sgber		vput(devvp);
1306235537Sgber		return (error);
1307235537Sgber	}
1308235537Sgber
1309235537Sgber	/* Check the access rights on the mount device */
1310235537Sgber	error = VOP_ACCESS(devvp, VREAD, curthread->td_ucred, curthread);
1311235537Sgber	if (error)
1312235537Sgber		error = priv_check(curthread, PRIV_VFS_MOUNT_PERM);
1313235537Sgber	if (error) {
1314235537Sgber		vput(devvp);
1315235537Sgber		return (error);
1316235537Sgber	}
1317235537Sgber
1318235537Sgber	vfs_getnewfsid(mp);
1319235537Sgber
1320235537Sgber	error = nandfs_mountfs(devvp, mp);
1321235537Sgber	if (error)
1322235537Sgber		return (error);
1323235537Sgber	vfs_mountedfrom(mp, from);
1324235537Sgber
1325235537Sgber	return (0);
1326235537Sgber}
1327235537Sgber
1328235537Sgberstatic int
1329235537Sgbernandfs_mountfs(struct vnode *devvp, struct mount *mp)
1330235537Sgber{
1331235537Sgber	struct nandfsmount *nmp = NULL;
1332235537Sgber	struct nandfs_args *args = NULL;
1333235537Sgber	struct nandfs_device *nandfsdev;
1334235537Sgber	char *from;
1335235537Sgber	int error, ronly;
1336235537Sgber	char *cpno;
1337235537Sgber
1338235537Sgber	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
1339235537Sgber
1340235537Sgber	if (devvp->v_rdev->si_iosize_max != 0)
1341235537Sgber		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
1342235537Sgber	VOP_UNLOCK(devvp, 0);
1343235537Sgber
1344235537Sgber	if (mp->mnt_iosize_max > MAXPHYS)
1345235537Sgber		mp->mnt_iosize_max = MAXPHYS;
1346235537Sgber
1347235537Sgber	from = vfs_getopts(mp->mnt_optnew, "from", &error);
1348235537Sgber	if (error)
1349235537Sgber		goto error;
1350235537Sgber
1351235537Sgber	error = vfs_getopt(mp->mnt_optnew, "snap", (void **)&cpno, NULL);
1352235537Sgber	if (error == ENOENT)
1353235537Sgber		cpno = NULL;
1354235537Sgber	else if (error)
1355235537Sgber		goto error;
1356235537Sgber
1357235537Sgber	args = (struct nandfs_args *)malloc(sizeof(struct nandfs_args),
1358235537Sgber	    M_NANDFSMNT, M_WAITOK | M_ZERO);
1359235537Sgber
1360235537Sgber	if (cpno != NULL)
1361235537Sgber		args->cpno = strtoul(cpno, (char **)NULL, 10);
1362235537Sgber	else
1363235537Sgber		args->cpno = 0;
1364235537Sgber	args->fspec = from;
1365235537Sgber
1366235537Sgber	if (args->cpno != 0 && !ronly) {
1367235537Sgber		error = EROFS;
1368235537Sgber		goto error;
1369235537Sgber	}
1370235537Sgber
1371235537Sgber	printf("WARNING: NANDFS is considered to be a highly experimental "
1372235537Sgber	    "feature in FreeBSD.\n");
1373235537Sgber
1374235537Sgber	error = nandfs_mount_device(devvp, mp, args, &nandfsdev);
1375235537Sgber	if (error)
1376235537Sgber		goto error;
1377235537Sgber
1378235537Sgber	nmp = (struct nandfsmount *) malloc(sizeof(struct nandfsmount),
1379235537Sgber	    M_NANDFSMNT, M_WAITOK | M_ZERO);
1380235537Sgber
1381235537Sgber	mp->mnt_data = nmp;
1382235537Sgber	nmp->nm_vfs_mountp = mp;
1383235537Sgber	nmp->nm_ronly = ronly;
1384235537Sgber	MNT_ILOCK(mp);
1385235537Sgber	mp->mnt_flag |= MNT_LOCAL;
1386235537Sgber	MNT_IUNLOCK(mp);
1387235537Sgber	nmp->nm_nandfsdev = nandfsdev;
1388235537Sgber	/* Add our mountpoint */
1389235537Sgber	STAILQ_INSERT_TAIL(&nandfsdev->nd_mounts, nmp, nm_next_mount);
1390235537Sgber
1391235537Sgber	if (args->cpno > nandfsdev->nd_last_cno) {
1392235537Sgber		printf("WARNING: supplied checkpoint number (%jd) is greater "
1393235537Sgber		    "than last known checkpoint on filesystem (%jd). Mounting"
1394235537Sgber		    " checkpoint %jd\n", (uintmax_t)args->cpno,
1395235537Sgber		    (uintmax_t)nandfsdev->nd_last_cno,
1396235537Sgber		    (uintmax_t)nandfsdev->nd_last_cno);
1397235537Sgber		args->cpno = nandfsdev->nd_last_cno;
1398235537Sgber	}
1399235537Sgber
1400235537Sgber	/* Setting up other parameters */
1401235537Sgber	nmp->nm_mount_args = *args;
1402235537Sgber	free(args, M_NANDFSMNT);
1403235537Sgber	error = nandfs_mount_checkpoint(nmp);
1404235537Sgber	if (error) {
1405235537Sgber		nandfs_unmount(mp, MNT_FORCE);
1406235537Sgber		goto unmounted;
1407235537Sgber	}
1408235537Sgber
1409235537Sgber	if (!ronly) {
1410235537Sgber		error = start_syncer(nmp);
1411235537Sgber		if (error == 0)
1412235537Sgber			error = nandfs_start_cleaner(nmp->nm_nandfsdev);
1413235537Sgber		if (error)
1414235537Sgber			nandfs_unmount(mp, MNT_FORCE);
1415235537Sgber	}
1416235537Sgber
1417235537Sgber	return (0);
1418235537Sgber
1419235537Sgbererror:
1420235537Sgber	if (args != NULL)
1421235537Sgber		free(args, M_NANDFSMNT);
1422235537Sgber
1423235537Sgber	if (nmp != NULL) {
1424235537Sgber		free(nmp, M_NANDFSMNT);
1425235537Sgber		mp->mnt_data = NULL;
1426235537Sgber	}
1427235537Sgberunmounted:
1428235537Sgber	return (error);
1429235537Sgber}
1430235537Sgber
1431235537Sgberstatic int
1432235537Sgbernandfs_unmount(struct mount *mp, int mntflags)
1433235537Sgber{
1434235537Sgber	struct nandfs_device *nandfsdev;
1435235537Sgber	struct nandfsmount *nmp;
1436235537Sgber	int error;
1437235537Sgber	int flags = 0;
1438235537Sgber
1439235537Sgber	DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp));
1440235537Sgber
1441235537Sgber	if (mntflags & MNT_FORCE)
1442235537Sgber		flags |= FORCECLOSE;
1443235537Sgber
1444235537Sgber	nmp = mp->mnt_data;
1445235537Sgber	nandfsdev = nmp->nm_nandfsdev;
1446235537Sgber
1447235537Sgber	error = vflush(mp, 0, flags | SKIPSYSTEM, curthread);
1448235537Sgber	if (error)
1449235537Sgber		return (error);
1450235537Sgber
1451235537Sgber	if (!(nmp->nm_ronly)) {
1452235537Sgber		nandfs_stop_cleaner(nandfsdev);
1453235537Sgber		stop_syncer(nmp);
1454235537Sgber	}
1455235537Sgber
1456235537Sgber	if (nmp->nm_ifile_node)
1457235537Sgber		NANDFS_UNSET_SYSTEMFILE(NTOV(nmp->nm_ifile_node));
1458235537Sgber
1459235537Sgber	/* Remove our mount point */
1460235537Sgber	STAILQ_REMOVE(&nandfsdev->nd_mounts, nmp, nandfsmount, nm_next_mount);
1461235537Sgber
1462235537Sgber	/* Unmount the device itself when we're the last one */
1463235537Sgber	nandfs_unmount_device(nandfsdev);
1464235537Sgber
1465235537Sgber	free_nandfs_mountinfo(mp);
1466235537Sgber
1467235537Sgber	/*
1468235537Sgber	 * Finally, throw away the null_mount structure
1469235537Sgber	 */
1470235537Sgber	mp->mnt_data = 0;
1471235537Sgber	MNT_ILOCK(mp);
1472235537Sgber	mp->mnt_flag &= ~MNT_LOCAL;
1473235537Sgber	MNT_IUNLOCK(mp);
1474235537Sgber
1475235537Sgber	return (0);
1476235537Sgber}
1477235537Sgber
1478235537Sgberstatic int
1479235537Sgbernandfs_statfs(struct mount *mp, struct statfs *sbp)
1480235537Sgber{
1481235537Sgber	struct nandfsmount *nmp;
1482235537Sgber	struct nandfs_device *nandfsdev;
1483235537Sgber	struct nandfs_fsdata *fsdata;
1484235537Sgber	struct nandfs_super_block *sb;
1485235537Sgber	struct nandfs_block_group_desc *groups;
1486235537Sgber	struct nandfs_node *ifile;
1487235537Sgber	struct nandfs_mdt *mdt;
1488235537Sgber	struct buf *bp;
1489235537Sgber	int i, error;
1490235537Sgber	uint32_t entries_per_group;
1491235537Sgber	uint64_t files = 0;
1492235537Sgber
1493235537Sgber	nmp = mp->mnt_data;
1494235537Sgber	nandfsdev = nmp->nm_nandfsdev;
1495235537Sgber	fsdata = &nandfsdev->nd_fsdata;
1496235537Sgber	sb = &nandfsdev->nd_super;
1497235537Sgber	ifile = nmp->nm_ifile_node;
1498235537Sgber	mdt = &nandfsdev->nd_ifile_mdt;
1499235537Sgber	entries_per_group = mdt->entries_per_group;
1500235537Sgber
1501235537Sgber	VOP_LOCK(NTOV(ifile), LK_SHARED);
1502235537Sgber	error = nandfs_bread(ifile, 0, NOCRED, 0, &bp);
1503235537Sgber	if (error) {
1504235537Sgber		brelse(bp);
1505235537Sgber		VOP_UNLOCK(NTOV(ifile), 0);
1506235537Sgber		return (error);
1507235537Sgber	}
1508235537Sgber
1509235537Sgber	groups = (struct nandfs_block_group_desc *)bp->b_data;
1510235537Sgber
1511235537Sgber	for (i = 0; i < mdt->groups_per_desc_block; i++)
1512235537Sgber		files += (entries_per_group - groups[i].bg_nfrees);
1513235537Sgber
1514235537Sgber	brelse(bp);
1515235537Sgber	VOP_UNLOCK(NTOV(ifile), 0);
1516235537Sgber
1517235537Sgber	sbp->f_bsize = nandfsdev->nd_blocksize;
1518235537Sgber	sbp->f_iosize = sbp->f_bsize;
1519235537Sgber	sbp->f_blocks = fsdata->f_blocks_per_segment * fsdata->f_nsegments;
1520235537Sgber	sbp->f_bfree = sb->s_free_blocks_count;
1521235537Sgber	sbp->f_bavail = sbp->f_bfree;
1522235537Sgber	sbp->f_files = files;
1523235537Sgber	sbp->f_ffree = 0;
1524235537Sgber	return (0);
1525235537Sgber}
1526235537Sgber
1527235537Sgberstatic int
1528235537Sgbernandfs_root(struct mount *mp, int flags, struct vnode **vpp)
1529235537Sgber{
1530235537Sgber	struct nandfsmount *nmp = VFSTONANDFS(mp);
1531235537Sgber	struct nandfs_node *node;
1532235537Sgber	int error;
1533235537Sgber
1534235537Sgber	error = nandfs_get_node(nmp, NANDFS_ROOT_INO, &node);
1535235537Sgber	if (error)
1536235537Sgber		return (error);
1537235537Sgber
1538235537Sgber	KASSERT(NTOV(node)->v_vflag & VV_ROOT,
1539235537Sgber	    ("root_vp->v_vflag & VV_ROOT"));
1540235537Sgber
1541235537Sgber	*vpp = NTOV(node);
1542235537Sgber
1543235537Sgber	return (error);
1544235537Sgber}
1545235537Sgber
1546235537Sgberstatic int
1547235537Sgbernandfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
1548235537Sgber{
1549235537Sgber	struct nandfsmount *nmp = VFSTONANDFS(mp);
1550235537Sgber	struct nandfs_node *node;
1551235537Sgber	int error;
1552235537Sgber
1553235537Sgber	error = nandfs_get_node(nmp, ino, &node);
1554235537Sgber	if (node)
1555235537Sgber		*vpp = NTOV(node);
1556235537Sgber
1557235537Sgber	return (error);
1558235537Sgber}
1559235537Sgber
1560235537Sgberstatic int
1561235537Sgbernandfs_sync(struct mount *mp, int waitfor)
1562235537Sgber{
1563235537Sgber	struct nandfsmount *nmp = VFSTONANDFS(mp);
1564235537Sgber
1565235537Sgber	DPRINTF(SYNC, ("%s: mp %p waitfor %d\n", __func__, mp, waitfor));
1566235537Sgber
1567235537Sgber	/*
1568235537Sgber	 * XXX: A hack to be removed soon
1569235537Sgber	 */
1570235537Sgber	if (waitfor == MNT_LAZY)
1571235537Sgber		return (0);
1572235537Sgber	if (waitfor == MNT_SUSPEND)
1573235537Sgber		return (0);
1574235537Sgber	nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_VFS_SYNC);
1575235537Sgber	return (0);
1576235537Sgber}
1577235537Sgber
1578235537Sgberstatic struct vfsops nandfs_vfsops = {
1579235537Sgber	.vfs_init =		nandfs_init,
1580235537Sgber	.vfs_mount =		nandfs_mount,
1581235537Sgber	.vfs_root =		nandfs_root,
1582235537Sgber	.vfs_statfs =		nandfs_statfs,
1583235537Sgber	.vfs_uninit =		nandfs_uninit,
1584235537Sgber	.vfs_unmount =		nandfs_unmount,
1585235537Sgber	.vfs_vget =		nandfs_vget,
1586235537Sgber	.vfs_sync =		nandfs_sync,
1587235537Sgber};
1588235537Sgber
1589235537SgberVFS_SET(nandfs_vfsops, nandfs, VFCF_LOOPBACK);
1590