/*
* 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.
*
*/
/* read on a node */
int
nilfs_bread(struct nilfs_node *node, uint64_t blocknr,
int flags, struct buf **bpp)
{
struct nilfs_device *nilfsdev = node->nilfsdev;
uint64_t vblocknr, pblockno;
int error;
error = nilfs_btree_lookup(node, blocknr, &vblocknr);
if (error)
return error;
/* Read special files through devvp as they have no vnode attached. */
if (node->ino < NILFS_USER_INO && node->ino != NILFS_ROOT_INO) {
error = nilfs_nvtop(node, 1, &vblocknr, &pblockno);
if (error)
return error;
return nilfs_dev_bread(nilfsdev, pblockno, flags, bpp);
}
/* SMALL, direct lookup */
*vblocknr = 0;
if ((btree_hdr->bn_flags & NILFS_BMAP_LARGE) == 0) {
if (lblocknr < NILFS_DIRECT_NBLOCKS) {
*vblocknr = dtrans[lblocknr];
return 0;
}
/* not mapped XXX could be considered error here */
return 0;
}
/* LARGE, select matching child; XXX could use binary search */
dtrans = NULL;
error = 0;
selected = 0;
for (i = 0; i < nilfs_rw16(btree_hdr->bn_nchildren); i++) {
if (dkeys[i] > lblocknr)
break;
selected = i;
}
/* if selected key > lblocknr, its not mapped */
if (dkeys[selected] > lblocknr)
return 0;
/* overshooting? then not mapped */
if (selected == nilfs_rw16(btree_hdr->bn_nchildren))
return 0;
/* level should be > 1 or otherwise it should be a direct one */
assert(btree_hdr->bn_level > 1);
/* node should be locked on entry to prevent btree changes (unlikely) */
int
nilfs_btree_nlookup(struct nilfs_node *node, uint64_t from, uint64_t blks,
uint64_t *l2vmap)
{
uint64_t lblocknr, *vblocknr;
int i, error;
/* TODO / OPTI multiple translations in one go possible */
error = EINVAL;
for (i = 0; i < blks; i++) {
lblocknr = from + i;
vblocknr = l2vmap + i;
error = nilfs_btree_lookup(node, lblocknr, vblocknr);
/* translate index to a file block number and an entry */
void
nilfs_mdt_trans(struct nilfs_mdt *mdt, uint64_t index,
uint64_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;
/* check if there is a superroot */
if ((nilfs_rw16(segsum->ss_flags) & NILFS_SS_SR) == 0) {
DPRINTF(VOLUMES, ("nilfs: no super root in pseg %"PRIu64"\n",
ri->pseg));
return ENOENT;
}
/* get our super root, located at the end of the pseg */
blocknr = ri->pseg + nsumblk + nfileblk - 1;
offset = 0;
size = sizeof(struct nilfs_super_root);
bp = NULL;
error = nilfs_get_segment_log(nilfsdev,
&blocknr, &offset, &bp,
size, (void *) &nilfsdev->super_root);
if (bp)
brelse(bp, BC_AGE);
if (error) {
printf("read in of superroot failed\n");
return EIO;
}
/* check super root crc */
super_root = &nilfsdev->super_root;
super_root_crc = nilfs_rw32(super_root->sr_sum);
off = sizeof(super_root->sr_sum);
comp_crc = crc32_le(nilfs_rw32(nilfsdev->super.s_crc_seed),
(uint8_t *) super_root + off,
NILFS_SR_BYTES - off);
if (super_root_crc != comp_crc) {
DPRINTF(VOLUMES, (" invalid superroot, likely from old format\n"));
return EINVAL;
}
DPRINTF(VOLUMES, (" got valid superroot\n"));
return 0;
}
/*
* Search for the last super root recorded.
*/
void
nilfs_search_super_root(struct nilfs_device *nilfsdev)
{
struct nilfs_super_block *super;
struct nilfs_segment_summary *segsum;
struct nilfs_recover_info *ri, *ori, *i_ri;
STAILQ_HEAD(,nilfs_recover_info) ri_list;
uint64_t seg_start, seg_end, cno;
uint32_t segsum_size;
uint32_t nsumblk, nfileblk;
int error;
STAILQ_INIT(&ri_list);
/* search for last super root */
ri = malloc(sizeof(struct nilfs_recover_info), M_NILFSTEMP, M_WAITOK);
memset(ri, 0, sizeof(struct nilfs_recover_info));
/* if enabled, start from the specified position */
if (0) {
/* start from set start */
nilfsdev->super.s_last_pseg = nilfsdev->super.s_first_data_block;
nilfsdev->super.s_last_cno = nilfs_rw64(1);
}
error = 0;
cno = nilfs_rw64(nilfsdev->super.s_last_cno);
DPRINTF(VOLUMES, ("nilfs: search_super_root start in pseg %"PRIu64"\n",
ri->pseg));
for (;;) {
DPRINTF(VOLUMES, (" at pseg %"PRIu64"\n", ri->pseg));
error = nilfs_load_segsum(nilfsdev, ri);
if (error)
break;
segsum = &ri->segsum;
/* try to load super root */
if (nilfs_rw16(segsum->ss_flags) & NILFS_SS_SR) {
DPRINTF(VOLUMES, (" try super root\n"));
error = nilfs_load_super_root(nilfsdev, ri);
if (error)
break; /* confused */
/* wipe current list of ri */
while (!STAILQ_EMPTY(&ri_list)) {
i_ri = STAILQ_FIRST(&ri_list);
STAILQ_REMOVE_HEAD(&ri_list, next);
free(i_ri, M_NILFSTEMP);
}
super = &nilfsdev->super;
super->s_last_pseg = nilfs_rw64(ri->pseg);
super->s_last_cno = cno++;
super->s_last_seq = segsum->ss_seq;
super->s_state = nilfs_rw16(NILFS_VALID_FS);
} else {
STAILQ_INSERT_TAIL(&ri_list, ri, next);
ori = ri;
ri = malloc(sizeof(struct nilfs_recover_info),
M_NILFSTEMP, M_WAITOK);
memset(ri, 0, sizeof(struct nilfs_recover_info));
ri->segnum = ori->segnum;
ri->pseg = ori->pseg;
/* segsum keeps pointing to the `old' ri */
}
/* continue to the next pseg */
segsum_size = nilfs_rw32(segsum->ss_sumbytes);
nsumblk = (segsum_size - 1) / nilfsdev->blocksize + 1;
nfileblk = nilfs_rw32(segsum->ss_nblocks) - nsumblk;
/* did we reach the end of the segment? if so, go to the next */
nilfs_get_segment_range(nilfsdev, ri->segnum, &seg_start, &seg_end);
if (ri->pseg >= seg_end)
ri->pseg = nilfs_rw64(segsum->ss_next);
ri->segnum = nilfs_get_segnum_of_block(nilfsdev, ri->pseg);
}
/*
* XXX No roll-forward yet of the remaining partial segments.
*/
/* wipe current list of ri */
while (!STAILQ_EMPTY(&ri_list)) {
i_ri = STAILQ_FIRST(&ri_list);
STAILQ_REMOVE_HEAD(&ri_list, next);
printf("nilfs: ignoring pseg at %"PRIu64"\n", i_ri->pseg);
free(i_ri, M_NILFSTEMP);
}
free(ri, M_NILFSTEMP);
}
/* fixup inode size for system nodes */
if ((ino < NILFS_USER_INO) && (ino != NILFS_ROOT_INO)) {
DPRINTF(VOLUMES, ("NEED TO GET my size for inode %"PRIu64"?\n",
ino));
/* for now set it to maximum, -1 is illegal */
DPRINTF(VOLUMES, (" current size of inode is %"PRIu64"\n", inode->i_size));
inode->i_size = nilfs_rw64(((uint64_t) -2));
}
/* get our dirhash and make sure its read in */
dirhash_get(&dir_node->dir_hash);
error = dirhash_fill(dir_node);
if (error) {
dirhash_put(dir_node->dir_hash);
return error;
}
dirh = dir_node->dir_hash;
/* allocate temporary space for fid */
DPRINTF(DIRHASH, ("dirhash_lookup looking for `%*.*s`\n",
namelen, namelen, name));
/* search our dirhash hits */
*ino = 0;
dirh_ep = NULL;
for (;;) {
hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
/* if no hit, abort the search */
if (!hit)
break;