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