11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Compressed rom filesystem for Linux. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1999 Linus Torvalds. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This file is released under the GPL. 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds /* 101da177e4SLinus Torvalds * These are the VFS interfaces to the compressed rom filesystem. 111da177e4SLinus Torvalds * The actual compression is based on zlib, see the other files. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 144f21e1eaSFabian Frederick #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 154f21e1eaSFabian Frederick 161da177e4SLinus Torvalds #include <linux/module.h> 171da177e4SLinus Torvalds #include <linux/fs.h> 181da177e4SLinus Torvalds #include <linux/pagemap.h> 191da177e4SLinus Torvalds #include <linux/init.h> 201da177e4SLinus Torvalds #include <linux/string.h> 211da177e4SLinus Torvalds #include <linux/blkdev.h> 2299c18ce5SNicolas Pitre #include <linux/mtd/mtd.h> 2399c18ce5SNicolas Pitre #include <linux/mtd/super.h> 241da177e4SLinus Torvalds #include <linux/slab.h> 251da177e4SLinus Torvalds #include <linux/vfs.h> 26353ab6e9SIngo Molnar #include <linux/mutex.h> 27f7f4f4ddSAl Viro #include <uapi/linux/cramfs_fs.h> 281508f3ebSFabian Frederick #include <linux/uaccess.h> 291da177e4SLinus Torvalds 30f7f4f4ddSAl Viro #include "internal.h" 31f7f4f4ddSAl Viro 32f7f4f4ddSAl Viro /* 33f7f4f4ddSAl Viro * cramfs super-block data in memory 34f7f4f4ddSAl Viro */ 35f7f4f4ddSAl Viro struct cramfs_sb_info { 36f7f4f4ddSAl Viro unsigned long magic; 37f7f4f4ddSAl Viro unsigned long size; 38f7f4f4ddSAl Viro unsigned long blocks; 39f7f4f4ddSAl Viro unsigned long files; 40f7f4f4ddSAl Viro unsigned long flags; 4199c18ce5SNicolas Pitre void *linear_virt_addr; 4299c18ce5SNicolas Pitre resource_size_t linear_phys_addr; 4399c18ce5SNicolas Pitre size_t mtd_point_size; 44f7f4f4ddSAl Viro }; 45f7f4f4ddSAl Viro 46f7f4f4ddSAl Viro static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) 47f7f4f4ddSAl Viro { 48f7f4f4ddSAl Viro return sb->s_fs_info; 49f7f4f4ddSAl Viro } 50f7f4f4ddSAl Viro 51ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations cramfs_ops; 52754661f1SArjan van de Ven static const struct inode_operations cramfs_dir_inode_operations; 534b6f5d20SArjan van de Ven static const struct file_operations cramfs_directory_operations; 54f5e54d6eSChristoph Hellwig static const struct address_space_operations cramfs_aops; 551da177e4SLinus Torvalds 56353ab6e9SIngo Molnar static DEFINE_MUTEX(read_mutex); 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds 596f772fe6SStefani Seibold /* These macros may change in future, to provide better st_ino semantics. */ 601da177e4SLinus Torvalds #define OFFSET(x) ((x)->i_ino) 611da177e4SLinus Torvalds 620577d1baSAl Viro static unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset) 631da177e4SLinus Torvalds { 646f772fe6SStefani Seibold if (!cino->offset) 656f772fe6SStefani Seibold return offset + 1; 666f772fe6SStefani Seibold if (!cino->size) 676f772fe6SStefani Seibold return offset + 1; 686f772fe6SStefani Seibold 696f772fe6SStefani Seibold /* 706f772fe6SStefani Seibold * The file mode test fixes buggy mkcramfs implementations where 716f772fe6SStefani Seibold * cramfs_inode->offset is set to a non zero value for entries 726f772fe6SStefani Seibold * which did not contain data, like devices node and fifos. 736f772fe6SStefani Seibold */ 746f772fe6SStefani Seibold switch (cino->mode & S_IFMT) { 756f772fe6SStefani Seibold case S_IFREG: 766f772fe6SStefani Seibold case S_IFDIR: 776f772fe6SStefani Seibold case S_IFLNK: 786f772fe6SStefani Seibold return cino->offset << 2; 796f772fe6SStefani Seibold default: 806f772fe6SStefani Seibold break; 816f772fe6SStefani Seibold } 826f772fe6SStefani Seibold return offset + 1; 836f772fe6SStefani Seibold } 846f772fe6SStefani Seibold 856f772fe6SStefani Seibold static struct inode *get_cramfs_inode(struct super_block *sb, 860577d1baSAl Viro const struct cramfs_inode *cramfs_inode, unsigned int offset) 876f772fe6SStefani Seibold { 886f772fe6SStefani Seibold struct inode *inode; 8982d63fc9SAl Viro static struct timespec zerotime; 906f772fe6SStefani Seibold 916f772fe6SStefani Seibold inode = iget_locked(sb, cramino(cramfs_inode, offset)); 926f772fe6SStefani Seibold if (!inode) 936f772fe6SStefani Seibold return ERR_PTR(-ENOMEM); 946f772fe6SStefani Seibold if (!(inode->i_state & I_NEW)) 956f772fe6SStefani Seibold return inode; 966f772fe6SStefani Seibold 976f772fe6SStefani Seibold switch (cramfs_inode->mode & S_IFMT) { 986f772fe6SStefani Seibold case S_IFREG: 996f772fe6SStefani Seibold inode->i_fop = &generic_ro_fops; 1006f772fe6SStefani Seibold inode->i_data.a_ops = &cramfs_aops; 1016f772fe6SStefani Seibold break; 1026f772fe6SStefani Seibold case S_IFDIR: 1036f772fe6SStefani Seibold inode->i_op = &cramfs_dir_inode_operations; 1046f772fe6SStefani Seibold inode->i_fop = &cramfs_directory_operations; 1056f772fe6SStefani Seibold break; 1066f772fe6SStefani Seibold case S_IFLNK: 1076f772fe6SStefani Seibold inode->i_op = &page_symlink_inode_operations; 10821fc61c7SAl Viro inode_nohighmem(inode); 1096f772fe6SStefani Seibold inode->i_data.a_ops = &cramfs_aops; 1106f772fe6SStefani Seibold break; 1116f772fe6SStefani Seibold default: 1126f772fe6SStefani Seibold init_special_inode(inode, cramfs_inode->mode, 1136f772fe6SStefani Seibold old_decode_dev(cramfs_inode->size)); 1146f772fe6SStefani Seibold } 1156f772fe6SStefani Seibold 1161da177e4SLinus Torvalds inode->i_mode = cramfs_inode->mode; 117a7d9cfe9SEric W. Biederman i_uid_write(inode, cramfs_inode->uid); 118a7d9cfe9SEric W. Biederman i_gid_write(inode, cramfs_inode->gid); 1196f772fe6SStefani Seibold 1206f772fe6SStefani Seibold /* if the lower 2 bits are zero, the inode contains data */ 1216f772fe6SStefani Seibold if (!(inode->i_ino & 3)) { 1221da177e4SLinus Torvalds inode->i_size = cramfs_inode->size; 1231da177e4SLinus Torvalds inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; 1246f772fe6SStefani Seibold } 1256f772fe6SStefani Seibold 1261da177e4SLinus Torvalds /* Struct copy intentional */ 1271da177e4SLinus Torvalds inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; 1281da177e4SLinus Torvalds /* inode->i_nlink is left 1 - arguably wrong for directories, 1291da177e4SLinus Torvalds but it's the best we can do without reading the directory 1301da177e4SLinus Torvalds contents. 1 yields the right result in GNU find, even 1311da177e4SLinus Torvalds without -noleaf option. */ 1321da177e4SLinus Torvalds 13377b8a75fSAl Viro unlock_new_inode(inode); 1346f772fe6SStefani Seibold 13577b8a75fSAl Viro return inode; 13682d63fc9SAl Viro } 13782d63fc9SAl Viro 1381da177e4SLinus Torvalds /* 1391da177e4SLinus Torvalds * We have our own block cache: don't fill up the buffer cache 1401da177e4SLinus Torvalds * with the rom-image, because the way the filesystem is set 1411da177e4SLinus Torvalds * up the accesses should be fairly regular and cached in the 1421da177e4SLinus Torvalds * page cache and dentry tree anyway.. 1431da177e4SLinus Torvalds * 1441da177e4SLinus Torvalds * This also acts as a way to guarantee contiguous areas of up to 145ea1754a0SKirill A. Shutemov * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to 1461da177e4SLinus Torvalds * worry about end-of-buffer issues even when decompressing a full 1471da177e4SLinus Torvalds * page cache. 14899c18ce5SNicolas Pitre * 14999c18ce5SNicolas Pitre * Note: This is all optimized away at compile time when 15099c18ce5SNicolas Pitre * CONFIG_CRAMFS_BLOCKDEV=n. 1511da177e4SLinus Torvalds */ 1521da177e4SLinus Torvalds #define READ_BUFFERS (2) 1531da177e4SLinus Torvalds /* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ 1541da177e4SLinus Torvalds #define NEXT_BUFFER(_ix) ((_ix) ^ 1) 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds /* 1571da177e4SLinus Torvalds * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" 1581da177e4SLinus Torvalds * data that takes up more space than the original and with unlucky 1591da177e4SLinus Torvalds * alignment. 1601da177e4SLinus Torvalds */ 1611da177e4SLinus Torvalds #define BLKS_PER_BUF_SHIFT (2) 1621da177e4SLinus Torvalds #define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) 16309cbfeafSKirill A. Shutemov #define BUFFER_SIZE (BLKS_PER_BUF*PAGE_SIZE) 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; 1661da177e4SLinus Torvalds static unsigned buffer_blocknr[READ_BUFFERS]; 1671da177e4SLinus Torvalds static struct super_block *buffer_dev[READ_BUFFERS]; 1681da177e4SLinus Torvalds static int next_buffer; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds /* 17199c18ce5SNicolas Pitre * Populate our block cache and return a pointer to it. 1721da177e4SLinus Torvalds */ 17399c18ce5SNicolas Pitre static void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset, 17499c18ce5SNicolas Pitre unsigned int len) 1751da177e4SLinus Torvalds { 1761da177e4SLinus Torvalds struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; 1771da177e4SLinus Torvalds struct page *pages[BLKS_PER_BUF]; 1786bbfb077SAndi Drebes unsigned i, blocknr, buffer; 1791da177e4SLinus Torvalds unsigned long devsize; 1801da177e4SLinus Torvalds char *data; 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds if (!len) 1831da177e4SLinus Torvalds return NULL; 18409cbfeafSKirill A. Shutemov blocknr = offset >> PAGE_SHIFT; 18509cbfeafSKirill A. Shutemov offset &= PAGE_SIZE - 1; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds /* Check if an existing buffer already has the data.. */ 1881da177e4SLinus Torvalds for (i = 0; i < READ_BUFFERS; i++) { 1891da177e4SLinus Torvalds unsigned int blk_offset; 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds if (buffer_dev[i] != sb) 1921da177e4SLinus Torvalds continue; 1931da177e4SLinus Torvalds if (blocknr < buffer_blocknr[i]) 1941da177e4SLinus Torvalds continue; 19509cbfeafSKirill A. Shutemov blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_SHIFT; 1961da177e4SLinus Torvalds blk_offset += offset; 1971da177e4SLinus Torvalds if (blk_offset + len > BUFFER_SIZE) 1981da177e4SLinus Torvalds continue; 1991da177e4SLinus Torvalds return read_buffers[i] + blk_offset; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 20209cbfeafSKirill A. Shutemov devsize = mapping->host->i_size >> PAGE_SHIFT; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /* Ok, read in BLKS_PER_BUF pages completely first. */ 2051da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 2061da177e4SLinus Torvalds struct page *page = NULL; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds if (blocknr + i < devsize) { 20967f9fd91SSasha Levin page = read_mapping_page(mapping, blocknr + i, NULL); 2101da177e4SLinus Torvalds /* synchronous error? */ 2111da177e4SLinus Torvalds if (IS_ERR(page)) 2121da177e4SLinus Torvalds page = NULL; 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds pages[i] = page; 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 2181da177e4SLinus Torvalds struct page *page = pages[i]; 21931d92e55SFabian Frederick 2201da177e4SLinus Torvalds if (page) { 2211da177e4SLinus Torvalds wait_on_page_locked(page); 2221da177e4SLinus Torvalds if (!PageUptodate(page)) { 2231da177e4SLinus Torvalds /* asynchronous error */ 22409cbfeafSKirill A. Shutemov put_page(page); 2251da177e4SLinus Torvalds pages[i] = NULL; 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds buffer = next_buffer; 2311da177e4SLinus Torvalds next_buffer = NEXT_BUFFER(buffer); 2321da177e4SLinus Torvalds buffer_blocknr[buffer] = blocknr; 2331da177e4SLinus Torvalds buffer_dev[buffer] = sb; 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds data = read_buffers[buffer]; 2361da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 2371da177e4SLinus Torvalds struct page *page = pages[i]; 23831d92e55SFabian Frederick 2391da177e4SLinus Torvalds if (page) { 24009cbfeafSKirill A. Shutemov memcpy(data, kmap(page), PAGE_SIZE); 2411da177e4SLinus Torvalds kunmap(page); 24209cbfeafSKirill A. Shutemov put_page(page); 2431da177e4SLinus Torvalds } else 24409cbfeafSKirill A. Shutemov memset(data, 0, PAGE_SIZE); 24509cbfeafSKirill A. Shutemov data += PAGE_SIZE; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds return read_buffers[buffer] + offset; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 25099c18ce5SNicolas Pitre /* 25199c18ce5SNicolas Pitre * Return a pointer to the linearly addressed cramfs image in memory. 25299c18ce5SNicolas Pitre */ 25399c18ce5SNicolas Pitre static void *cramfs_direct_read(struct super_block *sb, unsigned int offset, 25499c18ce5SNicolas Pitre unsigned int len) 25599c18ce5SNicolas Pitre { 25699c18ce5SNicolas Pitre struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 25799c18ce5SNicolas Pitre 25899c18ce5SNicolas Pitre if (!len) 25999c18ce5SNicolas Pitre return NULL; 26099c18ce5SNicolas Pitre if (len > sbi->size || offset > sbi->size - len) 26199c18ce5SNicolas Pitre return page_address(ZERO_PAGE(0)); 26299c18ce5SNicolas Pitre return sbi->linear_virt_addr + offset; 26399c18ce5SNicolas Pitre } 26499c18ce5SNicolas Pitre 26599c18ce5SNicolas Pitre /* 26699c18ce5SNicolas Pitre * Returns a pointer to a buffer containing at least LEN bytes of 26799c18ce5SNicolas Pitre * filesystem starting at byte offset OFFSET into the filesystem. 26899c18ce5SNicolas Pitre */ 26999c18ce5SNicolas Pitre static void *cramfs_read(struct super_block *sb, unsigned int offset, 27099c18ce5SNicolas Pitre unsigned int len) 27199c18ce5SNicolas Pitre { 27299c18ce5SNicolas Pitre struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 27399c18ce5SNicolas Pitre 27499c18ce5SNicolas Pitre if (IS_ENABLED(CONFIG_CRAMFS_MTD) && sbi->linear_virt_addr) 27599c18ce5SNicolas Pitre return cramfs_direct_read(sb, offset, len); 27699c18ce5SNicolas Pitre else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) 27799c18ce5SNicolas Pitre return cramfs_blkdev_read(sb, offset, len); 27899c18ce5SNicolas Pitre else 27999c18ce5SNicolas Pitre return NULL; 28099c18ce5SNicolas Pitre } 28199c18ce5SNicolas Pitre 2822309fb8eSAl Viro static void cramfs_kill_sb(struct super_block *sb) 2831da177e4SLinus Torvalds { 284f7f4f4ddSAl Viro struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 28531d92e55SFabian Frederick 28699c18ce5SNicolas Pitre if (IS_ENABLED(CCONFIG_CRAMFS_MTD) && sb->s_mtd) { 28799c18ce5SNicolas Pitre if (sbi && sbi->mtd_point_size) 28899c18ce5SNicolas Pitre mtd_unpoint(sb->s_mtd, 0, sbi->mtd_point_size); 28999c18ce5SNicolas Pitre kill_mtd_super(sb); 29099c18ce5SNicolas Pitre } else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV) && sb->s_bdev) { 2912309fb8eSAl Viro kill_block_super(sb); 29299c18ce5SNicolas Pitre } 2932309fb8eSAl Viro kfree(sbi); 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds static int cramfs_remount(struct super_block *sb, int *flags, char *data) 2971da177e4SLinus Torvalds { 29802b9984dSTheodore Ts'o sync_filesystem(sb); 2991da177e4SLinus Torvalds *flags |= MS_RDONLY; 3001da177e4SLinus Torvalds return 0; 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 30399c18ce5SNicolas Pitre static int cramfs_read_super(struct super_block *sb, 30499c18ce5SNicolas Pitre struct cramfs_super *super, int silent) 3051da177e4SLinus Torvalds { 30699c18ce5SNicolas Pitre struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 3071da177e4SLinus Torvalds unsigned long root_offset; 3081da177e4SLinus Torvalds 30999c18ce5SNicolas Pitre /* We don't know the real size yet */ 31099c18ce5SNicolas Pitre sbi->size = PAGE_SIZE; 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds /* Read the first block and get the superblock from it */ 31399c18ce5SNicolas Pitre mutex_lock(&read_mutex); 31499c18ce5SNicolas Pitre memcpy(super, cramfs_read(sb, 0, sizeof(*super)), sizeof(*super)); 315353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds /* Do sanity checks on the superblock */ 31899c18ce5SNicolas Pitre if (super->magic != CRAMFS_MAGIC) { 3190cc785ecSMasanari Iida /* check for wrong endianness */ 32099c18ce5SNicolas Pitre if (super->magic == CRAMFS_MAGIC_WEND) { 321ac8d35c5SAndi Drebes if (!silent) 3224f21e1eaSFabian Frederick pr_err("wrong endianness\n"); 3232309fb8eSAl Viro return -EINVAL; 324ac8d35c5SAndi Drebes } 325ac8d35c5SAndi Drebes 3261da177e4SLinus Torvalds /* check at 512 byte offset */ 327353ab6e9SIngo Molnar mutex_lock(&read_mutex); 32899c18ce5SNicolas Pitre memcpy(super, 32999c18ce5SNicolas Pitre cramfs_read(sb, 512, sizeof(*super)), 33099c18ce5SNicolas Pitre sizeof(*super)); 331353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 33299c18ce5SNicolas Pitre if (super->magic != CRAMFS_MAGIC) { 33399c18ce5SNicolas Pitre if (super->magic == CRAMFS_MAGIC_WEND && !silent) 3344f21e1eaSFabian Frederick pr_err("wrong endianness\n"); 335ac8d35c5SAndi Drebes else if (!silent) 3364f21e1eaSFabian Frederick pr_err("wrong magic\n"); 3372309fb8eSAl Viro return -EINVAL; 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds /* get feature flags first */ 34299c18ce5SNicolas Pitre if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) { 3434f21e1eaSFabian Frederick pr_err("unsupported filesystem features\n"); 3442309fb8eSAl Viro return -EINVAL; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds /* Check that the root inode is in a sane state */ 34899c18ce5SNicolas Pitre if (!S_ISDIR(super->root.mode)) { 3494f21e1eaSFabian Frederick pr_err("root is not a directory\n"); 3502309fb8eSAl Viro return -EINVAL; 3511da177e4SLinus Torvalds } 3526f772fe6SStefani Seibold /* correct strange, hard-coded permissions of mkcramfs */ 35399c18ce5SNicolas Pitre super->root.mode |= 0555; 3546f772fe6SStefani Seibold 35599c18ce5SNicolas Pitre root_offset = super->root.offset << 2; 35699c18ce5SNicolas Pitre if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { 35799c18ce5SNicolas Pitre sbi->size = super->size; 35899c18ce5SNicolas Pitre sbi->blocks = super->fsid.blocks; 35999c18ce5SNicolas Pitre sbi->files = super->fsid.files; 3601da177e4SLinus Torvalds } else { 3611da177e4SLinus Torvalds sbi->size = 1<<28; 3621da177e4SLinus Torvalds sbi->blocks = 0; 3631da177e4SLinus Torvalds sbi->files = 0; 3641da177e4SLinus Torvalds } 36599c18ce5SNicolas Pitre sbi->magic = super->magic; 36699c18ce5SNicolas Pitre sbi->flags = super->flags; 3671da177e4SLinus Torvalds if (root_offset == 0) 3684f21e1eaSFabian Frederick pr_info("empty filesystem"); 36999c18ce5SNicolas Pitre else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 3701da177e4SLinus Torvalds ((root_offset != sizeof(struct cramfs_super)) && 3711da177e4SLinus Torvalds (root_offset != 512 + sizeof(struct cramfs_super)))) 3721da177e4SLinus Torvalds { 3734f21e1eaSFabian Frederick pr_err("bad root offset %lu\n", root_offset); 3742309fb8eSAl Viro return -EINVAL; 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 37799c18ce5SNicolas Pitre return 0; 37899c18ce5SNicolas Pitre } 37999c18ce5SNicolas Pitre 38099c18ce5SNicolas Pitre static int cramfs_finalize_super(struct super_block *sb, 38199c18ce5SNicolas Pitre struct cramfs_inode *cramfs_root) 38299c18ce5SNicolas Pitre { 38399c18ce5SNicolas Pitre struct inode *root; 38499c18ce5SNicolas Pitre 3851da177e4SLinus Torvalds /* Set it all up.. */ 38699c18ce5SNicolas Pitre sb->s_flags |= MS_RDONLY; 3871da177e4SLinus Torvalds sb->s_op = &cramfs_ops; 38899c18ce5SNicolas Pitre root = get_cramfs_inode(sb, cramfs_root, 0); 3890577d1baSAl Viro if (IS_ERR(root)) 3902309fb8eSAl Viro return PTR_ERR(root); 39148fde701SAl Viro sb->s_root = d_make_root(root); 39248fde701SAl Viro if (!sb->s_root) 3932309fb8eSAl Viro return -ENOMEM; 3941da177e4SLinus Torvalds return 0; 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds 39799c18ce5SNicolas Pitre static int cramfs_blkdev_fill_super(struct super_block *sb, void *data, 39899c18ce5SNicolas Pitre int silent) 39999c18ce5SNicolas Pitre { 40099c18ce5SNicolas Pitre struct cramfs_sb_info *sbi; 40199c18ce5SNicolas Pitre struct cramfs_super super; 40299c18ce5SNicolas Pitre int i, err; 40399c18ce5SNicolas Pitre 40499c18ce5SNicolas Pitre sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); 40599c18ce5SNicolas Pitre if (!sbi) 40699c18ce5SNicolas Pitre return -ENOMEM; 40799c18ce5SNicolas Pitre sb->s_fs_info = sbi; 40899c18ce5SNicolas Pitre 40999c18ce5SNicolas Pitre /* Invalidate the read buffers on mount: think disk change.. */ 41099c18ce5SNicolas Pitre for (i = 0; i < READ_BUFFERS; i++) 41199c18ce5SNicolas Pitre buffer_blocknr[i] = -1; 41299c18ce5SNicolas Pitre 41399c18ce5SNicolas Pitre err = cramfs_read_super(sb, &super, silent); 41499c18ce5SNicolas Pitre if (err) 41599c18ce5SNicolas Pitre return err; 41699c18ce5SNicolas Pitre return cramfs_finalize_super(sb, &super.root); 41799c18ce5SNicolas Pitre } 41899c18ce5SNicolas Pitre 41999c18ce5SNicolas Pitre static int cramfs_mtd_fill_super(struct super_block *sb, void *data, 42099c18ce5SNicolas Pitre int silent) 42199c18ce5SNicolas Pitre { 42299c18ce5SNicolas Pitre struct cramfs_sb_info *sbi; 42399c18ce5SNicolas Pitre struct cramfs_super super; 42499c18ce5SNicolas Pitre int err; 42599c18ce5SNicolas Pitre 42699c18ce5SNicolas Pitre sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); 42799c18ce5SNicolas Pitre if (!sbi) 42899c18ce5SNicolas Pitre return -ENOMEM; 42999c18ce5SNicolas Pitre sb->s_fs_info = sbi; 43099c18ce5SNicolas Pitre 43199c18ce5SNicolas Pitre /* Map only one page for now. Will remap it when fs size is known. */ 43299c18ce5SNicolas Pitre err = mtd_point(sb->s_mtd, 0, PAGE_SIZE, &sbi->mtd_point_size, 43399c18ce5SNicolas Pitre &sbi->linear_virt_addr, &sbi->linear_phys_addr); 43499c18ce5SNicolas Pitre if (err || sbi->mtd_point_size != PAGE_SIZE) { 43599c18ce5SNicolas Pitre pr_err("unable to get direct memory access to mtd:%s\n", 43699c18ce5SNicolas Pitre sb->s_mtd->name); 43799c18ce5SNicolas Pitre return err ? : -ENODATA; 43899c18ce5SNicolas Pitre } 43999c18ce5SNicolas Pitre 44099c18ce5SNicolas Pitre pr_info("checking physical address %pap for linear cramfs image\n", 44199c18ce5SNicolas Pitre &sbi->linear_phys_addr); 44299c18ce5SNicolas Pitre err = cramfs_read_super(sb, &super, silent); 44399c18ce5SNicolas Pitre if (err) 44499c18ce5SNicolas Pitre return err; 44599c18ce5SNicolas Pitre 44699c18ce5SNicolas Pitre /* Remap the whole filesystem now */ 44799c18ce5SNicolas Pitre pr_info("linear cramfs image on mtd:%s appears to be %lu KB in size\n", 44899c18ce5SNicolas Pitre sb->s_mtd->name, sbi->size/1024); 44999c18ce5SNicolas Pitre mtd_unpoint(sb->s_mtd, 0, PAGE_SIZE); 45099c18ce5SNicolas Pitre err = mtd_point(sb->s_mtd, 0, sbi->size, &sbi->mtd_point_size, 45199c18ce5SNicolas Pitre &sbi->linear_virt_addr, &sbi->linear_phys_addr); 45299c18ce5SNicolas Pitre if (err || sbi->mtd_point_size != sbi->size) { 45399c18ce5SNicolas Pitre pr_err("unable to get direct memory access to mtd:%s\n", 45499c18ce5SNicolas Pitre sb->s_mtd->name); 45599c18ce5SNicolas Pitre return err ? : -ENODATA; 45699c18ce5SNicolas Pitre } 45799c18ce5SNicolas Pitre 45899c18ce5SNicolas Pitre return cramfs_finalize_super(sb, &super.root); 45999c18ce5SNicolas Pitre } 46099c18ce5SNicolas Pitre 461726c3342SDavid Howells static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) 4621da177e4SLinus Torvalds { 463726c3342SDavid Howells struct super_block *sb = dentry->d_sb; 46499c18ce5SNicolas Pitre u64 id = 0; 46599c18ce5SNicolas Pitre 46699c18ce5SNicolas Pitre if (sb->s_bdev) 46799c18ce5SNicolas Pitre id = huge_encode_dev(sb->s_bdev->bd_dev); 46899c18ce5SNicolas Pitre else if (sb->s_dev) 46999c18ce5SNicolas Pitre id = huge_encode_dev(sb->s_dev); 470726c3342SDavid Howells 4711da177e4SLinus Torvalds buf->f_type = CRAMFS_MAGIC; 47209cbfeafSKirill A. Shutemov buf->f_bsize = PAGE_SIZE; 4731da177e4SLinus Torvalds buf->f_blocks = CRAMFS_SB(sb)->blocks; 4741da177e4SLinus Torvalds buf->f_bfree = 0; 4751da177e4SLinus Torvalds buf->f_bavail = 0; 4761da177e4SLinus Torvalds buf->f_files = CRAMFS_SB(sb)->files; 4771da177e4SLinus Torvalds buf->f_ffree = 0; 47894ea77acSColy Li buf->f_fsid.val[0] = (u32)id; 47994ea77acSColy Li buf->f_fsid.val[1] = (u32)(id >> 32); 4801da177e4SLinus Torvalds buf->f_namelen = CRAMFS_MAXPATHLEN; 4811da177e4SLinus Torvalds return 0; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds /* 4851da177e4SLinus Torvalds * Read a cramfs directory entry. 4861da177e4SLinus Torvalds */ 4876f7f231eSAl Viro static int cramfs_readdir(struct file *file, struct dir_context *ctx) 4881da177e4SLinus Torvalds { 4896f7f231eSAl Viro struct inode *inode = file_inode(file); 4901da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 4911da177e4SLinus Torvalds char *buf; 4921da177e4SLinus Torvalds unsigned int offset; 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds /* Offset within the thing. */ 4956f7f231eSAl Viro if (ctx->pos >= inode->i_size) 4961da177e4SLinus Torvalds return 0; 4976f7f231eSAl Viro offset = ctx->pos; 4981da177e4SLinus Torvalds /* Directory entries are always 4-byte aligned */ 4991da177e4SLinus Torvalds if (offset & 3) 5001da177e4SLinus Torvalds return -EINVAL; 5011da177e4SLinus Torvalds 5024176ed59SAndi Drebes buf = kmalloc(CRAMFS_MAXPATHLEN, GFP_KERNEL); 5031da177e4SLinus Torvalds if (!buf) 5041da177e4SLinus Torvalds return -ENOMEM; 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds while (offset < inode->i_size) { 5071da177e4SLinus Torvalds struct cramfs_inode *de; 5081da177e4SLinus Torvalds unsigned long nextoffset; 5091da177e4SLinus Torvalds char *name; 5101da177e4SLinus Torvalds ino_t ino; 511175a4eb7SAl Viro umode_t mode; 5126f7f231eSAl Viro int namelen; 5131da177e4SLinus Torvalds 514353ab6e9SIngo Molnar mutex_lock(&read_mutex); 5154176ed59SAndi Drebes de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); 5161da177e4SLinus Torvalds name = (char *)(de+1); 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds /* 5191da177e4SLinus Torvalds * Namelengths on disk are shifted by two 5201da177e4SLinus Torvalds * and the name padded out to 4-byte boundaries 5211da177e4SLinus Torvalds * with zeroes. 5221da177e4SLinus Torvalds */ 5231da177e4SLinus Torvalds namelen = de->namelen << 2; 5241da177e4SLinus Torvalds memcpy(buf, name, namelen); 5256f772fe6SStefani Seibold ino = cramino(de, OFFSET(inode) + offset); 5261da177e4SLinus Torvalds mode = de->mode; 527353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 5281da177e4SLinus Torvalds nextoffset = offset + sizeof(*de) + namelen; 5291da177e4SLinus Torvalds for (;;) { 5301da177e4SLinus Torvalds if (!namelen) { 5311da177e4SLinus Torvalds kfree(buf); 5321da177e4SLinus Torvalds return -EIO; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds if (buf[namelen-1]) 5351da177e4SLinus Torvalds break; 5361da177e4SLinus Torvalds namelen--; 5371da177e4SLinus Torvalds } 5386f7f231eSAl Viro if (!dir_emit(ctx, buf, namelen, ino, mode >> 12)) 5391da177e4SLinus Torvalds break; 5401da177e4SLinus Torvalds 5416f7f231eSAl Viro ctx->pos = offset = nextoffset; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds kfree(buf); 5441da177e4SLinus Torvalds return 0; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds /* 5481da177e4SLinus Torvalds * Lookup and fill in the inode data.. 5491da177e4SLinus Torvalds */ 55000cd8dd3SAl Viro static struct dentry *cramfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 5511da177e4SLinus Torvalds { 5521da177e4SLinus Torvalds unsigned int offset = 0; 5530577d1baSAl Viro struct inode *inode = NULL; 5541da177e4SLinus Torvalds int sorted; 5551da177e4SLinus Torvalds 556353ab6e9SIngo Molnar mutex_lock(&read_mutex); 5571da177e4SLinus Torvalds sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS; 5581da177e4SLinus Torvalds while (offset < dir->i_size) { 5591da177e4SLinus Torvalds struct cramfs_inode *de; 5601da177e4SLinus Torvalds char *name; 5611da177e4SLinus Torvalds int namelen, retval; 5626f772fe6SStefani Seibold int dir_off = OFFSET(dir) + offset; 5631da177e4SLinus Torvalds 5646f772fe6SStefani Seibold de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN); 5651da177e4SLinus Torvalds name = (char *)(de+1); 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds /* Try to take advantage of sorted directories */ 5681da177e4SLinus Torvalds if (sorted && (dentry->d_name.name[0] < name[0])) 5691da177e4SLinus Torvalds break; 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds namelen = de->namelen << 2; 5721da177e4SLinus Torvalds offset += sizeof(*de) + namelen; 5731da177e4SLinus Torvalds 5741da177e4SLinus Torvalds /* Quick check that the name is roughly the right length */ 5751da177e4SLinus Torvalds if (((dentry->d_name.len + 3) & ~3) != namelen) 5761da177e4SLinus Torvalds continue; 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds for (;;) { 5791da177e4SLinus Torvalds if (!namelen) { 5800577d1baSAl Viro inode = ERR_PTR(-EIO); 5810577d1baSAl Viro goto out; 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds if (name[namelen-1]) 5841da177e4SLinus Torvalds break; 5851da177e4SLinus Torvalds namelen--; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds if (namelen != dentry->d_name.len) 5881da177e4SLinus Torvalds continue; 5891da177e4SLinus Torvalds retval = memcmp(dentry->d_name.name, name, namelen); 5901da177e4SLinus Torvalds if (retval > 0) 5911da177e4SLinus Torvalds continue; 5921da177e4SLinus Torvalds if (!retval) { 5930577d1baSAl Viro inode = get_cramfs_inode(dir->i_sb, de, dir_off); 5940577d1baSAl Viro break; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds /* else (retval < 0) */ 5971da177e4SLinus Torvalds if (sorted) 5981da177e4SLinus Torvalds break; 5991da177e4SLinus Torvalds } 6000577d1baSAl Viro out: 601353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 6020577d1baSAl Viro if (IS_ERR(inode)) 6030577d1baSAl Viro return ERR_CAST(inode); 6040577d1baSAl Viro d_add(dentry, inode); 6051da177e4SLinus Torvalds return NULL; 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds 6081da177e4SLinus Torvalds static int cramfs_readpage(struct file *file, struct page *page) 6091da177e4SLinus Torvalds { 6101da177e4SLinus Torvalds struct inode *inode = page->mapping->host; 61198310e58SDavid VomLehn u32 maxblock; 61298310e58SDavid VomLehn int bytes_filled; 6131da177e4SLinus Torvalds void *pgdata; 6141da177e4SLinus Torvalds 61509cbfeafSKirill A. Shutemov maxblock = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 6161da177e4SLinus Torvalds bytes_filled = 0; 61798310e58SDavid VomLehn pgdata = kmap(page); 61898310e58SDavid VomLehn 6191da177e4SLinus Torvalds if (page->index < maxblock) { 6201da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 6211da177e4SLinus Torvalds u32 blkptr_offset = OFFSET(inode) + page->index * 4; 622fd4f6f2aSNicolas Pitre u32 block_ptr, block_start, block_len; 623fd4f6f2aSNicolas Pitre bool uncompressed, direct; 6241da177e4SLinus Torvalds 625353ab6e9SIngo Molnar mutex_lock(&read_mutex); 626fd4f6f2aSNicolas Pitre block_ptr = *(u32 *) cramfs_read(sb, blkptr_offset, 4); 627fd4f6f2aSNicolas Pitre uncompressed = (block_ptr & CRAMFS_BLK_FLAG_UNCOMPRESSED); 628fd4f6f2aSNicolas Pitre direct = (block_ptr & CRAMFS_BLK_FLAG_DIRECT_PTR); 629fd4f6f2aSNicolas Pitre block_ptr &= ~CRAMFS_BLK_FLAGS; 63098310e58SDavid VomLehn 631fd4f6f2aSNicolas Pitre if (direct) { 632fd4f6f2aSNicolas Pitre /* 633fd4f6f2aSNicolas Pitre * The block pointer is an absolute start pointer, 634fd4f6f2aSNicolas Pitre * shifted by 2 bits. The size is included in the 635fd4f6f2aSNicolas Pitre * first 2 bytes of the data block when compressed, 636fd4f6f2aSNicolas Pitre * or PAGE_SIZE otherwise. 637fd4f6f2aSNicolas Pitre */ 638fd4f6f2aSNicolas Pitre block_start = block_ptr << CRAMFS_BLK_DIRECT_PTR_SHIFT; 639fd4f6f2aSNicolas Pitre if (uncompressed) { 640fd4f6f2aSNicolas Pitre block_len = PAGE_SIZE; 641fd4f6f2aSNicolas Pitre /* if last block: cap to file length */ 642fd4f6f2aSNicolas Pitre if (page->index == maxblock - 1) 643fd4f6f2aSNicolas Pitre block_len = 644fd4f6f2aSNicolas Pitre offset_in_page(inode->i_size); 64598310e58SDavid VomLehn } else { 646fd4f6f2aSNicolas Pitre block_len = *(u16 *) 647fd4f6f2aSNicolas Pitre cramfs_read(sb, block_start, 2); 648fd4f6f2aSNicolas Pitre block_start += 2; 649fd4f6f2aSNicolas Pitre } 650fd4f6f2aSNicolas Pitre } else { 651fd4f6f2aSNicolas Pitre /* 652fd4f6f2aSNicolas Pitre * The block pointer indicates one past the end of 653fd4f6f2aSNicolas Pitre * the current block (start of next block). If this 654fd4f6f2aSNicolas Pitre * is the first block then it starts where the block 655fd4f6f2aSNicolas Pitre * pointer table ends, otherwise its start comes 656fd4f6f2aSNicolas Pitre * from the previous block's pointer. 657fd4f6f2aSNicolas Pitre */ 658fd4f6f2aSNicolas Pitre block_start = OFFSET(inode) + maxblock * 4; 659fd4f6f2aSNicolas Pitre if (page->index) 660fd4f6f2aSNicolas Pitre block_start = *(u32 *) 661fd4f6f2aSNicolas Pitre cramfs_read(sb, blkptr_offset - 4, 4); 662fd4f6f2aSNicolas Pitre /* Beware... previous ptr might be a direct ptr */ 663fd4f6f2aSNicolas Pitre if (unlikely(block_start & CRAMFS_BLK_FLAG_DIRECT_PTR)) { 664fd4f6f2aSNicolas Pitre /* See comments on earlier code. */ 665fd4f6f2aSNicolas Pitre u32 prev_start = block_start; 666fd4f6f2aSNicolas Pitre block_start = prev_start & ~CRAMFS_BLK_FLAGS; 667fd4f6f2aSNicolas Pitre block_start <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; 668fd4f6f2aSNicolas Pitre if (prev_start & CRAMFS_BLK_FLAG_UNCOMPRESSED) { 669fd4f6f2aSNicolas Pitre block_start += PAGE_SIZE; 670fd4f6f2aSNicolas Pitre } else { 671fd4f6f2aSNicolas Pitre block_len = *(u16 *) 672fd4f6f2aSNicolas Pitre cramfs_read(sb, block_start, 2); 673fd4f6f2aSNicolas Pitre block_start += 2 + block_len; 674fd4f6f2aSNicolas Pitre } 675fd4f6f2aSNicolas Pitre } 676fd4f6f2aSNicolas Pitre block_start &= ~CRAMFS_BLK_FLAGS; 677fd4f6f2aSNicolas Pitre block_len = block_ptr - block_start; 678fd4f6f2aSNicolas Pitre } 679fd4f6f2aSNicolas Pitre 680fd4f6f2aSNicolas Pitre if (block_len == 0) 681fd4f6f2aSNicolas Pitre ; /* hole */ 682fd4f6f2aSNicolas Pitre else if (unlikely(block_len > 2*PAGE_SIZE || 683fd4f6f2aSNicolas Pitre (uncompressed && block_len > PAGE_SIZE))) { 684fd4f6f2aSNicolas Pitre mutex_unlock(&read_mutex); 685fd4f6f2aSNicolas Pitre pr_err("bad data blocksize %u\n", block_len); 686fd4f6f2aSNicolas Pitre goto err; 687fd4f6f2aSNicolas Pitre } else if (uncompressed) { 688fd4f6f2aSNicolas Pitre memcpy(pgdata, 689fd4f6f2aSNicolas Pitre cramfs_read(sb, block_start, block_len), 690fd4f6f2aSNicolas Pitre block_len); 691fd4f6f2aSNicolas Pitre bytes_filled = block_len; 692fd4f6f2aSNicolas Pitre } else { 6931da177e4SLinus Torvalds bytes_filled = cramfs_uncompress_block(pgdata, 69409cbfeafSKirill A. Shutemov PAGE_SIZE, 695fd4f6f2aSNicolas Pitre cramfs_read(sb, block_start, block_len), 696fd4f6f2aSNicolas Pitre block_len); 697fd4f6f2aSNicolas Pitre } 698353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 69998310e58SDavid VomLehn if (unlikely(bytes_filled < 0)) 70098310e58SDavid VomLehn goto err; 7011da177e4SLinus Torvalds } 70298310e58SDavid VomLehn 70309cbfeafSKirill A. Shutemov memset(pgdata + bytes_filled, 0, PAGE_SIZE - bytes_filled); 7041da177e4SLinus Torvalds flush_dcache_page(page); 70598310e58SDavid VomLehn kunmap(page); 7061da177e4SLinus Torvalds SetPageUptodate(page); 7071da177e4SLinus Torvalds unlock_page(page); 7081da177e4SLinus Torvalds return 0; 70998310e58SDavid VomLehn 71098310e58SDavid VomLehn err: 71198310e58SDavid VomLehn kunmap(page); 71298310e58SDavid VomLehn ClearPageUptodate(page); 71398310e58SDavid VomLehn SetPageError(page); 71498310e58SDavid VomLehn unlock_page(page); 71598310e58SDavid VomLehn return 0; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 718f5e54d6eSChristoph Hellwig static const struct address_space_operations cramfs_aops = { 7191da177e4SLinus Torvalds .readpage = cramfs_readpage 7201da177e4SLinus Torvalds }; 7211da177e4SLinus Torvalds 7221da177e4SLinus Torvalds /* 7231da177e4SLinus Torvalds * Our operations: 7241da177e4SLinus Torvalds */ 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds /* 7271da177e4SLinus Torvalds * A directory can only readdir 7281da177e4SLinus Torvalds */ 7294b6f5d20SArjan van de Ven static const struct file_operations cramfs_directory_operations = { 7301da177e4SLinus Torvalds .llseek = generic_file_llseek, 7311da177e4SLinus Torvalds .read = generic_read_dir, 732c51da20cSAl Viro .iterate_shared = cramfs_readdir, 7331da177e4SLinus Torvalds }; 7341da177e4SLinus Torvalds 735754661f1SArjan van de Ven static const struct inode_operations cramfs_dir_inode_operations = { 7361da177e4SLinus Torvalds .lookup = cramfs_lookup, 7371da177e4SLinus Torvalds }; 7381da177e4SLinus Torvalds 739ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations cramfs_ops = { 7401da177e4SLinus Torvalds .remount_fs = cramfs_remount, 7411da177e4SLinus Torvalds .statfs = cramfs_statfs, 7421da177e4SLinus Torvalds }; 7431da177e4SLinus Torvalds 74499c18ce5SNicolas Pitre static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags, 74599c18ce5SNicolas Pitre const char *dev_name, void *data) 7461da177e4SLinus Torvalds { 74799c18ce5SNicolas Pitre struct dentry *ret = ERR_PTR(-ENOPROTOOPT); 74899c18ce5SNicolas Pitre 74999c18ce5SNicolas Pitre if (IS_ENABLED(CONFIG_CRAMFS_MTD)) { 75099c18ce5SNicolas Pitre ret = mount_mtd(fs_type, flags, dev_name, data, 75199c18ce5SNicolas Pitre cramfs_mtd_fill_super); 75299c18ce5SNicolas Pitre if (!IS_ERR(ret)) 75399c18ce5SNicolas Pitre return ret; 75499c18ce5SNicolas Pitre } 75599c18ce5SNicolas Pitre if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) { 75699c18ce5SNicolas Pitre ret = mount_bdev(fs_type, flags, dev_name, data, 75799c18ce5SNicolas Pitre cramfs_blkdev_fill_super); 75899c18ce5SNicolas Pitre } 75999c18ce5SNicolas Pitre return ret; 7601da177e4SLinus Torvalds } 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds static struct file_system_type cramfs_fs_type = { 7631da177e4SLinus Torvalds .owner = THIS_MODULE, 7641da177e4SLinus Torvalds .name = "cramfs", 765152a0836SAl Viro .mount = cramfs_mount, 7662309fb8eSAl Viro .kill_sb = cramfs_kill_sb, 7671da177e4SLinus Torvalds .fs_flags = FS_REQUIRES_DEV, 7681da177e4SLinus Torvalds }; 7697f78e035SEric W. Biederman MODULE_ALIAS_FS("cramfs"); 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds static int __init init_cramfs_fs(void) 7721da177e4SLinus Torvalds { 77350d44ed0SAlexey Dobriyan int rv; 77450d44ed0SAlexey Dobriyan 77550d44ed0SAlexey Dobriyan rv = cramfs_uncompress_init(); 77650d44ed0SAlexey Dobriyan if (rv < 0) 77750d44ed0SAlexey Dobriyan return rv; 77850d44ed0SAlexey Dobriyan rv = register_filesystem(&cramfs_fs_type); 77950d44ed0SAlexey Dobriyan if (rv < 0) 78050d44ed0SAlexey Dobriyan cramfs_uncompress_exit(); 78150d44ed0SAlexey Dobriyan return rv; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds static void __exit exit_cramfs_fs(void) 7851da177e4SLinus Torvalds { 7861da177e4SLinus Torvalds cramfs_uncompress_exit(); 7871da177e4SLinus Torvalds unregister_filesystem(&cramfs_fs_type); 7881da177e4SLinus Torvalds } 7891da177e4SLinus Torvalds 7901da177e4SLinus Torvalds module_init(init_cramfs_fs) 7911da177e4SLinus Torvalds module_exit(exit_cramfs_fs) 7921da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 793