/* * fs/bfs/inode.c * BFS superblock and inode operations. * Copyright (C) 1999,2000 Tigran Aivazian * From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds. */ #include #include #include #include #include #include #include #include #include "bfs_defs.h" MODULE_AUTHOR("Tigran A. Aivazian "); MODULE_DESCRIPTION("SCO UnixWare BFS filesystem for Linux"); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; #undef DEBUG #ifdef DEBUG #define dprintf(x...) printf(x) #else #define dprintf(x...) #endif void dump_imap(const char *prefix, struct super_block * s); static void bfs_read_inode(struct inode * inode) { unsigned long ino = inode->i_ino; kdev_t dev = inode->i_dev; struct bfs_inode * di; struct buffer_head * bh; int block, off; if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) { printf("Bad inode number %s:%08lx\n", bdevname(dev), ino); make_bad_inode(inode); return; } block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; bh = bread(dev, block, BFS_BSIZE); if (!bh) { printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino); make_bad_inode(inode); return; } off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; di = (struct bfs_inode *)bh->b_data + off; inode->i_mode = 0x0000FFFF & di->i_mode; if (di->i_vtype == BFS_VDIR) { inode->i_mode |= S_IFDIR; inode->i_op = &bfs_dir_inops; inode->i_fop = &bfs_dir_operations; } else if (di->i_vtype == BFS_VREG) { inode->i_mode |= S_IFREG; inode->i_op = &bfs_file_inops; inode->i_fop = &bfs_file_operations; inode->i_mapping->a_ops = &bfs_aops; } inode->i_uid = di->i_uid; inode->i_gid = di->i_gid; inode->i_nlink = di->i_nlink; inode->i_size = BFS_FILESIZE(di); inode->i_blocks = BFS_FILEBLOCKS(di); inode->i_blksize = PAGE_SIZE; inode->i_atime = di->i_atime; inode->i_mtime = di->i_mtime; inode->i_ctime = di->i_ctime; inode->iu_dsk_ino = di->i_ino; /* can be 0 so we store a copy */ inode->iu_sblock = di->i_sblock; inode->iu_eblock = di->i_eblock; brelse(bh); } static void bfs_write_inode(struct inode * inode, int unused) { unsigned long ino = inode->i_ino; kdev_t dev = inode->i_dev; struct bfs_inode * di; struct buffer_head * bh; int block, off; if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) { printf("Bad inode number %s:%08lx\n", bdevname(dev), ino); return; } lock_kernel(); block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; bh = bread(dev, block, BFS_BSIZE); if (!bh) { printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino); unlock_kernel(); return; } off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; di = (struct bfs_inode *)bh->b_data + off; if (inode->i_ino == BFS_ROOT_INO) di->i_vtype = BFS_VDIR; else di->i_vtype = BFS_VREG; di->i_ino = inode->i_ino; di->i_mode = inode->i_mode; di->i_uid = inode->i_uid; di->i_gid = inode->i_gid; di->i_nlink = inode->i_nlink; di->i_atime = inode->i_atime; di->i_mtime = inode->i_mtime; di->i_ctime = inode->i_ctime; di->i_sblock = inode->iu_sblock; di->i_eblock = inode->iu_eblock; di->i_eoffset = di->i_sblock * BFS_BSIZE + inode->i_size - 1; mark_buffer_dirty(bh); brelse(bh); unlock_kernel(); } static void bfs_delete_inode(struct inode * inode) { unsigned long ino = inode->i_ino; kdev_t dev = inode->i_dev; struct bfs_inode * di; struct buffer_head * bh; int block, off; struct super_block * s = inode->i_sb; dprintf("ino=%08lx\n", inode->i_ino); if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > inode->i_sb->su_lasti) { printf("invalid ino=%08lx\n", inode->i_ino); return; } inode->i_size = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; lock_kernel(); mark_inode_dirty(inode); block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; bh = bread(dev, block, BFS_BSIZE); if (!bh) { printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino); unlock_kernel(); return; } off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; di = (struct bfs_inode *)bh->b_data + off; if (di->i_ino) { s->su_freeb += BFS_FILEBLOCKS(di); s->su_freei++; clear_bit(di->i_ino, s->su_imap); dump_imap("delete_inode", s); } di->i_ino = 0; di->i_sblock = 0; mark_buffer_dirty(bh); brelse(bh); /* if this was the last file, make the previous block "last files last block" even if there is no real file there, saves us 1 gap */ if (s->su_lf_eblk == inode->iu_eblock) { s->su_lf_eblk = inode->iu_sblock - 1; mark_buffer_dirty(s->su_sbh); } unlock_kernel(); clear_inode(inode); } static void bfs_put_super(struct super_block *s) { brelse(s->su_sbh); kfree(s->su_imap); } static int bfs_statfs(struct super_block *s, struct statfs *buf) { buf->f_type = BFS_MAGIC; buf->f_bsize = s->s_blocksize; buf->f_blocks = s->su_blocks; buf->f_bfree = buf->f_bavail = s->su_freeb; buf->f_files = s->su_lasti + 1 - BFS_ROOT_INO; buf->f_ffree = s->su_freei; buf->f_fsid.val[0] = kdev_t_to_nr(s->s_dev); buf->f_namelen = BFS_NAMELEN; return 0; } static void bfs_write_super(struct super_block *s) { if (!(s->s_flags & MS_RDONLY)) mark_buffer_dirty(s->su_sbh); s->s_dirt = 0; } static struct super_operations bfs_sops = { read_inode: bfs_read_inode, write_inode: bfs_write_inode, delete_inode: bfs_delete_inode, put_super: bfs_put_super, write_super: bfs_write_super, statfs: bfs_statfs, }; void dump_imap(const char *prefix, struct super_block * s) { #if 0 int i; char *tmpbuf = (char *)get_free_page(GFP_KERNEL); if (!tmpbuf) return; for (i=s->su_lasti; i>=0; i--) { if (i>PAGE_SIZE-100) break; if (test_bit(i, s->su_imap)) strcat(tmpbuf, "1"); else strcat(tmpbuf, "0"); } printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, s->su_lasti, tmpbuf); free_page((unsigned long)tmpbuf); #endif } static struct super_block * bfs_read_super(struct super_block * s, void * data, int silent) { kdev_t dev; struct buffer_head * bh; struct bfs_super_block * bfs_sb; struct inode * inode; int i, imap_len; dev = s->s_dev; set_blocksize(dev, BFS_BSIZE); s->s_blocksize = BFS_BSIZE; s->s_blocksize_bits = BFS_BSIZE_BITS; bh = bread(dev, 0, BFS_BSIZE); if(!bh) goto out; bfs_sb = (struct bfs_super_block *)bh->b_data; if (bfs_sb->s_magic != BFS_MAGIC) { if (!silent) printf("No BFS filesystem on %s (magic=%08x)\n", bdevname(dev), bfs_sb->s_magic); goto out; } if (BFS_UNCLEAN(bfs_sb, s) && !silent) printf("%s is unclean, continuing\n", bdevname(dev)); s->s_magic = BFS_MAGIC; s->su_bfs_sb = bfs_sb; s->su_sbh = bh; s->su_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) + BFS_ROOT_INO - 1; imap_len = s->su_lasti/8 + 1; s->su_imap = kmalloc(imap_len, GFP_KERNEL); if (!s->su_imap) goto out; memset(s->su_imap, 0, imap_len); for (i=0; isu_imap); s->s_op = &bfs_sops; inode = iget(s, BFS_ROOT_INO); if (!inode) { kfree(s->su_imap); goto out; } s->s_root = d_alloc_root(inode); if (!s->s_root) { iput(inode); kfree(s->su_imap); goto out; } s->su_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */ s->su_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS; s->su_freei = 0; s->su_lf_eblk = 0; s->su_lf_sblk = 0; s->su_lf_ioff = 0; for (i=BFS_ROOT_INO; i<=s->su_lasti; i++) { inode = iget(s,i); if (inode->iu_dsk_ino == 0) s->su_freei++; else { set_bit(i, s->su_imap); s->su_freeb -= inode->i_blocks; if (inode->iu_eblock > s->su_lf_eblk) { s->su_lf_eblk = inode->iu_eblock; s->su_lf_sblk = inode->iu_sblock; s->su_lf_ioff = BFS_INO2OFF(i); } } iput(inode); } if (!(s->s_flags & MS_RDONLY)) { mark_buffer_dirty(bh); s->s_dirt = 1; } dump_imap("read_super", s); return s; out: brelse(bh); return NULL; } static DECLARE_FSTYPE_DEV(bfs_fs_type, "bfs", bfs_read_super); static int __init init_bfs_fs(void) { return register_filesystem(&bfs_fs_type); } static void __exit exit_bfs_fs(void) { unregister_filesystem(&bfs_fs_type); } module_init(init_bfs_fs) module_exit(exit_bfs_fs)