]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/nandfs/nandfs_cpfile.c
Merge llvm trunk r321414 to contrib/llvm.
[FreeBSD/FreeBSD.git] / sys / fs / nandfs / nandfs_cpfile.c
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
56 static int
57 nandfs_checkpoint_size(struct nandfs_device *fsdev)
58 {
59
60         return (fsdev->nd_fsdata.f_checkpoint_size);
61 }
62
63 static int
64 nandfs_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
83 static int
84 nandfs_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
95 int
96 nandfs_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
140 int
141 nandfs_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
194 static int
195 nandfs_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
213 static int
214 nandfs_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
338 static int
339 nandfs_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
442 int
443 nandfs_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
471 static void
472 nandfs_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
487 static int
488 nandfs_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
558 static int
559 nandfs_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
634 int
635 nandfs_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
657 int
658 nandfs_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
685 int
686 nandfs_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
727 int
728 nandfs_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
739 int
740 nandfs_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
751 int 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 }