1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010-2012 Semihalf.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/conf.h>
35#include <sys/kernel.h>
36#include <sys/lock.h>
37#include <sys/malloc.h>
38#include <sys/mount.h>
39#include <sys/mutex.h>
40#include <sys/namei.h>
41#include <sys/sysctl.h>
42#include <sys/vnode.h>
43#include <sys/buf.h>
44#include <sys/bio.h>
45
46#include <vm/vm.h>
47#include <vm/vm_param.h>
48#include <vm/vm_kern.h>
49#include <vm/vm_page.h>
50
51#include "nandfs_mount.h"
52#include "nandfs.h"
53#include "nandfs_subr.h"
54
55
56static int
57nandfs_checkpoint_size(struct nandfs_device *fsdev)
58{
59
60	return (fsdev->nd_fsdata.f_checkpoint_size);
61}
62
63static int
64nandfs_checkpoint_blk_offset(struct nandfs_device *fsdev, uint64_t cn,
65    uint64_t *blk, uint64_t *offset)
66{
67	uint64_t off;
68	uint16_t cp_size, cp_per_blk;
69
70	KASSERT((cn), ("checkpoing cannot be zero"));
71
72	cp_size = fsdev->nd_fsdata.f_checkpoint_size;
73	cp_per_blk = fsdev->nd_blocksize / cp_size;
74	off = roundup(sizeof(struct nandfs_cpfile_header), cp_size) / cp_size;
75	off += (cn - 1);
76
77	*blk = off / cp_per_blk;
78	*offset = (off % cp_per_blk) * cp_size;
79
80	return (0);
81}
82
83static int
84nandfs_checkpoint_blk_remaining(struct nandfs_device *fsdev, uint64_t cn,
85    uint64_t blk, uint64_t offset)
86{
87	uint16_t cp_size, cp_remaining;
88
89	cp_size = fsdev->nd_fsdata.f_checkpoint_size;
90	cp_remaining = (fsdev->nd_blocksize - offset) / cp_size;
91
92	return (cp_remaining);
93}
94
95int
96nandfs_get_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
97    uint64_t cn)
98{
99	struct buf *bp;
100	uint64_t blk, offset;
101	int error;
102
103	if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
104		return (-1);
105	}
106
107	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
108	if (error) {
109		brelse(bp);
110		return (-1);
111	}
112
113	error = nandfs_dirty_buf(bp, 0);
114	if (error)
115		return (-1);
116
117
118	nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
119
120	if (blk != 0) {
121		if (blk < cp_node->nn_inode.i_blocks)
122			error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
123		else
124			error = nandfs_bcreate(cp_node, blk, NOCRED, 0, &bp);
125		if (error) {
126			if (bp)
127				brelse(bp);
128			return (-1);
129		}
130
131		nandfs_dirty_buf(bp, 1);
132	}
133
134	DPRINTF(CPFILE, ("%s: cn:%#jx entry block:%#jx offset:%#jx\n",
135	    __func__, (uintmax_t)cn, (uintmax_t)blk, (uintmax_t)offset));
136
137	return (0);
138}
139
140int
141nandfs_set_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
142    uint64_t cn, struct nandfs_inode *ifile_inode, uint64_t nblocks)
143{
144	struct nandfs_cpfile_header *cnh;
145	struct nandfs_checkpoint *cnp;
146	struct buf *bp;
147	uint64_t blk, offset;
148	int error;
149
150	if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
151		nandfs_error("%s: trying to set invalid chekpoint %jx - %jx\n",
152		    __func__, cn, fsdev->nd_last_cno);
153		return (-1);
154	}
155
156	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
157	if (error) {
158		brelse(bp);
159		return error;
160	}
161
162	cnh = (struct nandfs_cpfile_header *) bp->b_data;
163	cnh->ch_ncheckpoints++;
164
165	nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
166
167	if(blk != 0) {
168		brelse(bp);
169		error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
170		if (error) {
171			brelse(bp);
172			return error;
173		}
174	}
175
176	cnp = (struct nandfs_checkpoint *)((uint8_t *)bp->b_data + offset);
177	cnp->cp_flags = 0;
178	cnp->cp_checkpoints_count = 1;
179	memset(&cnp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list));
180	cnp->cp_cno = cn;
181	cnp->cp_create = fsdev->nd_ts.tv_sec;
182	cnp->cp_nblk_inc = nblocks;
183	cnp->cp_blocks_count = 0;
184	memcpy (&cnp->cp_ifile_inode, ifile_inode, sizeof(cnp->cp_ifile_inode));
185
186	DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx nblk:%#jx\n",
187	    __func__, (uintmax_t)cn, (uintmax_t)cnp->cp_create,
188	    (uintmax_t)nblocks));
189
190	brelse(bp);
191	return (0);
192}
193
194static int
195nandfs_cp_mounted(struct nandfs_device *nandfsdev, uint64_t cno)
196{
197	struct nandfsmount *nmp;
198	int mounted = 0;
199
200	mtx_lock(&nandfsdev->nd_mutex);
201	/* No double-mounting of the same checkpoint */
202	STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
203		if (nmp->nm_mount_args.cpno == cno) {
204			mounted = 1;
205			break;
206		}
207	}
208	mtx_unlock(&nandfsdev->nd_mutex);
209
210	return (mounted);
211}
212
213static int
214nandfs_cp_set_snapshot(struct nandfs_node *cp_node, uint64_t cno)
215{
216	struct nandfs_device *fsdev;
217	struct nandfs_cpfile_header *cnh;
218	struct nandfs_checkpoint *cnp;
219	struct nandfs_snapshot_list *list;
220	struct buf *bp;
221	uint64_t blk, prev_blk, offset;
222	uint64_t curr, prev;
223	int error;
224
225	fsdev = cp_node->nn_nandfsdev;
226
227	/* Get snapshot data */
228	nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
229	error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
230	if (error) {
231		brelse(bp);
232		return (error);
233	}
234	cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
235	if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
236		brelse(bp);
237		return (ENOENT);
238	}
239	if ((cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
240		brelse(bp);
241		return (EINVAL);
242	}
243
244	brelse(bp);
245	/* Get list from header */
246	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
247	if (error) {
248		brelse(bp);
249		return (error);
250	}
251
252	cnh = (struct nandfs_cpfile_header *) bp->b_data;
253	list = &cnh->ch_snapshot_list;
254	prev = list->ssl_prev;
255	brelse(bp);
256	prev_blk = ~(0);
257	curr = 0;
258	while (prev > cno) {
259		curr = prev;
260		nandfs_checkpoint_blk_offset(fsdev, prev, &prev_blk, &offset);
261		error = nandfs_bread(cp_node, prev_blk, NOCRED, 0, &bp);
262		if (error) {
263			brelse(bp);
264			return (error);
265		}
266		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
267		list = &cnp->cp_snapshot_list;
268		prev = list->ssl_prev;
269		brelse(bp);
270	}
271
272	if (curr == 0) {
273		nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
274		cnh = (struct nandfs_cpfile_header *) bp->b_data;
275		list = &cnh->ch_snapshot_list;
276	} else {
277		nandfs_checkpoint_blk_offset(fsdev, curr, &blk, &offset);
278		error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
279		if (error) {
280			brelse(bp);
281			return (error);
282		}
283		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
284		list = &cnp->cp_snapshot_list;
285	}
286
287	list->ssl_prev = cno;
288	error = nandfs_dirty_buf(bp, 0);
289	if (error)
290		return (error);
291
292
293	/* Update snapshot for cno */
294	nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
295	error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
296	if (error) {
297		brelse(bp);
298		return (error);
299	}
300	cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
301	list = &cnp->cp_snapshot_list;
302	list->ssl_prev = prev;
303	list->ssl_next = curr;
304	cnp->cp_flags |= NANDFS_CHECKPOINT_SNAPSHOT;
305	nandfs_dirty_buf(bp, 1);
306
307	if (prev == 0) {
308		nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
309		cnh = (struct nandfs_cpfile_header *) bp->b_data;
310		list = &cnh->ch_snapshot_list;
311	} else {
312		/* Update snapshot list for prev */
313		nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
314		error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
315		if (error) {
316			brelse(bp);
317			return (error);
318		}
319		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
320		list = &cnp->cp_snapshot_list;
321	}
322	list->ssl_next = cno;
323	nandfs_dirty_buf(bp, 1);
324
325	/* Update header */
326	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
327	if (error) {
328		brelse(bp);
329		return (error);
330	}
331	cnh = (struct nandfs_cpfile_header *) bp->b_data;
332	cnh->ch_nsnapshots++;
333	nandfs_dirty_buf(bp, 1);
334
335	return (0);
336}
337
338static int
339nandfs_cp_clr_snapshot(struct nandfs_node *cp_node, uint64_t cno)
340{
341	struct nandfs_device *fsdev;
342	struct nandfs_cpfile_header *cnh;
343	struct nandfs_checkpoint *cnp;
344	struct nandfs_snapshot_list *list;
345	struct buf *bp;
346	uint64_t blk, offset, snapshot_cnt;
347	uint64_t next, prev;
348	int error;
349
350	fsdev = cp_node->nn_nandfsdev;
351
352	/* Get snapshot data */
353	nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
354	error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
355	if (error) {
356		brelse(bp);
357		return (error);
358	}
359	cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
360	if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
361		brelse(bp);
362		return (ENOENT);
363	}
364	if (!(cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
365		brelse(bp);
366		return (EINVAL);
367	}
368
369	list = &cnp->cp_snapshot_list;
370	next = list->ssl_next;
371	prev = list->ssl_prev;
372	brelse(bp);
373
374	/* Get previous snapshot */
375	if (prev != 0) {
376		nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
377		error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
378		if (error) {
379			brelse(bp);
380			return (error);
381		}
382		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
383		list = &cnp->cp_snapshot_list;
384	} else {
385		nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
386		cnh = (struct nandfs_cpfile_header *) bp->b_data;
387		list = &cnh->ch_snapshot_list;
388	}
389
390	list->ssl_next = next;
391	error = nandfs_dirty_buf(bp, 0);
392	if (error)
393		return (error);
394
395	/* Get next snapshot */
396	if (next != 0) {
397		nandfs_checkpoint_blk_offset(fsdev, next, &blk, &offset);
398		error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
399		if (error) {
400			brelse(bp);
401			return (error);
402		}
403		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
404		list = &cnp->cp_snapshot_list;
405	} else {
406		nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
407		cnh = (struct nandfs_cpfile_header *) bp->b_data;
408		list = &cnh->ch_snapshot_list;
409	}
410	list->ssl_prev = prev;
411	nandfs_dirty_buf(bp, 1);
412
413	/* Update snapshot list for cno */
414	nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
415	error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
416	if (error) {
417		brelse(bp);
418		return (error);
419	}
420	cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
421	list = &cnp->cp_snapshot_list;
422	list->ssl_prev = 0;
423	list->ssl_next = 0;
424	cnp->cp_flags &= !NANDFS_CHECKPOINT_SNAPSHOT;
425	nandfs_dirty_buf(bp, 1);
426
427	/* Update header */
428	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
429	if (error) {
430		brelse(bp);
431		return (error);
432	}
433	cnh = (struct nandfs_cpfile_header *) bp->b_data;
434	snapshot_cnt = cnh->ch_nsnapshots;
435	snapshot_cnt--;
436	cnh->ch_nsnapshots = snapshot_cnt;
437	nandfs_dirty_buf(bp, 1);
438
439	return (0);
440}
441
442int
443nandfs_chng_cpmode(struct nandfs_node *node, struct nandfs_cpmode *ncpm)
444{
445	struct nandfs_device *fsdev;
446	uint64_t cno = ncpm->ncpm_cno;
447	int mode = ncpm->ncpm_mode;
448	int ret;
449
450	fsdev = node->nn_nandfsdev;
451	VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
452	switch (mode) {
453	case NANDFS_CHECKPOINT:
454		if (nandfs_cp_mounted(fsdev, cno)) {
455			ret = EBUSY;
456		} else
457			ret = nandfs_cp_clr_snapshot(node, cno);
458		break;
459	case NANDFS_SNAPSHOT:
460		ret = nandfs_cp_set_snapshot(node, cno);
461		break;
462	default:
463		ret = EINVAL;
464		break;
465	}
466	VOP_UNLOCK(NTOV(node), 0);
467
468	return (ret);
469}
470
471static void
472nandfs_cpinfo_fill(struct nandfs_checkpoint *cnp, struct nandfs_cpinfo *nci)
473{
474
475	nci->nci_flags = cnp->cp_flags;
476	nci->nci_pad = 0;
477	nci->nci_cno = cnp->cp_cno;
478	nci->nci_create = cnp->cp_create;
479	nci->nci_nblk_inc = cnp->cp_nblk_inc;
480	nci->nci_blocks_count = cnp->cp_blocks_count;
481	nci->nci_next = cnp->cp_snapshot_list.ssl_next;
482	DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx\n",
483	    __func__, (uintmax_t)cnp->cp_cno,
484	    (uintmax_t)cnp->cp_create));
485}
486
487static int
488nandfs_get_cpinfo_cp(struct nandfs_node *node, uint64_t cno,
489    struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
490{
491	struct nandfs_device *fsdev;
492	struct buf *bp;
493	uint64_t blk, offset, last_cno, i;
494	uint16_t remaining;
495	int error;
496#ifdef INVARIANTS
497	uint64_t testblk, testoffset;
498#endif
499
500	if (cno == 0) {
501		return (ENOENT);
502	}
503
504	if (mnmembs < 1) {
505		return (EINVAL);
506	}
507
508	fsdev = node->nn_nandfsdev;
509	last_cno = fsdev->nd_last_cno;
510	DPRINTF(CPFILE, ("%s: cno:%#jx mnmembs: %#jx last:%#jx\n", __func__,
511	    (uintmax_t)cno, (uintmax_t)mnmembs,
512	    (uintmax_t)fsdev->nd_last_cno));
513
514	/*
515	 * do {
516	 * 	get block
517	 * 	read checkpoints until we hit last checkpoint, end of block or
518	 * 	requested number
519	 * } while (last read checkpoint <= last checkpoint on fs &&
520	 * 		read checkpoints < request number);
521	 */
522	*nmembs = i = 0;
523	do {
524		nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
525		remaining = nandfs_checkpoint_blk_remaining(fsdev, cno,
526		    blk, offset);
527		error = nandfs_bread(node, blk, NOCRED, 0, &bp);
528		if (error) {
529			brelse(bp);
530			return (error);
531		}
532
533		while (cno <= last_cno && i < mnmembs && remaining) {
534#ifdef INVARIANTS
535			nandfs_checkpoint_blk_offset(fsdev, cno, &testblk,
536			    &testoffset);
537			KASSERT(testblk == blk, ("testblk != blk"));
538			KASSERT(testoffset == offset, ("testoffset != offset"));
539#endif
540			DPRINTF(CPFILE, ("%s: cno %#jx\n", __func__,
541			    (uintmax_t)cno));
542
543			nandfs_cpinfo_fill((struct nandfs_checkpoint *)
544			    (bp->b_data + offset), nci);
545			offset += nandfs_checkpoint_size(fsdev);
546			i++;
547			nci++;
548			cno++;
549			(*nmembs)++;
550			remaining--;
551		}
552		brelse(bp);
553	} while (cno <= last_cno && i < mnmembs);
554
555	return (0);
556}
557
558static int
559nandfs_get_cpinfo_sp(struct nandfs_node *node, uint64_t cno,
560    struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
561{
562	struct nandfs_checkpoint *cnp;
563	struct nandfs_cpfile_header *cnh;
564	struct nandfs_device *fsdev;
565	struct buf *bp = NULL;
566	uint64_t curr = 0;
567	uint64_t blk, offset, curr_cno;
568	uint32_t flag;
569	int i, error;
570
571	if (cno == 0 || cno == ~(0))
572		return (ENOENT);
573
574	fsdev = node->nn_nandfsdev;
575	curr_cno = cno;
576
577	if (nmembs)
578		*nmembs = 0;
579	if (curr_cno == 1) {
580		/* Get list from header */
581		error = nandfs_bread(node, 0, NOCRED, 0, &bp);
582		if (error) {
583			brelse(bp);
584			return (error);
585		}
586		cnh = (struct nandfs_cpfile_header *) bp->b_data;
587		curr_cno = cnh->ch_snapshot_list.ssl_next;
588		brelse(bp);
589		bp = NULL;
590
591		/* No snapshots */
592		if (curr_cno == 0)
593			return (0);
594	}
595
596	for (i = 0; i < mnmembs; i++, nci++) {
597		nandfs_checkpoint_blk_offset(fsdev, curr_cno, &blk, &offset);
598		if (i == 0 || curr != blk) {
599			if (bp)
600				brelse(bp);
601			error = nandfs_bread(node, blk, NOCRED, 0, &bp);
602			if (error) {
603				brelse(bp);
604				return (ENOENT);
605			}
606			curr = blk;
607		}
608		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
609		flag = cnp->cp_flags;
610		if (!(flag & NANDFS_CHECKPOINT_SNAPSHOT) ||
611		    (flag & NANDFS_CHECKPOINT_INVALID))
612			break;
613
614		nci->nci_flags = flag;
615		nci->nci_pad = 0;
616		nci->nci_cno = cnp->cp_cno;
617		nci->nci_create = cnp->cp_create;
618		nci->nci_nblk_inc = cnp->cp_nblk_inc;
619		nci->nci_blocks_count = cnp->cp_blocks_count;
620		nci->nci_next = cnp->cp_snapshot_list.ssl_next;
621		if (nmembs)
622			(*nmembs)++;
623
624		curr_cno = nci->nci_next;
625		if (!curr_cno)
626			break;
627	}
628
629	brelse(bp);
630
631	return (0);
632}
633
634int
635nandfs_get_cpinfo(struct nandfs_node *node, uint64_t cno, uint16_t flags,
636    struct nandfs_cpinfo *nci, uint32_t nmembs, uint32_t *nnmembs)
637{
638	int error;
639
640	VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
641	switch (flags) {
642	case NANDFS_CHECKPOINT:
643		error = nandfs_get_cpinfo_cp(node, cno, nci, nmembs, nnmembs);
644		break;
645	case NANDFS_SNAPSHOT:
646		error = nandfs_get_cpinfo_sp(node, cno, nci, nmembs, nnmembs);
647		break;
648	default:
649		error = EINVAL;
650		break;
651	}
652	VOP_UNLOCK(NTOV(node), 0);
653
654	return (error);
655}
656
657int
658nandfs_get_cpinfo_ioctl(struct nandfs_node *node, struct nandfs_argv *nargv)
659{
660	struct nandfs_cpinfo *nci;
661	uint64_t cno = nargv->nv_index;
662	void *buf = (void *)((uintptr_t)nargv->nv_base);
663	uint16_t flags = nargv->nv_flags;
664	uint32_t nmembs = 0;
665	int error;
666
667	if (nargv->nv_nmembs > NANDFS_CPINFO_MAX)
668		return (EINVAL);
669
670	nci = malloc(sizeof(struct nandfs_cpinfo) * nargv->nv_nmembs,
671	    M_NANDFSTEMP, M_WAITOK | M_ZERO);
672
673	error = nandfs_get_cpinfo(node, cno, flags, nci, nargv->nv_nmembs, &nmembs);
674
675	if (error == 0) {
676		nargv->nv_nmembs = nmembs;
677		error = copyout(nci, buf,
678		    sizeof(struct nandfs_cpinfo) * nmembs);
679	}
680
681	free(nci, M_NANDFSTEMP);
682	return (error);
683}
684
685int
686nandfs_delete_cp(struct nandfs_node *node, uint64_t start, uint64_t end)
687{
688	struct nandfs_checkpoint *cnp;
689	struct nandfs_device *fsdev;
690	struct buf *bp;
691	uint64_t cno = start, blk, offset;
692	int error;
693
694	DPRINTF(CPFILE, ("%s: delete cno %jx-%jx\n", __func__, start, end));
695	VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
696	fsdev = node->nn_nandfsdev;
697	for (cno = start; cno <= end; cno++) {
698		if (!cno)
699			continue;
700
701		nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
702		error = nandfs_bread(node, blk, NOCRED, 0, &bp);
703		if (error) {
704			VOP_UNLOCK(NTOV(node), 0);
705			brelse(bp);
706			return (error);
707		}
708
709		cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
710		if (cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) {
711			brelse(bp);
712			VOP_UNLOCK(NTOV(node), 0);
713			return (0);
714		}
715
716		cnp->cp_flags |= NANDFS_CHECKPOINT_INVALID;
717
718		error = nandfs_dirty_buf(bp, 0);
719		if (error)
720			return (error);
721	}
722	VOP_UNLOCK(NTOV(node), 0);
723
724	return (0);
725}
726
727int
728nandfs_make_snap(struct nandfs_device *fsdev, uint64_t *cno)
729{
730	struct nandfs_cpmode cpm;
731	int error;
732
733	*cno = cpm.ncpm_cno = fsdev->nd_last_cno;
734	cpm.ncpm_mode = NANDFS_SNAPSHOT;
735	error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
736	return (error);
737}
738
739int
740nandfs_delete_snap(struct nandfs_device *fsdev, uint64_t cno)
741{
742	struct nandfs_cpmode cpm;
743	int error;
744
745	cpm.ncpm_cno = cno;
746	cpm.ncpm_mode = NANDFS_CHECKPOINT;
747	error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
748	return (error);
749}
750
751int nandfs_get_cpstat(struct nandfs_node *cp_node, struct nandfs_cpstat *ncp)
752{
753	struct nandfs_device *fsdev;
754	struct nandfs_cpfile_header *cnh;
755	struct buf *bp;
756	int error;
757
758	VOP_LOCK(NTOV(cp_node), LK_EXCLUSIVE);
759	fsdev = cp_node->nn_nandfsdev;
760
761	/* Get header */
762	error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
763	if (error) {
764		brelse(bp);
765		VOP_UNLOCK(NTOV(cp_node), 0);
766		return (error);
767	}
768	cnh = (struct nandfs_cpfile_header *) bp->b_data;
769	ncp->ncp_cno = fsdev->nd_last_cno;
770	ncp->ncp_ncps = cnh->ch_ncheckpoints;
771	ncp->ncp_nss = cnh->ch_nsnapshots;
772	DPRINTF(CPFILE, ("%s: cno:%#jx ncps:%#jx nss:%#jx\n",
773	    __func__, ncp->ncp_cno, ncp->ncp_ncps, ncp->ncp_nss));
774	brelse(bp);
775	VOP_UNLOCK(NTOV(cp_node), 0);
776
777	return (0);
778}
779