Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 17 Mar 2012 03:00:52 +0000 (UTC)
From:      Grzegorz Bernacki <gber@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r233070 - projects/nand/lib/libstand
Message-ID:  <201203170300.q2H30qf0047026@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gber
Date: Sat Mar 17 03:00:51 2012
New Revision: 233070
URL: http://svn.freebsd.org/changeset/base/233070

Log:
  Add NANDFS support for boot loader.
  
  Obtained from: Semihalf
  Supported by:  FreeBSD Foundation, Juniper Networks

Added:
  projects/nand/lib/libstand/nandfs.c
Modified:
  projects/nand/lib/libstand/Makefile
  projects/nand/lib/libstand/stand.h

Modified: projects/nand/lib/libstand/Makefile
==============================================================================
--- projects/nand/lib/libstand/Makefile	Sat Mar 17 02:52:16 2012	(r233069)
+++ projects/nand/lib/libstand/Makefile	Sat Mar 17 03:00:51 2012	(r233070)
@@ -150,6 +150,9 @@ SRCS+=	bootp.c rarp.c bootparam.c
 SRCS+=	ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c
 SRCS+=	dosfs.c ext2fs.c
 SRCS+=	splitfs.c
+.if ${MK_NAND} != "no"
+SRCS+=	nandfs.c
+.endif
 
 .include <bsd.lib.mk>
 

Added: projects/nand/lib/libstand/nandfs.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/nand/lib/libstand/nandfs.c	Sat Mar 17 03:00:51 2012	(r233070)
@@ -0,0 +1,1075 @@
+/*-
+ * 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"
+
+#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 *path, struct open_file *f);
+static int nandfs_close(struct open_file *f);
+static int nandfs_read(struct open_file *f, void *buf, size_t size,
+    size_t *resid);
+static off_t nandfs_seek(struct open_file *f, off_t offset, int where);
+static int nandfs_stat(struct open_file *f, struct stat *sb);
+static int nandfs_readdir(struct open_file *f, struct dirent *d);
+
+static int nandfs_buf_read(struct nandfs *fs, char **buf_p, size_t *size_p);
+static struct nandfs_node *nandfs_lookup_inode(struct nandfs *fs, nandfs_daddr_t ino);
+static struct nandfs_node *nandfs_lookup_path(struct nandfs *fs,
+    const char *path);
+static int nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node,
+    nandfs_lbn_t blknr, u_int nblks, void *buf, int raw);
+static int nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf,
+    int phys);
+static int nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *ino,
+    nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys);
+static int nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno,
+    struct nandfs_checkpoint *cp);
+static nandfs_daddr_t nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr);
+static void nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt,
+    int entry_size);
+static void nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
+    nandfs_daddr_t *blocknr, uint32_t *entry_in_block);
+static int ioread(struct open_file *f, off_t pos, void *buf, u_int length);
+static int nandfs_probe_sectorsize(struct open_file *f);
+
+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
+crc32_le(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;
+
+	for (i = 0; i < len; i++) {
+		crc ^= buf[i];
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+	}
+
+	return (crc);
+}
+
+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 = crc32_le(fsdata->f_crc_seed, (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 = crc32_le(fsdata->f_crc_seed, (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;
+	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);
+		if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) {
+			if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata))
+				continue;
+			else {
+				sb += (sizeof(struct nandfs_fsdata) /
+				    sizeof(struct nandfs_super_block));
+				n -= (sizeof(struct nandfs_fsdata) /
+				    sizeof(struct nandfs_super_block));
+			}
+		}
+
+		for (j = 0; j < n; j++) {
+			if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j]))
+				continue;
+			NANDFS_DEBUG("magic %x wtime %jd\n", sb->s_magic,
+			    sb->s_wtime);
+			if (sb[j].s_wtime > fs->nf_sb->s_wtime)
+				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;
+	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;
+		int level;
+
+		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;
+
+	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;
+		int level;
+
+		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 int
+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);
+}
+
+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;
+	uint8_t *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, (void *)&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;
+	uint8_t *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, (void *)&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, char **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, j, done, counter, pinode, inode;
+	int nlinks = 0, 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 *)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)
+{
+	int i;
+	uint64_t *pblks;
+	uint64_t *vblks;
+	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 += 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, *indir;
+	uint64_t idx0, idx1, vblk, tmp;
+	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();
+		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)
+{
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203170300.q2H30qf0047026>