freebsd-nq/sys/fs/nandfs/nandfs_cpfile.c
Pedro F. Giffuni d63027b668 sys/fs: further adoption of SPDX licensing ID tags.
Mainly focus on files that use BSD 2-Clause license, however the tool I
was using misidentified many licenses so this was mostly a manual - error
prone - task.

The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.
2017-11-27 15:15:37 +00:00

779 lines
18 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* 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);
}