freebsd-skq/lib/libstand/nandfs.c
Ian Lepore 7668336c8e Add a divisor parameter to twiddle() so that callers can request that output
only happen on every Nth call.  Update the existing twiddle() calls done in
various IO loops to roughly reflect the relative IO sizes.  That is, tftp
and nfs call twiddle() on every 1K block, ufs on every filesystem block,
so the network calls now use a much larger divisor than disk IO calls.

Also add a new twiddle_divisor() function that allows an application to set
a global divisor that is applied on top of the per-call divisors.  Nothing
calls this yet, but loader(8) will be using it to further throttle the
cursor for slow serial consoles.
2014-12-22 20:42:36 +00:00

1062 lines
25 KiB
C

/*-
* 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/queue.h>
#include <sys/stdint.h>
#include <ufs/ufs/dinode.h>
#include <fs/nandfs/nandfs_fs.h>
#include "stand.h"
#include "string.h"
#include "zlib.h"
#define DEBUG
#undef DEBUG
#ifdef DEBUG
#define NANDFS_DEBUG(fmt, args...) do { \
printf("NANDFS_DEBUG:" fmt "\n", ##args); } while (0)
#else
#define NANDFS_DEBUG(fmt, args...)
#endif
struct nandfs_mdt {
uint32_t entries_per_block;
uint32_t entries_per_group;
uint32_t blocks_per_group;
uint32_t groups_per_desc_block; /* desc is super group */
uint32_t blocks_per_desc_block; /* desc is super group */
};
struct bmap_buf {
LIST_ENTRY(bmap_buf) list;
nandfs_daddr_t blknr;
uint64_t *map;
};
struct nandfs_node {
struct nandfs_inode *inode;
LIST_HEAD(, bmap_buf) bmap_bufs;
};
struct nandfs {
int nf_blocksize;
int nf_sectorsize;
int nf_cpno;
struct open_file *nf_file;
struct nandfs_node *nf_opened_node;
u_int nf_offset;
uint8_t *nf_buf;
int64_t nf_buf_blknr;
struct nandfs_fsdata *nf_fsdata;
struct nandfs_super_block *nf_sb;
struct nandfs_segment_summary nf_segsum;
struct nandfs_checkpoint nf_checkpoint;
struct nandfs_super_root nf_sroot;
struct nandfs_node nf_ifile;
struct nandfs_node nf_datfile;
struct nandfs_node nf_cpfile;
struct nandfs_mdt nf_datfile_mdt;
struct nandfs_mdt nf_ifile_mdt;
int nf_nindir[NIADDR];
};
static int nandfs_open(const char *, struct open_file *);
static int nandfs_close(struct open_file *);
static int nandfs_read(struct open_file *, void *, size_t, size_t *);
static off_t nandfs_seek(struct open_file *, off_t, int);
static int nandfs_stat(struct open_file *, struct stat *);
static int nandfs_readdir(struct open_file *, struct dirent *);
static int nandfs_buf_read(struct nandfs *, void **, size_t *);
static struct nandfs_node *nandfs_lookup_path(struct nandfs *, const char *);
static int nandfs_read_inode(struct nandfs *, struct nandfs_node *,
nandfs_lbn_t, u_int, void *, int);
static int nandfs_read_blk(struct nandfs *, nandfs_daddr_t, void *, int);
static int nandfs_bmap_lookup(struct nandfs *, struct nandfs_node *,
nandfs_lbn_t, nandfs_daddr_t *, int);
static int nandfs_get_checkpoint(struct nandfs *, uint64_t,
struct nandfs_checkpoint *);
static nandfs_daddr_t nandfs_vtop(struct nandfs *, nandfs_daddr_t);
static void nandfs_calc_mdt_consts(int, struct nandfs_mdt *, int);
static void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t,
nandfs_daddr_t *, uint32_t *);
static int ioread(struct open_file *, off_t, void *, u_int);
static int nandfs_probe_sectorsize(struct open_file *);
struct fs_ops nandfs_fsops = {
"nandfs",
nandfs_open,
nandfs_close,
nandfs_read,
null_write,
nandfs_seek,
nandfs_stat,
nandfs_readdir
};
#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
/* from NetBSD's src/sys/net/if_ethersubr.c */
static uint32_t
nandfs_crc32(uint32_t crc, const uint8_t *buf, size_t len)
{
static const uint32_t crctab[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
size_t i;
crc = crc ^ ~0U;
for (i = 0; i < len; i++) {
crc ^= buf[i];
crc = (crc >> 4) ^ crctab[crc & 0xf];
crc = (crc >> 4) ^ crctab[crc & 0xf];
}
return (crc ^ ~0U);
}
static int
nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
{
uint32_t fsdata_crc, comp_crc;
if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
return (0);
/* Preserve crc */
fsdata_crc = fsdata->f_sum;
/* Calculate */
fsdata->f_sum = (0);
comp_crc = nandfs_crc32(0, (uint8_t *)fsdata, fsdata->f_bytes);
/* Restore */
fsdata->f_sum = fsdata_crc;
/* Check CRC */
return (fsdata_crc == comp_crc);
}
static int
nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
struct nandfs_super_block *super)
{
uint32_t super_crc, comp_crc;
/* Check super block magic */
if (super->s_magic != NANDFS_SUPER_MAGIC)
return (0);
/* Preserve CRC */
super_crc = super->s_sum;
/* Calculate */
super->s_sum = (0);
comp_crc = nandfs_crc32(0, (uint8_t *)super, fsdata->f_sbbytes);
/* Restore */
super->s_sum = super_crc;
/* Check CRC */
return (super_crc == comp_crc);
}
static int
nandfs_find_super_block(struct nandfs *fs, struct open_file *f)
{
struct nandfs_super_block *sb;
int i, j, n, s;
int sectors_to_read, error;
sb = malloc(fs->nf_sectorsize);
if (sb == NULL)
return (ENOMEM);
memset(fs->nf_sb, 0, sizeof(*fs->nf_sb));
sectors_to_read = (NANDFS_NFSAREAS * fs->nf_fsdata->f_erasesize) /
fs->nf_sectorsize;
for (i = 0; i < sectors_to_read; i++) {
NANDFS_DEBUG("reading i %d offset %d\n", i,
i * fs->nf_sectorsize);
error = ioread(f, i * fs->nf_sectorsize, (char *)sb,
fs->nf_sectorsize);
if (error) {
NANDFS_DEBUG("error %d\n", error);
continue;
}
n = fs->nf_sectorsize / sizeof(struct nandfs_super_block);
s = 0;
if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) {
if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata))
continue;
else {
s += (sizeof(struct nandfs_fsdata) /
sizeof(struct nandfs_super_block));
}
}
for (j = s; j < n; j++) {
if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j]))
continue;
NANDFS_DEBUG("magic %x wtime %jd, lastcp 0x%jx\n",
sb[j].s_magic, sb[j].s_wtime, sb[j].s_last_cno);
if (sb[j].s_last_cno > fs->nf_sb->s_last_cno)
memcpy(fs->nf_sb, &sb[j], sizeof(*fs->nf_sb));
}
}
free(sb);
return (fs->nf_sb->s_magic != 0 ? 0 : EINVAL);
}
static int
nandfs_find_fsdata(struct nandfs *fs, struct open_file *f)
{
int offset, error, i;
NANDFS_DEBUG("starting\n");
offset = 0;
for (i = 0; i < 64 * NANDFS_NFSAREAS; i++) {
error = ioread(f, offset, (char *)fs->nf_fsdata,
sizeof(struct nandfs_fsdata));
if (error)
return (error);
if (fs->nf_fsdata->f_magic == NANDFS_FSDATA_MAGIC) {
NANDFS_DEBUG("found at %x, volume %s\n", offset,
fs->nf_fsdata->f_volume_name);
if (nandfs_check_fsdata_crc(fs->nf_fsdata))
break;
}
offset += fs->nf_sectorsize;
}
return (error);
}
static int
nandfs_read_structures(struct nandfs *fs, struct open_file *f)
{
int error;
error = nandfs_find_fsdata(fs, f);
if (error)
return (error);
error = nandfs_find_super_block(fs, f);
if (error == 0)
NANDFS_DEBUG("selected sb with w_time %jd last_pseg %jx\n",
fs->nf_sb->s_wtime, fs->nf_sb->s_last_pseg);
return (error);
}
static int
nandfs_mount(struct nandfs *fs, struct open_file *f)
{
int err = 0, level;
uint64_t last_pseg;
fs->nf_fsdata = malloc(sizeof(struct nandfs_fsdata));
fs->nf_sb = malloc(sizeof(struct nandfs_super_block));
err = nandfs_read_structures(fs, f);
if (err) {
free(fs->nf_fsdata);
free(fs->nf_sb);
return (err);
}
fs->nf_blocksize = 1 << (fs->nf_fsdata->f_log_block_size + 10);
NANDFS_DEBUG("using superblock with wtime %jd\n", fs->nf_sb->s_wtime);
fs->nf_cpno = fs->nf_sb->s_last_cno;
last_pseg = fs->nf_sb->s_last_pseg;
/*
* Calculate indirect block levels.
*/
nandfs_daddr_t mult;
mult = 1;
for (level = 0; level < NIADDR; level++) {
mult *= NINDIR(fs);
fs->nf_nindir[level] = mult;
}
nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_datfile_mdt,
fs->nf_fsdata->f_dat_entry_size);
nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_ifile_mdt,
fs->nf_fsdata->f_inode_size);
err = ioread(f, last_pseg * fs->nf_blocksize, &fs->nf_segsum,
sizeof(struct nandfs_segment_summary));
if (err) {
free(fs->nf_sb);
free(fs->nf_fsdata);
return (err);
}
err = ioread(f, (last_pseg + fs->nf_segsum.ss_nblocks - 1) *
fs->nf_blocksize, &fs->nf_sroot, sizeof(struct nandfs_super_root));
if (err) {
free(fs->nf_sb);
free(fs->nf_fsdata);
return (err);
}
fs->nf_datfile.inode = &fs->nf_sroot.sr_dat;
LIST_INIT(&fs->nf_datfile.bmap_bufs);
fs->nf_cpfile.inode = &fs->nf_sroot.sr_cpfile;
LIST_INIT(&fs->nf_cpfile.bmap_bufs);
err = nandfs_get_checkpoint(fs, fs->nf_cpno, &fs->nf_checkpoint);
if (err) {
free(fs->nf_sb);
free(fs->nf_fsdata);
return (err);
}
NANDFS_DEBUG("checkpoint cp_cno=%lld\n", fs->nf_checkpoint.cp_cno);
NANDFS_DEBUG("checkpoint cp_inodes_count=%lld\n",
fs->nf_checkpoint.cp_inodes_count);
NANDFS_DEBUG("checkpoint cp_ifile_inode.i_blocks=%lld\n",
fs->nf_checkpoint.cp_ifile_inode.i_blocks);
fs->nf_ifile.inode = &fs->nf_checkpoint.cp_ifile_inode;
LIST_INIT(&fs->nf_ifile.bmap_bufs);
return (0);
}
#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
static int
nandfs_open(const char *path, struct open_file *f)
{
struct nandfs *fs;
struct nandfs_node *node;
int err, bsize, level;
NANDFS_DEBUG("nandfs_open('%s', %p)\n", path, f);
fs = malloc(sizeof(struct nandfs));
f->f_fsdata = fs;
fs->nf_file = f;
bsize = nandfs_probe_sectorsize(f);
if (bsize < 0) {
printf("Cannot probe medium sector size\n");
return (EINVAL);
}
fs->nf_sectorsize = bsize;
/*
* Calculate indirect block levels.
*/
nandfs_daddr_t mult;
mult = 1;
for (level = 0; level < NIADDR; level++) {
mult *= NINDIR(fs);
fs->nf_nindir[level] = mult;
}
NANDFS_DEBUG("fs %p nf_sectorsize=%x\n", fs, fs->nf_sectorsize);
err = nandfs_mount(fs, f);
if (err) {
NANDFS_DEBUG("Cannot mount nandfs: %s\n", strerror(err));
return (err);
}
node = nandfs_lookup_path(fs, path);
if (node == NULL)
return (EINVAL);
fs->nf_offset = 0;
fs->nf_buf = NULL;
fs->nf_buf_blknr = -1;
fs->nf_opened_node = node;
LIST_INIT(&fs->nf_opened_node->bmap_bufs);
return (0);
}
static void
nandfs_free_node(struct nandfs_node *node)
{
struct bmap_buf *bmap, *tmp;
free(node->inode);
LIST_FOREACH_SAFE(bmap, &node->bmap_bufs, list, tmp) {
LIST_REMOVE(bmap, list);
free(bmap->map);
free(bmap);
}
free(node);
}
static int
nandfs_close(struct open_file *f)
{
struct nandfs *fs = f->f_fsdata;
NANDFS_DEBUG("nandfs_close(%p)\n", f);
if (fs->nf_buf != NULL)
free(fs->nf_buf);
nandfs_free_node(fs->nf_opened_node);
free(fs->nf_sb);
free(fs);
return (0);
}
static int
nandfs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
{
struct nandfs *fs = (struct nandfs *)f->f_fsdata;
size_t csize, buf_size;
void *buf;
int error = 0;
NANDFS_DEBUG("nandfs_read(file=%p, addr=%p, size=%d)\n", f, addr, size);
while (size != 0) {
if (fs->nf_offset >= fs->nf_opened_node->inode->i_size)
break;
error = nandfs_buf_read(fs, &buf, &buf_size);
if (error)
break;
csize = size;
if (csize > buf_size)
csize = buf_size;
bcopy(buf, addr, csize);
fs->nf_offset += csize;
addr = (char *)addr + csize;
size -= csize;
}
if (resid)
*resid = size;
return (error);
}
static off_t
nandfs_seek(struct open_file *f, off_t offset, int where)
{
struct nandfs *fs = f->f_fsdata;
off_t off;
u_int size;
NANDFS_DEBUG("nandfs_seek(file=%p, offset=%lld, where=%d)\n", f,
offset, where);
size = fs->nf_opened_node->inode->i_size;
switch (where) {
case SEEK_SET:
off = 0;
break;
case SEEK_CUR:
off = fs->nf_offset;
break;
case SEEK_END:
off = size;
break;
default:
errno = EINVAL;
return (-1);
}
off += offset;
if (off < 0 || off > size) {
errno = EINVAL;
return(-1);
}
fs->nf_offset = (u_int)off;
return (off);
}
static int
nandfs_stat(struct open_file *f, struct stat *sb)
{
struct nandfs *fs = f->f_fsdata;
NANDFS_DEBUG("nandfs_stat(file=%p, stat=%p)\n", f, sb);
sb->st_size = fs->nf_opened_node->inode->i_size;
sb->st_mode = fs->nf_opened_node->inode->i_mode;
sb->st_uid = fs->nf_opened_node->inode->i_uid;
sb->st_gid = fs->nf_opened_node->inode->i_gid;
return (0);
}
static int
nandfs_readdir(struct open_file *f, struct dirent *d)
{
struct nandfs *fs = f->f_fsdata;
struct nandfs_dir_entry *dirent;
void *buf;
size_t buf_size;
NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)\n", f, d);
if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) {
NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) ENOENT\n",
f, d);
return (ENOENT);
}
if (nandfs_buf_read(fs, &buf, &buf_size)) {
NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)"
"buf_read failed\n", f, d);
return (EIO);
}
NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) moving forward\n",
f, d);
dirent = (struct nandfs_dir_entry *)buf;
fs->nf_offset += dirent->rec_len;
strncpy(d->d_name, dirent->name, dirent->name_len);
d->d_name[dirent->name_len] = '\0';
d->d_type = dirent->file_type;
return (0);
}
static int
nandfs_buf_read(struct nandfs *fs, void **buf_p, size_t *size_p)
{
nandfs_daddr_t blknr, blkoff;
blknr = fs->nf_offset / fs->nf_blocksize;
blkoff = fs->nf_offset % fs->nf_blocksize;
if (blknr != fs->nf_buf_blknr) {
if (fs->nf_buf == NULL)
fs->nf_buf = malloc(fs->nf_blocksize);
if (nandfs_read_inode(fs, fs->nf_opened_node, blknr, 1,
fs->nf_buf, 0))
return (EIO);
fs->nf_buf_blknr = blknr;
}
*buf_p = fs->nf_buf + blkoff;
*size_p = fs->nf_blocksize - blkoff;
NANDFS_DEBUG("nandfs_buf_read buf_p=%p size_p=%d\n", *buf_p, *size_p);
if (*size_p > fs->nf_opened_node->inode->i_size - fs->nf_offset)
*size_p = fs->nf_opened_node->inode->i_size - fs->nf_offset;
return (0);
}
static struct nandfs_node *
nandfs_lookup_node(struct nandfs *fs, uint64_t ino)
{
uint64_t blocknr;
int entrynr;
struct nandfs_inode *buffer;
struct nandfs_node *node;
struct nandfs_inode *inode;
NANDFS_DEBUG("nandfs_lookup_node ino=%lld\n", ino);
if (ino == 0) {
printf("nandfs_lookup_node: invalid inode requested\n");
return (NULL);
}
buffer = malloc(fs->nf_blocksize);
inode = malloc(sizeof(struct nandfs_inode));
node = malloc(sizeof(struct nandfs_node));
nandfs_mdt_trans(&fs->nf_ifile_mdt, ino, &blocknr, &entrynr);
if (nandfs_read_inode(fs, &fs->nf_ifile, blocknr, 1, buffer, 0))
return (NULL);
memcpy(inode, &buffer[entrynr], sizeof(struct nandfs_inode));
node->inode = inode;
free(buffer);
return (node);
}
static struct nandfs_node *
nandfs_lookup_path(struct nandfs *fs, const char *path)
{
struct nandfs_node *node;
struct nandfs_dir_entry *dirent;
char *namebuf;
uint64_t i, done, pinode, inode;
int nlinks = 0, counter, len, link_len, nameidx;
uint8_t *buffer, *orig;
char *strp, *lpath;
buffer = malloc(fs->nf_blocksize);
orig = buffer;
namebuf = malloc(2 * MAXPATHLEN + 2);
strncpy(namebuf, path, MAXPATHLEN);
namebuf[MAXPATHLEN] = '\0';
done = nameidx = 0;
lpath = namebuf;
/* Get the root inode */
node = nandfs_lookup_node(fs, NANDFS_ROOT_INO);
inode = NANDFS_ROOT_INO;
while ((strp = strsep(&lpath, "/")) != NULL) {
if (*strp == '\0')
continue;
if ((node->inode->i_mode & IFMT) != IFDIR) {
nandfs_free_node(node);
node = NULL;
goto out;
}
len = strlen(strp);
NANDFS_DEBUG("%s: looking for %s\n", __func__, strp);
for (i = 0; i < node->inode->i_blocks; i++) {
if (nandfs_read_inode(fs, node, i, 1, orig, 0)) {
node = NULL;
goto out;
}
buffer = orig;
done = counter = 0;
while (1) {
dirent =
(struct nandfs_dir_entry *)(void *)buffer;
NANDFS_DEBUG("%s: dirent.name = %s\n",
__func__, dirent->name);
NANDFS_DEBUG("%s: dirent.rec_len = %d\n",
__func__, dirent->rec_len);
NANDFS_DEBUG("%s: dirent.inode = %lld\n",
__func__, dirent->inode);
if (len == dirent->name_len &&
(strncmp(strp, dirent->name, len) == 0) &&
dirent->inode != 0) {
nandfs_free_node(node);
node = nandfs_lookup_node(fs,
dirent->inode);
pinode = inode;
inode = dirent->inode;
done = 1;
break;
}
counter += dirent->rec_len;
buffer += dirent->rec_len;
if (counter == fs->nf_blocksize)
break;
}
if (done)
break;
}
if (!done) {
node = NULL;
goto out;
}
NANDFS_DEBUG("%s: %.*s has mode %o\n", __func__,
dirent->name_len, dirent->name, node->inode->i_mode);
if ((node->inode->i_mode & IFMT) == IFLNK) {
NANDFS_DEBUG("%s: %.*s is symlink\n",
__func__, dirent->name_len, dirent->name);
link_len = node->inode->i_size;
if (++nlinks > MAXSYMLINKS) {
nandfs_free_node(node);
node = NULL;
goto out;
}
if (nandfs_read_inode(fs, node, 0, 1, orig, 0)) {
nandfs_free_node(node);
node = NULL;
goto out;
}
NANDFS_DEBUG("%s: symlink is %.*s\n",
__func__, link_len, (char *)orig);
nameidx = (nameidx == 0) ? MAXPATHLEN + 1 : 0;
bcopy((char *)orig, namebuf + nameidx,
(unsigned)link_len);
if (lpath != NULL) {
namebuf[nameidx + link_len++] = '/';
strncpy(namebuf + nameidx + link_len, lpath,
MAXPATHLEN - link_len);
namebuf[nameidx + MAXPATHLEN] = '\0';
} else
namebuf[nameidx + link_len] = '\0';
NANDFS_DEBUG("%s: strp=%s, lpath=%s, namebuf0=%s, "
"namebuf1=%s, idx=%d\n", __func__, strp, lpath,
namebuf + 0, namebuf + MAXPATHLEN + 1, nameidx);
lpath = namebuf + nameidx;
nandfs_free_node(node);
/*
* If absolute pathname, restart at root. Otherwise
* continue with out parent inode.
*/
inode = (orig[0] == '/') ? NANDFS_ROOT_INO : pinode;
node = nandfs_lookup_node(fs, inode);
}
}
out:
free(namebuf);
free(orig);
return (node);
}
static int
nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node,
nandfs_daddr_t blknr, u_int nblks, void *buf, int raw)
{
uint64_t *pblks;
uint64_t *vblks;
u_int i;
int error;
pblks = malloc(nblks * sizeof(uint64_t));
vblks = malloc(nblks * sizeof(uint64_t));
NANDFS_DEBUG("nandfs_read_inode fs=%p node=%p blknr=%lld nblks=%d\n",
fs, node, blknr, nblks);
for (i = 0; i < nblks; i++) {
error = nandfs_bmap_lookup(fs, node, blknr + i, &vblks[i], raw);
if (error) {
free(pblks);
free(vblks);
return (error);
}
if (raw == 0)
pblks[i] = nandfs_vtop(fs, vblks[i]);
else
pblks[i] = vblks[i];
}
for (i = 0; i < nblks; i++) {
if (ioread(fs->nf_file, pblks[i] * fs->nf_blocksize, buf,
fs->nf_blocksize)) {
free(pblks);
free(vblks);
return (EIO);
}
buf = (void *)((uintptr_t)buf + fs->nf_blocksize);
}
free(pblks);
free(vblks);
return (0);
}
static int
nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf, int phys)
{
uint64_t pblknr;
pblknr = (phys ? blknr : nandfs_vtop(fs, blknr));
return (ioread(fs->nf_file, pblknr * fs->nf_blocksize, buf,
fs->nf_blocksize));
}
static int
nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno,
struct nandfs_checkpoint *cp)
{
uint64_t blocknr;
int blockoff, cp_per_block, dlen;
uint8_t *buf;
NANDFS_DEBUG("nandfs_get_checkpoint(fs=%p cpno=%lld)\n", fs, cpno);
buf = malloc(fs->nf_blocksize);
cpno += NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
dlen = fs->nf_fsdata->f_checkpoint_size;
cp_per_block = fs->nf_blocksize / dlen;
blocknr = cpno / cp_per_block;
blockoff = (cpno % cp_per_block) * dlen;
if (nandfs_read_inode(fs, &fs->nf_cpfile, blocknr, 1, buf, 0)) {
free(buf);
return (EINVAL);
}
memcpy(cp, buf + blockoff, sizeof(struct nandfs_checkpoint));
free(buf);
return (0);
}
static uint64_t *
nandfs_get_map(struct nandfs *fs, struct nandfs_node *node, nandfs_daddr_t blknr,
int phys)
{
struct bmap_buf *bmap;
uint64_t *map;
LIST_FOREACH(bmap, &node->bmap_bufs, list) {
if (bmap->blknr == blknr)
return (bmap->map);
}
map = malloc(fs->nf_blocksize);
if (nandfs_read_blk(fs, blknr, map, phys)) {
free(map);
return (NULL);
}
bmap = malloc(sizeof(struct bmap_buf));
bmap->blknr = blknr;
bmap->map = map;
LIST_INSERT_HEAD(&node->bmap_bufs, bmap, list);
NANDFS_DEBUG("%s:(node=%p, map=%p)\n", __func__, node, map);
return (map);
}
static int
nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *node,
nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys)
{
struct nandfs_inode *ino;
nandfs_daddr_t ind_block_num;
uint64_t *map;
int idx;
int level;
ino = node->inode;
if (lblknr < NDADDR) {
*vblknr = ino->i_db[lblknr];
return (0);
}
lblknr -= NDADDR;
/*
* nindir[0] = NINDIR
* nindir[1] = NINDIR**2
* nindir[2] = NINDIR**3
* etc
*/
for (level = 0; level < NIADDR; level++) {
NANDFS_DEBUG("lblknr=%jx fs->nf_nindir[%d]=%d\n", lblknr, level, fs->nf_nindir[level]);
if (lblknr < fs->nf_nindir[level])
break;
lblknr -= fs->nf_nindir[level];
}
if (level == NIADDR) {
/* Block number too high */
NANDFS_DEBUG("lblknr %jx too high\n", lblknr);
return (EFBIG);
}
ind_block_num = ino->i_ib[level];
for (; level >= 0; level--) {
if (ind_block_num == 0) {
*vblknr = 0; /* missing */
return (0);
}
twiddle(1);
NANDFS_DEBUG("calling get_map with %jx\n", ind_block_num);
map = nandfs_get_map(fs, node, ind_block_num, phys);
if (map == NULL)
return (EIO);
if (level > 0) {
idx = lblknr / fs->nf_nindir[level - 1];
lblknr %= fs->nf_nindir[level - 1];
} else
idx = lblknr;
ind_block_num = ((nandfs_daddr_t *)map)[idx];
}
*vblknr = ind_block_num;
return (0);
}
static nandfs_daddr_t
nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr)
{
nandfs_lbn_t blocknr;
nandfs_daddr_t pblocknr;
int entrynr;
struct nandfs_dat_entry *dat;
dat = malloc(fs->nf_blocksize);
nandfs_mdt_trans(&fs->nf_datfile_mdt, vblocknr, &blocknr, &entrynr);
if (nandfs_read_inode(fs, &fs->nf_datfile, blocknr, 1, dat, 1)) {
free(dat);
return (0);
}
NANDFS_DEBUG("nandfs_vtop entrynr=%d vblocknr=%lld pblocknr=%lld\n",
entrynr, vblocknr, dat[entrynr].de_blocknr);
pblocknr = dat[entrynr].de_blocknr;
free(dat);
return (pblocknr);
}
static void
nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt, int entry_size)
{
mdt->entries_per_group = blocksize * 8; /* bits in sector */
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;
}
static void
nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
nandfs_daddr_t *blocknr, uint32_t *entry_in_block)
{
nandfs_daddr_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;
}
static int
ioread(struct open_file *f, off_t pos, void *buf, u_int length)
{
void *buffer;
int err;
int bsize = ((struct nandfs *)f->f_fsdata)->nf_sectorsize;
u_int off, nsec;
off = pos % bsize;
pos /= bsize;
nsec = (length + (bsize - 1)) / bsize;
NANDFS_DEBUG("pos=%lld length=%d off=%d nsec=%d\n", pos, length,
off, nsec);
buffer = malloc(nsec * bsize);
err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, pos,
nsec * bsize, buffer, NULL);
memcpy(buf, (void *)((uintptr_t)buffer + off), length);
free(buffer);
return (err);
}
static int
nandfs_probe_sectorsize(struct open_file *f)
{
void *buffer;
int i, err;
buffer = malloc(16 * 1024);
NANDFS_DEBUG("probing for sector size: ");
for (i = 512; i < (16 * 1024); i <<= 1) {
NANDFS_DEBUG("%d ", i);
err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 0, i,
buffer, NULL);
if (err == 0) {
NANDFS_DEBUG("found");
free(buffer);
return (i);
}
}
free(buffer);
NANDFS_DEBUG("not found\n");
return (-1);
}