]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/fs/nandfs/nandfs_cpfile.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / fs / nandfs / nandfs_cpfile.c
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
55 nandfs_checkpoint_size(struct nandfs_device *fsdev)
56 {
57
58         return (fsdev->nd_fsdata.f_checkpoint_size);
59 }
60
61 static int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }