/*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #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); }