lkml.org 
[lkml]   [2007]   [Jun]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[Patch 16/18] fs/logfs/progs/fsck.c
    --- /dev/null	2007-03-13 19:15:28.862769062 +0100
    +++ linux-2.6.21logfs/fs/logfs/progs/fsck.c 2007-06-03 19:18:57.000000000 +0200
    @@ -0,0 +1,316 @@
    +/*
    + * fs/logfs/prog/fsck.c - filesystem check
    + *
    + * As should be obvious for Linux kernel code, license is GPLv2
    + *
    + * Copyright (c) 2005-2007 Joern Engel
    + *
    + * In principle this could get moved to userspace. However it might still
    + * make some sense to keep it in the kernel. It is a pure checker and will
    + * only report problems, not attempt to repair them.
    + */
    +#include "../logfs.h"
    +
    +static u64 used_bytes;
    +static u64 free_bytes;
    +static u64 last_ino;
    +static u64 *inode_bytes;
    +static u64 *inode_links;
    +
    +/*
    + * Pass 1: blocks
    + */
    +
    +static u32 logfs_free_bytes(struct super_block *sb, u32 segno)
    +{
    + struct logfs_super *super = logfs_super(sb);
    + struct logfs_segment_header sh;
    + struct logfs_object_header h;
    + u64 ofs, ino, pos;
    + u32 seg_ofs, free, size;
    + u16 len;
    + int err;
    + void *reserved;
    +
    + /* Some segments are reserved. Just pretend they were all valid */
    + reserved = btree_lookup(&super->s_reserved_segments, segno);
    + if (reserved)
    + return 0;
    +
    + err = wbuf_read(sb, dev_ofs(sb, segno, 0), sizeof(sh), &sh);
    + BUG_ON(err);
    + if (!memchr_inv(&sh, 0xff, sizeof(sh)))
    + return super->s_segsize;
    +
    + free = super->s_segsize;
    + for (seg_ofs = sizeof(h); seg_ofs + sizeof(h) < super->s_segsize; ) {
    + wbuf_read(sb, dev_ofs(sb, segno, seg_ofs), sizeof(h), &h);
    + BUG_ON(err);
    + if (!memchr_inv(&h, 0xff, sizeof(h)))
    + break;
    +
    + ofs = dev_ofs(sb, segno, seg_ofs);
    + ino = be64_to_cpu(h.ino);
    + pos = be64_to_cpu(h.pos);
    + len = be16_to_cpu(h.len);
    + size = (u32)be16_to_cpu(h.len) + sizeof(h);
    + if (logfs_is_valid_block(sb, ofs, ino, pos)) {
    + if (sh.level != 0)
    + len = sb->s_blocksize;
    + inode_bytes[ino] += len + sizeof(h);
    + free -= len + sizeof(h);
    + }
    + seg_ofs += size;
    + }
    + return free;
    +}
    +
    +static void logfsck_blocks(struct super_block *sb)
    +{
    + struct logfs_super *super = logfs_super(sb);
    + int i;
    + int free;
    +
    + printk(KERN_INFO);
    + for (i=0; i<super->s_no_segs; i++) {
    + free = logfs_free_bytes(sb, i);
    + free_bytes += free;
    + printk(" %5x", free);
    + if (i % 8 == 7)
    + printk("\n" KERN_INFO);
    + }
    + printk("\n");
    +}
    +
    +/*
    + * Pass 2: directories
    + */
    +
    +static noinline int read_one_dd(struct inode *dir, loff_t pos, u64 *ino,
    + u8 *type)
    +{
    + struct logfs_disk_dentry dd;
    + int err;
    +
    + err = logfs_inode_read(dir, &dd, sizeof(dd), pos);
    + if (err)
    + return err;
    + *ino = be64_to_cpu(dd.ino);
    + *type = dd.type;
    + return 0;
    +}
    +
    +static s64 dir_seek_data(struct inode *inode, s64 pos)
    +{
    + s64 new_pos = logfs_seek_data(inode, pos);
    +
    + return max((s64)pos, new_pos - 1);
    +}
    +
    +static int __logfsck_dirs(struct inode *dir)
    +{
    + struct inode *inode;
    + loff_t pos;
    + u64 ino;
    + u8 type;
    + int cookie, err, ret = 0;
    +
    + for (pos=0; ; pos++) {
    + err = read_one_dd(dir, pos, &ino, &type);
    + if (err == -ENODATA) {
    + /* dentry was deleted */
    + pos = dir_seek_data(dir, pos);
    + continue;
    + }
    + if (err == -EOF)
    + break;
    + if (err)
    + goto error0;
    +
    + err = -EIO;
    + if (ino > last_ino) {
    + printk(KERN_INFO "ino %llx > last_ino %llx\n",
    + ino, last_ino);
    + goto error0;
    + }
    + inode = logfs_iget(dir->i_sb, ino, &cookie);
    + if (!inode) {
    + printk(KERN_INFO "Could not find inode #%llx\n", ino);
    + goto error0;
    + }
    + if (type != logfs_type(inode)) {
    + printk(KERN_INFO "dd type %x != inode type %x\n",
    + type, logfs_type(inode));
    + goto error1;
    + }
    + inode_links[ino]++;
    + err = 0;
    + if (type == DT_DIR) {
    + inode_links[dir->i_ino]++;
    + inode_links[ino]++;
    + err = __logfsck_dirs(inode);
    + }
    +error1:
    + logfs_iput(inode, cookie);
    +error0:
    + if (!ret)
    + ret = err;
    + continue;
    + }
    + return 1;
    +}
    +
    +static int logfsck_dirs(struct super_block *sb)
    +{
    + struct inode *dir;
    + int cookie;
    +
    + dir = logfs_iget(sb, LOGFS_INO_ROOT, &cookie);
    + if (!dir)
    + return 0;
    +
    + inode_links[LOGFS_INO_MASTER] += 1;
    + inode_links[LOGFS_INO_ROOT] += 2;
    + __logfsck_dirs(dir);
    +
    + logfs_iput(dir, cookie);
    + return 1;
    +}
    +
    +/*
    + * Pass 3: inodes
    + */
    +
    +static int logfs_check_inode(struct inode *inode)
    +{
    + struct logfs_inode *li = logfs_inode(inode);
    + u64 bytes0 = li->li_used_bytes;
    + u64 bytes1 = inode_bytes[inode->i_ino];
    + u64 links0 = inode->i_nlink;
    + u64 links1 = inode_links[inode->i_ino];
    +
    + if (bytes0 || bytes1 || links0 || links1
    + || inode->i_ino == logfs_super(inode->i_sb)->s_last_ino)
    + printk(KERN_INFO "%lx: %llx(%llx) bytes, %llx(%llx) links\n",
    + inode->i_ino, bytes0, bytes1, links0, links1);
    + used_bytes += bytes0;
    + return (bytes0 == bytes1) && (links0 == links1);
    +}
    +
    +static int logfs_check_ino(struct super_block *sb, u64 ino)
    +{
    + struct inode *inode;
    + int ret, cookie;
    +
    + inode = logfs_iget(sb, ino, &cookie);
    + if (!inode)
    + return 1;
    + ret = logfs_check_inode(inode);
    + logfs_iput(inode, cookie);
    + return ret;
    +}
    +
    +static int logfsck_inodes(struct super_block *sb)
    +{
    + struct logfs_super *super = logfs_super(sb);
    + s64 i;
    + int ret = 1;
    +
    + if (!logfs_check_ino(sb, LOGFS_INO_MASTER))
    + ret = 0;;
    + if (!logfs_check_ino(sb, LOGFS_INO_ROOT))
    + ret = 0;
    + for (i=16; i<super->s_last_ino; i++) {
    + i = dir_seek_data(super->s_master_inode, i);
    + if (!logfs_check_ino(sb, i))
    + ret = 0;;
    + }
    + return ret;
    +}
    +
    +/*
    + * Pass 4: Total blocks
    + */
    +
    +static int logfsck_stats(struct super_block *sb)
    +{
    + struct logfs_super *super = logfs_super(sb);
    + u64 ostore_segs, total, expected;
    + int i, reserved_segs;
    +
    + /* one for the superblock */
    + reserved_segs = 1;
    + journal_for_each(i)
    + if (super->s_journal_seg[i])
    + reserved_segs++;
    + reserved_segs += super->s_bad_segments;
    +
    + ostore_segs = super->s_no_segs - reserved_segs;
    + expected = ostore_segs << super->s_segshift;
    + total = free_bytes + used_bytes;
    +
    + printk(KERN_INFO "free:%8llx, used:%8llx, total:%8llx",
    + free_bytes, used_bytes, expected);
    + if (total > expected)
    + printk(" + %llx\n", total - expected);
    + else if (total < expected)
    + printk(" - %llx\n", expected - total);
    + else
    + printk("\n");
    +
    + return total == expected;
    +}
    +
    +static int __logfs_fsck(struct super_block *sb)
    +{
    + int ret;
    + int err = 0;
    +
    + /* pass 1: check blocks */
    + logfsck_blocks(sb);
    + /* pass 2: check directories */
    + ret = logfsck_dirs(sb);
    + if (!ret) {
    + printk(KERN_ERR "Pass 2: directory check failed\n");
    + err = -EIO;
    + }
    + /* pass 3: check inodes */
    + ret = logfsck_inodes(sb);
    + if (!ret) {
    + printk(KERN_ERR "Pass 3: inode check failed\n");
    + err = -EIO;
    + }
    + /* Pass 4: Total blocks */
    + ret = logfsck_stats(sb);
    + if (!ret) {
    + printk(KERN_ERR "Pass 4: statistic check failed\n");
    + err = -EIO;
    + }
    +
    + return err;
    +}
    +
    +int logfs_fsck(struct super_block *sb)
    +{
    + struct logfs_super *super = logfs_super(sb);
    + int ret = -ENOMEM;
    +
    + used_bytes = 0;
    + free_bytes = 0;
    + last_ino = super->s_last_ino;
    + inode_bytes = kzalloc(last_ino * sizeof(u64), GFP_KERNEL);
    + if (!inode_bytes)
    + return ret;
    + inode_links = kzalloc(last_ino * sizeof(u64), GFP_KERNEL);
    + if (!inode_links)
    + goto err;
    +
    + ret = __logfs_fsck(sb);
    +
    + kfree(inode_links);
    + inode_links = NULL;
    +err:
    + kfree(inode_bytes);
    + inode_bytes = NULL;
    + return ret;
    +}
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2007-06-03 20:57    [W:4.227 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site