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 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/fs.h> 161da177e4SLinus Torvalds #include <linux/pagemap.h> 171da177e4SLinus Torvalds #include <linux/init.h> 181da177e4SLinus Torvalds #include <linux/string.h> 191da177e4SLinus Torvalds #include <linux/blkdev.h> 201da177e4SLinus Torvalds #include <linux/cramfs_fs.h> 211da177e4SLinus Torvalds #include <linux/slab.h> 221da177e4SLinus Torvalds #include <linux/cramfs_fs_sb.h> 231da177e4SLinus Torvalds #include <linux/buffer_head.h> 241da177e4SLinus Torvalds #include <linux/vfs.h> 25353ab6e9SIngo Molnar #include <linux/mutex.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds #include <asm/uaccess.h> 281da177e4SLinus Torvalds 29ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations cramfs_ops; 30754661f1SArjan van de Ven static const struct inode_operations cramfs_dir_inode_operations; 314b6f5d20SArjan van de Ven static const struct file_operations cramfs_directory_operations; 32f5e54d6eSChristoph Hellwig static const struct address_space_operations cramfs_aops; 331da177e4SLinus Torvalds 34353ab6e9SIngo Molnar static DEFINE_MUTEX(read_mutex); 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds /* These two macros may change in future, to provide better st_ino 381da177e4SLinus Torvalds semantics. */ 39ff3aea0eSDave Johnson #define CRAMINO(x) (((x)->offset && (x)->size)?(x)->offset<<2:1) 401da177e4SLinus Torvalds #define OFFSET(x) ((x)->i_ino) 411da177e4SLinus Torvalds 42a97c9bf3SDave Johnson 43a97c9bf3SDave Johnson static int cramfs_iget5_test(struct inode *inode, void *opaque) 441da177e4SLinus Torvalds { 45a97c9bf3SDave Johnson struct cramfs_inode *cramfs_inode = opaque; 4682d63fc9SAl Viro return inode->i_ino == CRAMINO(cramfs_inode) && inode->i_ino != 1; 47a97c9bf3SDave Johnson } 48a97c9bf3SDave Johnson 49a97c9bf3SDave Johnson static int cramfs_iget5_set(struct inode *inode, void *opaque) 50a97c9bf3SDave Johnson { 51ff3aea0eSDave Johnson struct cramfs_inode *cramfs_inode = opaque; 5282d63fc9SAl Viro inode->i_ino = CRAMINO(cramfs_inode); 5382d63fc9SAl Viro return 0; 5482d63fc9SAl Viro } 5582d63fc9SAl Viro 5682d63fc9SAl Viro static struct inode *get_cramfs_inode(struct super_block *sb, 5782d63fc9SAl Viro struct cramfs_inode * cramfs_inode) 5882d63fc9SAl Viro { 5982d63fc9SAl Viro struct inode *inode = iget5_locked(sb, CRAMINO(cramfs_inode), 6082d63fc9SAl Viro cramfs_iget5_test, cramfs_iget5_set, 6182d63fc9SAl Viro cramfs_inode); 6282d63fc9SAl Viro static struct timespec zerotime; 6382d63fc9SAl Viro 6482d63fc9SAl Viro if (inode && (inode->i_state & I_NEW)) { 651da177e4SLinus Torvalds inode->i_mode = cramfs_inode->mode; 661da177e4SLinus Torvalds inode->i_uid = cramfs_inode->uid; 671da177e4SLinus Torvalds inode->i_size = cramfs_inode->size; 681da177e4SLinus Torvalds inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; 691da177e4SLinus Torvalds inode->i_gid = cramfs_inode->gid; 701da177e4SLinus Torvalds /* Struct copy intentional */ 711da177e4SLinus Torvalds inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; 721da177e4SLinus Torvalds /* inode->i_nlink is left 1 - arguably wrong for directories, 731da177e4SLinus Torvalds but it's the best we can do without reading the directory 741da177e4SLinus Torvalds contents. 1 yields the right result in GNU find, even 751da177e4SLinus Torvalds without -noleaf option. */ 761da177e4SLinus Torvalds if (S_ISREG(inode->i_mode)) { 771da177e4SLinus Torvalds inode->i_fop = &generic_ro_fops; 781da177e4SLinus Torvalds inode->i_data.a_ops = &cramfs_aops; 791da177e4SLinus Torvalds } else if (S_ISDIR(inode->i_mode)) { 801da177e4SLinus Torvalds inode->i_op = &cramfs_dir_inode_operations; 811da177e4SLinus Torvalds inode->i_fop = &cramfs_directory_operations; 821da177e4SLinus Torvalds } else if (S_ISLNK(inode->i_mode)) { 831da177e4SLinus Torvalds inode->i_op = &page_symlink_inode_operations; 841da177e4SLinus Torvalds inode->i_data.a_ops = &cramfs_aops; 851da177e4SLinus Torvalds } else { 861da177e4SLinus Torvalds inode->i_size = 0; 871da177e4SLinus Torvalds inode->i_blocks = 0; 881da177e4SLinus Torvalds init_special_inode(inode, inode->i_mode, 891da177e4SLinus Torvalds old_decode_dev(cramfs_inode->size)); 901da177e4SLinus Torvalds } 91a97c9bf3SDave Johnson unlock_new_inode(inode); 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds return inode; 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 9682d63fc9SAl Viro static void cramfs_drop_inode(struct inode *inode) 9782d63fc9SAl Viro { 9882d63fc9SAl Viro if (inode->i_ino == 1) 9982d63fc9SAl Viro generic_delete_inode(inode); 10082d63fc9SAl Viro else 10182d63fc9SAl Viro generic_drop_inode(inode); 10282d63fc9SAl Viro } 10382d63fc9SAl Viro 1041da177e4SLinus Torvalds /* 1051da177e4SLinus Torvalds * We have our own block cache: don't fill up the buffer cache 1061da177e4SLinus Torvalds * with the rom-image, because the way the filesystem is set 1071da177e4SLinus Torvalds * up the accesses should be fairly regular and cached in the 1081da177e4SLinus Torvalds * page cache and dentry tree anyway.. 1091da177e4SLinus Torvalds * 1101da177e4SLinus Torvalds * This also acts as a way to guarantee contiguous areas of up to 1111da177e4SLinus Torvalds * BLKS_PER_BUF*PAGE_CACHE_SIZE, so that the caller doesn't need to 1121da177e4SLinus Torvalds * worry about end-of-buffer issues even when decompressing a full 1131da177e4SLinus Torvalds * page cache. 1141da177e4SLinus Torvalds */ 1151da177e4SLinus Torvalds #define READ_BUFFERS (2) 1161da177e4SLinus Torvalds /* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ 1171da177e4SLinus Torvalds #define NEXT_BUFFER(_ix) ((_ix) ^ 1) 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds /* 1201da177e4SLinus Torvalds * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" 1211da177e4SLinus Torvalds * data that takes up more space than the original and with unlucky 1221da177e4SLinus Torvalds * alignment. 1231da177e4SLinus Torvalds */ 1241da177e4SLinus Torvalds #define BLKS_PER_BUF_SHIFT (2) 1251da177e4SLinus Torvalds #define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) 1261da177e4SLinus Torvalds #define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE) 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; 1291da177e4SLinus Torvalds static unsigned buffer_blocknr[READ_BUFFERS]; 1301da177e4SLinus Torvalds static struct super_block * buffer_dev[READ_BUFFERS]; 1311da177e4SLinus Torvalds static int next_buffer; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds /* 1341da177e4SLinus Torvalds * Returns a pointer to a buffer containing at least LEN bytes of 1351da177e4SLinus Torvalds * filesystem starting at byte offset OFFSET into the filesystem. 1361da177e4SLinus Torvalds */ 1371da177e4SLinus Torvalds static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len) 1381da177e4SLinus Torvalds { 1391da177e4SLinus Torvalds struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; 1401da177e4SLinus Torvalds struct page *pages[BLKS_PER_BUF]; 1416bbfb077SAndi Drebes unsigned i, blocknr, buffer; 1421da177e4SLinus Torvalds unsigned long devsize; 1431da177e4SLinus Torvalds char *data; 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds if (!len) 1461da177e4SLinus Torvalds return NULL; 1471da177e4SLinus Torvalds blocknr = offset >> PAGE_CACHE_SHIFT; 1481da177e4SLinus Torvalds offset &= PAGE_CACHE_SIZE - 1; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds /* Check if an existing buffer already has the data.. */ 1511da177e4SLinus Torvalds for (i = 0; i < READ_BUFFERS; i++) { 1521da177e4SLinus Torvalds unsigned int blk_offset; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds if (buffer_dev[i] != sb) 1551da177e4SLinus Torvalds continue; 1561da177e4SLinus Torvalds if (blocknr < buffer_blocknr[i]) 1571da177e4SLinus Torvalds continue; 1581da177e4SLinus Torvalds blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT; 1591da177e4SLinus Torvalds blk_offset += offset; 1601da177e4SLinus Torvalds if (blk_offset + len > BUFFER_SIZE) 1611da177e4SLinus Torvalds continue; 1621da177e4SLinus Torvalds return read_buffers[i] + blk_offset; 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds devsize = mapping->host->i_size >> PAGE_CACHE_SHIFT; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds /* Ok, read in BLKS_PER_BUF pages completely first. */ 1681da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 1691da177e4SLinus Torvalds struct page *page = NULL; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds if (blocknr + i < devsize) { 1726fe6900eSNick Piggin page = read_mapping_page_async(mapping, blocknr + i, 1736fe6900eSNick Piggin NULL); 1741da177e4SLinus Torvalds /* synchronous error? */ 1751da177e4SLinus Torvalds if (IS_ERR(page)) 1761da177e4SLinus Torvalds page = NULL; 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds pages[i] = page; 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 1821da177e4SLinus Torvalds struct page *page = pages[i]; 1831da177e4SLinus Torvalds if (page) { 1841da177e4SLinus Torvalds wait_on_page_locked(page); 1851da177e4SLinus Torvalds if (!PageUptodate(page)) { 1861da177e4SLinus Torvalds /* asynchronous error */ 1871da177e4SLinus Torvalds page_cache_release(page); 1881da177e4SLinus Torvalds pages[i] = NULL; 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds buffer = next_buffer; 1941da177e4SLinus Torvalds next_buffer = NEXT_BUFFER(buffer); 1951da177e4SLinus Torvalds buffer_blocknr[buffer] = blocknr; 1961da177e4SLinus Torvalds buffer_dev[buffer] = sb; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds data = read_buffers[buffer]; 1991da177e4SLinus Torvalds for (i = 0; i < BLKS_PER_BUF; i++) { 2001da177e4SLinus Torvalds struct page *page = pages[i]; 2011da177e4SLinus Torvalds if (page) { 2021da177e4SLinus Torvalds memcpy(data, kmap(page), PAGE_CACHE_SIZE); 2031da177e4SLinus Torvalds kunmap(page); 2041da177e4SLinus Torvalds page_cache_release(page); 2051da177e4SLinus Torvalds } else 2061da177e4SLinus Torvalds memset(data, 0, PAGE_CACHE_SIZE); 2071da177e4SLinus Torvalds data += PAGE_CACHE_SIZE; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds return read_buffers[buffer] + offset; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds static void cramfs_put_super(struct super_block *sb) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds kfree(sb->s_fs_info); 2151da177e4SLinus Torvalds sb->s_fs_info = NULL; 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds static int cramfs_remount(struct super_block *sb, int *flags, char *data) 2191da177e4SLinus Torvalds { 2201da177e4SLinus Torvalds *flags |= MS_RDONLY; 2211da177e4SLinus Torvalds return 0; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds static int cramfs_fill_super(struct super_block *sb, void *data, int silent) 2251da177e4SLinus Torvalds { 2261da177e4SLinus Torvalds int i; 2271da177e4SLinus Torvalds struct cramfs_super super; 2281da177e4SLinus Torvalds unsigned long root_offset; 2291da177e4SLinus Torvalds struct cramfs_sb_info *sbi; 2301da177e4SLinus Torvalds struct inode *root; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds sb->s_flags |= MS_RDONLY; 2331da177e4SLinus Torvalds 234f8314dc6SPanagiotis Issaris sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); 2351da177e4SLinus Torvalds if (!sbi) 2361da177e4SLinus Torvalds return -ENOMEM; 2371da177e4SLinus Torvalds sb->s_fs_info = sbi; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* Invalidate the read buffers on mount: think disk change.. */ 240353ab6e9SIngo Molnar mutex_lock(&read_mutex); 2411da177e4SLinus Torvalds for (i = 0; i < READ_BUFFERS; i++) 2421da177e4SLinus Torvalds buffer_blocknr[i] = -1; 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds /* Read the first block and get the superblock from it */ 2451da177e4SLinus Torvalds memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super)); 246353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds /* Do sanity checks on the superblock */ 2491da177e4SLinus Torvalds if (super.magic != CRAMFS_MAGIC) { 250ac8d35c5SAndi Drebes /* check for wrong endianess */ 251ac8d35c5SAndi Drebes if (super.magic == CRAMFS_MAGIC_WEND) { 252ac8d35c5SAndi Drebes if (!silent) 253ac8d35c5SAndi Drebes printk(KERN_ERR "cramfs: wrong endianess\n"); 254ac8d35c5SAndi Drebes goto out; 255ac8d35c5SAndi Drebes } 256ac8d35c5SAndi Drebes 2571da177e4SLinus Torvalds /* check at 512 byte offset */ 258353ab6e9SIngo Molnar mutex_lock(&read_mutex); 2591da177e4SLinus Torvalds memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super)); 260353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 2611da177e4SLinus Torvalds if (super.magic != CRAMFS_MAGIC) { 262ac8d35c5SAndi Drebes if (super.magic == CRAMFS_MAGIC_WEND && !silent) 263ac8d35c5SAndi Drebes printk(KERN_ERR "cramfs: wrong endianess\n"); 264ac8d35c5SAndi Drebes else if (!silent) 2651da177e4SLinus Torvalds printk(KERN_ERR "cramfs: wrong magic\n"); 2661da177e4SLinus Torvalds goto out; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds /* get feature flags first */ 2711da177e4SLinus Torvalds if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { 2721da177e4SLinus Torvalds printk(KERN_ERR "cramfs: unsupported filesystem features\n"); 2731da177e4SLinus Torvalds goto out; 2741da177e4SLinus Torvalds } 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds /* Check that the root inode is in a sane state */ 2771da177e4SLinus Torvalds if (!S_ISDIR(super.root.mode)) { 2781da177e4SLinus Torvalds printk(KERN_ERR "cramfs: root is not a directory\n"); 2791da177e4SLinus Torvalds goto out; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds root_offset = super.root.offset << 2; 2821da177e4SLinus Torvalds if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { 2831da177e4SLinus Torvalds sbi->size=super.size; 2841da177e4SLinus Torvalds sbi->blocks=super.fsid.blocks; 2851da177e4SLinus Torvalds sbi->files=super.fsid.files; 2861da177e4SLinus Torvalds } else { 2871da177e4SLinus Torvalds sbi->size=1<<28; 2881da177e4SLinus Torvalds sbi->blocks=0; 2891da177e4SLinus Torvalds sbi->files=0; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds sbi->magic=super.magic; 2921da177e4SLinus Torvalds sbi->flags=super.flags; 2931da177e4SLinus Torvalds if (root_offset == 0) 2941da177e4SLinus Torvalds printk(KERN_INFO "cramfs: empty filesystem"); 2951da177e4SLinus Torvalds else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 2961da177e4SLinus Torvalds ((root_offset != sizeof(struct cramfs_super)) && 2971da177e4SLinus Torvalds (root_offset != 512 + sizeof(struct cramfs_super)))) 2981da177e4SLinus Torvalds { 2991da177e4SLinus Torvalds printk(KERN_ERR "cramfs: bad root offset %lu\n", root_offset); 3001da177e4SLinus Torvalds goto out; 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds /* Set it all up.. */ 3041da177e4SLinus Torvalds sb->s_op = &cramfs_ops; 3051da177e4SLinus Torvalds root = get_cramfs_inode(sb, &super.root); 3061da177e4SLinus Torvalds if (!root) 3071da177e4SLinus Torvalds goto out; 3081da177e4SLinus Torvalds sb->s_root = d_alloc_root(root); 3091da177e4SLinus Torvalds if (!sb->s_root) { 3101da177e4SLinus Torvalds iput(root); 3111da177e4SLinus Torvalds goto out; 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds return 0; 3141da177e4SLinus Torvalds out: 3151da177e4SLinus Torvalds kfree(sbi); 3161da177e4SLinus Torvalds sb->s_fs_info = NULL; 3171da177e4SLinus Torvalds return -EINVAL; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 320726c3342SDavid Howells static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) 3211da177e4SLinus Torvalds { 322726c3342SDavid Howells struct super_block *sb = dentry->d_sb; 323726c3342SDavid Howells 3241da177e4SLinus Torvalds buf->f_type = CRAMFS_MAGIC; 3251da177e4SLinus Torvalds buf->f_bsize = PAGE_CACHE_SIZE; 3261da177e4SLinus Torvalds buf->f_blocks = CRAMFS_SB(sb)->blocks; 3271da177e4SLinus Torvalds buf->f_bfree = 0; 3281da177e4SLinus Torvalds buf->f_bavail = 0; 3291da177e4SLinus Torvalds buf->f_files = CRAMFS_SB(sb)->files; 3301da177e4SLinus Torvalds buf->f_ffree = 0; 3311da177e4SLinus Torvalds buf->f_namelen = CRAMFS_MAXPATHLEN; 3321da177e4SLinus Torvalds return 0; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds /* 3361da177e4SLinus Torvalds * Read a cramfs directory entry. 3371da177e4SLinus Torvalds */ 3381da177e4SLinus Torvalds static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir) 3391da177e4SLinus Torvalds { 340352d8af7SJosef Sipek struct inode *inode = filp->f_path.dentry->d_inode; 3411da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 3421da177e4SLinus Torvalds char *buf; 3431da177e4SLinus Torvalds unsigned int offset; 3441da177e4SLinus Torvalds int copied; 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds /* Offset within the thing. */ 3471da177e4SLinus Torvalds offset = filp->f_pos; 3481da177e4SLinus Torvalds if (offset >= inode->i_size) 3491da177e4SLinus Torvalds return 0; 3501da177e4SLinus Torvalds /* Directory entries are always 4-byte aligned */ 3511da177e4SLinus Torvalds if (offset & 3) 3521da177e4SLinus Torvalds return -EINVAL; 3531da177e4SLinus Torvalds 3544176ed59SAndi Drebes buf = kmalloc(CRAMFS_MAXPATHLEN, GFP_KERNEL); 3551da177e4SLinus Torvalds if (!buf) 3561da177e4SLinus Torvalds return -ENOMEM; 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds copied = 0; 3591da177e4SLinus Torvalds while (offset < inode->i_size) { 3601da177e4SLinus Torvalds struct cramfs_inode *de; 3611da177e4SLinus Torvalds unsigned long nextoffset; 3621da177e4SLinus Torvalds char *name; 3631da177e4SLinus Torvalds ino_t ino; 3641da177e4SLinus Torvalds mode_t mode; 3651da177e4SLinus Torvalds int namelen, error; 3661da177e4SLinus Torvalds 367353ab6e9SIngo Molnar mutex_lock(&read_mutex); 3684176ed59SAndi Drebes de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); 3691da177e4SLinus Torvalds name = (char *)(de+1); 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds /* 3721da177e4SLinus Torvalds * Namelengths on disk are shifted by two 3731da177e4SLinus Torvalds * and the name padded out to 4-byte boundaries 3741da177e4SLinus Torvalds * with zeroes. 3751da177e4SLinus Torvalds */ 3761da177e4SLinus Torvalds namelen = de->namelen << 2; 3771da177e4SLinus Torvalds memcpy(buf, name, namelen); 3781da177e4SLinus Torvalds ino = CRAMINO(de); 3791da177e4SLinus Torvalds mode = de->mode; 380353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 3811da177e4SLinus Torvalds nextoffset = offset + sizeof(*de) + namelen; 3821da177e4SLinus Torvalds for (;;) { 3831da177e4SLinus Torvalds if (!namelen) { 3841da177e4SLinus Torvalds kfree(buf); 3851da177e4SLinus Torvalds return -EIO; 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds if (buf[namelen-1]) 3881da177e4SLinus Torvalds break; 3891da177e4SLinus Torvalds namelen--; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds error = filldir(dirent, buf, namelen, offset, ino, mode >> 12); 3921da177e4SLinus Torvalds if (error) 3931da177e4SLinus Torvalds break; 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds offset = nextoffset; 3961da177e4SLinus Torvalds filp->f_pos = offset; 3971da177e4SLinus Torvalds copied++; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds kfree(buf); 4001da177e4SLinus Torvalds return 0; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds /* 4041da177e4SLinus Torvalds * Lookup and fill in the inode data.. 4051da177e4SLinus Torvalds */ 4061da177e4SLinus Torvalds static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) 4071da177e4SLinus Torvalds { 4081da177e4SLinus Torvalds unsigned int offset = 0; 4091da177e4SLinus Torvalds int sorted; 4101da177e4SLinus Torvalds 411353ab6e9SIngo Molnar mutex_lock(&read_mutex); 4121da177e4SLinus Torvalds sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS; 4131da177e4SLinus Torvalds while (offset < dir->i_size) { 4141da177e4SLinus Torvalds struct cramfs_inode *de; 4151da177e4SLinus Torvalds char *name; 4161da177e4SLinus Torvalds int namelen, retval; 4171da177e4SLinus Torvalds 4184176ed59SAndi Drebes de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); 4191da177e4SLinus Torvalds name = (char *)(de+1); 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds /* Try to take advantage of sorted directories */ 4221da177e4SLinus Torvalds if (sorted && (dentry->d_name.name[0] < name[0])) 4231da177e4SLinus Torvalds break; 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds namelen = de->namelen << 2; 4261da177e4SLinus Torvalds offset += sizeof(*de) + namelen; 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds /* Quick check that the name is roughly the right length */ 4291da177e4SLinus Torvalds if (((dentry->d_name.len + 3) & ~3) != namelen) 4301da177e4SLinus Torvalds continue; 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds for (;;) { 4331da177e4SLinus Torvalds if (!namelen) { 434353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4351da177e4SLinus Torvalds return ERR_PTR(-EIO); 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds if (name[namelen-1]) 4381da177e4SLinus Torvalds break; 4391da177e4SLinus Torvalds namelen--; 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds if (namelen != dentry->d_name.len) 4421da177e4SLinus Torvalds continue; 4431da177e4SLinus Torvalds retval = memcmp(dentry->d_name.name, name, namelen); 4441da177e4SLinus Torvalds if (retval > 0) 4451da177e4SLinus Torvalds continue; 4461da177e4SLinus Torvalds if (!retval) { 4471da177e4SLinus Torvalds struct cramfs_inode entry = *de; 448353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4491da177e4SLinus Torvalds d_add(dentry, get_cramfs_inode(dir->i_sb, &entry)); 4501da177e4SLinus Torvalds return NULL; 4511da177e4SLinus Torvalds } 4521da177e4SLinus Torvalds /* else (retval < 0) */ 4531da177e4SLinus Torvalds if (sorted) 4541da177e4SLinus Torvalds break; 4551da177e4SLinus Torvalds } 456353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4571da177e4SLinus Torvalds d_add(dentry, NULL); 4581da177e4SLinus Torvalds return NULL; 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds static int cramfs_readpage(struct file *file, struct page * page) 4621da177e4SLinus Torvalds { 4631da177e4SLinus Torvalds struct inode *inode = page->mapping->host; 4641da177e4SLinus Torvalds u32 maxblock, bytes_filled; 4651da177e4SLinus Torvalds void *pgdata; 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 4681da177e4SLinus Torvalds bytes_filled = 0; 4691da177e4SLinus Torvalds if (page->index < maxblock) { 4701da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 4711da177e4SLinus Torvalds u32 blkptr_offset = OFFSET(inode) + page->index*4; 4721da177e4SLinus Torvalds u32 start_offset, compr_len; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds start_offset = OFFSET(inode) + maxblock*4; 475353ab6e9SIngo Molnar mutex_lock(&read_mutex); 4761da177e4SLinus Torvalds if (page->index) 4771da177e4SLinus Torvalds start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4); 4781da177e4SLinus Torvalds compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset); 479353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4801da177e4SLinus Torvalds pgdata = kmap(page); 4811da177e4SLinus Torvalds if (compr_len == 0) 4821da177e4SLinus Torvalds ; /* hole */ 4838bb02691SPhillip Lougher else if (compr_len > (PAGE_CACHE_SIZE << 1)) 4848bb02691SPhillip Lougher printk(KERN_ERR "cramfs: bad compressed blocksize %u\n", compr_len); 4851da177e4SLinus Torvalds else { 486353ab6e9SIngo Molnar mutex_lock(&read_mutex); 4871da177e4SLinus Torvalds bytes_filled = cramfs_uncompress_block(pgdata, 4881da177e4SLinus Torvalds PAGE_CACHE_SIZE, 4891da177e4SLinus Torvalds cramfs_read(sb, start_offset, compr_len), 4901da177e4SLinus Torvalds compr_len); 491353ab6e9SIngo Molnar mutex_unlock(&read_mutex); 4921da177e4SLinus Torvalds } 4931da177e4SLinus Torvalds } else 4941da177e4SLinus Torvalds pgdata = kmap(page); 4951da177e4SLinus Torvalds memset(pgdata + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled); 4961da177e4SLinus Torvalds kunmap(page); 4971da177e4SLinus Torvalds flush_dcache_page(page); 4981da177e4SLinus Torvalds SetPageUptodate(page); 4991da177e4SLinus Torvalds unlock_page(page); 5001da177e4SLinus Torvalds return 0; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds 503f5e54d6eSChristoph Hellwig static const struct address_space_operations cramfs_aops = { 5041da177e4SLinus Torvalds .readpage = cramfs_readpage 5051da177e4SLinus Torvalds }; 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds /* 5081da177e4SLinus Torvalds * Our operations: 5091da177e4SLinus Torvalds */ 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds /* 5121da177e4SLinus Torvalds * A directory can only readdir 5131da177e4SLinus Torvalds */ 5144b6f5d20SArjan van de Ven static const struct file_operations cramfs_directory_operations = { 5151da177e4SLinus Torvalds .llseek = generic_file_llseek, 5161da177e4SLinus Torvalds .read = generic_read_dir, 5171da177e4SLinus Torvalds .readdir = cramfs_readdir, 5181da177e4SLinus Torvalds }; 5191da177e4SLinus Torvalds 520754661f1SArjan van de Ven static const struct inode_operations cramfs_dir_inode_operations = { 5211da177e4SLinus Torvalds .lookup = cramfs_lookup, 5221da177e4SLinus Torvalds }; 5231da177e4SLinus Torvalds 524ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations cramfs_ops = { 5251da177e4SLinus Torvalds .put_super = cramfs_put_super, 5261da177e4SLinus Torvalds .remount_fs = cramfs_remount, 5271da177e4SLinus Torvalds .statfs = cramfs_statfs, 52882d63fc9SAl Viro .drop_inode = cramfs_drop_inode, 5291da177e4SLinus Torvalds }; 5301da177e4SLinus Torvalds 531454e2398SDavid Howells static int cramfs_get_sb(struct file_system_type *fs_type, 532454e2398SDavid Howells int flags, const char *dev_name, void *data, struct vfsmount *mnt) 5331da177e4SLinus Torvalds { 534454e2398SDavid Howells return get_sb_bdev(fs_type, flags, dev_name, data, cramfs_fill_super, 535454e2398SDavid Howells mnt); 5361da177e4SLinus Torvalds } 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds static struct file_system_type cramfs_fs_type = { 5391da177e4SLinus Torvalds .owner = THIS_MODULE, 5401da177e4SLinus Torvalds .name = "cramfs", 5411da177e4SLinus Torvalds .get_sb = cramfs_get_sb, 5421da177e4SLinus Torvalds .kill_sb = kill_block_super, 5431da177e4SLinus Torvalds .fs_flags = FS_REQUIRES_DEV, 5441da177e4SLinus Torvalds }; 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds static int __init init_cramfs_fs(void) 5471da177e4SLinus Torvalds { 54850d44ed0SAlexey Dobriyan int rv; 54950d44ed0SAlexey Dobriyan 55050d44ed0SAlexey Dobriyan rv = cramfs_uncompress_init(); 55150d44ed0SAlexey Dobriyan if (rv < 0) 55250d44ed0SAlexey Dobriyan return rv; 55350d44ed0SAlexey Dobriyan rv = register_filesystem(&cramfs_fs_type); 55450d44ed0SAlexey Dobriyan if (rv < 0) 55550d44ed0SAlexey Dobriyan cramfs_uncompress_exit(); 55650d44ed0SAlexey Dobriyan return rv; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds static void __exit exit_cramfs_fs(void) 5601da177e4SLinus Torvalds { 5611da177e4SLinus Torvalds cramfs_uncompress_exit(); 5621da177e4SLinus Torvalds unregister_filesystem(&cramfs_fs_type); 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds module_init(init_cramfs_fs) 5661da177e4SLinus Torvalds module_exit(exit_cramfs_fs) 5671da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 568