/* * Read-only filesystem code for SCO's bfs filesystem * aeb, 990907 * * Comments are welcome - aeb@cwi.nl * * Compile with gcc -c -O2 bfsmod.c * Usage: * insmod bfsmod.o; mount -t bfs dev dir; ...; umount dev; rmmod bfsmod * This was used successfully with Linux 2.2.12. */ #define MODULE #define __KERNEL__ #include #include #include /* lock_super */ #include /* copy_to_user */ #define BFS_BLOCKSIZE 512 #define BFS_BLOCKSIZEBITS 9 #define BFS_ROOT_INO 2 /* Some information about the superblock can be found in the Unixware man page fs_bfs(4). There, the superblock magic is given as 0xBADFACE. (Different versions of the fs? Or a typo?) */ /* on disk superblock */ struct bfssb { unsigned int magic; unsigned int data_start; unsigned int data_end; /* sizeof(slice)-1 */ /* for recovery during compaction */ int fromblock, toblock; /* src and dest of current transfer */ int backup_fromblock, backup_toblock; /* labels - may well contain garbage */ char fsname[6]; char volume[6]; char stuff[472]; /* unused */ }; #define BFS_SUPER_MAGIC 0x1badface /* in core data */ struct bfs_sb_info { unsigned int s_max_ino; unsigned int s_data_end; }; struct bfs_i_info { unsigned int i_first_block; }; /* Unfortunately, one has to change to put bfs_sb_info into the union u. Solve using an ugly define. */ #if 0 #define u_bfs_sb_info_p(s) (&((s)->u.bfs_sb_info)) #else #define u_bfs_sb_info_p(s) ((struct bfs_sb_info *)(&((s)->u))) #endif #if 0 #define u_bfs_i_info_p(inode) (&((inode)->u.bfs_inode_info)) #else #define u_bfs_i_info_p(inode) ((struct bfs_i_info *)(&((inode)->u))) #endif /* on disk inode - 64 bytes, 8 per block */ #define BFS_INODE_SIZE 64 #define BFS_INODES_PER_BLOCK (BFS_BLOCKSIZE / BFS_INODE_SIZE) struct bfsi { unsigned short i_nr; unsigned char i_stuff1[2]; /* only nonzero for root */ /* not mentioned in inode_bfs(4) */ unsigned long i_first_block; unsigned long i_last_block; unsigned long i_bytes_to_end; unsigned long i_type; /* 1: file, 2: the unique dir */ unsigned long i_mode; unsigned long i_uid, i_gid; unsigned long i_nlinks; unsigned long i_atime, i_mtime, i_ctime; unsigned char i_stuff3[16]; /* only nonzero for root */ }; /* on disk directory entry */ #define BFS_DESIZE 16 #define BFS_NAMELEN 14 struct bfsde { unsigned short i_nr; char name[BFS_NAMELEN]; }; static int bfs_bmap(struct inode *inode, int block) { return u_bfs_i_info_p(inode)->i_first_block + block; } static int bfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { unsigned int offset, block, curblock = 0; struct buffer_head *bh = NULL; struct bfsde *de; struct inode *inode = filp->f_dentry->d_inode; if (filp->f_pos & (BFS_DESIZE-1)) return -EBADF; while (filp->f_pos < inode->i_size) { offset = filp->f_pos & (BFS_BLOCKSIZE-1); block = u_bfs_i_info_p(inode)->i_first_block + (filp->f_pos >> BFS_BLOCKSIZEBITS); if (!bh || curblock != block) { if (bh) brelse(bh); bh = bread(inode->i_dev, block, BFS_BLOCKSIZE); if (!bh) return -EIO; curblock = block; } de = (struct bfsde *) (bh->b_data + offset); if (de->i_nr) { int size = strnlen(de->name, BFS_NAMELEN); if (filldir(dirent, de->name, size, filp->f_pos, de->i_nr) < 0) break; } filp->f_pos += BFS_DESIZE; } if (bh) brelse(bh); return 0; } static struct dentry * bfs_lookup(struct inode *dir, struct dentry *dentry) { unsigned int ino, pos, offset, block, curblock = 0; struct buffer_head *bh = NULL; struct bfsde *de; struct inode *inode; const char *name = dentry->d_name.name; int len = dentry->d_name.len; if (len > BFS_NAMELEN) len = BFS_NAMELEN; pos = ino = 0; while (pos < dir->i_size) { offset = pos & (BFS_BLOCKSIZE-1); block = u_bfs_i_info_p(dir)->i_first_block + (pos >> BFS_BLOCKSIZEBITS); if (!bh || curblock != block) { if (bh) brelse(bh); bh = bread(dir->i_dev, block, BFS_BLOCKSIZE); if (!bh) return ERR_PTR(-EIO); curblock = block; } de = (struct bfsde *) (bh->b_data + offset); if (de->i_nr) { int size = strnlen(de->name, BFS_NAMELEN); if (size == len && strncmp(de->name, name, len) == 0) { ino = de->i_nr; break; } } pos += BFS_DESIZE; } if (bh) brelse(bh); if (!ino) return ERR_PTR(-ENOENT); inode = iget(dir->i_sb, ino); if (!inode) return ERR_PTR(-EACCES); d_add(dentry, inode); return NULL; } static struct file_operations bfs_file_operations = { NULL, /* lseek - default */ generic_file_read, /* read */ NULL, /* write - bad */ NULL, /* readdir - bad */ NULL, /* poll - default */ NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ NULL, /* open */ NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */ }; static struct inode_operations bfs_file_inode_operations = { &bfs_file_operations, /* default file operations */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ bfs_bmap, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ }; static struct file_operations bfs_dir_operations = { NULL, /* lseek - default */ NULL, /* read */ NULL, /* write - bad */ bfs_readdir, /* readdir */ NULL, /* poll - default */ NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* open */ NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */ }; static struct inode_operations bfs_dir_inode_operations = { &bfs_dir_operations, NULL, /* create */ bfs_lookup, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ }; static void bfs_read_inode(struct inode *inode) { unsigned int ino; struct super_block *s; struct buffer_head *bh; struct bfsi *ri; /* raw inode */ int block; static int warn = 0, warn2 = 0; s = inode->i_sb; ino = inode->i_ino; inode->i_op = NULL; inode->i_mode = 0; if (ino < BFS_ROOT_INO || ino > u_bfs_sb_info_p(s)->s_max_ino) { printk("Bad inode number %d on dev %s\n", ino, kdevname(inode->i_dev)); return; } block = 1 + (ino-BFS_ROOT_INO)/BFS_INODES_PER_BLOCK; if (!(bh = bread(inode->i_dev, block, BFS_BLOCKSIZE))) { printk("Unable to read inode %d from dev %s\n", ino, kdevname(inode->i_dev)); return; } ri = ((struct bfsi *)(bh->b_data)) + (ino-BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; if (ri->i_nr != ino && warn++ == 0) printk("bfs: warning: bad fs: inode %d has nr %d\n", ino, ri->i_nr); inode->i_mode = (ri->i_mode & 0777); if (ri->i_type == 2) { inode->i_mode |= S_IFDIR; inode->i_op = &bfs_dir_inode_operations; } else if (ri->i_type == 1) { inode->i_mode |= S_IFREG; inode->i_op = &bfs_file_inode_operations; } else if(warn2++ == 0) printk("bfs: warning: bad fs: inode of unknown type\n"); inode->i_uid = ri->i_uid; inode->i_gid = ri->i_gid; inode->i_nlink = ri->i_nlinks; inode->i_size = (ri->i_first_block == 0) ? 0 : ri->i_bytes_to_end - ri->i_first_block*BFS_BLOCKSIZE + 1; inode->i_atime = ri->i_atime; inode->i_mtime = ri->i_mtime; inode->i_ctime = ri->i_ctime; inode->i_blocks = inode->i_blksize = 0; u_bfs_i_info_p(inode)->i_first_block = ri->i_first_block; brelse(bh); } static void bfs_put_super(struct super_block *s) { MOD_DEC_USE_COUNT; return; } static int bfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize) { struct statfs tmp; memset(&tmp, 0, sizeof(tmp)); tmp.f_type = BFS_SUPER_MAGIC; tmp.f_bsize = BFS_BLOCKSIZE; tmp.f_blocks = (u_bfs_sb_info_p(sb)->s_data_end + 1) / BFS_BLOCKSIZE; tmp.f_namelen = BFS_NAMELEN; return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0; } static struct super_operations bfs_ops = { bfs_read_inode, /* read inode */ NULL, /* write inode */ NULL, /* put inode */ NULL, /* delete inode */ NULL, /* notify change */ bfs_put_super, /* put super */ NULL, /* write super */ bfs_statfs, /* statfs */ NULL /* remount */ }; static struct super_block * bfs_read_super(struct super_block *s, void *data, int silent) { kdev_t dev; struct buffer_head *bh; struct bfssb *sb; struct inode *root; MOD_INC_USE_COUNT; lock_super(s); dev = s->s_dev; set_blocksize(dev, BFS_BLOCKSIZE); s->s_blocksize = BFS_BLOCKSIZE; s->s_blocksize_bits = BFS_BLOCKSIZEBITS; bh = bread(dev, 0, BFS_BLOCKSIZE); if (!bh) { printk ("bfs: unable to read superblock\n"); goto outnobh; } sb = (struct bfssb *) bh->b_data; if (sb->magic != BFS_SUPER_MAGIC) { if (!silent) printk ("VFS: Can't find a bfs filesystem on dev " "%s.\n", kdevname(dev)); goto out; } s->s_magic = BFS_SUPER_MAGIC; s->s_flags |= MS_RDONLY; s->s_op = &bfs_ops; u_bfs_sb_info_p(s)->s_max_ino = BFS_ROOT_INO + (sb->data_start - 512) / BFS_INODE_SIZE - 1; u_bfs_sb_info_p(s)->s_data_end = sb->data_end; root = iget(s, BFS_ROOT_INO); if (!root) goto out; s->s_root = d_alloc_root(root, NULL); if (!s->s_root) goto outiput; brelse(bh); unlock_super(s); return s; outiput: iput(root); out: brelse(bh); outnobh: s->s_dev = 0; unlock_super(s); MOD_DEC_USE_COUNT; return NULL; } static struct file_system_type bfs_fs_type = { "bfs", FS_REQUIRES_DEV, bfs_read_super, NULL }; int init_bfs_fs(void) { return register_filesystem(&bfs_fs_type); } #ifdef MODULE EXPORT_NO_SYMBOLS; int init_module(void) { return init_bfs_fs(); } void cleanup_module(void) { unregister_filesystem(&bfs_fs_type); } #endif /* MODULE */