summaryrefslogtreecommitdiffstats
path: root/sys/fs/nandfs/nandfs_cpfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/nandfs/nandfs_cpfile.c')
-rw-r--r--sys/fs/nandfs/nandfs_cpfile.c776
1 files changed, 776 insertions, 0 deletions
diff --git a/sys/fs/nandfs/nandfs_cpfile.c b/sys/fs/nandfs/nandfs_cpfile.c
new file mode 100644
index 0000000..8814fc0
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_cpfile.c
@@ -0,0 +1,776 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include "nandfs_mount.h"
+#include "nandfs.h"
+#include "nandfs_subr.h"
+
+
+static int
+nandfs_checkpoint_size(struct nandfs_device *fsdev)
+{
+
+ return (fsdev->nd_fsdata.f_checkpoint_size);
+}
+
+static int
+nandfs_checkpoint_blk_offset(struct nandfs_device *fsdev, uint64_t cn,
+ uint64_t *blk, uint64_t *offset)
+{
+ uint64_t off;
+ uint16_t cp_size, cp_per_blk;
+
+ KASSERT((cn), ("checkpoing cannot be zero"));
+
+ cp_size = fsdev->nd_fsdata.f_checkpoint_size;
+ cp_per_blk = fsdev->nd_blocksize / cp_size;
+ off = roundup(sizeof(struct nandfs_cpfile_header), cp_size) / cp_size;
+ off += (cn - 1);
+
+ *blk = off / cp_per_blk;
+ *offset = (off % cp_per_blk) * cp_size;
+
+ return (0);
+}
+
+static int
+nandfs_checkpoint_blk_remaining(struct nandfs_device *fsdev, uint64_t cn,
+ uint64_t blk, uint64_t offset)
+{
+ uint16_t cp_size, cp_remaining;
+
+ cp_size = fsdev->nd_fsdata.f_checkpoint_size;
+ cp_remaining = (fsdev->nd_blocksize - offset) / cp_size;
+
+ return (cp_remaining);
+}
+
+int
+nandfs_get_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
+ uint64_t cn)
+{
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
+ return (-1);
+ }
+
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (-1);
+ }
+
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (-1);
+
+
+ nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
+
+ if (blk != 0) {
+ if (blk < cp_node->nn_inode.i_blocks)
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ else
+ error = nandfs_bcreate(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return (-1);
+ }
+
+ nandfs_dirty_buf(bp, 1);
+ }
+
+ DPRINTF(CPFILE, ("%s: cn:%#jx entry block:%#jx offset:%#jx\n",
+ __func__, (uintmax_t)cn, (uintmax_t)blk, (uintmax_t)offset));
+
+ return (0);
+}
+
+int
+nandfs_set_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
+ uint64_t cn, struct nandfs_inode *ifile_inode, uint64_t nblocks)
+{
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_checkpoint *cnp;
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
+ nandfs_error("%s: trying to set invalid chekpoint %jx - %jx\n",
+ __func__, cn, fsdev->nd_last_cno);
+ return (-1);
+ }
+
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ cnh->ch_ncheckpoints++;
+
+ nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
+
+ if(blk != 0) {
+ brelse(bp);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ }
+
+ cnp = (struct nandfs_checkpoint *)((uint8_t *)bp->b_data + offset);
+ cnp->cp_flags = 0;
+ cnp->cp_checkpoints_count = 1;
+ memset(&cnp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list));
+ cnp->cp_cno = cn;
+ cnp->cp_create = fsdev->nd_ts.tv_sec;
+ cnp->cp_nblk_inc = nblocks;
+ cnp->cp_blocks_count = 0;
+ memcpy (&cnp->cp_ifile_inode, ifile_inode, sizeof(cnp->cp_ifile_inode));
+
+ DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx nblk:%#jx\n",
+ __func__, (uintmax_t)cn, (uintmax_t)cnp->cp_create,
+ (uintmax_t)nblocks));
+
+ brelse(bp);
+ return (0);
+}
+
+static int
+nandfs_cp_mounted(struct nandfs_device *nandfsdev, uint64_t cno)
+{
+ struct nandfsmount *nmp;
+ int mounted = 0;
+
+ mtx_lock(&nandfsdev->nd_mutex);
+ /* No double-mounting of the same checkpoint */
+ STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
+ if (nmp->nm_mount_args.cpno == cno) {
+ mounted = 1;
+ break;
+ }
+ }
+ mtx_unlock(&nandfsdev->nd_mutex);
+
+ return (mounted);
+}
+
+static int
+nandfs_cp_set_snapshot(struct nandfs_node *cp_node, uint64_t cno)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_snapshot_list *list;
+ struct buf *bp;
+ uint64_t blk, prev_blk, offset;
+ uint64_t curr, prev;
+ int error;
+
+ fsdev = cp_node->nn_nandfsdev;
+
+ /* Get snapshot data */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
+ brelse(bp);
+ return (ENOENT);
+ }
+ if ((cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
+ brelse(bp);
+ return (EINVAL);
+ }
+
+ brelse(bp);
+ /* Get list from header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ prev = list->ssl_prev;
+ brelse(bp);
+ prev_blk = ~(0);
+ curr = 0;
+ while (prev > cno) {
+ curr = prev;
+ nandfs_checkpoint_blk_offset(fsdev, prev, &prev_blk, &offset);
+ error = nandfs_bread(cp_node, prev_blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ prev = list->ssl_prev;
+ brelse(bp);
+ }
+
+ if (curr == 0) {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ } else {
+ nandfs_checkpoint_blk_offset(fsdev, curr, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ }
+
+ list->ssl_prev = cno;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+
+ /* Update snapshot for cno */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ list->ssl_prev = prev;
+ list->ssl_next = curr;
+ cnp->cp_flags |= NANDFS_CHECKPOINT_SNAPSHOT;
+ nandfs_dirty_buf(bp, 1);
+
+ if (prev == 0) {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ } else {
+ /* Update snapshot list for prev */
+ nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ }
+ list->ssl_next = cno;
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ cnh->ch_nsnapshots++;
+ nandfs_dirty_buf(bp, 1);
+
+ return (0);
+}
+
+static int
+nandfs_cp_clr_snapshot(struct nandfs_node *cp_node, uint64_t cno)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_snapshot_list *list;
+ struct buf *bp;
+ uint64_t blk, offset, snapshot_cnt;
+ uint64_t next, prev;
+ int error;
+
+ fsdev = cp_node->nn_nandfsdev;
+
+ /* Get snapshot data */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
+ brelse(bp);
+ return (ENOENT);
+ }
+ if (!(cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
+ brelse(bp);
+ return (EINVAL);
+ }
+
+ list = &cnp->cp_snapshot_list;
+ next = list->ssl_next;
+ prev = list->ssl_prev;
+ brelse(bp);
+
+ /* Get previous snapshot */
+ if (prev != 0) {
+ nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ } else {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ }
+
+ list->ssl_next = next;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+ /* Get next snapshot */
+ if (next != 0) {
+ nandfs_checkpoint_blk_offset(fsdev, next, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ } else {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ }
+ list->ssl_prev = prev;
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update snapshot list for cno */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ list->ssl_prev = 0;
+ list->ssl_next = 0;
+ cnp->cp_flags &= !NANDFS_CHECKPOINT_SNAPSHOT;
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ snapshot_cnt = cnh->ch_nsnapshots;
+ snapshot_cnt--;
+ cnh->ch_nsnapshots = snapshot_cnt;
+ nandfs_dirty_buf(bp, 1);
+
+ return (0);
+}
+
+int
+nandfs_chng_cpmode(struct nandfs_node *node, struct nandfs_cpmode *ncpm)
+{
+ struct nandfs_device *fsdev;
+ uint64_t cno = ncpm->ncpm_cno;
+ int mode = ncpm->ncpm_mode;
+ int ret;
+
+ fsdev = node->nn_nandfsdev;
+ VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
+ switch (mode) {
+ case NANDFS_CHECKPOINT:
+ if (nandfs_cp_mounted(fsdev, cno)) {
+ ret = EBUSY;
+ } else
+ ret = nandfs_cp_clr_snapshot(node, cno);
+ break;
+ case NANDFS_SNAPSHOT:
+ ret = nandfs_cp_set_snapshot(node, cno);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+ VOP_UNLOCK(NTOV(node), 0);
+
+ return (ret);
+}
+
+static void
+nandfs_cpinfo_fill(struct nandfs_checkpoint *cnp, struct nandfs_cpinfo *nci)
+{
+
+ nci->nci_flags = cnp->cp_flags;
+ nci->nci_pad = 0;
+ nci->nci_cno = cnp->cp_cno;
+ nci->nci_create = cnp->cp_create;
+ nci->nci_nblk_inc = cnp->cp_nblk_inc;
+ nci->nci_blocks_count = cnp->cp_blocks_count;
+ nci->nci_next = cnp->cp_snapshot_list.ssl_next;
+ DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx\n",
+ __func__, (uintmax_t)cnp->cp_cno,
+ (uintmax_t)cnp->cp_create));
+}
+
+static int
+nandfs_get_cpinfo_cp(struct nandfs_node *node, uint64_t cno,
+ struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
+{
+ struct nandfs_device *fsdev;
+ struct buf *bp;
+ uint64_t blk, offset, last_cno, i;
+ uint16_t remaining;
+ int error;
+#ifdef INVARIANTS
+ uint64_t testblk, testoffset;
+#endif
+
+ if (cno == 0) {
+ return (ENOENT);
+ }
+
+ if (mnmembs < 1) {
+ return (EINVAL);
+ }
+
+ fsdev = node->nn_nandfsdev;
+ last_cno = fsdev->nd_last_cno;
+ DPRINTF(CPFILE, ("%s: cno:%#jx mnmembs: %#jx last:%#jx\n", __func__,
+ (uintmax_t)cno, (uintmax_t)mnmembs,
+ (uintmax_t)fsdev->nd_last_cno));
+
+ /*
+ * do {
+ * get block
+ * read checkpoints until we hit last checkpoint, end of block or
+ * requested number
+ * } while (last read checkpoint <= last checkpoint on fs &&
+ * read checkpoints < request number);
+ */
+ *nmembs = i = 0;
+ do {
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ remaining = nandfs_checkpoint_blk_remaining(fsdev, cno,
+ blk, offset);
+ error = nandfs_bread(node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ while (cno <= last_cno && i < mnmembs && remaining) {
+#ifdef INVARIANTS
+ nandfs_checkpoint_blk_offset(fsdev, cno, &testblk,
+ &testoffset);
+ KASSERT(testblk == blk, ("testblk != blk"));
+ KASSERT(testoffset == offset, ("testoffset != offset"));
+#endif
+ DPRINTF(CPFILE, ("%s: cno %#jx\n", __func__,
+ (uintmax_t)cno));
+
+ nandfs_cpinfo_fill((struct nandfs_checkpoint *)
+ (bp->b_data + offset), nci);
+ offset += nandfs_checkpoint_size(fsdev);
+ i++;
+ nci++;
+ cno++;
+ (*nmembs)++;
+ remaining--;
+ }
+ brelse(bp);
+ } while (cno <= last_cno && i < mnmembs);
+
+ return (0);
+}
+
+static int
+nandfs_get_cpinfo_sp(struct nandfs_node *node, uint64_t cno,
+ struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
+{
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_device *fsdev;
+ struct buf *bp = NULL;
+ uint64_t curr = 0;
+ uint64_t blk, offset, curr_cno;
+ uint32_t flag;
+ int i, error;
+
+ if (cno == 0 || cno == ~(0))
+ return (ENOENT);
+
+ fsdev = node->nn_nandfsdev;
+ curr_cno = cno;
+
+ if (nmembs)
+ *nmembs = 0;
+ if (curr_cno == 1) {
+ /* Get list from header */
+ error = nandfs_bread(node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ curr_cno = cnh->ch_snapshot_list.ssl_next;
+ brelse(bp);
+ bp = NULL;
+
+ /* No snapshots */
+ if (curr_cno == 0)
+ return (0);
+ }
+
+ for (i = 0; i < mnmembs; i++, nci++) {
+ nandfs_checkpoint_blk_offset(fsdev, curr_cno, &blk, &offset);
+ if (i == 0 || curr != blk) {
+ if (bp)
+ brelse(bp);
+ error = nandfs_bread(node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (ENOENT);
+ }
+ curr = blk;
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ flag = cnp->cp_flags;
+ if (!(flag & NANDFS_CHECKPOINT_SNAPSHOT) ||
+ (flag & NANDFS_CHECKPOINT_INVALID))
+ break;
+
+ nci->nci_flags = flag;
+ nci->nci_pad = 0;
+ nci->nci_cno = cnp->cp_cno;
+ nci->nci_create = cnp->cp_create;
+ nci->nci_nblk_inc = cnp->cp_nblk_inc;
+ nci->nci_blocks_count = cnp->cp_blocks_count;
+ nci->nci_next = cnp->cp_snapshot_list.ssl_next;
+ if (nmembs)
+ (*nmembs)++;
+
+ curr_cno = nci->nci_next;
+ if (!curr_cno)
+ break;
+ }
+
+ brelse(bp);
+
+ return (0);
+}
+
+int
+nandfs_get_cpinfo(struct nandfs_node *node, uint64_t cno, uint16_t flags,
+ struct nandfs_cpinfo *nci, uint32_t nmembs, uint32_t *nnmembs)
+{
+ int error;
+
+ VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
+ switch (flags) {
+ case NANDFS_CHECKPOINT:
+ error = nandfs_get_cpinfo_cp(node, cno, nci, nmembs, nnmembs);
+ break;
+ case NANDFS_SNAPSHOT:
+ error = nandfs_get_cpinfo_sp(node, cno, nci, nmembs, nnmembs);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ VOP_UNLOCK(NTOV(node), 0);
+
+ return (error);
+}
+
+int
+nandfs_get_cpinfo_ioctl(struct nandfs_node *node, struct nandfs_argv *nargv)
+{
+ struct nandfs_cpinfo *nci;
+ uint64_t cno = nargv->nv_index;
+ void *buf = (void *)((uintptr_t)nargv->nv_base);
+ uint16_t flags = nargv->nv_flags;
+ uint32_t nmembs = 0;
+ int error;
+
+ if (nargv->nv_nmembs > NANDFS_CPINFO_MAX)
+ return (EINVAL);
+
+ nci = malloc(sizeof(struct nandfs_cpinfo) * nargv->nv_nmembs,
+ M_NANDFSTEMP, M_WAITOK | M_ZERO);
+
+ error = nandfs_get_cpinfo(node, cno, flags, nci, nargv->nv_nmembs, &nmembs);
+
+ if (error == 0) {
+ nargv->nv_nmembs = nmembs;
+ error = copyout(nci, buf,
+ sizeof(struct nandfs_cpinfo) * nmembs);
+ }
+
+ free(nci, M_NANDFSTEMP);
+ return (error);
+}
+
+int
+nandfs_delete_cp(struct nandfs_node *node, uint64_t start, uint64_t end)
+{
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_device *fsdev;
+ struct buf *bp;
+ uint64_t cno = start, blk, offset;
+ int error;
+
+ DPRINTF(CPFILE, ("%s: delete cno %jx-%jx\n", __func__, start, end));
+ VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
+ fsdev = node->nn_nandfsdev;
+ for (cno = start; cno <= end; cno++) {
+ if (!cno)
+ continue;
+
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(node, blk, NOCRED, 0, &bp);
+ if (error) {
+ VOP_UNLOCK(NTOV(node), 0);
+ brelse(bp);
+ return (error);
+ }
+
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ if (cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(node), 0);
+ return (0);
+ }
+
+ cnp->cp_flags |= NANDFS_CHECKPOINT_INVALID;
+
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+ }
+ VOP_UNLOCK(NTOV(node), 0);
+
+ return (0);
+}
+
+int
+nandfs_make_snap(struct nandfs_device *fsdev, uint64_t *cno)
+{
+ struct nandfs_cpmode cpm;
+ int error;
+
+ *cno = cpm.ncpm_cno = fsdev->nd_last_cno;
+ cpm.ncpm_mode = NANDFS_SNAPSHOT;
+ error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
+ return (error);
+}
+
+int
+nandfs_delete_snap(struct nandfs_device *fsdev, uint64_t cno)
+{
+ struct nandfs_cpmode cpm;
+ int error;
+
+ cpm.ncpm_cno = cno;
+ cpm.ncpm_mode = NANDFS_CHECKPOINT;
+ error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
+ return (error);
+}
+
+int nandfs_get_cpstat(struct nandfs_node *cp_node, struct nandfs_cpstat *ncp)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_cpfile_header *cnh;
+ struct buf *bp;
+ int error;
+
+ VOP_LOCK(NTOV(cp_node), LK_EXCLUSIVE);
+ fsdev = cp_node->nn_nandfsdev;
+
+ /* Get header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ ncp->ncp_cno = fsdev->nd_last_cno;
+ ncp->ncp_ncps = cnh->ch_ncheckpoints;
+ ncp->ncp_nss = cnh->ch_nsnapshots;
+ DPRINTF(CPFILE, ("%s: cno:%#jx ncps:%#jx nss:%#jx\n",
+ __func__, ncp->ncp_cno, ncp->ncp_ncps, ncp->ncp_nss));
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+
+ return (0);
+}
OpenPOWER on IntegriCloud