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> 221da177e4SLinus Torvalds #include <linux/slab.h> 231da177e4SLinus Torvalds #include <linux/vfs.h> 24353ab6e9SIngo Molnar #include <linux/mutex.h> 25f7f4f4ddSAl Viro #include <uapi/linux/cramfs_fs.h> 261da177e4SLinus Torvalds #include <asm/uaccess.h> 271da177e4SLinus Torvalds 28f7f4f4ddSAl Viro #include "internal.h" 29f7f4f4ddSAl Viro 30f7f4f4ddSAl Viro /* 31f7f4f4ddSAl Viro * cramfs super-block data in memory 32f7f4f4ddSAl Viro */ 33f7f4f4ddSAl Viro struct cramfs_sb_info { 34f7f4f4ddSAl Viro unsigned long magic; 35f7f4f4ddSAl Viro unsigned long size; 36f7f4f4ddSAl Viro unsigned long blocks; 37f7f4f4ddSAl Viro unsigned long files; 38f7f4f4ddSAl Viro unsigned long flags; 39f7f4f4ddSAl Viro }; 40f7f4f4ddSAl Viro 41f7f4f4ddSAl Viro static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) 42f7f4f4ddSAl Viro { 43f7f4f4ddSAl Viro return sb->s_fs_info; 44f7f4f4ddSAl Viro } 45f7f4f4ddSAl Viro 46ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations cramfs_ops; 47754661f1SArjan van de Ven static const struct inode_operations cramfs_dir_inode_operations; 484b6f5d20SArjan van de Ven static const struct file_operations cramfs_directory_operations; 49f5e54d6eSChristoph Hellwig static const struct address_space_operations cramfs_aops; 501da177e4SLinus Torvalds 51353ab6e9SIngo Molnar static DEFINE_MUTEX(read_mutex); 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds 546f772fe6SStefani Seibold /* These macros may change in future, to provide better st_ino semantics. */ 551da177e4SLinus Torvalds #define OFFSET(x) ((x)->i_ino) 561da177e4SLinus Torvalds 570577d1baSAl Viro static unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset) 581da177e4SLinus Torvalds { 596f772fe6SStefani Seibold if (!cino->offset) 606f772fe6SStefani Seibold return offset + 1; 616f772fe6SStefani Seibold if (!cino->size) 626f772fe6SStefani Seibold return offset + 1; 636f772fe6SStefani Seibold 646f772fe6SStefani Seibold /* 656f772fe6SStefani Seibold * The file mode test fixes buggy mkcramfs implementations where 666f772fe6SStefani Seibold * cramfs_inode->offset is set to a non zero value for entries 676f772fe6SStefani Seibold * which did not contain data, like devices node and fifos. 686f772fe6SStefani Seibold */ 696f772fe6SStefani Seibold switch (cino->mode & S_IFMT) { 706f772fe6SStefani Seibold case S_IFREG: 716f772fe6SStefani Seibold case S_IFDIR: 726f772fe6SStefani Seibold case S_IFLNK: 736f772fe6SStefani Seibold return cino->offset << 2; 746f772fe6SStefani Seibold default: 756f772fe6SStefani Seibold break; 766f772fe6SStefani Seibold } 776f772fe6SStefani Seibold return offset + 1; 786f772fe6SStefani Seibold } 796f772fe6SStefani Seibold 806f772fe6SStefani Seibold static struct inode *get_cramfs_inode(struct super_block *sb, 810577d1baSAl Viro const struct cramfs_inode *cramfs_inode, unsigned int offset) 826f772fe6SStefani Seibold { 836f772fe6SStefani Seibold struct inode *inode; 8482d63fc9SAl Viro static struct timespec zerotime; 856f772fe6SStefani Seibold 866f772fe6SStefani Seibold inode = iget_locked(sb, cramino(cramfs_inode, offset)); 876f772fe6SStefani Seibold if (!inode) 886f772fe6SStefani Seibold return ERR_PTR(-ENOMEM); 896f772fe6SStefani Seibold if (!(inode->i_state & I_NEW)) 906f772fe6SStefani Seibold return inode; 916f772fe6SStefani Seibold 926f772fe6SStefani Seibold switch (cramfs_inode->mode & S_IFMT) { 936f772fe6SStefani Seibold case S_IFREG: 946f772fe6SStefani Seibold inode->i_fop = &generic_ro_fops; 956f772fe6SStefani Seibold inode->i_data.a_ops = &cramfs_aops; 966f772fe6SStefani Seibold break; 976f772fe6SStefani Seibold case S_IFDIR: 986f772fe6SStefani Seibold inode->i_op = &cramfs_dir_inode_operations; 996f772fe6SStefani Seibold inode->i_fop = &cramfs_directory_operations; 1006f772fe6SStefani Seibold break; 1016f772fe6SStefani Seibold case S_IFLNK: 1026f772fe6SStefani Seibold inode->i_op = &page_symlink_inode_operations; 1036f772fe6SStefani Seibold inode->i_data.a_ops = &cramfs_aops; 1046f772fe6SStefani Seibold break; 1056f772fe6SStefani Seibold default: 1066f772fe6SStefani Seibold init_special_inode(inode, cramfs_inode->mode, 1076f772fe6SStefani Seibold old_decode_dev(cramfs_inode->size)); 1086f772fe6SStefani Seibold } 1096f772fe6SStefani Seibold 1101da177e4SLinus Torvalds inode->i_mode = cramfs_inode->mode; 111a7d9cfe9SEric W. Biederman i_uid_write(inode, cramfs_inode->uid); 112a7d9cfe9SEric W. Biederman i_gid_write(inode, cramfs_inode->gid); 1136f772fe6SStefani Seibold 1146f772fe6SStefani Seibold /* if the lower 2 bits are zero, the inode contains data */ 1156f772fe6SStefani Seibold if (!(inode->i_ino & 3)) { 1161da177e4SLinus Torvalds inode->i_size = cramfs_inode->size; 1171da177e4SLinus Torvalds inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; 1186f772fe6SStefani Seibold } 1196f772fe6SStefani Seibold 1201da177e4SLinus Torvalds /* Struct copy intentional */ 1211da177e4SLinus Torvalds inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; 1221da177e4SLinus Torvalds /* inode->i_nlink is left 1 - arguably wrong for directories, 1231da177e4SLinus Torvalds but it's the best we can do without reading the directory 1241da177e4SLinus Torvalds contents. 1 yields the right result in GNU find, even 1251da177e4SLinus Torvalds without -noleaf option. */ 1261da177e4SLinus Torvalds 12777b8a75fSAl Viro unlock_new_inode(inode); 1286f772fe6SStefani Seibold 12977b8a75fSAl Viro return inode; 13082d63fc9SAl Viro } 13182d63fc9SAl Viro 1321da177e4SLinus Torvalds /* 1331da177e4SLinus Torvalds * We have our own block cache: don't fill up the buffer cache 1341da177e4SLinus Torvalds * with the rom-image, because the way the filesystem is set 1351da177e4SLinus Torvalds * up the accesses should be fairly regular and cached in the 1361da177e4SLinus Torvalds * page cache and dentry tree anyway.. 1371da177e4SLinus Torvalds * 1381da177e4SLinus Torvalds * This also acts as a way to guarantee contiguous areas of up to 1391da177e4SLinus Torvalds * BLKS_PER_BUF*PAGE_CACHE_SIZE, so that the caller doesn't need to 1401da177e4SLinus Torvalds * worry about end-of-buffer issues even when decompressing a full 1411da177e4SLinus Torvalds * page cache. 1421da177e4SLinus Torvalds */ 1431da177e4SLinus Torvalds #define READ_BUFFERS (2) 1441da177e4SLinus Torvalds /* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ 1451da177e4SLinus Torvalds #define NEXT_BUFFER(_ix) ((_ix) ^ 1) 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds /* 1481da177e4SLinus Torvalds * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" 1491da177e4SLinus Torvalds * data that takes up more space than the original and with unlucky 1501da177e4SLinus Torvalds * alignment. 1511da177e4SLinus Torvalds */ 1521da177e4SLinus Torvalds #define BLKS_PER_BUF_SHIFT (2) 1531da177e4SLinus Torvalds #define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) 1541da177e4SLinus Torvalds #define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE) 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; 1571da177e4SLinus Torvalds static unsigned buffer_blocknr[READ_BUFFERS]; 1581da177e4SLinus Torvalds static struct super_block * buffer_dev[READ_BUFFERS]; 1591da177e4SLinus Torvalds static int next_buffer; 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* 1621da177e4SLinus Torvalds * Returns a pointer to a buffer containing at least LEN bytes of 1631da177e4SLinus Torvalds * filesystem starting at byte offset OFFSET into the filesystem. 1641da177e4SLinus Torvalds */ 1651da177e4SLinus Torvalds static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; 1681da177e4SLinus Torvalds struct page *pages[BLKS_PER_BUF]; 1696bbfb077SAndi Drebes unsigned i, blocknr, buffer; 1701da177e4SLinus Torvalds unsigned long devsize; 1711da177e4SLinus Torvalds char *data; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds if (!len) 1741da177e4SLinus Torvalds return NULL; 1751da177e4SLinus Torvalds blocknr = offset >> PAGE_CACHE_SHIFT; 1761da177e4SLinus Torvalds offset &= PAGE_CACHE_SIZE - 1; 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* Check if an existing buffer already has the data.. */ 1791da177e4SLinus Torvalds for (i = 0; i < READ_BUFFERS; i++) { 1801da177e4SLinus Torvalds unsigned int blk_offset; 1811da177e4SLinus Torvalds 1821da177e4SLinus Torvalds if (buffer_dev[i] != sb) 1831da177e4SLinus Torvalds continue; 1841da177e4SLinus Torvalds if (blocknr < buffer_blocknr[i]) 1851da177e4SLinus Torvalds continue; 1861da177e4SLinus Torvalds blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT; 1871da177e4SLinus Torvalds blk_offset += offset; 1881da177e4SLinus Torvalds if (blk_offset + len > BUFFER_SIZE) 1891da177e4SLinus Torvalds continue; 1901da177e4SLinus Torvalds return read_buffers[i] + blk_offset; 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds devsize = mapping->host->i_size >> PAGE_CACHE_SHIFT; 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds /* Ok, read in BLKS_PER_BUF pages completely first. */ 1961da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 1971da177e4SLinus Torvalds struct page *page = NULL; 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds if (blocknr + i < devsize) { 20067f9fd91SSasha Levin page = read_mapping_page(mapping, blocknr + i, NULL); 2011da177e4SLinus Torvalds /* synchronous error? */ 2021da177e4SLinus Torvalds if (IS_ERR(page)) 2031da177e4SLinus Torvalds page = NULL; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds pages[i] = page; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 2091da177e4SLinus Torvalds struct page *page = pages[i]; 2101da177e4SLinus Torvalds if (page) { 2111da177e4SLinus Torvalds wait_on_page_locked(page); 2121da177e4SLinus Torvalds if (!PageUptodate(page)) { 2131da177e4SLinus Torvalds /* asynchronous error */ 2141da177e4SLinus Torvalds page_cache_release(page); 2151da177e4SLinus Torvalds pages[i] = NULL; 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds buffer = next_buffer; 2211da177e4SLinus Torvalds next_buffer = NEXT_BUFFER(buffer); 2221da177e4SLinus Torvalds buffer_blocknr[buffer] = blocknr; 2231da177e4SLinus Torvalds buffer_dev[buffer] = sb; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds data = read_buffers[buffer]; 2261da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 2271da177e4SLinus Torvalds struct page *page = pages[i]; 2281da177e4SLinus Torvalds if (page) { 2291da177e4SLinus Torvalds memcpy(data, kmap(page), PAGE_CACHE_SIZE); 2301da177e4SLinus Torvalds kunmap(page); 2311da177e4SLinus Torvalds page_cache_release(page); 2321da177e4SLinus Torvalds } else 2331da177e4SLinus Torvalds memset(data, 0, PAGE_CACHE_SIZE); 2341da177e4SLinus Torvalds data += PAGE_CACHE_SIZE; 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds return read_buffers[buffer] + offset; 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds 2392309fb8eSAl Viro static void cramfs_kill_sb(struct super_block *sb) 2401da177e4SLinus Torvalds { 241f7f4f4ddSAl Viro struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 2422309fb8eSAl Viro kill_block_super(sb); 2432309fb8eSAl Viro kfree(sbi); 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds static int cramfs_remount(struct super_block *sb, int *flags, char *data) 2471da177e4SLinus Torvalds { 24802b9984dSTheodore Ts'o sync_filesystem(sb); 2491da177e4SLinus Torvalds *flags |= MS_RDONLY; 2501da177e4SLinus Torvalds return 0; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds static int cramfs_fill_super(struct super_block *sb, void *data, int silent) 2541da177e4SLinus Torvalds { 2551da177e4SLinus Torvalds int i; 2561da177e4SLinus Torvalds struct cramfs_super super; 2571da177e4SLinus Torvalds unsigned long root_offset; 2581da177e4SLinus Torvalds struct cramfs_sb_info *sbi; 2591da177e4SLinus Torvalds struct inode *root; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds sb->s_flags |= MS_RDONLY; 2621da177e4SLinus Torvalds 263f8314dc6SPanagiotis Issaris sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); 2641da177e4SLinus Torvalds if (!sbi) 2651da177e4SLinus Torvalds return -ENOMEM; 2661da177e4SLinus Torvalds sb->s_fs_info = sbi; 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds /* Invalidate the read buffers on mount: think disk change.. */ 269353ab6e9SIngo Molnar mutex_lock(&read_mutex); 2701da177e4SLinus Torvalds for (i = 0; i < READ_BUFFERS; i++) 2711da177e4SLinus Torvalds buffer_blocknr[i] = -1; 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds /* Read the first block and get the superblock from it */ 2741da177e4SLinus Torvalds memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super)); 275353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds /* Do sanity checks on the superblock */ 2781da177e4SLinus Torvalds if (super.magic != CRAMFS_MAGIC) { 2790cc785ecSMasanari Iida /* check for wrong endianness */ 280ac8d35c5SAndi Drebes if (super.magic == CRAMFS_MAGIC_WEND) { 281ac8d35c5SAndi Drebes if (!silent) 2824f21e1eaSFabian Frederick pr_err("wrong endianness\n"); 2832309fb8eSAl Viro return -EINVAL; 284ac8d35c5SAndi Drebes } 285ac8d35c5SAndi Drebes 2861da177e4SLinus Torvalds /* check at 512 byte offset */ 287353ab6e9SIngo Molnar mutex_lock(&read_mutex); 2881da177e4SLinus Torvalds memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super)); 289353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 2901da177e4SLinus Torvalds if (super.magic != CRAMFS_MAGIC) { 291ac8d35c5SAndi Drebes if (super.magic == CRAMFS_MAGIC_WEND && !silent) 2924f21e1eaSFabian Frederick pr_err("wrong endianness\n"); 293ac8d35c5SAndi Drebes else if (!silent) 2944f21e1eaSFabian Frederick pr_err("wrong magic\n"); 2952309fb8eSAl Viro return -EINVAL; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds /* get feature flags first */ 3001da177e4SLinus Torvalds if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { 3014f21e1eaSFabian Frederick pr_err("unsupported filesystem features\n"); 3022309fb8eSAl Viro return -EINVAL; 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds /* Check that the root inode is in a sane state */ 3061da177e4SLinus Torvalds if (!S_ISDIR(super.root.mode)) { 3074f21e1eaSFabian Frederick pr_err("root is not a directory\n"); 3082309fb8eSAl Viro return -EINVAL; 3091da177e4SLinus Torvalds } 3106f772fe6SStefani Seibold /* correct strange, hard-coded permissions of mkcramfs */ 3116f772fe6SStefani Seibold super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 3126f772fe6SStefani Seibold 3131da177e4SLinus Torvalds root_offset = super.root.offset << 2; 3141da177e4SLinus Torvalds if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { 3151da177e4SLinus Torvalds sbi->size=super.size; 3161da177e4SLinus Torvalds sbi->blocks=super.fsid.blocks; 3171da177e4SLinus Torvalds sbi->files=super.fsid.files; 3181da177e4SLinus Torvalds } else { 3191da177e4SLinus Torvalds sbi->size=1<<28; 3201da177e4SLinus Torvalds sbi->blocks=0; 3211da177e4SLinus Torvalds sbi->files=0; 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds sbi->magic=super.magic; 3241da177e4SLinus Torvalds sbi->flags=super.flags; 3251da177e4SLinus Torvalds if (root_offset == 0) 3264f21e1eaSFabian Frederick pr_info("empty filesystem"); 3271da177e4SLinus Torvalds else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 3281da177e4SLinus Torvalds ((root_offset != sizeof(struct cramfs_super)) && 3291da177e4SLinus Torvalds (root_offset != 512 + sizeof(struct cramfs_super)))) 3301da177e4SLinus Torvalds { 3314f21e1eaSFabian Frederick pr_err("bad root offset %lu\n", root_offset); 3322309fb8eSAl Viro return -EINVAL; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds /* Set it all up.. */ 3361da177e4SLinus Torvalds sb->s_op = &cramfs_ops; 3376f772fe6SStefani Seibold root = get_cramfs_inode(sb, &super.root, 0); 3380577d1baSAl Viro if (IS_ERR(root)) 3392309fb8eSAl Viro return PTR_ERR(root); 34048fde701SAl Viro sb->s_root = d_make_root(root); 34148fde701SAl Viro if (!sb->s_root) 3422309fb8eSAl Viro return -ENOMEM; 3431da177e4SLinus Torvalds return 0; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 346726c3342SDavid Howells static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) 3471da177e4SLinus Torvalds { 348726c3342SDavid Howells struct super_block *sb = dentry->d_sb; 34994ea77acSColy Li u64 id = huge_encode_dev(sb->s_bdev->bd_dev); 350726c3342SDavid Howells 3511da177e4SLinus Torvalds buf->f_type = CRAMFS_MAGIC; 3521da177e4SLinus Torvalds buf->f_bsize = PAGE_CACHE_SIZE; 3531da177e4SLinus Torvalds buf->f_blocks = CRAMFS_SB(sb)->blocks; 3541da177e4SLinus Torvalds buf->f_bfree = 0; 3551da177e4SLinus Torvalds buf->f_bavail = 0; 3561da177e4SLinus Torvalds buf->f_files = CRAMFS_SB(sb)->files; 3571da177e4SLinus Torvalds buf->f_ffree = 0; 35894ea77acSColy Li buf->f_fsid.val[0] = (u32)id; 35994ea77acSColy Li buf->f_fsid.val[1] = (u32)(id >> 32); 3601da177e4SLinus Torvalds buf->f_namelen = CRAMFS_MAXPATHLEN; 3611da177e4SLinus Torvalds return 0; 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds /* 3651da177e4SLinus Torvalds * Read a cramfs directory entry. 3661da177e4SLinus Torvalds */ 3676f7f231eSAl Viro static int cramfs_readdir(struct file *file, struct dir_context *ctx) 3681da177e4SLinus Torvalds { 3696f7f231eSAl Viro struct inode *inode = file_inode(file); 3701da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 3711da177e4SLinus Torvalds char *buf; 3721da177e4SLinus Torvalds unsigned int offset; 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds /* Offset within the thing. */ 3756f7f231eSAl Viro if (ctx->pos >= inode->i_size) 3761da177e4SLinus Torvalds return 0; 3776f7f231eSAl Viro offset = ctx->pos; 3781da177e4SLinus Torvalds /* Directory entries are always 4-byte aligned */ 3791da177e4SLinus Torvalds if (offset & 3) 3801da177e4SLinus Torvalds return -EINVAL; 3811da177e4SLinus Torvalds 3824176ed59SAndi Drebes buf = kmalloc(CRAMFS_MAXPATHLEN, GFP_KERNEL); 3831da177e4SLinus Torvalds if (!buf) 3841da177e4SLinus Torvalds return -ENOMEM; 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds while (offset < inode->i_size) { 3871da177e4SLinus Torvalds struct cramfs_inode *de; 3881da177e4SLinus Torvalds unsigned long nextoffset; 3891da177e4SLinus Torvalds char *name; 3901da177e4SLinus Torvalds ino_t ino; 391175a4eb7SAl Viro umode_t mode; 3926f7f231eSAl Viro int namelen; 3931da177e4SLinus Torvalds 394353ab6e9SIngo Molnar mutex_lock(&read_mutex); 3954176ed59SAndi Drebes de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); 3961da177e4SLinus Torvalds name = (char *)(de+1); 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds /* 3991da177e4SLinus Torvalds * Namelengths on disk are shifted by two 4001da177e4SLinus Torvalds * and the name padded out to 4-byte boundaries 4011da177e4SLinus Torvalds * with zeroes. 4021da177e4SLinus Torvalds */ 4031da177e4SLinus Torvalds namelen = de->namelen << 2; 4041da177e4SLinus Torvalds memcpy(buf, name, namelen); 4056f772fe6SStefani Seibold ino = cramino(de, OFFSET(inode) + offset); 4061da177e4SLinus Torvalds mode = de->mode; 407353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4081da177e4SLinus Torvalds nextoffset = offset + sizeof(*de) + namelen; 4091da177e4SLinus Torvalds for (;;) { 4101da177e4SLinus Torvalds if (!namelen) { 4111da177e4SLinus Torvalds kfree(buf); 4121da177e4SLinus Torvalds return -EIO; 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds if (buf[namelen-1]) 4151da177e4SLinus Torvalds break; 4161da177e4SLinus Torvalds namelen--; 4171da177e4SLinus Torvalds } 4186f7f231eSAl Viro if (!dir_emit(ctx, buf, namelen, ino, mode >> 12)) 4191da177e4SLinus Torvalds break; 4201da177e4SLinus Torvalds 4216f7f231eSAl Viro ctx->pos = offset = nextoffset; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds kfree(buf); 4241da177e4SLinus Torvalds return 0; 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds /* 4281da177e4SLinus Torvalds * Lookup and fill in the inode data.. 4291da177e4SLinus Torvalds */ 43000cd8dd3SAl Viro static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 4311da177e4SLinus Torvalds { 4321da177e4SLinus Torvalds unsigned int offset = 0; 4330577d1baSAl Viro struct inode *inode = NULL; 4341da177e4SLinus Torvalds int sorted; 4351da177e4SLinus Torvalds 436353ab6e9SIngo Molnar mutex_lock(&read_mutex); 4371da177e4SLinus Torvalds sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS; 4381da177e4SLinus Torvalds while (offset < dir->i_size) { 4391da177e4SLinus Torvalds struct cramfs_inode *de; 4401da177e4SLinus Torvalds char *name; 4411da177e4SLinus Torvalds int namelen, retval; 4426f772fe6SStefani Seibold int dir_off = OFFSET(dir) + offset; 4431da177e4SLinus Torvalds 4446f772fe6SStefani Seibold de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN); 4451da177e4SLinus Torvalds name = (char *)(de+1); 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds /* Try to take advantage of sorted directories */ 4481da177e4SLinus Torvalds if (sorted && (dentry->d_name.name[0] < name[0])) 4491da177e4SLinus Torvalds break; 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds namelen = de->namelen << 2; 4521da177e4SLinus Torvalds offset += sizeof(*de) + namelen; 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds /* Quick check that the name is roughly the right length */ 4551da177e4SLinus Torvalds if (((dentry->d_name.len + 3) & ~3) != namelen) 4561da177e4SLinus Torvalds continue; 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds for (;;) { 4591da177e4SLinus Torvalds if (!namelen) { 4600577d1baSAl Viro inode = ERR_PTR(-EIO); 4610577d1baSAl Viro goto out; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds if (name[namelen-1]) 4641da177e4SLinus Torvalds break; 4651da177e4SLinus Torvalds namelen--; 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds if (namelen != dentry->d_name.len) 4681da177e4SLinus Torvalds continue; 4691da177e4SLinus Torvalds retval = memcmp(dentry->d_name.name, name, namelen); 4701da177e4SLinus Torvalds if (retval > 0) 4711da177e4SLinus Torvalds continue; 4721da177e4SLinus Torvalds if (!retval) { 4730577d1baSAl Viro inode = get_cramfs_inode(dir->i_sb, de, dir_off); 4740577d1baSAl Viro break; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds /* else (retval < 0) */ 4771da177e4SLinus Torvalds if (sorted) 4781da177e4SLinus Torvalds break; 4791da177e4SLinus Torvalds } 4800577d1baSAl Viro out: 481353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4820577d1baSAl Viro if (IS_ERR(inode)) 4830577d1baSAl Viro return ERR_CAST(inode); 4840577d1baSAl Viro d_add(dentry, inode); 4851da177e4SLinus Torvalds return NULL; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds static int cramfs_readpage(struct file *file, struct page * page) 4891da177e4SLinus Torvalds { 4901da177e4SLinus Torvalds struct inode *inode = page->mapping->host; 49198310e58SDavid VomLehn u32 maxblock; 49298310e58SDavid VomLehn int bytes_filled; 4931da177e4SLinus Torvalds void *pgdata; 4941da177e4SLinus Torvalds 4951da177e4SLinus Torvalds maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 4961da177e4SLinus Torvalds bytes_filled = 0; 49798310e58SDavid VomLehn pgdata = kmap(page); 49898310e58SDavid VomLehn 4991da177e4SLinus Torvalds if (page->index < maxblock) { 5001da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 5011da177e4SLinus Torvalds u32 blkptr_offset = OFFSET(inode) + page->index*4; 5021da177e4SLinus Torvalds u32 start_offset, compr_len; 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds start_offset = OFFSET(inode) + maxblock*4; 505353ab6e9SIngo Molnar mutex_lock(&read_mutex); 5061da177e4SLinus Torvalds if (page->index) 50798310e58SDavid VomLehn start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 50898310e58SDavid VomLehn 4); 50998310e58SDavid VomLehn compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - 51098310e58SDavid VomLehn start_offset); 511353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 51298310e58SDavid VomLehn 5131da177e4SLinus Torvalds if (compr_len == 0) 5141da177e4SLinus Torvalds ; /* hole */ 51598310e58SDavid VomLehn else if (unlikely(compr_len > (PAGE_CACHE_SIZE << 1))) { 5164f21e1eaSFabian Frederick pr_err("bad compressed blocksize %u\n", 51798310e58SDavid VomLehn compr_len); 51898310e58SDavid VomLehn goto err; 51998310e58SDavid VomLehn } else { 520353ab6e9SIngo Molnar mutex_lock(&read_mutex); 5211da177e4SLinus Torvalds bytes_filled = cramfs_uncompress_block(pgdata, 5221da177e4SLinus Torvalds PAGE_CACHE_SIZE, 5231da177e4SLinus Torvalds cramfs_read(sb, start_offset, compr_len), 5241da177e4SLinus Torvalds compr_len); 525353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 52698310e58SDavid VomLehn if (unlikely(bytes_filled < 0)) 52798310e58SDavid VomLehn goto err; 5281da177e4SLinus Torvalds } 52998310e58SDavid VomLehn } 53098310e58SDavid VomLehn 5311da177e4SLinus Torvalds memset(pgdata + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled); 5321da177e4SLinus Torvalds flush_dcache_page(page); 53398310e58SDavid VomLehn kunmap(page); 5341da177e4SLinus Torvalds SetPageUptodate(page); 5351da177e4SLinus Torvalds unlock_page(page); 5361da177e4SLinus Torvalds return 0; 53798310e58SDavid VomLehn 53898310e58SDavid VomLehn err: 53998310e58SDavid VomLehn kunmap(page); 54098310e58SDavid VomLehn ClearPageUptodate(page); 54198310e58SDavid VomLehn SetPageError(page); 54298310e58SDavid VomLehn unlock_page(page); 54398310e58SDavid VomLehn return 0; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 546f5e54d6eSChristoph Hellwig static const struct address_space_operations cramfs_aops = { 5471da177e4SLinus Torvalds .readpage = cramfs_readpage 5481da177e4SLinus Torvalds }; 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds /* 5511da177e4SLinus Torvalds * Our operations: 5521da177e4SLinus Torvalds */ 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds /* 5551da177e4SLinus Torvalds * A directory can only readdir 5561da177e4SLinus Torvalds */ 5574b6f5d20SArjan van de Ven static const struct file_operations cramfs_directory_operations = { 5581da177e4SLinus Torvalds .llseek = generic_file_llseek, 5591da177e4SLinus Torvalds .read = generic_read_dir, 5606f7f231eSAl Viro .iterate = cramfs_readdir, 5611da177e4SLinus Torvalds }; 5621da177e4SLinus Torvalds 563754661f1SArjan van de Ven static const struct inode_operations cramfs_dir_inode_operations = { 5641da177e4SLinus Torvalds .lookup = cramfs_lookup, 5651da177e4SLinus Torvalds }; 5661da177e4SLinus Torvalds 567ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations cramfs_ops = { 5681da177e4SLinus Torvalds .remount_fs = cramfs_remount, 5691da177e4SLinus Torvalds .statfs = cramfs_statfs, 5701da177e4SLinus Torvalds }; 5711da177e4SLinus Torvalds 572152a0836SAl Viro static struct dentry *cramfs_mount(struct file_system_type *fs_type, 573152a0836SAl Viro int flags, const char *dev_name, void *data) 5741da177e4SLinus Torvalds { 575152a0836SAl Viro return mount_bdev(fs_type, flags, dev_name, data, cramfs_fill_super); 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds static struct file_system_type cramfs_fs_type = { 5791da177e4SLinus Torvalds .owner = THIS_MODULE, 5801da177e4SLinus Torvalds .name = "cramfs", 581152a0836SAl Viro .mount = cramfs_mount, 5822309fb8eSAl Viro .kill_sb = cramfs_kill_sb, 5831da177e4SLinus Torvalds .fs_flags = FS_REQUIRES_DEV, 5841da177e4SLinus Torvalds }; 5857f78e035SEric W. Biederman MODULE_ALIAS_FS("cramfs"); 5861da177e4SLinus Torvalds 5871da177e4SLinus Torvalds static int __init init_cramfs_fs(void) 5881da177e4SLinus Torvalds { 58950d44ed0SAlexey Dobriyan int rv; 59050d44ed0SAlexey Dobriyan 59150d44ed0SAlexey Dobriyan rv = cramfs_uncompress_init(); 59250d44ed0SAlexey Dobriyan if (rv < 0) 59350d44ed0SAlexey Dobriyan return rv; 59450d44ed0SAlexey Dobriyan rv = register_filesystem(&cramfs_fs_type); 59550d44ed0SAlexey Dobriyan if (rv < 0) 59650d44ed0SAlexey Dobriyan cramfs_uncompress_exit(); 59750d44ed0SAlexey Dobriyan return rv; 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds static void __exit exit_cramfs_fs(void) 6011da177e4SLinus Torvalds { 6021da177e4SLinus Torvalds cramfs_uncompress_exit(); 6031da177e4SLinus Torvalds unregister_filesystem(&cramfs_fs_type); 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds module_init(init_cramfs_fs) 6071da177e4SLinus Torvalds module_exit(exit_cramfs_fs) 6081da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 609