18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Compressed rom filesystem for Linux. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1999 Linus Torvalds. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is released under the GPL. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * These are the VFS interfaces to the compressed rom filesystem. 118c2ecf20Sopenharmony_ci * The actual compression is based on zlib, see the other files. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/fs.h> 188c2ecf20Sopenharmony_ci#include <linux/file.h> 198c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 208c2ecf20Sopenharmony_ci#include <linux/pfn_t.h> 218c2ecf20Sopenharmony_ci#include <linux/ramfs.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 258c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 268c2ecf20Sopenharmony_ci#include <linux/mtd/super.h> 278c2ecf20Sopenharmony_ci#include <linux/fs_context.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/vfs.h> 308c2ecf20Sopenharmony_ci#include <linux/mutex.h> 318c2ecf20Sopenharmony_ci#include <uapi/linux/cramfs_fs.h> 328c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "internal.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * cramfs super-block data in memory 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistruct cramfs_sb_info { 408c2ecf20Sopenharmony_ci unsigned long magic; 418c2ecf20Sopenharmony_ci unsigned long size; 428c2ecf20Sopenharmony_ci unsigned long blocks; 438c2ecf20Sopenharmony_ci unsigned long files; 448c2ecf20Sopenharmony_ci unsigned long flags; 458c2ecf20Sopenharmony_ci void *linear_virt_addr; 468c2ecf20Sopenharmony_ci resource_size_t linear_phys_addr; 478c2ecf20Sopenharmony_ci size_t mtd_point_size; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return sb->s_fs_info; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct super_operations cramfs_ops; 568c2ecf20Sopenharmony_cistatic const struct inode_operations cramfs_dir_inode_operations; 578c2ecf20Sopenharmony_cistatic const struct file_operations cramfs_directory_operations; 588c2ecf20Sopenharmony_cistatic const struct file_operations cramfs_physmem_fops; 598c2ecf20Sopenharmony_cistatic const struct address_space_operations cramfs_aops; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(read_mutex); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* These macros may change in future, to provide better st_ino semantics. */ 658c2ecf20Sopenharmony_ci#define OFFSET(x) ((x)->i_ino) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci if (!cino->offset) 708c2ecf20Sopenharmony_ci return offset + 1; 718c2ecf20Sopenharmony_ci if (!cino->size) 728c2ecf20Sopenharmony_ci return offset + 1; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * The file mode test fixes buggy mkcramfs implementations where 768c2ecf20Sopenharmony_ci * cramfs_inode->offset is set to a non zero value for entries 778c2ecf20Sopenharmony_ci * which did not contain data, like devices node and fifos. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci switch (cino->mode & S_IFMT) { 808c2ecf20Sopenharmony_ci case S_IFREG: 818c2ecf20Sopenharmony_ci case S_IFDIR: 828c2ecf20Sopenharmony_ci case S_IFLNK: 838c2ecf20Sopenharmony_ci return cino->offset << 2; 848c2ecf20Sopenharmony_ci default: 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci return offset + 1; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct inode *get_cramfs_inode(struct super_block *sb, 918c2ecf20Sopenharmony_ci const struct cramfs_inode *cramfs_inode, unsigned int offset) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct inode *inode; 948c2ecf20Sopenharmony_ci static struct timespec64 zerotime; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci inode = iget_locked(sb, cramino(cramfs_inode, offset)); 978c2ecf20Sopenharmony_ci if (!inode) 988c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 998c2ecf20Sopenharmony_ci if (!(inode->i_state & I_NEW)) 1008c2ecf20Sopenharmony_ci return inode; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci switch (cramfs_inode->mode & S_IFMT) { 1038c2ecf20Sopenharmony_ci case S_IFREG: 1048c2ecf20Sopenharmony_ci inode->i_fop = &generic_ro_fops; 1058c2ecf20Sopenharmony_ci inode->i_data.a_ops = &cramfs_aops; 1068c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CRAMFS_MTD) && 1078c2ecf20Sopenharmony_ci CRAMFS_SB(sb)->flags & CRAMFS_FLAG_EXT_BLOCK_POINTERS && 1088c2ecf20Sopenharmony_ci CRAMFS_SB(sb)->linear_phys_addr) 1098c2ecf20Sopenharmony_ci inode->i_fop = &cramfs_physmem_fops; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case S_IFDIR: 1128c2ecf20Sopenharmony_ci inode->i_op = &cramfs_dir_inode_operations; 1138c2ecf20Sopenharmony_ci inode->i_fop = &cramfs_directory_operations; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case S_IFLNK: 1168c2ecf20Sopenharmony_ci inode->i_op = &page_symlink_inode_operations; 1178c2ecf20Sopenharmony_ci inode_nohighmem(inode); 1188c2ecf20Sopenharmony_ci inode->i_data.a_ops = &cramfs_aops; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci init_special_inode(inode, cramfs_inode->mode, 1228c2ecf20Sopenharmony_ci old_decode_dev(cramfs_inode->size)); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci inode->i_mode = cramfs_inode->mode; 1268c2ecf20Sopenharmony_ci i_uid_write(inode, cramfs_inode->uid); 1278c2ecf20Sopenharmony_ci i_gid_write(inode, cramfs_inode->gid); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* if the lower 2 bits are zero, the inode contains data */ 1308c2ecf20Sopenharmony_ci if (!(inode->i_ino & 3)) { 1318c2ecf20Sopenharmony_ci inode->i_size = cramfs_inode->size; 1328c2ecf20Sopenharmony_ci inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Struct copy intentional */ 1368c2ecf20Sopenharmony_ci inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; 1378c2ecf20Sopenharmony_ci /* inode->i_nlink is left 1 - arguably wrong for directories, 1388c2ecf20Sopenharmony_ci but it's the best we can do without reading the directory 1398c2ecf20Sopenharmony_ci contents. 1 yields the right result in GNU find, even 1408c2ecf20Sopenharmony_ci without -noleaf option. */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci unlock_new_inode(inode); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return inode; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * We have our own block cache: don't fill up the buffer cache 1498c2ecf20Sopenharmony_ci * with the rom-image, because the way the filesystem is set 1508c2ecf20Sopenharmony_ci * up the accesses should be fairly regular and cached in the 1518c2ecf20Sopenharmony_ci * page cache and dentry tree anyway.. 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * This also acts as a way to guarantee contiguous areas of up to 1548c2ecf20Sopenharmony_ci * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to 1558c2ecf20Sopenharmony_ci * worry about end-of-buffer issues even when decompressing a full 1568c2ecf20Sopenharmony_ci * page cache. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Note: This is all optimized away at compile time when 1598c2ecf20Sopenharmony_ci * CONFIG_CRAMFS_BLOCKDEV=n. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci#define READ_BUFFERS (2) 1628c2ecf20Sopenharmony_ci/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ 1638c2ecf20Sopenharmony_ci#define NEXT_BUFFER(_ix) ((_ix) ^ 1) 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" 1678c2ecf20Sopenharmony_ci * data that takes up more space than the original and with unlucky 1688c2ecf20Sopenharmony_ci * alignment. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci#define BLKS_PER_BUF_SHIFT (2) 1718c2ecf20Sopenharmony_ci#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) 1728c2ecf20Sopenharmony_ci#define BUFFER_SIZE (BLKS_PER_BUF*PAGE_SIZE) 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; 1758c2ecf20Sopenharmony_cistatic unsigned buffer_blocknr[READ_BUFFERS]; 1768c2ecf20Sopenharmony_cistatic struct super_block *buffer_dev[READ_BUFFERS]; 1778c2ecf20Sopenharmony_cistatic int next_buffer; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Populate our block cache and return a pointer to it. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistatic void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset, 1838c2ecf20Sopenharmony_ci unsigned int len) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; 1868c2ecf20Sopenharmony_ci struct page *pages[BLKS_PER_BUF]; 1878c2ecf20Sopenharmony_ci unsigned i, blocknr, buffer; 1888c2ecf20Sopenharmony_ci unsigned long devsize; 1898c2ecf20Sopenharmony_ci char *data; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (!len) 1928c2ecf20Sopenharmony_ci return NULL; 1938c2ecf20Sopenharmony_ci blocknr = offset >> PAGE_SHIFT; 1948c2ecf20Sopenharmony_ci offset &= PAGE_SIZE - 1; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Check if an existing buffer already has the data.. */ 1978c2ecf20Sopenharmony_ci for (i = 0; i < READ_BUFFERS; i++) { 1988c2ecf20Sopenharmony_ci unsigned int blk_offset; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (buffer_dev[i] != sb) 2018c2ecf20Sopenharmony_ci continue; 2028c2ecf20Sopenharmony_ci if (blocknr < buffer_blocknr[i]) 2038c2ecf20Sopenharmony_ci continue; 2048c2ecf20Sopenharmony_ci blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_SHIFT; 2058c2ecf20Sopenharmony_ci blk_offset += offset; 2068c2ecf20Sopenharmony_ci if (blk_offset > BUFFER_SIZE || 2078c2ecf20Sopenharmony_ci blk_offset + len > BUFFER_SIZE) 2088c2ecf20Sopenharmony_ci continue; 2098c2ecf20Sopenharmony_ci return read_buffers[i] + blk_offset; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci devsize = mapping->host->i_size >> PAGE_SHIFT; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Ok, read in BLKS_PER_BUF pages completely first. */ 2158c2ecf20Sopenharmony_ci for (i = 0; i < BLKS_PER_BUF; i++) { 2168c2ecf20Sopenharmony_ci struct page *page = NULL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (blocknr + i < devsize) { 2198c2ecf20Sopenharmony_ci page = read_mapping_page(mapping, blocknr + i, NULL); 2208c2ecf20Sopenharmony_ci /* synchronous error? */ 2218c2ecf20Sopenharmony_ci if (IS_ERR(page)) 2228c2ecf20Sopenharmony_ci page = NULL; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci pages[i] = page; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for (i = 0; i < BLKS_PER_BUF; i++) { 2288c2ecf20Sopenharmony_ci struct page *page = pages[i]; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (page) { 2318c2ecf20Sopenharmony_ci wait_on_page_locked(page); 2328c2ecf20Sopenharmony_ci if (!PageUptodate(page)) { 2338c2ecf20Sopenharmony_ci /* asynchronous error */ 2348c2ecf20Sopenharmony_ci put_page(page); 2358c2ecf20Sopenharmony_ci pages[i] = NULL; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci buffer = next_buffer; 2418c2ecf20Sopenharmony_ci next_buffer = NEXT_BUFFER(buffer); 2428c2ecf20Sopenharmony_ci buffer_blocknr[buffer] = blocknr; 2438c2ecf20Sopenharmony_ci buffer_dev[buffer] = sb; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci data = read_buffers[buffer]; 2468c2ecf20Sopenharmony_ci for (i = 0; i < BLKS_PER_BUF; i++) { 2478c2ecf20Sopenharmony_ci struct page *page = pages[i]; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (page) { 2508c2ecf20Sopenharmony_ci memcpy(data, kmap(page), PAGE_SIZE); 2518c2ecf20Sopenharmony_ci kunmap(page); 2528c2ecf20Sopenharmony_ci put_page(page); 2538c2ecf20Sopenharmony_ci } else 2548c2ecf20Sopenharmony_ci memset(data, 0, PAGE_SIZE); 2558c2ecf20Sopenharmony_ci data += PAGE_SIZE; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci return read_buffers[buffer] + offset; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/* 2618c2ecf20Sopenharmony_ci * Return a pointer to the linearly addressed cramfs image in memory. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_cistatic void *cramfs_direct_read(struct super_block *sb, unsigned int offset, 2648c2ecf20Sopenharmony_ci unsigned int len) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!len) 2698c2ecf20Sopenharmony_ci return NULL; 2708c2ecf20Sopenharmony_ci if (len > sbi->size || offset > sbi->size - len) 2718c2ecf20Sopenharmony_ci return page_address(ZERO_PAGE(0)); 2728c2ecf20Sopenharmony_ci return sbi->linear_virt_addr + offset; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* 2768c2ecf20Sopenharmony_ci * Returns a pointer to a buffer containing at least LEN bytes of 2778c2ecf20Sopenharmony_ci * filesystem starting at byte offset OFFSET into the filesystem. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_cistatic void *cramfs_read(struct super_block *sb, unsigned int offset, 2808c2ecf20Sopenharmony_ci unsigned int len) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CRAMFS_MTD) && sbi->linear_virt_addr) 2858c2ecf20Sopenharmony_ci return cramfs_direct_read(sb, offset, len); 2868c2ecf20Sopenharmony_ci else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) 2878c2ecf20Sopenharmony_ci return cramfs_blkdev_read(sb, offset, len); 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci return NULL; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * For a mapping to be possible, we need a range of uncompressed and 2948c2ecf20Sopenharmony_ci * contiguous blocks. Return the offset for the first block and number of 2958c2ecf20Sopenharmony_ci * valid blocks for which that is true, or zero otherwise. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic u32 cramfs_get_block_range(struct inode *inode, u32 pgoff, u32 *pages) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(inode->i_sb); 3008c2ecf20Sopenharmony_ci int i; 3018c2ecf20Sopenharmony_ci u32 *blockptrs, first_block_addr; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * We can dereference memory directly here as this code may be 3058c2ecf20Sopenharmony_ci * reached only when there is a direct filesystem image mapping 3068c2ecf20Sopenharmony_ci * available in memory. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci blockptrs = (u32 *)(sbi->linear_virt_addr + OFFSET(inode) + pgoff * 4); 3098c2ecf20Sopenharmony_ci first_block_addr = blockptrs[0] & ~CRAMFS_BLK_FLAGS; 3108c2ecf20Sopenharmony_ci i = 0; 3118c2ecf20Sopenharmony_ci do { 3128c2ecf20Sopenharmony_ci u32 block_off = i * (PAGE_SIZE >> CRAMFS_BLK_DIRECT_PTR_SHIFT); 3138c2ecf20Sopenharmony_ci u32 expect = (first_block_addr + block_off) | 3148c2ecf20Sopenharmony_ci CRAMFS_BLK_FLAG_DIRECT_PTR | 3158c2ecf20Sopenharmony_ci CRAMFS_BLK_FLAG_UNCOMPRESSED; 3168c2ecf20Sopenharmony_ci if (blockptrs[i] != expect) { 3178c2ecf20Sopenharmony_ci pr_debug("range: block %d/%d got %#x expects %#x\n", 3188c2ecf20Sopenharmony_ci pgoff+i, pgoff + *pages - 1, 3198c2ecf20Sopenharmony_ci blockptrs[i], expect); 3208c2ecf20Sopenharmony_ci if (i == 0) 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci } while (++i < *pages); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci *pages = i; 3278c2ecf20Sopenharmony_ci return first_block_addr << CRAMFS_BLK_DIRECT_PTR_SHIFT; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 3338c2ecf20Sopenharmony_ci * Return true if the last page of a file in the filesystem image contains 3348c2ecf20Sopenharmony_ci * some other data that doesn't belong to that file. It is assumed that the 3358c2ecf20Sopenharmony_ci * last block is CRAMFS_BLK_FLAG_DIRECT_PTR | CRAMFS_BLK_FLAG_UNCOMPRESSED 3368c2ecf20Sopenharmony_ci * (verified by cramfs_get_block_range() and directly accessible in memory. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_cistatic bool cramfs_last_page_is_shared(struct inode *inode) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(inode->i_sb); 3418c2ecf20Sopenharmony_ci u32 partial, last_page, blockaddr, *blockptrs; 3428c2ecf20Sopenharmony_ci char *tail_data; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci partial = offset_in_page(inode->i_size); 3458c2ecf20Sopenharmony_ci if (!partial) 3468c2ecf20Sopenharmony_ci return false; 3478c2ecf20Sopenharmony_ci last_page = inode->i_size >> PAGE_SHIFT; 3488c2ecf20Sopenharmony_ci blockptrs = (u32 *)(sbi->linear_virt_addr + OFFSET(inode)); 3498c2ecf20Sopenharmony_ci blockaddr = blockptrs[last_page] & ~CRAMFS_BLK_FLAGS; 3508c2ecf20Sopenharmony_ci blockaddr <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; 3518c2ecf20Sopenharmony_ci tail_data = sbi->linear_virt_addr + blockaddr + partial; 3528c2ecf20Sopenharmony_ci return memchr_inv(tail_data, 0, PAGE_SIZE - partial) ? true : false; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 3588c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(inode->i_sb); 3598c2ecf20Sopenharmony_ci unsigned int pages, max_pages, offset; 3608c2ecf20Sopenharmony_ci unsigned long address, pgoff = vma->vm_pgoff; 3618c2ecf20Sopenharmony_ci char *bailout_reason; 3628c2ecf20Sopenharmony_ci int ret; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = generic_file_readonly_mmap(file, vma); 3658c2ecf20Sopenharmony_ci if (ret) 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci * Now try to pre-populate ptes for this vma with a direct 3708c2ecf20Sopenharmony_ci * mapping avoiding memory allocation when possible. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Could COW work here? */ 3748c2ecf20Sopenharmony_ci bailout_reason = "vma is writable"; 3758c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_WRITE) 3768c2ecf20Sopenharmony_ci goto bailout; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 3798c2ecf20Sopenharmony_ci bailout_reason = "beyond file limit"; 3808c2ecf20Sopenharmony_ci if (pgoff >= max_pages) 3818c2ecf20Sopenharmony_ci goto bailout; 3828c2ecf20Sopenharmony_ci pages = min(vma_pages(vma), max_pages - pgoff); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci offset = cramfs_get_block_range(inode, pgoff, &pages); 3858c2ecf20Sopenharmony_ci bailout_reason = "unsuitable block layout"; 3868c2ecf20Sopenharmony_ci if (!offset) 3878c2ecf20Sopenharmony_ci goto bailout; 3888c2ecf20Sopenharmony_ci address = sbi->linear_phys_addr + offset; 3898c2ecf20Sopenharmony_ci bailout_reason = "data is not page aligned"; 3908c2ecf20Sopenharmony_ci if (!PAGE_ALIGNED(address)) 3918c2ecf20Sopenharmony_ci goto bailout; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Don't map the last page if it contains some other data */ 3948c2ecf20Sopenharmony_ci if (pgoff + pages == max_pages && cramfs_last_page_is_shared(inode)) { 3958c2ecf20Sopenharmony_ci pr_debug("mmap: %s: last page is shared\n", 3968c2ecf20Sopenharmony_ci file_dentry(file)->d_name.name); 3978c2ecf20Sopenharmony_ci pages--; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!pages) { 4018c2ecf20Sopenharmony_ci bailout_reason = "no suitable block remaining"; 4028c2ecf20Sopenharmony_ci goto bailout; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (pages == vma_pages(vma)) { 4068c2ecf20Sopenharmony_ci /* 4078c2ecf20Sopenharmony_ci * The entire vma is mappable. remap_pfn_range() will 4088c2ecf20Sopenharmony_ci * make it distinguishable from a non-direct mapping 4098c2ecf20Sopenharmony_ci * in /proc/<pid>/maps by substituting the file offset 4108c2ecf20Sopenharmony_ci * with the actual physical address. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci ret = remap_pfn_range(vma, vma->vm_start, address >> PAGE_SHIFT, 4138c2ecf20Sopenharmony_ci pages * PAGE_SIZE, vma->vm_page_prot); 4148c2ecf20Sopenharmony_ci } else { 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * Let's create a mixed map if we can't map it all. 4178c2ecf20Sopenharmony_ci * The normal paging machinery will take care of the 4188c2ecf20Sopenharmony_ci * unpopulated ptes via cramfs_readpage(). 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci int i; 4218c2ecf20Sopenharmony_ci vma->vm_flags |= VM_MIXEDMAP; 4228c2ecf20Sopenharmony_ci for (i = 0; i < pages && !ret; i++) { 4238c2ecf20Sopenharmony_ci vm_fault_t vmf; 4248c2ecf20Sopenharmony_ci unsigned long off = i * PAGE_SIZE; 4258c2ecf20Sopenharmony_ci pfn_t pfn = phys_to_pfn_t(address + off, PFN_DEV); 4268c2ecf20Sopenharmony_ci vmf = vmf_insert_mixed(vma, vma->vm_start + off, pfn); 4278c2ecf20Sopenharmony_ci if (vmf & VM_FAULT_ERROR) 4288c2ecf20Sopenharmony_ci ret = vm_fault_to_errno(vmf, 0); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!ret) 4338c2ecf20Sopenharmony_ci pr_debug("mapped %s[%lu] at 0x%08lx (%u/%lu pages) " 4348c2ecf20Sopenharmony_ci "to vma 0x%08lx, page_prot 0x%llx\n", 4358c2ecf20Sopenharmony_ci file_dentry(file)->d_name.name, pgoff, 4368c2ecf20Sopenharmony_ci address, pages, vma_pages(vma), vma->vm_start, 4378c2ecf20Sopenharmony_ci (unsigned long long)pgprot_val(vma->vm_page_prot)); 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cibailout: 4418c2ecf20Sopenharmony_ci pr_debug("%s[%lu]: direct mmap impossible: %s\n", 4428c2ecf20Sopenharmony_ci file_dentry(file)->d_name.name, pgoff, bailout_reason); 4438c2ecf20Sopenharmony_ci /* Didn't manage any direct map, but normal paging is still possible */ 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci#else /* CONFIG_MMU */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic unsigned long cramfs_physmem_get_unmapped_area(struct file *file, 4558c2ecf20Sopenharmony_ci unsigned long addr, unsigned long len, 4568c2ecf20Sopenharmony_ci unsigned long pgoff, unsigned long flags) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 4598c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 4608c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 4618c2ecf20Sopenharmony_ci unsigned int pages, block_pages, max_pages, offset; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; 4648c2ecf20Sopenharmony_ci max_pages = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 4658c2ecf20Sopenharmony_ci if (pgoff >= max_pages || pages > max_pages - pgoff) 4668c2ecf20Sopenharmony_ci return -EINVAL; 4678c2ecf20Sopenharmony_ci block_pages = pages; 4688c2ecf20Sopenharmony_ci offset = cramfs_get_block_range(inode, pgoff, &block_pages); 4698c2ecf20Sopenharmony_ci if (!offset || block_pages != pages) 4708c2ecf20Sopenharmony_ci return -ENOSYS; 4718c2ecf20Sopenharmony_ci addr = sbi->linear_phys_addr + offset; 4728c2ecf20Sopenharmony_ci pr_debug("get_unmapped for %s ofs %#lx siz %lu at 0x%08lx\n", 4738c2ecf20Sopenharmony_ci file_dentry(file)->d_name.name, pgoff*PAGE_SIZE, len, addr); 4748c2ecf20Sopenharmony_ci return addr; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic unsigned int cramfs_physmem_mmap_capabilities(struct file *file) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | 4808c2ecf20Sopenharmony_ci NOMMU_MAP_READ | NOMMU_MAP_EXEC; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic const struct file_operations cramfs_physmem_fops = { 4868c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 4878c2ecf20Sopenharmony_ci .read_iter = generic_file_read_iter, 4888c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 4898c2ecf20Sopenharmony_ci .mmap = cramfs_physmem_mmap, 4908c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 4918c2ecf20Sopenharmony_ci .get_unmapped_area = cramfs_physmem_get_unmapped_area, 4928c2ecf20Sopenharmony_ci .mmap_capabilities = cramfs_physmem_mmap_capabilities, 4938c2ecf20Sopenharmony_ci#endif 4948c2ecf20Sopenharmony_ci}; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void cramfs_kill_sb(struct super_block *sb) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CRAMFS_MTD) && sb->s_mtd) { 5018c2ecf20Sopenharmony_ci if (sbi && sbi->mtd_point_size) 5028c2ecf20Sopenharmony_ci mtd_unpoint(sb->s_mtd, 0, sbi->mtd_point_size); 5038c2ecf20Sopenharmony_ci kill_mtd_super(sb); 5048c2ecf20Sopenharmony_ci } else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV) && sb->s_bdev) { 5058c2ecf20Sopenharmony_ci kill_block_super(sb); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci kfree(sbi); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int cramfs_reconfigure(struct fs_context *fc) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci sync_filesystem(fc->root->d_sb); 5138c2ecf20Sopenharmony_ci fc->sb_flags |= SB_RDONLY; 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int cramfs_read_super(struct super_block *sb, struct fs_context *fc, 5188c2ecf20Sopenharmony_ci struct cramfs_super *super) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi = CRAMFS_SB(sb); 5218c2ecf20Sopenharmony_ci unsigned long root_offset; 5228c2ecf20Sopenharmony_ci bool silent = fc->sb_flags & SB_SILENT; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* We don't know the real size yet */ 5258c2ecf20Sopenharmony_ci sbi->size = PAGE_SIZE; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Read the first block and get the superblock from it */ 5288c2ecf20Sopenharmony_ci mutex_lock(&read_mutex); 5298c2ecf20Sopenharmony_ci memcpy(super, cramfs_read(sb, 0, sizeof(*super)), sizeof(*super)); 5308c2ecf20Sopenharmony_ci mutex_unlock(&read_mutex); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* Do sanity checks on the superblock */ 5338c2ecf20Sopenharmony_ci if (super->magic != CRAMFS_MAGIC) { 5348c2ecf20Sopenharmony_ci /* check for wrong endianness */ 5358c2ecf20Sopenharmony_ci if (super->magic == CRAMFS_MAGIC_WEND) { 5368c2ecf20Sopenharmony_ci if (!silent) 5378c2ecf20Sopenharmony_ci errorfc(fc, "wrong endianness"); 5388c2ecf20Sopenharmony_ci return -EINVAL; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* check at 512 byte offset */ 5428c2ecf20Sopenharmony_ci mutex_lock(&read_mutex); 5438c2ecf20Sopenharmony_ci memcpy(super, 5448c2ecf20Sopenharmony_ci cramfs_read(sb, 512, sizeof(*super)), 5458c2ecf20Sopenharmony_ci sizeof(*super)); 5468c2ecf20Sopenharmony_ci mutex_unlock(&read_mutex); 5478c2ecf20Sopenharmony_ci if (super->magic != CRAMFS_MAGIC) { 5488c2ecf20Sopenharmony_ci if (super->magic == CRAMFS_MAGIC_WEND && !silent) 5498c2ecf20Sopenharmony_ci errorfc(fc, "wrong endianness"); 5508c2ecf20Sopenharmony_ci else if (!silent) 5518c2ecf20Sopenharmony_ci errorfc(fc, "wrong magic"); 5528c2ecf20Sopenharmony_ci return -EINVAL; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* get feature flags first */ 5578c2ecf20Sopenharmony_ci if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) { 5588c2ecf20Sopenharmony_ci errorfc(fc, "unsupported filesystem features"); 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Check that the root inode is in a sane state */ 5638c2ecf20Sopenharmony_ci if (!S_ISDIR(super->root.mode)) { 5648c2ecf20Sopenharmony_ci errorfc(fc, "root is not a directory"); 5658c2ecf20Sopenharmony_ci return -EINVAL; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci /* correct strange, hard-coded permissions of mkcramfs */ 5688c2ecf20Sopenharmony_ci super->root.mode |= 0555; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci root_offset = super->root.offset << 2; 5718c2ecf20Sopenharmony_ci if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { 5728c2ecf20Sopenharmony_ci sbi->size = super->size; 5738c2ecf20Sopenharmony_ci sbi->blocks = super->fsid.blocks; 5748c2ecf20Sopenharmony_ci sbi->files = super->fsid.files; 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci sbi->size = 1<<28; 5778c2ecf20Sopenharmony_ci sbi->blocks = 0; 5788c2ecf20Sopenharmony_ci sbi->files = 0; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci sbi->magic = super->magic; 5818c2ecf20Sopenharmony_ci sbi->flags = super->flags; 5828c2ecf20Sopenharmony_ci if (root_offset == 0) 5838c2ecf20Sopenharmony_ci infofc(fc, "empty filesystem"); 5848c2ecf20Sopenharmony_ci else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && 5858c2ecf20Sopenharmony_ci ((root_offset != sizeof(struct cramfs_super)) && 5868c2ecf20Sopenharmony_ci (root_offset != 512 + sizeof(struct cramfs_super)))) 5878c2ecf20Sopenharmony_ci { 5888c2ecf20Sopenharmony_ci errorfc(fc, "bad root offset %lu", root_offset); 5898c2ecf20Sopenharmony_ci return -EINVAL; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int cramfs_finalize_super(struct super_block *sb, 5968c2ecf20Sopenharmony_ci struct cramfs_inode *cramfs_root) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct inode *root; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Set it all up.. */ 6018c2ecf20Sopenharmony_ci sb->s_flags |= SB_RDONLY; 6028c2ecf20Sopenharmony_ci sb->s_time_min = 0; 6038c2ecf20Sopenharmony_ci sb->s_time_max = 0; 6048c2ecf20Sopenharmony_ci sb->s_op = &cramfs_ops; 6058c2ecf20Sopenharmony_ci root = get_cramfs_inode(sb, cramfs_root, 0); 6068c2ecf20Sopenharmony_ci if (IS_ERR(root)) 6078c2ecf20Sopenharmony_ci return PTR_ERR(root); 6088c2ecf20Sopenharmony_ci sb->s_root = d_make_root(root); 6098c2ecf20Sopenharmony_ci if (!sb->s_root) 6108c2ecf20Sopenharmony_ci return -ENOMEM; 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int cramfs_blkdev_fill_super(struct super_block *sb, struct fs_context *fc) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi; 6178c2ecf20Sopenharmony_ci struct cramfs_super super; 6188c2ecf20Sopenharmony_ci int i, err; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); 6218c2ecf20Sopenharmony_ci if (!sbi) 6228c2ecf20Sopenharmony_ci return -ENOMEM; 6238c2ecf20Sopenharmony_ci sb->s_fs_info = sbi; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Invalidate the read buffers on mount: think disk change.. */ 6268c2ecf20Sopenharmony_ci for (i = 0; i < READ_BUFFERS; i++) 6278c2ecf20Sopenharmony_ci buffer_blocknr[i] = -1; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci err = cramfs_read_super(sb, fc, &super); 6308c2ecf20Sopenharmony_ci if (err) 6318c2ecf20Sopenharmony_ci return err; 6328c2ecf20Sopenharmony_ci return cramfs_finalize_super(sb, &super.root); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int cramfs_mtd_fill_super(struct super_block *sb, struct fs_context *fc) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct cramfs_sb_info *sbi; 6388c2ecf20Sopenharmony_ci struct cramfs_super super; 6398c2ecf20Sopenharmony_ci int err; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); 6428c2ecf20Sopenharmony_ci if (!sbi) 6438c2ecf20Sopenharmony_ci return -ENOMEM; 6448c2ecf20Sopenharmony_ci sb->s_fs_info = sbi; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Map only one page for now. Will remap it when fs size is known. */ 6478c2ecf20Sopenharmony_ci err = mtd_point(sb->s_mtd, 0, PAGE_SIZE, &sbi->mtd_point_size, 6488c2ecf20Sopenharmony_ci &sbi->linear_virt_addr, &sbi->linear_phys_addr); 6498c2ecf20Sopenharmony_ci if (err || sbi->mtd_point_size != PAGE_SIZE) { 6508c2ecf20Sopenharmony_ci pr_err("unable to get direct memory access to mtd:%s\n", 6518c2ecf20Sopenharmony_ci sb->s_mtd->name); 6528c2ecf20Sopenharmony_ci return err ? : -ENODATA; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci pr_info("checking physical address %pap for linear cramfs image\n", 6568c2ecf20Sopenharmony_ci &sbi->linear_phys_addr); 6578c2ecf20Sopenharmony_ci err = cramfs_read_super(sb, fc, &super); 6588c2ecf20Sopenharmony_ci if (err) 6598c2ecf20Sopenharmony_ci return err; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Remap the whole filesystem now */ 6628c2ecf20Sopenharmony_ci pr_info("linear cramfs image on mtd:%s appears to be %lu KB in size\n", 6638c2ecf20Sopenharmony_ci sb->s_mtd->name, sbi->size/1024); 6648c2ecf20Sopenharmony_ci mtd_unpoint(sb->s_mtd, 0, PAGE_SIZE); 6658c2ecf20Sopenharmony_ci err = mtd_point(sb->s_mtd, 0, sbi->size, &sbi->mtd_point_size, 6668c2ecf20Sopenharmony_ci &sbi->linear_virt_addr, &sbi->linear_phys_addr); 6678c2ecf20Sopenharmony_ci if (err || sbi->mtd_point_size != sbi->size) { 6688c2ecf20Sopenharmony_ci pr_err("unable to get direct memory access to mtd:%s\n", 6698c2ecf20Sopenharmony_ci sb->s_mtd->name); 6708c2ecf20Sopenharmony_ci return err ? : -ENODATA; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return cramfs_finalize_super(sb, &super.root); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct super_block *sb = dentry->d_sb; 6798c2ecf20Sopenharmony_ci u64 id = 0; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (sb->s_bdev) 6828c2ecf20Sopenharmony_ci id = huge_encode_dev(sb->s_bdev->bd_dev); 6838c2ecf20Sopenharmony_ci else if (sb->s_dev) 6848c2ecf20Sopenharmony_ci id = huge_encode_dev(sb->s_dev); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci buf->f_type = CRAMFS_MAGIC; 6878c2ecf20Sopenharmony_ci buf->f_bsize = PAGE_SIZE; 6888c2ecf20Sopenharmony_ci buf->f_blocks = CRAMFS_SB(sb)->blocks; 6898c2ecf20Sopenharmony_ci buf->f_bfree = 0; 6908c2ecf20Sopenharmony_ci buf->f_bavail = 0; 6918c2ecf20Sopenharmony_ci buf->f_files = CRAMFS_SB(sb)->files; 6928c2ecf20Sopenharmony_ci buf->f_ffree = 0; 6938c2ecf20Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 6948c2ecf20Sopenharmony_ci buf->f_namelen = CRAMFS_MAXPATHLEN; 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/* 6998c2ecf20Sopenharmony_ci * Read a cramfs directory entry. 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_cistatic int cramfs_readdir(struct file *file, struct dir_context *ctx) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 7048c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 7058c2ecf20Sopenharmony_ci char *buf; 7068c2ecf20Sopenharmony_ci unsigned int offset; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Offset within the thing. */ 7098c2ecf20Sopenharmony_ci if (ctx->pos >= inode->i_size) 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci offset = ctx->pos; 7128c2ecf20Sopenharmony_ci /* Directory entries are always 4-byte aligned */ 7138c2ecf20Sopenharmony_ci if (offset & 3) 7148c2ecf20Sopenharmony_ci return -EINVAL; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci buf = kmalloc(CRAMFS_MAXPATHLEN, GFP_KERNEL); 7178c2ecf20Sopenharmony_ci if (!buf) 7188c2ecf20Sopenharmony_ci return -ENOMEM; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci while (offset < inode->i_size) { 7218c2ecf20Sopenharmony_ci struct cramfs_inode *de; 7228c2ecf20Sopenharmony_ci unsigned long nextoffset; 7238c2ecf20Sopenharmony_ci char *name; 7248c2ecf20Sopenharmony_ci ino_t ino; 7258c2ecf20Sopenharmony_ci umode_t mode; 7268c2ecf20Sopenharmony_ci int namelen; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci mutex_lock(&read_mutex); 7298c2ecf20Sopenharmony_ci de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); 7308c2ecf20Sopenharmony_ci name = (char *)(de+1); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* 7338c2ecf20Sopenharmony_ci * Namelengths on disk are shifted by two 7348c2ecf20Sopenharmony_ci * and the name padded out to 4-byte boundaries 7358c2ecf20Sopenharmony_ci * with zeroes. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci namelen = de->namelen << 2; 7388c2ecf20Sopenharmony_ci memcpy(buf, name, namelen); 7398c2ecf20Sopenharmony_ci ino = cramino(de, OFFSET(inode) + offset); 7408c2ecf20Sopenharmony_ci mode = de->mode; 7418c2ecf20Sopenharmony_ci mutex_unlock(&read_mutex); 7428c2ecf20Sopenharmony_ci nextoffset = offset + sizeof(*de) + namelen; 7438c2ecf20Sopenharmony_ci for (;;) { 7448c2ecf20Sopenharmony_ci if (!namelen) { 7458c2ecf20Sopenharmony_ci kfree(buf); 7468c2ecf20Sopenharmony_ci return -EIO; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci if (buf[namelen-1]) 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci namelen--; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci if (!dir_emit(ctx, buf, namelen, ino, mode >> 12)) 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ctx->pos = offset = nextoffset; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci kfree(buf); 7588c2ecf20Sopenharmony_ci return 0; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci/* 7628c2ecf20Sopenharmony_ci * Lookup and fill in the inode data.. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_cistatic struct dentry *cramfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci unsigned int offset = 0; 7678c2ecf20Sopenharmony_ci struct inode *inode = NULL; 7688c2ecf20Sopenharmony_ci int sorted; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci mutex_lock(&read_mutex); 7718c2ecf20Sopenharmony_ci sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS; 7728c2ecf20Sopenharmony_ci while (offset < dir->i_size) { 7738c2ecf20Sopenharmony_ci struct cramfs_inode *de; 7748c2ecf20Sopenharmony_ci char *name; 7758c2ecf20Sopenharmony_ci int namelen, retval; 7768c2ecf20Sopenharmony_ci int dir_off = OFFSET(dir) + offset; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN); 7798c2ecf20Sopenharmony_ci name = (char *)(de+1); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* Try to take advantage of sorted directories */ 7828c2ecf20Sopenharmony_ci if (sorted && (dentry->d_name.name[0] < name[0])) 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci namelen = de->namelen << 2; 7868c2ecf20Sopenharmony_ci offset += sizeof(*de) + namelen; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Quick check that the name is roughly the right length */ 7898c2ecf20Sopenharmony_ci if (((dentry->d_name.len + 3) & ~3) != namelen) 7908c2ecf20Sopenharmony_ci continue; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci for (;;) { 7938c2ecf20Sopenharmony_ci if (!namelen) { 7948c2ecf20Sopenharmony_ci inode = ERR_PTR(-EIO); 7958c2ecf20Sopenharmony_ci goto out; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci if (name[namelen-1]) 7988c2ecf20Sopenharmony_ci break; 7998c2ecf20Sopenharmony_ci namelen--; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci if (namelen != dentry->d_name.len) 8028c2ecf20Sopenharmony_ci continue; 8038c2ecf20Sopenharmony_ci retval = memcmp(dentry->d_name.name, name, namelen); 8048c2ecf20Sopenharmony_ci if (retval > 0) 8058c2ecf20Sopenharmony_ci continue; 8068c2ecf20Sopenharmony_ci if (!retval) { 8078c2ecf20Sopenharmony_ci inode = get_cramfs_inode(dir->i_sb, de, dir_off); 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci /* else (retval < 0) */ 8118c2ecf20Sopenharmony_ci if (sorted) 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ciout: 8158c2ecf20Sopenharmony_ci mutex_unlock(&read_mutex); 8168c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int cramfs_readpage(struct file *file, struct page *page) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 8228c2ecf20Sopenharmony_ci u32 maxblock; 8238c2ecf20Sopenharmony_ci int bytes_filled; 8248c2ecf20Sopenharmony_ci void *pgdata; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci maxblock = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 8278c2ecf20Sopenharmony_ci bytes_filled = 0; 8288c2ecf20Sopenharmony_ci pgdata = kmap(page); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (page->index < maxblock) { 8318c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 8328c2ecf20Sopenharmony_ci u32 blkptr_offset = OFFSET(inode) + page->index * 4; 8338c2ecf20Sopenharmony_ci u32 block_ptr, block_start, block_len; 8348c2ecf20Sopenharmony_ci bool uncompressed, direct; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci mutex_lock(&read_mutex); 8378c2ecf20Sopenharmony_ci block_ptr = *(u32 *) cramfs_read(sb, blkptr_offset, 4); 8388c2ecf20Sopenharmony_ci uncompressed = (block_ptr & CRAMFS_BLK_FLAG_UNCOMPRESSED); 8398c2ecf20Sopenharmony_ci direct = (block_ptr & CRAMFS_BLK_FLAG_DIRECT_PTR); 8408c2ecf20Sopenharmony_ci block_ptr &= ~CRAMFS_BLK_FLAGS; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (direct) { 8438c2ecf20Sopenharmony_ci /* 8448c2ecf20Sopenharmony_ci * The block pointer is an absolute start pointer, 8458c2ecf20Sopenharmony_ci * shifted by 2 bits. The size is included in the 8468c2ecf20Sopenharmony_ci * first 2 bytes of the data block when compressed, 8478c2ecf20Sopenharmony_ci * or PAGE_SIZE otherwise. 8488c2ecf20Sopenharmony_ci */ 8498c2ecf20Sopenharmony_ci block_start = block_ptr << CRAMFS_BLK_DIRECT_PTR_SHIFT; 8508c2ecf20Sopenharmony_ci if (uncompressed) { 8518c2ecf20Sopenharmony_ci block_len = PAGE_SIZE; 8528c2ecf20Sopenharmony_ci /* if last block: cap to file length */ 8538c2ecf20Sopenharmony_ci if (page->index == maxblock - 1) 8548c2ecf20Sopenharmony_ci block_len = 8558c2ecf20Sopenharmony_ci offset_in_page(inode->i_size); 8568c2ecf20Sopenharmony_ci } else { 8578c2ecf20Sopenharmony_ci block_len = *(u16 *) 8588c2ecf20Sopenharmony_ci cramfs_read(sb, block_start, 2); 8598c2ecf20Sopenharmony_ci block_start += 2; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci } else { 8628c2ecf20Sopenharmony_ci /* 8638c2ecf20Sopenharmony_ci * The block pointer indicates one past the end of 8648c2ecf20Sopenharmony_ci * the current block (start of next block). If this 8658c2ecf20Sopenharmony_ci * is the first block then it starts where the block 8668c2ecf20Sopenharmony_ci * pointer table ends, otherwise its start comes 8678c2ecf20Sopenharmony_ci * from the previous block's pointer. 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_ci block_start = OFFSET(inode) + maxblock * 4; 8708c2ecf20Sopenharmony_ci if (page->index) 8718c2ecf20Sopenharmony_ci block_start = *(u32 *) 8728c2ecf20Sopenharmony_ci cramfs_read(sb, blkptr_offset - 4, 4); 8738c2ecf20Sopenharmony_ci /* Beware... previous ptr might be a direct ptr */ 8748c2ecf20Sopenharmony_ci if (unlikely(block_start & CRAMFS_BLK_FLAG_DIRECT_PTR)) { 8758c2ecf20Sopenharmony_ci /* See comments on earlier code. */ 8768c2ecf20Sopenharmony_ci u32 prev_start = block_start; 8778c2ecf20Sopenharmony_ci block_start = prev_start & ~CRAMFS_BLK_FLAGS; 8788c2ecf20Sopenharmony_ci block_start <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; 8798c2ecf20Sopenharmony_ci if (prev_start & CRAMFS_BLK_FLAG_UNCOMPRESSED) { 8808c2ecf20Sopenharmony_ci block_start += PAGE_SIZE; 8818c2ecf20Sopenharmony_ci } else { 8828c2ecf20Sopenharmony_ci block_len = *(u16 *) 8838c2ecf20Sopenharmony_ci cramfs_read(sb, block_start, 2); 8848c2ecf20Sopenharmony_ci block_start += 2 + block_len; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci block_start &= ~CRAMFS_BLK_FLAGS; 8888c2ecf20Sopenharmony_ci block_len = block_ptr - block_start; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (block_len == 0) 8928c2ecf20Sopenharmony_ci ; /* hole */ 8938c2ecf20Sopenharmony_ci else if (unlikely(block_len > 2*PAGE_SIZE || 8948c2ecf20Sopenharmony_ci (uncompressed && block_len > PAGE_SIZE))) { 8958c2ecf20Sopenharmony_ci mutex_unlock(&read_mutex); 8968c2ecf20Sopenharmony_ci pr_err("bad data blocksize %u\n", block_len); 8978c2ecf20Sopenharmony_ci goto err; 8988c2ecf20Sopenharmony_ci } else if (uncompressed) { 8998c2ecf20Sopenharmony_ci memcpy(pgdata, 9008c2ecf20Sopenharmony_ci cramfs_read(sb, block_start, block_len), 9018c2ecf20Sopenharmony_ci block_len); 9028c2ecf20Sopenharmony_ci bytes_filled = block_len; 9038c2ecf20Sopenharmony_ci } else { 9048c2ecf20Sopenharmony_ci bytes_filled = cramfs_uncompress_block(pgdata, 9058c2ecf20Sopenharmony_ci PAGE_SIZE, 9068c2ecf20Sopenharmony_ci cramfs_read(sb, block_start, block_len), 9078c2ecf20Sopenharmony_ci block_len); 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci mutex_unlock(&read_mutex); 9108c2ecf20Sopenharmony_ci if (unlikely(bytes_filled < 0)) 9118c2ecf20Sopenharmony_ci goto err; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci memset(pgdata + bytes_filled, 0, PAGE_SIZE - bytes_filled); 9158c2ecf20Sopenharmony_ci flush_dcache_page(page); 9168c2ecf20Sopenharmony_ci kunmap(page); 9178c2ecf20Sopenharmony_ci SetPageUptodate(page); 9188c2ecf20Sopenharmony_ci unlock_page(page); 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cierr: 9228c2ecf20Sopenharmony_ci kunmap(page); 9238c2ecf20Sopenharmony_ci ClearPageUptodate(page); 9248c2ecf20Sopenharmony_ci SetPageError(page); 9258c2ecf20Sopenharmony_ci unlock_page(page); 9268c2ecf20Sopenharmony_ci return 0; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic const struct address_space_operations cramfs_aops = { 9308c2ecf20Sopenharmony_ci .readpage = cramfs_readpage 9318c2ecf20Sopenharmony_ci}; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci/* 9348c2ecf20Sopenharmony_ci * Our operations: 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci/* 9388c2ecf20Sopenharmony_ci * A directory can only readdir 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_cistatic const struct file_operations cramfs_directory_operations = { 9418c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 9428c2ecf20Sopenharmony_ci .read = generic_read_dir, 9438c2ecf20Sopenharmony_ci .iterate_shared = cramfs_readdir, 9448c2ecf20Sopenharmony_ci}; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic const struct inode_operations cramfs_dir_inode_operations = { 9478c2ecf20Sopenharmony_ci .lookup = cramfs_lookup, 9488c2ecf20Sopenharmony_ci}; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic const struct super_operations cramfs_ops = { 9518c2ecf20Sopenharmony_ci .statfs = cramfs_statfs, 9528c2ecf20Sopenharmony_ci}; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic int cramfs_get_tree(struct fs_context *fc) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci int ret = -ENOPROTOOPT; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CRAMFS_MTD)) { 9598c2ecf20Sopenharmony_ci ret = get_tree_mtd(fc, cramfs_mtd_fill_super); 9608c2ecf20Sopenharmony_ci if (!ret) 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) 9648c2ecf20Sopenharmony_ci ret = get_tree_bdev(fc, cramfs_blkdev_fill_super); 9658c2ecf20Sopenharmony_ci return ret; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic const struct fs_context_operations cramfs_context_ops = { 9698c2ecf20Sopenharmony_ci .get_tree = cramfs_get_tree, 9708c2ecf20Sopenharmony_ci .reconfigure = cramfs_reconfigure, 9718c2ecf20Sopenharmony_ci}; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci/* 9748c2ecf20Sopenharmony_ci * Set up the filesystem mount context. 9758c2ecf20Sopenharmony_ci */ 9768c2ecf20Sopenharmony_cistatic int cramfs_init_fs_context(struct fs_context *fc) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci fc->ops = &cramfs_context_ops; 9798c2ecf20Sopenharmony_ci return 0; 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic struct file_system_type cramfs_fs_type = { 9838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9848c2ecf20Sopenharmony_ci .name = "cramfs", 9858c2ecf20Sopenharmony_ci .init_fs_context = cramfs_init_fs_context, 9868c2ecf20Sopenharmony_ci .kill_sb = cramfs_kill_sb, 9878c2ecf20Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 9888c2ecf20Sopenharmony_ci}; 9898c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("cramfs"); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic int __init init_cramfs_fs(void) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci int rv; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci rv = cramfs_uncompress_init(); 9968c2ecf20Sopenharmony_ci if (rv < 0) 9978c2ecf20Sopenharmony_ci return rv; 9988c2ecf20Sopenharmony_ci rv = register_filesystem(&cramfs_fs_type); 9998c2ecf20Sopenharmony_ci if (rv < 0) 10008c2ecf20Sopenharmony_ci cramfs_uncompress_exit(); 10018c2ecf20Sopenharmony_ci return rv; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void __exit exit_cramfs_fs(void) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci cramfs_uncompress_exit(); 10078c2ecf20Sopenharmony_ci unregister_filesystem(&cramfs_fs_type); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cimodule_init(init_cramfs_fs) 10118c2ecf20Sopenharmony_cimodule_exit(exit_cramfs_fs) 10128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1013