/* * fdiskfs.c * * Compile with * * gcc -I/path-to-linux/include -nostdinc -iwithprefix include \ * -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=fdiskfs \ * -O2 -Wall -Wstrict-prototypes -c -o fdiskfs.o fdiskfs.c */ #include #include #include #include #include #include #include #include static inline int is_extended_type(int type) { return (type == DOS_EXTENDED_PARTITION || type == WIN98_EXTENDED_PARTITION || type == LINUX_EXTENDED_PARTITION); } typedef struct { unsigned char h,s,c; } chs; struct fdisk_partition { unsigned char bootable; /* 0 or 0x80 */ chs begin_chs; unsigned char sys_type; chs end_chs; unsigned int start_sect; unsigned int nr_sects; }; static struct buffer_head * fdisk_read_sector(struct super_block *s, int sector) { struct buffer_head *bh; unsigned char *data; bh = sb_bread(s, sector); if (!bh) { printk ("fdiskfs: unable to read sector %d on dev %s\n", sector, s->s_id); } else { data = (unsigned char *) bh->b_data; if (data[510] != 0x55 || data[511] != 0xaa) { printk ("No aa55 signature on sector %d of dev %s\n", sector, s->s_id); brelse(bh); bh = 0; } } return bh; } /* inos: root: 1, primary: 2-5, elsewhere E+(H<<2)+(L<<4) E entry (0-3), H chain head (0-3), L chain length (1-max) */ #define ROOT_INO 1 /* must not be 0 */ #define PRIMARY_SHIFT 2 #define IS_ROOT(a) ((a) == ROOT_INO) #define IS_PRIMARY(a) ((a) < 4 + PRIMARY_SHIFT) #define ROOT_SUB_INO (PRIMARY_SHIFT) #define PRIMARY_SUB_INO(p) ((((p) - PRIMARY_SHIFT)<<2) + (1<<4)) #define OTHER_SUB_INO(p) (((p) & ~3) + (1<<4)) static inline int sub_ino(int ino, int pos) { return pos + (IS_ROOT(ino) ? ROOT_SUB_INO : IS_PRIMARY(ino) ? PRIMARY_SUB_INO(ino) : OTHER_SUB_INO(ino)); } static struct fdisk_partition * fdiskfs_find_inode(struct super_block *s, int ino, struct buffer_head **abh) { int head, pos, depth; unsigned char *data; struct fdisk_partition *p; int sector, extd, i; if (IS_ROOT(ino)) return NULL; if (IS_PRIMARY(ino)) { pos = head = ino - PRIMARY_SHIFT; depth = 0; } else { pos = (ino & 3); head = ((ino >> 2) & 3); depth = (ino >> 4); } *abh = fdisk_read_sector(s, 0); if (!*abh) return NULL; data = (*abh)->b_data; p = (struct fdisk_partition *)(data + 446 + 16*head); if (depth == 0) return p; extd = sector = p->start_sect; for (;;) { brelse(*abh); *abh = fdisk_read_sector(s, sector); if (!*abh) return NULL; data = (*abh)->b_data; p = (struct fdisk_partition *)(data + 446); if (--depth == 0) return p+pos; for (i = 0; i< 4; i++) if (p[i].nr_sects != 0 && is_extended_type(p[i].sys_type)) break; if (i == 4) { brelse(*abh); *abh = 0; return NULL; } sector = extd + p[i].start_sect; } } static int fdiskfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *dir; unsigned long offset, maxoff; int i, len, ino, dino, fino; int stored = 0; char name[3]; lock_kernel(); dir = filp->f_dentry->d_inode; dino = dir->i_ino; ino = sub_ino(dino, 0); offset = filp->f_pos; maxoff = 6; for(;;) { if (offset >= maxoff) { offset = maxoff; filp->f_pos = offset; goto out; } filp->f_pos = offset; if (offset == 0) { strcpy(name, "."); fino = dino; } else if (offset == 1) { strcpy(name, ".."); fino = parent_ino(filp->f_dentry); } else { i = offset-2; name[0] = '1' + i; name[1] = 0; fino = ino + i; } len = strlen(name); if (filldir(dirent, name, len, offset, fino, 0) < 0) goto out; stored++; offset++; } out: unlock_kernel(); return stored; } static struct dentry * fdiskfs_lookup(struct inode *dir, struct dentry *dentry) { struct inode *inode = NULL; const char *name; int dino, ino, len, pos; lock_kernel(); name = dentry->d_name.name; len = dentry->d_name.len; if (len != 1 || *name < '1' || *name > '4') goto out; pos = *name - '1'; dino = dir->i_ino; ino = sub_ino(dino, pos); inode = iget(dir->i_sb, ino); out: d_add(dentry, inode); unlock_kernel(); return NULL; } static ssize_t fdiskfs_read(struct file *filp, char *buf, size_t count, loff_t *ppos) { struct inode *inode; struct buffer_head *bh; struct fdisk_partition *p; int ino, len, offset; char file_contents[200]; inode = filp->f_dentry->d_inode; ino = inode->i_ino; p = fdiskfs_find_inode(inode->i_sb, ino, &bh); if (!p) goto out; if (p->nr_sects == 0) sprintf(file_contents, "empty slot\n"); else sprintf(file_contents, "sectors %d-%d, type %02X%s\n", p->start_sect, p->start_sect + p->nr_sects - 1, p->sys_type, p->bootable ? " boot" : ""); brelse(bh); len = strlen(file_contents); offset = *ppos; if (offset >= len) { offset = len; *ppos = offset; return 0; } len -= offset; if (len > count) len = count; strncpy(buf, file_contents, len); *ppos += len; return len; out: return -EIO; } static struct file_operations fdiskfs_dir_operations = { .read = generic_read_dir, .readdir = fdiskfs_readdir, }; static struct inode_operations fdiskfs_dir_inode_operations = { .lookup = fdiskfs_lookup, }; static struct file_operations fdiskfs_file_operations = { .read = fdiskfs_read, }; static void fdiskfs_read_inode(struct inode *i) { struct buffer_head *bh = NULL; struct fdisk_partition *p; int ino, isdir; ino = i->i_ino; if (ino == ROOT_INO) { isdir = 1; } else { p = fdiskfs_find_inode(i->i_sb, ino, &bh); if (!p) { printk("fdiskfs: error reading ino %d\n", ino); return; } isdir = is_extended_type(p->sys_type); brelse(bh); } i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0; i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0; i->i_uid = i->i_gid = 0; if (isdir) { i->i_op = &fdiskfs_dir_inode_operations; i->i_fop = &fdiskfs_dir_operations; i->i_mode = S_IFDIR + 0555; i->i_nlink = 3; /* ., .., subdirs */ i->i_size = 6; } else { i->i_fop = &fdiskfs_file_operations; i->i_mode = S_IFREG + 0444; i->i_nlink = 1; i->i_size = 16; } } static struct super_operations fdiskfs_ops = { .read_inode = fdiskfs_read_inode, }; static int fdiskfs_fill_super(struct super_block *s, void *data, int silent) { struct buffer_head *bh; sb_set_blocksize(s, 512); s->s_maxbytes = 1024; bh = fdisk_read_sector(s, 0); if (!bh) goto out; brelse(bh); s->s_flags |= MS_RDONLY; s->s_op = &fdiskfs_ops; s->s_root = d_alloc_root(iget(s, ROOT_INO)); if (!s->s_root) goto out; return 0; out: return -EINVAL; } static struct super_block * fdiskfs_get_sb(struct file_system_type *fs_type, int flags, char *dev_name, void *data) { return get_sb_bdev(fs_type, flags, dev_name, data, fdiskfs_fill_super); } static struct file_system_type fdiskfs_type = { .owner = THIS_MODULE, .name = "fdiskfs", .get_sb = fdiskfs_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; static int __init init_fdiskfs(void) { return register_filesystem(&fdiskfs_type); } static void __exit exit_fdiskfs(void) { unregister_filesystem(&fdiskfs_type); } module_init(init_fdiskfs) module_exit(exit_fdiskfs) MODULE_LICENSE("GPL");