diff options
Diffstat (limited to 'sys/fs/nandfs/nandfs_cpfile.c')
-rw-r--r-- | sys/fs/nandfs/nandfs_cpfile.c | 776 |
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); +} |