/** * @file tarfs_module.c * * Contains interface for communicating with VFS * * @author Jaroslav Drazan * @author Petr Cermak */ /* ************************************************************************* * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * ************************************************************************* */ #include #include #include #include #include #include #include #include "tarfs_tar.h" #include "tarfs_common.h" MODULE_DESCRIPTION("TAR FS"); MODULE_AUTHOR("Petr Cermak; Jaroslav Drazan"); MODULE_LICENSE("GPL"); // *** forward declarations static struct super_block* tarfs_read_super(struct super_block *s, void *data, int silent); static void tarfs_read_inode(struct inode *i); static int tarfs_statfs(struct super_block *sb, struct statfs *buf); static struct dentry* tarfs_lookup(struct inode* dir, struct dentry* dentry); static int tarfs_readdir(struct file* filp, void* dirent, filldir_t filldir); static int tarfs_readpage(struct file* file, struct page* page); //static int tarfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname); // *** structures // operations on superblock static struct super_operations tarfs_ops = { read_inode: tarfs_read_inode, statfs: tarfs_statfs, }; // operations on inode static struct inode_operations tarfs_inode_ops = { lookup: tarfs_lookup, // symlink: tarfs_symlink, }; // operations on directories static struct file_operations tarfs_dir_ops = { read: generic_read_dir, readdir: tarfs_readdir, }; // memory operations static struct address_space_operations tarfs_aops = { readpage: tarfs_readpage, }; // declaration of structure for our filesystem registration static DECLARE_FSTYPE_DEV(tarfs_type, FSNAME, tarfs_read_super); /** * @brief Reads super block of fs * * Reads super block of tarfs, finds root inode and initializes kernel structures * * @param s A partially filled super block structure from kernel * @param data Mount data passed by mount syscall * @param silent Bool value, disables / enables error output when mounting fs. * @return Filled super_block structure */ static struct super_block* tarfs_read_super(struct super_block *s, void *data, int silent) { int ino; kdev_t dev = s->s_dev; PRINTD("*** tarfs_read_super"); set_blocksize(dev, TARFS_BLKSIZE); s->s_blocksize = TARFS_BLKSIZE; s->s_blocksize_bits = TARFS_BLKSIZE_BITS; s->u.generic_sbp = (void *) 0; s->s_maxbytes = 0xFFFFFFFF; // Zkontroluje se prvni blok - zda se jedna o tar if (!tar_quick_validation(s)) { goto EOut1; } s->s_magic = TARFS_MAGIC; s->s_flags |= MS_RDONLY; s->s_op = &tarfs_ops; if ((ino = tar_get_root_ino(s)) == TARFS_EVAL) { goto EOut1; } s->s_root = d_alloc_root(iget(s, ino)); return s; EOut1: return 0; } /* * @brief Reads directory content * * Reads directory entries, until stopped by returning <0 from filldir function, * after that, it stores last entry id read into filp->f_pos. When called again, * it continues from the last f_pos stored. * * @param filp A file structure representing directory being read * @param dirent Falls through this function to the filldir call * @param filldir Pointer to a function called to add new dir entry * @return 0 - there are more entries to be read, -ENOENT no more entries to be read */ static int tarfs_readdir(struct file* filp, void* dirent, filldir_t filldir) { int retval; PRINTD("*** tarfs_readdir"); //printk("<1> inode: %d, filpos: %d\n", filp->f_dentry->d_inode->i_ino, filp->f_pos); retval = tar_readdir(filp, dirent, filldir); if (retval == 0 || retval == TARFS_EVAL) { // pokud chyba tak taky skoncim return -ENOENT; } else { return 0; } } /** * @brief Looks up dentry in directory dir * * When found, relevant inode is joined with the dentry by calling d_add function * (added to dcache) * * @param dir Direstory to perform search in * @param dentry Directory entry to be searched for * @return 0 - OK, ERR_PTR(-EACCES) - given entry was not found */ static struct dentry* tarfs_lookup(struct inode* dir, struct dentry* dentry) { struct inode* inode; unsigned long retval; PRINTD("*** tarfs_lookup"); // tady kontrola + urceni cisla inodu retval = tar_lookup(dir->i_sb, dir->i_ino, dentry->d_name.name, dentry->d_name.len); inode = 0; if (retval != TARFS_EVAL) { inode = iget(dir->i_sb, retval); if (!inode) { return ERR_PTR(-EACCES); } } d_add(dentry, inode); return 0; } /** * @brief Fills given inode as root inode. * * Called, if there is no root inode present in tar file * * @param i Inode structure to be filled */ void tarfs_get_new_root_inode(struct inode* i) { PRINTD("*** tarfs_get_new_root_inode"); i->i_nlink = 1; i->i_size = 0; i->i_atime = i->i_ctime = i->i_mtime = 0; i->i_uid = i->i_gid = 0; i->i_op = &tarfs_inode_ops; i->i_fop = &tarfs_dir_ops; i->i_mode = S_IFDIR + 0555; } /** * @brief Reads given inode from device * * @param i Pre-filled inode structure to be read. */ static void tarfs_read_inode(struct inode *i) { int rdev = 0; PRINTD("*** tarfs_read_inode"); // Pokud je to nula, root adresar nebyl pritomen v taru a musime si ho vymyslet if (i->i_ino == TARFS_ROOT_INO) { tarfs_get_new_root_inode(i); return; } switch (tar_fill_inode(i->i_sb, i, &rdev)) { case S_IFREG: i->i_fop = &generic_ro_fops; i->i_data.a_ops = &tarfs_aops; break; case S_IFDIR: i->i_op = &tarfs_inode_ops; i->i_fop = &tarfs_dir_ops; break; case S_IFLNK: i->i_op = &page_symlink_inode_operations; i->i_data.a_ops = &tarfs_aops; break; case S_IFCHR: case S_IFIFO: case S_IFBLK: init_special_inode(i, i->i_mode, rdev); break; default: break; } return; } /** * @brief Reads one (or a part of) page from tar. * * This function is called when reading from file, mapping into memory, reading symlinks etc. * * @param file File from which to map desired page. We don't need/use it. * @param page Structure describing page to be read. Contains relevant inode in page->mapping->host. * @return 0 - OK, -EIO - error reading page */ static int tarfs_readpage(struct file* file, struct page* page) { struct inode* i = page->mapping->host; unsigned long offset; char* buf; int result = -EIO; PRINTD("*** tarfs_readpage"); page_cache_get(page); lock_kernel(); // tohle je pro SMP.. je to tu potreba? bylo to v romfs buf = (char*) kmap(page); // namapuje stranku do adr. space kernelu if (!buf) { goto err_out; } // ofset v bloku - od kolikateho b. mame cist, cte se do konce stranky offset = page->index << PAGE_CACHE_SHIFT; if (tar_read_page(i->i_sb, i, offset, buf) != TARFS_EVAL) { result = 0; SetPageUptodate(page); } else { memset(buf, 0, PAGE_SIZE); SetPageError(page); } flush_dcache_page(page); UnlockPage(page); kunmap(page); err_out: page_cache_release(page); unlock_kernel(); return result; } /** * @brief Returns some info on mounted fs. * * @param s Super block structure. * @param stats Structure to fill fs info into. * @return 0 - Success */ static int tarfs_statfs(struct super_block *s, struct statfs *stats) { PRINTD("*** tarfs_statfs"); stats->f_type = TARFS_MAGIC; stats->f_bsize = TARFS_BLKSIZE; stats->f_bfree = 0; stats->f_blocks = tar_length(s); stats->f_namelen = 128; return 0; } //------------------------------------------------- /** * @brief Registers filesystem * * @return 0 - OK, < 0 otherwise */ static int __init init_module() { return register_filesystem(&tarfs_type); } /** * @brief Unregisters filesystem */ static void __exit cleanup_module() { unregister_filesystem(&tarfs_type); }