d63027b668
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.
1091 lines
25 KiB
C
1091 lines
25 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2010-2012 Semihalf
|
|
* Copyright (c) 2008, 2009 Reinoud Zandijk
|
|
* 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 ``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 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.
|
|
*
|
|
* From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/resourcevar.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/file.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/bio.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/dirent.h>
|
|
#include <sys/lockf.h>
|
|
#include <sys/libkern.h>
|
|
|
|
#include <geom/geom.h>
|
|
#include <geom/geom_vfs.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/vm_extern.h>
|
|
|
|
#include <machine/_inttypes.h>
|
|
#include "nandfs_mount.h"
|
|
#include "nandfs.h"
|
|
#include "nandfs_subr.h"
|
|
|
|
MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount");
|
|
MALLOC_DEFINE(M_NANDFSTEMP, "nandfs_tmt", "NANDFS tmp");
|
|
|
|
uma_zone_t nandfs_node_zone;
|
|
|
|
void nandfs_bdflush(struct bufobj *bo, struct buf *bp);
|
|
int nandfs_bufsync(struct bufobj *bo, int waitfor);
|
|
|
|
struct buf_ops buf_ops_nandfs = {
|
|
.bop_name = "buf_ops_nandfs",
|
|
.bop_write = bufwrite,
|
|
.bop_strategy = bufstrategy,
|
|
.bop_sync = nandfs_bufsync,
|
|
.bop_bdflush = nandfs_bdflush,
|
|
};
|
|
|
|
int
|
|
nandfs_bufsync(struct bufobj *bo, int waitfor)
|
|
{
|
|
struct vnode *vp;
|
|
int error = 0;
|
|
|
|
vp = bo2vnode(bo);
|
|
|
|
ASSERT_VOP_LOCKED(vp, __func__);
|
|
error = nandfs_sync_file(vp);
|
|
if (error)
|
|
nandfs_warning("%s: cannot flush buffers err:%d\n",
|
|
__func__, error);
|
|
|
|
return (error);
|
|
}
|
|
|
|
void
|
|
nandfs_bdflush(bo, bp)
|
|
struct bufobj *bo;
|
|
struct buf *bp;
|
|
{
|
|
struct vnode *vp;
|
|
int error;
|
|
|
|
if (bo->bo_dirty.bv_cnt <= ((dirtybufthresh * 8) / 10))
|
|
return;
|
|
|
|
vp = bp->b_vp;
|
|
if (NANDFS_SYS_NODE(VTON(vp)->nn_ino))
|
|
return;
|
|
|
|
if (NANDFS_IS_INDIRECT(bp))
|
|
return;
|
|
|
|
error = nandfs_sync_file(vp);
|
|
if (error)
|
|
nandfs_warning("%s: cannot flush buffers err:%d\n",
|
|
__func__, error);
|
|
}
|
|
|
|
int
|
|
nandfs_init(struct vfsconf *vfsp)
|
|
{
|
|
|
|
nandfs_node_zone = uma_zcreate("nandfs node zone",
|
|
sizeof(struct nandfs_node), NULL, NULL, NULL, NULL, 0, 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_uninit(struct vfsconf *vfsp)
|
|
{
|
|
|
|
uma_zdestroy(nandfs_node_zone);
|
|
return (0);
|
|
}
|
|
|
|
/* Basic calculators */
|
|
uint64_t
|
|
nandfs_get_segnum_of_block(struct nandfs_device *nandfsdev,
|
|
nandfs_daddr_t blocknr)
|
|
{
|
|
uint64_t segnum, blks_per_seg;
|
|
|
|
MPASS(blocknr >= nandfsdev->nd_fsdata.f_first_data_block);
|
|
|
|
blks_per_seg = nandfsdev->nd_fsdata.f_blocks_per_segment;
|
|
|
|
segnum = blocknr / blks_per_seg;
|
|
segnum -= nandfsdev->nd_fsdata.f_first_data_block / blks_per_seg;
|
|
|
|
DPRINTF(SYNC, ("%s: returning blocknr %jx -> segnum %jx\n", __func__,
|
|
blocknr, segnum));
|
|
|
|
return (segnum);
|
|
}
|
|
|
|
void
|
|
nandfs_get_segment_range(struct nandfs_device *nandfsdev, uint64_t segnum,
|
|
uint64_t *seg_start, uint64_t *seg_end)
|
|
{
|
|
uint64_t blks_per_seg;
|
|
|
|
blks_per_seg = nandfsdev->nd_fsdata.f_blocks_per_segment;
|
|
*seg_start = nandfsdev->nd_fsdata.f_first_data_block +
|
|
blks_per_seg * segnum;
|
|
if (seg_end != NULL)
|
|
*seg_end = *seg_start + blks_per_seg -1;
|
|
}
|
|
|
|
void nandfs_calc_mdt_consts(struct nandfs_device *nandfsdev,
|
|
struct nandfs_mdt *mdt, int entry_size)
|
|
{
|
|
uint32_t blocksize = nandfsdev->nd_blocksize;
|
|
|
|
mdt->entries_per_group = blocksize * 8;
|
|
mdt->entries_per_block = blocksize / entry_size;
|
|
|
|
mdt->blocks_per_group =
|
|
(mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1;
|
|
mdt->groups_per_desc_block =
|
|
blocksize / sizeof(struct nandfs_block_group_desc);
|
|
mdt->blocks_per_desc_block =
|
|
mdt->groups_per_desc_block * mdt->blocks_per_group + 1;
|
|
}
|
|
|
|
int
|
|
nandfs_dev_bread(struct nandfs_device *nandfsdev, nandfs_lbn_t blocknr,
|
|
struct ucred *cred, int flags, struct buf **bpp)
|
|
{
|
|
int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE;
|
|
int error;
|
|
|
|
DPRINTF(BLOCK, ("%s: read from block %jx vp %p\n", __func__,
|
|
blocknr * blk2dev, nandfsdev->nd_devvp));
|
|
error = bread(nandfsdev->nd_devvp, blocknr * blk2dev,
|
|
nandfsdev->nd_blocksize, NOCRED, bpp);
|
|
if (error)
|
|
nandfs_error("%s: cannot read from device - blk:%jx\n",
|
|
__func__, blocknr);
|
|
return (error);
|
|
}
|
|
|
|
/* Read on a node */
|
|
int
|
|
nandfs_bread(struct nandfs_node *node, nandfs_lbn_t blocknr,
|
|
struct ucred *cred, int flags, struct buf **bpp)
|
|
{
|
|
nandfs_daddr_t vblk;
|
|
int error;
|
|
|
|
DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
|
|
blocknr));
|
|
|
|
error = bread(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
|
|
cred, bpp);
|
|
|
|
KASSERT(error == 0, ("%s: vp:%p lbn:%#jx err:%d\n", __func__,
|
|
NTOV(node), blocknr, error));
|
|
|
|
if (!nandfs_vblk_get(*bpp) &&
|
|
((*bpp)->b_flags & B_CACHE) && node->nn_ino != NANDFS_DAT_INO) {
|
|
nandfs_bmap_lookup(node, blocknr, &vblk);
|
|
nandfs_vblk_set(*bpp, vblk);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
nandfs_bread_meta(struct nandfs_node *node, nandfs_lbn_t blocknr,
|
|
struct ucred *cred, int flags, struct buf **bpp)
|
|
{
|
|
nandfs_daddr_t vblk;
|
|
int error;
|
|
|
|
DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
|
|
blocknr));
|
|
|
|
error = bread(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
|
|
cred, bpp);
|
|
|
|
KASSERT(error == 0, ("%s: vp:%p lbn:%#jx err:%d\n", __func__,
|
|
NTOV(node), blocknr, error));
|
|
|
|
if (!nandfs_vblk_get(*bpp) &&
|
|
((*bpp)->b_flags & B_CACHE) && node->nn_ino != NANDFS_DAT_INO) {
|
|
nandfs_bmap_lookup(node, blocknr, &vblk);
|
|
nandfs_vblk_set(*bpp, vblk);
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
nandfs_bdestroy(struct nandfs_node *node, nandfs_daddr_t vblk)
|
|
{
|
|
int error;
|
|
|
|
if (!NANDFS_SYS_NODE(node->nn_ino))
|
|
NANDFS_WRITEASSERT(node->nn_nandfsdev);
|
|
|
|
error = nandfs_vblock_end(node->nn_nandfsdev, vblk);
|
|
if (error) {
|
|
nandfs_error("%s: ending vblk: %jx failed\n",
|
|
__func__, (uintmax_t)vblk);
|
|
return (error);
|
|
}
|
|
node->nn_inode.i_blocks--;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_bcreate(struct nandfs_node *node, nandfs_lbn_t blocknr,
|
|
struct ucred *cred, int flags, struct buf **bpp)
|
|
{
|
|
int error;
|
|
|
|
ASSERT_VOP_LOCKED(NTOV(node), __func__);
|
|
if (!NANDFS_SYS_NODE(node->nn_ino))
|
|
NANDFS_WRITEASSERT(node->nn_nandfsdev);
|
|
|
|
DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
|
|
blocknr));
|
|
|
|
*bpp = getblk(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
|
|
0, 0, 0);
|
|
|
|
KASSERT((*bpp), ("%s: vp:%p lbn:%#jx\n", __func__,
|
|
NTOV(node), blocknr));
|
|
|
|
if (*bpp) {
|
|
vfs_bio_clrbuf(*bpp);
|
|
(*bpp)->b_blkno = ~(0); /* To avoid VOP_BMAP in bdwrite */
|
|
error = nandfs_bmap_insert_block(node, blocknr, *bpp);
|
|
if (error) {
|
|
nandfs_warning("%s: failed bmap insert node:%p"
|
|
" blk:%jx\n", __func__, node, blocknr);
|
|
brelse(*bpp);
|
|
return (error);
|
|
}
|
|
node->nn_inode.i_blocks++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
nandfs_bcreate_meta(struct nandfs_node *node, nandfs_lbn_t blocknr,
|
|
struct ucred *cred, int flags, struct buf **bpp)
|
|
{
|
|
struct nandfs_device *fsdev;
|
|
nandfs_daddr_t vblk;
|
|
int error;
|
|
|
|
ASSERT_VOP_LOCKED(NTOV(node), __func__);
|
|
NANDFS_WRITEASSERT(node->nn_nandfsdev);
|
|
|
|
DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
|
|
blocknr));
|
|
|
|
fsdev = node->nn_nandfsdev;
|
|
|
|
*bpp = getblk(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
|
|
0, 0, 0);
|
|
|
|
KASSERT((*bpp), ("%s: vp:%p lbn:%#jx\n", __func__,
|
|
NTOV(node), blocknr));
|
|
|
|
memset((*bpp)->b_data, 0, fsdev->nd_blocksize);
|
|
|
|
vfs_bio_clrbuf(*bpp);
|
|
(*bpp)->b_blkno = ~(0); /* To avoid VOP_BMAP in bdwrite */
|
|
|
|
nandfs_buf_set(*bpp, NANDFS_VBLK_ASSIGNED);
|
|
|
|
if (node->nn_ino != NANDFS_DAT_INO) {
|
|
error = nandfs_vblock_alloc(fsdev, &vblk);
|
|
if (error) {
|
|
nandfs_buf_clear(*bpp, NANDFS_VBLK_ASSIGNED);
|
|
brelse(*bpp);
|
|
return (error);
|
|
}
|
|
} else
|
|
vblk = fsdev->nd_fakevblk++;
|
|
|
|
nandfs_vblk_set(*bpp, vblk);
|
|
|
|
nandfs_bmap_insert_block(node, blocknr, *bpp);
|
|
return (0);
|
|
}
|
|
|
|
/* Translate index to a file block number and an entry */
|
|
void
|
|
nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
|
|
nandfs_lbn_t *blocknr, uint32_t *entry_in_block)
|
|
{
|
|
uint64_t blknr;
|
|
uint64_t group, group_offset, blocknr_in_group;
|
|
uint64_t desc_block, desc_offset;
|
|
|
|
/* Calculate our offset in the file */
|
|
group = index / mdt->entries_per_group;
|
|
group_offset = index % mdt->entries_per_group;
|
|
desc_block = group / mdt->groups_per_desc_block;
|
|
desc_offset = group % mdt->groups_per_desc_block;
|
|
blocknr_in_group = group_offset / mdt->entries_per_block;
|
|
|
|
/* To descgroup offset */
|
|
blknr = 1 + desc_block * mdt->blocks_per_desc_block;
|
|
|
|
/* To group offset */
|
|
blknr += desc_offset * mdt->blocks_per_group;
|
|
|
|
/* To actual file block */
|
|
blknr += 1 + blocknr_in_group;
|
|
|
|
*blocknr = blknr;
|
|
*entry_in_block = group_offset % mdt->entries_per_block;
|
|
}
|
|
|
|
void
|
|
nandfs_mdt_trans_blk(struct nandfs_mdt *mdt, uint64_t index,
|
|
uint64_t *desc, uint64_t *bitmap, nandfs_lbn_t *blocknr,
|
|
uint32_t *entry_in_block)
|
|
{
|
|
uint64_t blknr;
|
|
uint64_t group, group_offset, blocknr_in_group;
|
|
uint64_t desc_block, desc_offset;
|
|
|
|
/* Calculate our offset in the file */
|
|
group = index / mdt->entries_per_group;
|
|
group_offset = index % mdt->entries_per_group;
|
|
desc_block = group / mdt->groups_per_desc_block;
|
|
desc_offset = group % mdt->groups_per_desc_block;
|
|
blocknr_in_group = group_offset / mdt->entries_per_block;
|
|
|
|
/* To descgroup offset */
|
|
*desc = desc_block * mdt->blocks_per_desc_block;
|
|
blknr = 1 + desc_block * mdt->blocks_per_desc_block;
|
|
|
|
/* To group offset */
|
|
blknr += desc_offset * mdt->blocks_per_group;
|
|
*bitmap = blknr;
|
|
|
|
/* To actual file block */
|
|
blknr += 1 + blocknr_in_group;
|
|
|
|
*blocknr = blknr;
|
|
*entry_in_block = group_offset % mdt->entries_per_block;
|
|
|
|
DPRINTF(ALLOC,
|
|
("%s: desc_buf: %jx bitmap_buf: %jx entry_buf: %jx entry: %x\n",
|
|
__func__, (uintmax_t)*desc, (uintmax_t)*bitmap,
|
|
(uintmax_t)*blocknr, *entry_in_block));
|
|
}
|
|
|
|
int
|
|
nandfs_vtop(struct nandfs_node *node, nandfs_daddr_t vblocknr,
|
|
nandfs_daddr_t *pblocknr)
|
|
{
|
|
struct nandfs_node *dat_node;
|
|
struct nandfs_dat_entry *entry;
|
|
struct buf *bp;
|
|
nandfs_lbn_t ldatblknr;
|
|
uint32_t entry_in_block;
|
|
int locked, error;
|
|
|
|
if (node->nn_ino == NANDFS_DAT_INO || node->nn_ino == NANDFS_GC_INO) {
|
|
*pblocknr = vblocknr;
|
|
return (0);
|
|
}
|
|
|
|
/* only translate valid vblocknrs */
|
|
if (vblocknr == 0)
|
|
return (0);
|
|
|
|
dat_node = node->nn_nandfsdev->nd_dat_node;
|
|
nandfs_mdt_trans(&node->nn_nandfsdev->nd_dat_mdt, vblocknr, &ldatblknr,
|
|
&entry_in_block);
|
|
|
|
locked = NANDFS_VOP_ISLOCKED(NTOV(dat_node));
|
|
if (!locked)
|
|
VOP_LOCK(NTOV(dat_node), LK_SHARED);
|
|
error = nandfs_bread(dat_node, ldatblknr, NOCRED, 0, &bp);
|
|
if (error) {
|
|
DPRINTF(TRANSLATE, ("vtop: can't read in DAT block %#jx!\n",
|
|
(uintmax_t)ldatblknr));
|
|
brelse(bp);
|
|
VOP_UNLOCK(NTOV(dat_node), 0);
|
|
return (error);
|
|
}
|
|
|
|
/* Get our translation */
|
|
entry = ((struct nandfs_dat_entry *) bp->b_data) + entry_in_block;
|
|
DPRINTF(TRANSLATE, ("\tentry %p data %p entry_in_block %x\n",
|
|
entry, bp->b_data, entry_in_block))
|
|
DPRINTF(TRANSLATE, ("\tvblk %#jx -> %#jx for cp [%#jx-%#jx]\n",
|
|
(uintmax_t)vblocknr, (uintmax_t)entry->de_blocknr,
|
|
(uintmax_t)entry->de_start, (uintmax_t)entry->de_end));
|
|
|
|
*pblocknr = entry->de_blocknr;
|
|
brelse(bp);
|
|
if (!locked)
|
|
VOP_UNLOCK(NTOV(dat_node), 0);
|
|
|
|
MPASS(*pblocknr >= node->nn_nandfsdev->nd_fsdata.f_first_data_block ||
|
|
*pblocknr == 0);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_segsum_valid(struct nandfs_segment_summary *segsum)
|
|
{
|
|
|
|
return (segsum->ss_magic == NANDFS_SEGSUM_MAGIC);
|
|
}
|
|
|
|
int
|
|
nandfs_load_segsum(struct nandfs_device *fsdev, nandfs_daddr_t blocknr,
|
|
struct nandfs_segment_summary *segsum)
|
|
{
|
|
struct buf *bp;
|
|
int error;
|
|
|
|
DPRINTF(VOLUMES, ("nandfs: try segsum at block %jx\n",
|
|
(uintmax_t)blocknr));
|
|
|
|
error = nandfs_dev_bread(fsdev, blocknr, NOCRED, 0, &bp);
|
|
if (error)
|
|
return (error);
|
|
|
|
memcpy(segsum, bp->b_data, sizeof(struct nandfs_segment_summary));
|
|
brelse(bp);
|
|
|
|
if (!nandfs_segsum_valid(segsum)) {
|
|
DPRINTF(VOLUMES, ("%s: bad magic pseg:%jx\n", __func__,
|
|
blocknr));
|
|
return (EINVAL);
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
nandfs_load_super_root(struct nandfs_device *nandfsdev,
|
|
struct nandfs_segment_summary *segsum, uint64_t pseg)
|
|
{
|
|
struct nandfs_super_root super_root;
|
|
struct buf *bp;
|
|
uint64_t blocknr;
|
|
uint32_t super_root_crc, comp_crc;
|
|
int off, error;
|
|
|
|
/* Check if there is a superroot */
|
|
if ((segsum->ss_flags & NANDFS_SS_SR) == 0) {
|
|
DPRINTF(VOLUMES, ("%s: no super root in pseg:%jx\n", __func__,
|
|
pseg));
|
|
return (ENOENT);
|
|
}
|
|
|
|
/* Get our super root, located at the end of the pseg */
|
|
blocknr = pseg + segsum->ss_nblocks - 1;
|
|
DPRINTF(VOLUMES, ("%s: try at %#jx\n", __func__, (uintmax_t)blocknr));
|
|
|
|
error = nandfs_dev_bread(nandfsdev, blocknr, NOCRED, 0, &bp);
|
|
if (error)
|
|
return (error);
|
|
|
|
memcpy(&super_root, bp->b_data, sizeof(struct nandfs_super_root));
|
|
brelse(bp);
|
|
|
|
/* Check super root CRC */
|
|
super_root_crc = super_root.sr_sum;
|
|
off = sizeof(super_root.sr_sum);
|
|
comp_crc = crc32((uint8_t *)&super_root + off,
|
|
NANDFS_SR_BYTES - off);
|
|
|
|
if (super_root_crc != comp_crc) {
|
|
DPRINTF(VOLUMES, ("%s: invalid crc:%#x [expect:%#x]\n",
|
|
__func__, super_root_crc, comp_crc));
|
|
return (EINVAL);
|
|
}
|
|
|
|
nandfsdev->nd_super_root = super_root;
|
|
DPRINTF(VOLUMES, ("%s: got valid superroot\n", __func__));
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Search for the last super root recorded.
|
|
*/
|
|
int
|
|
nandfs_search_super_root(struct nandfs_device *nandfsdev)
|
|
{
|
|
struct nandfs_super_block *super;
|
|
struct nandfs_segment_summary segsum;
|
|
uint64_t seg_start, seg_end, cno, seq, create, pseg;
|
|
uint64_t segnum;
|
|
int error, found;
|
|
|
|
error = found = 0;
|
|
|
|
/* Search for last super root */
|
|
pseg = nandfsdev->nd_super.s_last_pseg;
|
|
segnum = nandfs_get_segnum_of_block(nandfsdev, pseg);
|
|
|
|
cno = nandfsdev->nd_super.s_last_cno;
|
|
create = seq = 0;
|
|
DPRINTF(VOLUMES, ("%s: start in pseg %#jx\n", __func__,
|
|
(uintmax_t)pseg));
|
|
|
|
for (;;) {
|
|
error = nandfs_load_segsum(nandfsdev, pseg, &segsum);
|
|
if (error)
|
|
break;
|
|
|
|
if (segsum.ss_seq < seq || segsum.ss_create < create)
|
|
break;
|
|
|
|
/* Try to load super root */
|
|
if (segsum.ss_flags & NANDFS_SS_SR) {
|
|
error = nandfs_load_super_root(nandfsdev, &segsum, pseg);
|
|
if (error)
|
|
break; /* confused */
|
|
found = 1;
|
|
|
|
super = &nandfsdev->nd_super;
|
|
nandfsdev->nd_last_segsum = segsum;
|
|
super->s_last_pseg = pseg;
|
|
super->s_last_cno = cno++;
|
|
super->s_last_seq = segsum.ss_seq;
|
|
super->s_state = NANDFS_VALID_FS;
|
|
seq = segsum.ss_seq;
|
|
create = segsum.ss_create;
|
|
} else {
|
|
seq = segsum.ss_seq;
|
|
create = segsum.ss_create;
|
|
}
|
|
|
|
/* Calculate next partial segment location */
|
|
pseg += segsum.ss_nblocks;
|
|
DPRINTF(VOLUMES, ("%s: next partial seg is %jx\n", __func__,
|
|
(uintmax_t)pseg));
|
|
|
|
/* Did we reach the end of the segment? if so, go to the next */
|
|
nandfs_get_segment_range(nandfsdev, segnum, &seg_start,
|
|
&seg_end);
|
|
if (pseg >= seg_end) {
|
|
pseg = segsum.ss_next;
|
|
DPRINTF(VOLUMES,
|
|
(" partial seg oor next is %jx[%jx - %jx]\n",
|
|
(uintmax_t)pseg, (uintmax_t)seg_start,
|
|
(uintmax_t)seg_end));
|
|
}
|
|
segnum = nandfs_get_segnum_of_block(nandfsdev, pseg);
|
|
}
|
|
|
|
if (error && !found)
|
|
return (error);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_get_node_raw(struct nandfs_device *nandfsdev, struct nandfsmount *nmp,
|
|
uint64_t ino, struct nandfs_inode *inode, struct nandfs_node **nodep)
|
|
{
|
|
struct nandfs_node *node;
|
|
struct vnode *nvp;
|
|
struct mount *mp;
|
|
int error;
|
|
|
|
*nodep = NULL;
|
|
|
|
/* Associate with mountpoint if present */
|
|
if (nmp) {
|
|
mp = nmp->nm_vfs_mountp;
|
|
error = getnewvnode("nandfs", mp, &nandfs_vnodeops, &nvp);
|
|
if (error)
|
|
return (error);
|
|
} else {
|
|
mp = NULL;
|
|
error = getnewvnode("snandfs", mp, &nandfs_system_vnodeops,
|
|
&nvp);
|
|
if (error)
|
|
return (error);
|
|
}
|
|
|
|
if (mp)
|
|
NANDFS_WRITELOCK(nandfsdev);
|
|
|
|
DPRINTF(IFILE, ("%s: ino: %#jx -> vp: %p\n",
|
|
__func__, (uintmax_t)ino, nvp));
|
|
/* Lock node */
|
|
lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
|
|
|
|
if (mp) {
|
|
error = insmntque(nvp, mp);
|
|
if (error != 0) {
|
|
*nodep = NULL;
|
|
return (error);
|
|
}
|
|
}
|
|
|
|
node = uma_zalloc(nandfs_node_zone, M_WAITOK | M_ZERO);
|
|
|
|
/* Crosslink */
|
|
node->nn_vnode = nvp;
|
|
nvp->v_bufobj.bo_ops = &buf_ops_nandfs;
|
|
node->nn_nmp = nmp;
|
|
node->nn_nandfsdev = nandfsdev;
|
|
nvp->v_data = node;
|
|
|
|
/* Initiase NANDFS node */
|
|
node->nn_ino = ino;
|
|
if (inode != NULL)
|
|
node->nn_inode = *inode;
|
|
|
|
nandfs_vinit(nvp, ino);
|
|
|
|
/* Return node */
|
|
*nodep = node;
|
|
DPRINTF(IFILE, ("%s: ino:%#jx vp:%p node:%p\n",
|
|
__func__, (uintmax_t)ino, nvp, *nodep));
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_get_node(struct nandfsmount *nmp, uint64_t ino,
|
|
struct nandfs_node **nodep)
|
|
{
|
|
struct nandfs_device *nandfsdev;
|
|
struct nandfs_inode inode, *entry;
|
|
struct vnode *nvp, *vpp;
|
|
struct thread *td;
|
|
struct buf *bp;
|
|
uint64_t ivblocknr;
|
|
uint32_t entry_in_block;
|
|
int error;
|
|
|
|
/* Look up node in hash table */
|
|
td = curthread;
|
|
*nodep = NULL;
|
|
|
|
if ((ino < NANDFS_ATIME_INO) && (ino != NANDFS_ROOT_INO)) {
|
|
printf("nandfs_get_node: system ino %"PRIu64" not in mount "
|
|
"point!\n", ino);
|
|
return (ENOENT);
|
|
}
|
|
|
|
error = vfs_hash_get(nmp->nm_vfs_mountp, ino, LK_EXCLUSIVE, td, &nvp,
|
|
NULL, NULL);
|
|
if (error)
|
|
return (error);
|
|
|
|
if (nvp != NULL) {
|
|
*nodep = (struct nandfs_node *)nvp->v_data;
|
|
return (0);
|
|
}
|
|
|
|
/* Look up inode structure in mountpoints ifile */
|
|
nandfsdev = nmp->nm_nandfsdev;
|
|
nandfs_mdt_trans(&nandfsdev->nd_ifile_mdt, ino, &ivblocknr,
|
|
&entry_in_block);
|
|
|
|
VOP_LOCK(NTOV(nmp->nm_ifile_node), LK_SHARED);
|
|
error = nandfs_bread(nmp->nm_ifile_node, ivblocknr, NOCRED, 0, &bp);
|
|
if (error) {
|
|
brelse(bp);
|
|
VOP_UNLOCK(NTOV(nmp->nm_ifile_node), 0);
|
|
return (ENOENT);
|
|
}
|
|
|
|
/* Get inode entry */
|
|
entry = (struct nandfs_inode *) bp->b_data + entry_in_block;
|
|
memcpy(&inode, entry, sizeof(struct nandfs_inode));
|
|
brelse(bp);
|
|
VOP_UNLOCK(NTOV(nmp->nm_ifile_node), 0);
|
|
|
|
/* Get node */
|
|
error = nandfs_get_node_raw(nmp->nm_nandfsdev, nmp, ino, &inode, nodep);
|
|
if (error) {
|
|
*nodep = NULL;
|
|
return (error);
|
|
}
|
|
|
|
nvp = (*nodep)->nn_vnode;
|
|
error = vfs_hash_insert(nvp, ino, 0, td, &vpp, NULL, NULL);
|
|
if (error) {
|
|
*nodep = NULL;
|
|
return (error);
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
void
|
|
nandfs_dispose_node(struct nandfs_node **nodep)
|
|
{
|
|
struct nandfs_node *node;
|
|
struct vnode *vp;
|
|
|
|
/* Protect against rogue values */
|
|
node = *nodep;
|
|
if (!node) {
|
|
return;
|
|
}
|
|
DPRINTF(NODE, ("nandfs_dispose_node: %p\n", *nodep));
|
|
|
|
vp = NTOV(node);
|
|
vp->v_data = NULL;
|
|
|
|
/* Free our associated memory */
|
|
uma_zfree(nandfs_node_zone, node);
|
|
|
|
*nodep = NULL;
|
|
}
|
|
|
|
int
|
|
nandfs_lookup_name_in_dir(struct vnode *dvp, const char *name, int namelen,
|
|
uint64_t *ino, int *found, uint64_t *off)
|
|
{
|
|
struct nandfs_node *dir_node = VTON(dvp);
|
|
struct nandfs_dir_entry *ndirent;
|
|
struct buf *bp;
|
|
uint64_t file_size, diroffset, blkoff;
|
|
uint64_t blocknr;
|
|
uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize;
|
|
uint8_t *pos, name_len;
|
|
int error;
|
|
|
|
*found = 0;
|
|
|
|
DPRINTF(VNCALL, ("%s: %s file\n", __func__, name));
|
|
if (dvp->v_type != VDIR) {
|
|
return (ENOTDIR);
|
|
}
|
|
|
|
/* Get directory filesize */
|
|
file_size = dir_node->nn_inode.i_size;
|
|
|
|
/* Walk the directory */
|
|
diroffset = 0;
|
|
blocknr = 0;
|
|
blkoff = 0;
|
|
error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
|
|
if (error) {
|
|
brelse(bp);
|
|
return (EIO);
|
|
}
|
|
|
|
while (diroffset < file_size) {
|
|
if (blkoff >= blocksize) {
|
|
blkoff = 0; blocknr++;
|
|
brelse(bp);
|
|
error = nandfs_bread(dir_node, blocknr, NOCRED, 0,
|
|
&bp);
|
|
if (error) {
|
|
brelse(bp);
|
|
return (EIO);
|
|
}
|
|
}
|
|
|
|
/* Read in one dirent */
|
|
pos = (uint8_t *) bp->b_data + blkoff;
|
|
ndirent = (struct nandfs_dir_entry *) pos;
|
|
name_len = ndirent->name_len;
|
|
|
|
if ((name_len == namelen) &&
|
|
(strncmp(name, ndirent->name, name_len) == 0) &&
|
|
(ndirent->inode != 0)) {
|
|
*ino = ndirent->inode;
|
|
*off = diroffset;
|
|
DPRINTF(LOOKUP, ("found `%.*s` with ino %"PRIx64"\n",
|
|
name_len, ndirent->name, *ino));
|
|
*found = 1;
|
|
break;
|
|
}
|
|
|
|
/* Advance */
|
|
diroffset += ndirent->rec_len;
|
|
blkoff += ndirent->rec_len;
|
|
}
|
|
brelse(bp);
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
nandfs_get_fsinfo(struct nandfsmount *nmp, struct nandfs_fsinfo *fsinfo)
|
|
{
|
|
struct nandfs_device *fsdev;
|
|
|
|
fsdev = nmp->nm_nandfsdev;
|
|
|
|
memcpy(&fsinfo->fs_fsdata, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata));
|
|
memcpy(&fsinfo->fs_super, &fsdev->nd_super, sizeof(fsdev->nd_super));
|
|
snprintf(fsinfo->fs_dev, sizeof(fsinfo->fs_dev),
|
|
"%s", nmp->nm_vfs_mountp->mnt_stat.f_mntfromname);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
nandfs_inode_init(struct nandfs_inode *inode, uint16_t mode)
|
|
{
|
|
struct timespec ts;
|
|
|
|
vfs_timestamp(&ts);
|
|
|
|
inode->i_blocks = 0;
|
|
inode->i_size = 0;
|
|
inode->i_ctime = ts.tv_sec;
|
|
inode->i_ctime_nsec = ts.tv_nsec;
|
|
inode->i_mtime = ts.tv_sec;
|
|
inode->i_mtime_nsec = ts.tv_nsec;
|
|
inode->i_mode = mode;
|
|
inode->i_links_count = 1;
|
|
if (S_ISDIR(mode))
|
|
inode->i_links_count = 2;
|
|
inode->i_flags = 0;
|
|
|
|
inode->i_special = 0;
|
|
memset(inode->i_db, 0, sizeof(inode->i_db));
|
|
memset(inode->i_ib, 0, sizeof(inode->i_ib));
|
|
}
|
|
|
|
void
|
|
nandfs_inode_destroy(struct nandfs_inode *inode)
|
|
{
|
|
|
|
MPASS(inode->i_blocks == 0);
|
|
bzero(inode, sizeof(*inode));
|
|
}
|
|
|
|
int
|
|
nandfs_fs_full(struct nandfs_device *nffsdev)
|
|
{
|
|
uint64_t space, bps;
|
|
|
|
bps = nffsdev->nd_fsdata.f_blocks_per_segment;
|
|
space = (nffsdev->nd_clean_segs - 1) * bps;
|
|
|
|
DPRINTF(BUF, ("%s: bufs:%jx space:%jx\n", __func__,
|
|
(uintmax_t)nffsdev->nd_dirty_bufs, (uintmax_t)space));
|
|
|
|
if (nffsdev->nd_dirty_bufs + (nffsdev->nd_segs_reserved * bps) >= space)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
_nandfs_dirty_buf(struct buf *bp, int dirty_meta, int force)
|
|
{
|
|
struct nandfs_device *nffsdev;
|
|
struct nandfs_node *node;
|
|
uint64_t ino, bps;
|
|
|
|
if (NANDFS_ISGATHERED(bp)) {
|
|
bqrelse(bp);
|
|
return (0);
|
|
}
|
|
if ((bp->b_flags & (B_MANAGED | B_DELWRI)) == (B_MANAGED | B_DELWRI)) {
|
|
bqrelse(bp);
|
|
return (0);
|
|
}
|
|
|
|
node = VTON(bp->b_vp);
|
|
nffsdev = node->nn_nandfsdev;
|
|
DPRINTF(BUF, ("%s: buf:%p\n", __func__, bp));
|
|
ino = node->nn_ino;
|
|
|
|
if (nandfs_fs_full(nffsdev) && !NANDFS_SYS_NODE(ino) && !force) {
|
|
brelse(bp);
|
|
return (ENOSPC);
|
|
}
|
|
|
|
bp->b_flags |= B_MANAGED;
|
|
bdwrite(bp);
|
|
|
|
nandfs_dirty_bufs_increment(nffsdev);
|
|
|
|
KASSERT((bp->b_vp), ("vp missing for bp"));
|
|
KASSERT((nandfs_vblk_get(bp) || ino == NANDFS_DAT_INO),
|
|
("bp vblk is 0"));
|
|
|
|
/*
|
|
* To maintain consistency of FS we need to force making
|
|
* meta buffers dirty, even if free space is low.
|
|
*/
|
|
if (dirty_meta && ino != NANDFS_GC_INO)
|
|
nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1);
|
|
|
|
bps = nffsdev->nd_fsdata.f_blocks_per_segment;
|
|
|
|
if (nffsdev->nd_dirty_bufs >= (bps * nandfs_max_dirty_segs)) {
|
|
mtx_lock(&nffsdev->nd_sync_mtx);
|
|
if (nffsdev->nd_syncing == 0) {
|
|
DPRINTF(SYNC, ("%s: wakeup gc\n", __func__));
|
|
nffsdev->nd_syncing = 1;
|
|
wakeup(&nffsdev->nd_syncing);
|
|
}
|
|
mtx_unlock(&nffsdev->nd_sync_mtx);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_dirty_buf(struct buf *bp, int force)
|
|
{
|
|
|
|
return (_nandfs_dirty_buf(bp, 1, force));
|
|
}
|
|
|
|
int
|
|
nandfs_dirty_buf_meta(struct buf *bp, int force)
|
|
{
|
|
|
|
return (_nandfs_dirty_buf(bp, 0, force));
|
|
}
|
|
|
|
void
|
|
nandfs_undirty_buf_fsdev(struct nandfs_device *nffsdev, struct buf *bp)
|
|
{
|
|
|
|
BUF_ASSERT_HELD(bp);
|
|
|
|
if (bp->b_flags & B_DELWRI) {
|
|
bp->b_flags &= ~(B_DELWRI|B_MANAGED);
|
|
nandfs_dirty_bufs_decrement(nffsdev);
|
|
}
|
|
/*
|
|
* Since it is now being written, we can clear its deferred write flag.
|
|
*/
|
|
bp->b_flags &= ~B_DEFERRED;
|
|
|
|
brelse(bp);
|
|
}
|
|
|
|
void
|
|
nandfs_undirty_buf(struct buf *bp)
|
|
{
|
|
struct nandfs_node *node;
|
|
|
|
node = VTON(bp->b_vp);
|
|
|
|
nandfs_undirty_buf_fsdev(node->nn_nandfsdev, bp);
|
|
}
|
|
|
|
void
|
|
nandfs_vblk_set(struct buf *bp, nandfs_daddr_t blocknr)
|
|
{
|
|
|
|
nandfs_daddr_t *vblk = (nandfs_daddr_t *)(&bp->b_fsprivate1);
|
|
*vblk = blocknr;
|
|
}
|
|
|
|
nandfs_daddr_t
|
|
nandfs_vblk_get(struct buf *bp)
|
|
{
|
|
|
|
nandfs_daddr_t *vblk = (nandfs_daddr_t *)(&bp->b_fsprivate1);
|
|
return (*vblk);
|
|
}
|
|
|
|
void
|
|
nandfs_buf_set(struct buf *bp, uint32_t bits)
|
|
{
|
|
uintptr_t flags;
|
|
|
|
flags = (uintptr_t)bp->b_fsprivate3;
|
|
flags |= (uintptr_t)bits;
|
|
bp->b_fsprivate3 = (void *)flags;
|
|
}
|
|
|
|
void
|
|
nandfs_buf_clear(struct buf *bp, uint32_t bits)
|
|
{
|
|
uintptr_t flags;
|
|
|
|
flags = (uintptr_t)bp->b_fsprivate3;
|
|
flags &= ~(uintptr_t)bits;
|
|
bp->b_fsprivate3 = (void *)flags;
|
|
}
|
|
|
|
int
|
|
nandfs_buf_check(struct buf *bp, uint32_t bits)
|
|
{
|
|
uintptr_t flags;
|
|
|
|
flags = (uintptr_t)bp->b_fsprivate3;
|
|
if (flags & bits)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nandfs_erase(struct nandfs_device *fsdev, off_t offset, size_t size)
|
|
{
|
|
DPRINTF(BLOCK, ("%s: performing erase at offset %jx size %zx\n",
|
|
__func__, offset, size));
|
|
|
|
MPASS(size % fsdev->nd_erasesize == 0);
|
|
|
|
return (g_delete_data(fsdev->nd_gconsumer, offset, size));
|
|
}
|
|
|
|
int
|
|
nandfs_vop_islocked(struct vnode *vp)
|
|
{
|
|
int islocked;
|
|
|
|
islocked = VOP_ISLOCKED(vp);
|
|
return (islocked == LK_EXCLUSIVE || islocked == LK_SHARED);
|
|
}
|
|
|
|
nandfs_daddr_t
|
|
nandfs_block_to_dblock(struct nandfs_device *fsdev, nandfs_lbn_t block)
|
|
{
|
|
|
|
return (btodb(block * fsdev->nd_blocksize));
|
|
}
|