18c2ecf20Sopenharmony_ci/* Block- or MTD-based romfs 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Copyright © 2007 Red Hat, Inc. All Rights Reserved. 48c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Derived from: ROMFS file system, Linux implementation 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright © 1997-1999 Janos Farkas <chexum@shadow.banki.hu> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Using parts of the minix filesystem 118c2ecf20Sopenharmony_ci * Copyright © 1991, 1992 Linus Torvalds 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * and parts of the affs filesystem additionally 148c2ecf20Sopenharmony_ci * Copyright © 1993 Ray Burr 158c2ecf20Sopenharmony_ci * Copyright © 1996 Hans-Joachim Widmaier 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Changes 188c2ecf20Sopenharmony_ci * Changed for 2.1.19 modules 198c2ecf20Sopenharmony_ci * Jan 1997 Initial release 208c2ecf20Sopenharmony_ci * Jun 1997 2.1.43+ changes 218c2ecf20Sopenharmony_ci * Proper page locking in readpage 228c2ecf20Sopenharmony_ci * Changed to work with 2.1.45+ fs 238c2ecf20Sopenharmony_ci * Jul 1997 Fixed follow_link 248c2ecf20Sopenharmony_ci * 2.1.47 258c2ecf20Sopenharmony_ci * lookup shouldn't return -ENOENT 268c2ecf20Sopenharmony_ci * from Horst von Brand: 278c2ecf20Sopenharmony_ci * fail on wrong checksum 288c2ecf20Sopenharmony_ci * double unlock_super was possible 298c2ecf20Sopenharmony_ci * correct namelen for statfs 308c2ecf20Sopenharmony_ci * spotted by Bill Hawes: 318c2ecf20Sopenharmony_ci * readlink shouldn't iput() 328c2ecf20Sopenharmony_ci * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir() 338c2ecf20Sopenharmony_ci * exposed a problem in readdir 348c2ecf20Sopenharmony_ci * 2.1.107 code-freeze spellchecker run 358c2ecf20Sopenharmony_ci * Aug 1998 2.1.118+ VFS changes 368c2ecf20Sopenharmony_ci * Sep 1998 2.1.122 another VFS change (follow_link) 378c2ecf20Sopenharmony_ci * Apr 1999 2.2.7 no more EBADF checking in 388c2ecf20Sopenharmony_ci * lookup/readdir, use ERR_PTR 398c2ecf20Sopenharmony_ci * Jun 1999 2.3.6 d_alloc_root use changed 408c2ecf20Sopenharmony_ci * 2.3.9 clean up usage of ENOENT/negative 418c2ecf20Sopenharmony_ci * dentries in lookup 428c2ecf20Sopenharmony_ci * clean up page flags setting 438c2ecf20Sopenharmony_ci * (error, uptodate, locking) in 448c2ecf20Sopenharmony_ci * in readpage 458c2ecf20Sopenharmony_ci * use init_special_inode for 468c2ecf20Sopenharmony_ci * fifos/sockets (and streamline) in 478c2ecf20Sopenharmony_ci * read_inode, fix _ops table order 488c2ecf20Sopenharmony_ci * Aug 1999 2.3.16 __initfunc() => __init change 498c2ecf20Sopenharmony_ci * Oct 1999 2.3.24 page->owner hack obsoleted 508c2ecf20Sopenharmony_ci * Nov 1999 2.3.27 2.3.25+ page->offset => index change 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 548c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public Licence 558c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 568c2ecf20Sopenharmony_ci * 2 of the Licence, or (at your option) any later version. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include <linux/module.h> 628c2ecf20Sopenharmony_ci#include <linux/string.h> 638c2ecf20Sopenharmony_ci#include <linux/fs.h> 648c2ecf20Sopenharmony_ci#include <linux/time.h> 658c2ecf20Sopenharmony_ci#include <linux/slab.h> 668c2ecf20Sopenharmony_ci#include <linux/init.h> 678c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 688c2ecf20Sopenharmony_ci#include <linux/fs_context.h> 698c2ecf20Sopenharmony_ci#include <linux/mount.h> 708c2ecf20Sopenharmony_ci#include <linux/namei.h> 718c2ecf20Sopenharmony_ci#include <linux/statfs.h> 728c2ecf20Sopenharmony_ci#include <linux/mtd/super.h> 738c2ecf20Sopenharmony_ci#include <linux/ctype.h> 748c2ecf20Sopenharmony_ci#include <linux/highmem.h> 758c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 768c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 778c2ecf20Sopenharmony_ci#include <linux/major.h> 788c2ecf20Sopenharmony_ci#include "internal.h" 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic struct kmem_cache *romfs_inode_cachep; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const umode_t romfs_modemap[8] = { 838c2ecf20Sopenharmony_ci 0, /* hard link */ 848c2ecf20Sopenharmony_ci S_IFDIR | 0644, /* directory */ 858c2ecf20Sopenharmony_ci S_IFREG | 0644, /* regular file */ 868c2ecf20Sopenharmony_ci S_IFLNK | 0777, /* symlink */ 878c2ecf20Sopenharmony_ci S_IFBLK | 0600, /* blockdev */ 888c2ecf20Sopenharmony_ci S_IFCHR | 0600, /* chardev */ 898c2ecf20Sopenharmony_ci S_IFSOCK | 0644, /* socket */ 908c2ecf20Sopenharmony_ci S_IFIFO | 0644 /* FIFO */ 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const unsigned char romfs_dtype_table[] = { 948c2ecf20Sopenharmony_ci DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct inode *romfs_iget(struct super_block *sb, unsigned long pos); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * read a page worth of data from the image 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic int romfs_readpage(struct file *file, struct page *page) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct inode *inode = page->mapping->host; 1058c2ecf20Sopenharmony_ci loff_t offset, size; 1068c2ecf20Sopenharmony_ci unsigned long fillsize, pos; 1078c2ecf20Sopenharmony_ci void *buf; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci buf = kmap(page); 1118c2ecf20Sopenharmony_ci if (!buf) 1128c2ecf20Sopenharmony_ci return -ENOMEM; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 32 bit warning -- but not for us :) */ 1158c2ecf20Sopenharmony_ci offset = page_offset(page); 1168c2ecf20Sopenharmony_ci size = i_size_read(inode); 1178c2ecf20Sopenharmony_ci fillsize = 0; 1188c2ecf20Sopenharmony_ci ret = 0; 1198c2ecf20Sopenharmony_ci if (offset < size) { 1208c2ecf20Sopenharmony_ci size -= offset; 1218c2ecf20Sopenharmony_ci fillsize = size > PAGE_SIZE ? PAGE_SIZE : size; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci pos = ROMFS_I(inode)->i_dataoffset + offset; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = romfs_dev_read(inode->i_sb, pos, buf, fillsize); 1268c2ecf20Sopenharmony_ci if (ret < 0) { 1278c2ecf20Sopenharmony_ci SetPageError(page); 1288c2ecf20Sopenharmony_ci fillsize = 0; 1298c2ecf20Sopenharmony_ci ret = -EIO; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (fillsize < PAGE_SIZE) 1348c2ecf20Sopenharmony_ci memset(buf + fillsize, 0, PAGE_SIZE - fillsize); 1358c2ecf20Sopenharmony_ci if (ret == 0) 1368c2ecf20Sopenharmony_ci SetPageUptodate(page); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci flush_dcache_page(page); 1398c2ecf20Sopenharmony_ci kunmap(page); 1408c2ecf20Sopenharmony_ci unlock_page(page); 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const struct address_space_operations romfs_aops = { 1458c2ecf20Sopenharmony_ci .readpage = romfs_readpage 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * read the entries from a directory 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int romfs_readdir(struct file *file, struct dir_context *ctx) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct inode *i = file_inode(file); 1548c2ecf20Sopenharmony_ci struct romfs_inode ri; 1558c2ecf20Sopenharmony_ci unsigned long offset, maxoff; 1568c2ecf20Sopenharmony_ci int j, ino, nextfh; 1578c2ecf20Sopenharmony_ci char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ 1588c2ecf20Sopenharmony_ci int ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci maxoff = romfs_maxsize(i->i_sb); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci offset = ctx->pos; 1638c2ecf20Sopenharmony_ci if (!offset) { 1648c2ecf20Sopenharmony_ci offset = i->i_ino & ROMFH_MASK; 1658c2ecf20Sopenharmony_ci ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); 1668c2ecf20Sopenharmony_ci if (ret < 0) 1678c2ecf20Sopenharmony_ci goto out; 1688c2ecf20Sopenharmony_ci offset = be32_to_cpu(ri.spec) & ROMFH_MASK; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Not really failsafe, but we are read-only... */ 1728c2ecf20Sopenharmony_ci for (;;) { 1738c2ecf20Sopenharmony_ci if (!offset || offset >= maxoff) { 1748c2ecf20Sopenharmony_ci offset = maxoff; 1758c2ecf20Sopenharmony_ci ctx->pos = offset; 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci ctx->pos = offset; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Fetch inode info */ 1818c2ecf20Sopenharmony_ci ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); 1828c2ecf20Sopenharmony_ci if (ret < 0) 1838c2ecf20Sopenharmony_ci goto out; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE, 1868c2ecf20Sopenharmony_ci sizeof(fsname) - 1); 1878c2ecf20Sopenharmony_ci if (j < 0) 1888c2ecf20Sopenharmony_ci goto out; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j); 1918c2ecf20Sopenharmony_ci if (ret < 0) 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci fsname[j] = '\0'; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ino = offset; 1968c2ecf20Sopenharmony_ci nextfh = be32_to_cpu(ri.next); 1978c2ecf20Sopenharmony_ci if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) 1988c2ecf20Sopenharmony_ci ino = be32_to_cpu(ri.spec); 1998c2ecf20Sopenharmony_ci if (!dir_emit(ctx, fsname, j, ino, 2008c2ecf20Sopenharmony_ci romfs_dtype_table[nextfh & ROMFH_TYPE])) 2018c2ecf20Sopenharmony_ci goto out; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci offset = nextfh & ROMFH_MASK; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ciout: 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * look up an entry in a directory 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry, 2138c2ecf20Sopenharmony_ci unsigned int flags) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned long offset, maxoff; 2168c2ecf20Sopenharmony_ci struct inode *inode = NULL; 2178c2ecf20Sopenharmony_ci struct romfs_inode ri; 2188c2ecf20Sopenharmony_ci const char *name; /* got from dentry */ 2198c2ecf20Sopenharmony_ci int len, ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci offset = dir->i_ino & ROMFH_MASK; 2228c2ecf20Sopenharmony_ci ret = romfs_dev_read(dir->i_sb, offset, &ri, ROMFH_SIZE); 2238c2ecf20Sopenharmony_ci if (ret < 0) 2248c2ecf20Sopenharmony_ci goto error; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* search all the file entries in the list starting from the one 2278c2ecf20Sopenharmony_ci * pointed to by the directory's special data */ 2288c2ecf20Sopenharmony_ci maxoff = romfs_maxsize(dir->i_sb); 2298c2ecf20Sopenharmony_ci offset = be32_to_cpu(ri.spec) & ROMFH_MASK; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci name = dentry->d_name.name; 2328c2ecf20Sopenharmony_ci len = dentry->d_name.len; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (;;) { 2358c2ecf20Sopenharmony_ci if (!offset || offset >= maxoff) 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci ret = romfs_dev_read(dir->i_sb, offset, &ri, sizeof(ri)); 2398c2ecf20Sopenharmony_ci if (ret < 0) 2408c2ecf20Sopenharmony_ci goto error; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* try to match the first 16 bytes of name */ 2438c2ecf20Sopenharmony_ci ret = romfs_dev_strcmp(dir->i_sb, offset + ROMFH_SIZE, name, 2448c2ecf20Sopenharmony_ci len); 2458c2ecf20Sopenharmony_ci if (ret < 0) 2468c2ecf20Sopenharmony_ci goto error; 2478c2ecf20Sopenharmony_ci if (ret == 1) { 2488c2ecf20Sopenharmony_ci /* Hard link handling */ 2498c2ecf20Sopenharmony_ci if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) 2508c2ecf20Sopenharmony_ci offset = be32_to_cpu(ri.spec) & ROMFH_MASK; 2518c2ecf20Sopenharmony_ci inode = romfs_iget(dir->i_sb, offset); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* next entry */ 2568c2ecf20Sopenharmony_ci offset = be32_to_cpu(ri.next) & ROMFH_MASK; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 2608c2ecf20Sopenharmony_cierror: 2618c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const struct file_operations romfs_dir_operations = { 2658c2ecf20Sopenharmony_ci .read = generic_read_dir, 2668c2ecf20Sopenharmony_ci .iterate_shared = romfs_readdir, 2678c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic const struct inode_operations romfs_dir_inode_operations = { 2718c2ecf20Sopenharmony_ci .lookup = romfs_lookup, 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * get a romfs inode based on its position in the image (which doubles as the 2768c2ecf20Sopenharmony_ci * inode number) 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic struct inode *romfs_iget(struct super_block *sb, unsigned long pos) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct romfs_inode_info *inode; 2818c2ecf20Sopenharmony_ci struct romfs_inode ri; 2828c2ecf20Sopenharmony_ci struct inode *i; 2838c2ecf20Sopenharmony_ci unsigned long nlen; 2848c2ecf20Sopenharmony_ci unsigned nextfh; 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci umode_t mode; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* we might have to traverse a chain of "hard link" file entries to get 2898c2ecf20Sopenharmony_ci * to the actual file */ 2908c2ecf20Sopenharmony_ci for (;;) { 2918c2ecf20Sopenharmony_ci ret = romfs_dev_read(sb, pos, &ri, sizeof(ri)); 2928c2ecf20Sopenharmony_ci if (ret < 0) 2938c2ecf20Sopenharmony_ci goto error; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* XXX: do romfs_checksum here too (with name) */ 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci nextfh = be32_to_cpu(ri.next); 2988c2ecf20Sopenharmony_ci if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci pos = be32_to_cpu(ri.spec) & ROMFH_MASK; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* determine the length of the filename */ 3058c2ecf20Sopenharmony_ci nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN); 3068c2ecf20Sopenharmony_ci if (IS_ERR_VALUE(nlen)) 3078c2ecf20Sopenharmony_ci goto eio; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* get an inode for this image position */ 3108c2ecf20Sopenharmony_ci i = iget_locked(sb, pos); 3118c2ecf20Sopenharmony_ci if (!i) 3128c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (!(i->i_state & I_NEW)) 3158c2ecf20Sopenharmony_ci return i; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* precalculate the data offset */ 3188c2ecf20Sopenharmony_ci inode = ROMFS_I(i); 3198c2ecf20Sopenharmony_ci inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK; 3208c2ecf20Sopenharmony_ci inode->i_dataoffset = pos + inode->i_metasize; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci set_nlink(i, 1); /* Hard to decide.. */ 3238c2ecf20Sopenharmony_ci i->i_size = be32_to_cpu(ri.size); 3248c2ecf20Sopenharmony_ci i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0; 3258c2ecf20Sopenharmony_ci i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* set up mode and ops */ 3288c2ecf20Sopenharmony_ci mode = romfs_modemap[nextfh & ROMFH_TYPE]; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci switch (nextfh & ROMFH_TYPE) { 3318c2ecf20Sopenharmony_ci case ROMFH_DIR: 3328c2ecf20Sopenharmony_ci i->i_size = ROMFS_I(i)->i_metasize; 3338c2ecf20Sopenharmony_ci i->i_op = &romfs_dir_inode_operations; 3348c2ecf20Sopenharmony_ci i->i_fop = &romfs_dir_operations; 3358c2ecf20Sopenharmony_ci if (nextfh & ROMFH_EXEC) 3368c2ecf20Sopenharmony_ci mode |= S_IXUGO; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci case ROMFH_REG: 3398c2ecf20Sopenharmony_ci i->i_fop = &romfs_ro_fops; 3408c2ecf20Sopenharmony_ci i->i_data.a_ops = &romfs_aops; 3418c2ecf20Sopenharmony_ci if (nextfh & ROMFH_EXEC) 3428c2ecf20Sopenharmony_ci mode |= S_IXUGO; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci case ROMFH_SYM: 3458c2ecf20Sopenharmony_ci i->i_op = &page_symlink_inode_operations; 3468c2ecf20Sopenharmony_ci inode_nohighmem(i); 3478c2ecf20Sopenharmony_ci i->i_data.a_ops = &romfs_aops; 3488c2ecf20Sopenharmony_ci mode |= S_IRWXUGO; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci default: 3518c2ecf20Sopenharmony_ci /* depending on MBZ for sock/fifos */ 3528c2ecf20Sopenharmony_ci nextfh = be32_to_cpu(ri.spec); 3538c2ecf20Sopenharmony_ci init_special_inode(i, mode, MKDEV(nextfh >> 16, 3548c2ecf20Sopenharmony_ci nextfh & 0xffff)); 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci i->i_mode = mode; 3598c2ecf20Sopenharmony_ci i->i_blocks = (i->i_size + 511) >> 9; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci unlock_new_inode(i); 3628c2ecf20Sopenharmony_ci return i; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cieio: 3658c2ecf20Sopenharmony_ci ret = -EIO; 3668c2ecf20Sopenharmony_cierror: 3678c2ecf20Sopenharmony_ci pr_err("read error for inode 0x%lx\n", pos); 3688c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/* 3728c2ecf20Sopenharmony_ci * allocate a new inode 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_cistatic struct inode *romfs_alloc_inode(struct super_block *sb) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct romfs_inode_info *inode; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci inode = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL); 3798c2ecf20Sopenharmony_ci return inode ? &inode->vfs_inode : NULL; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * return a spent inode to the slab cache 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistatic void romfs_free_inode(struct inode *inode) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/* 3918c2ecf20Sopenharmony_ci * get filesystem statistics 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_cistatic int romfs_statfs(struct dentry *dentry, struct kstatfs *buf) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct super_block *sb = dentry->d_sb; 3968c2ecf20Sopenharmony_ci u64 id = 0; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* When calling huge_encode_dev(), 3998c2ecf20Sopenharmony_ci * use sb->s_bdev->bd_dev when, 4008c2ecf20Sopenharmony_ci * - CONFIG_ROMFS_ON_BLOCK defined 4018c2ecf20Sopenharmony_ci * use sb->s_dev when, 4028c2ecf20Sopenharmony_ci * - CONFIG_ROMFS_ON_BLOCK undefined and 4038c2ecf20Sopenharmony_ci * - CONFIG_ROMFS_ON_MTD defined 4048c2ecf20Sopenharmony_ci * leave id as 0 when, 4058c2ecf20Sopenharmony_ci * - CONFIG_ROMFS_ON_BLOCK undefined and 4068c2ecf20Sopenharmony_ci * - CONFIG_ROMFS_ON_MTD undefined 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci if (sb->s_bdev) 4098c2ecf20Sopenharmony_ci id = huge_encode_dev(sb->s_bdev->bd_dev); 4108c2ecf20Sopenharmony_ci else if (sb->s_dev) 4118c2ecf20Sopenharmony_ci id = huge_encode_dev(sb->s_dev); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci buf->f_type = ROMFS_MAGIC; 4148c2ecf20Sopenharmony_ci buf->f_namelen = ROMFS_MAXFN; 4158c2ecf20Sopenharmony_ci buf->f_bsize = ROMBSIZE; 4168c2ecf20Sopenharmony_ci buf->f_bfree = buf->f_bavail = buf->f_ffree; 4178c2ecf20Sopenharmony_ci buf->f_blocks = 4188c2ecf20Sopenharmony_ci (romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS; 4198c2ecf20Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* 4248c2ecf20Sopenharmony_ci * remounting must involve read-only 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_cistatic int romfs_reconfigure(struct fs_context *fc) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci sync_filesystem(fc->root->d_sb); 4298c2ecf20Sopenharmony_ci fc->sb_flags |= SB_RDONLY; 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct super_operations romfs_super_ops = { 4348c2ecf20Sopenharmony_ci .alloc_inode = romfs_alloc_inode, 4358c2ecf20Sopenharmony_ci .free_inode = romfs_free_inode, 4368c2ecf20Sopenharmony_ci .statfs = romfs_statfs, 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* 4408c2ecf20Sopenharmony_ci * checksum check on part of a romfs filesystem 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic __u32 romfs_checksum(const void *data, int size) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci const __be32 *ptr = data; 4458c2ecf20Sopenharmony_ci __u32 sum; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci sum = 0; 4488c2ecf20Sopenharmony_ci size >>= 2; 4498c2ecf20Sopenharmony_ci while (size > 0) { 4508c2ecf20Sopenharmony_ci sum += be32_to_cpu(*ptr++); 4518c2ecf20Sopenharmony_ci size--; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci return sum; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * fill in the superblock 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_cistatic int romfs_fill_super(struct super_block *sb, struct fs_context *fc) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct romfs_super_block *rsb; 4628c2ecf20Sopenharmony_ci struct inode *root; 4638c2ecf20Sopenharmony_ci unsigned long pos, img_size; 4648c2ecf20Sopenharmony_ci const char *storage; 4658c2ecf20Sopenharmony_ci size_t len; 4668c2ecf20Sopenharmony_ci int ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci#ifdef CONFIG_BLOCK 4698c2ecf20Sopenharmony_ci if (!sb->s_mtd) { 4708c2ecf20Sopenharmony_ci sb_set_blocksize(sb, ROMBSIZE); 4718c2ecf20Sopenharmony_ci } else { 4728c2ecf20Sopenharmony_ci sb->s_blocksize = ROMBSIZE; 4738c2ecf20Sopenharmony_ci sb->s_blocksize_bits = blksize_bits(ROMBSIZE); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci#endif 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci sb->s_maxbytes = 0xFFFFFFFF; 4788c2ecf20Sopenharmony_ci sb->s_magic = ROMFS_MAGIC; 4798c2ecf20Sopenharmony_ci sb->s_flags |= SB_RDONLY | SB_NOATIME; 4808c2ecf20Sopenharmony_ci sb->s_time_min = 0; 4818c2ecf20Sopenharmony_ci sb->s_time_max = 0; 4828c2ecf20Sopenharmony_ci sb->s_op = &romfs_super_ops; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 4858c2ecf20Sopenharmony_ci /* Use same dev ID from the underlying mtdblock device */ 4868c2ecf20Sopenharmony_ci if (sb->s_mtd) 4878c2ecf20Sopenharmony_ci sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index); 4888c2ecf20Sopenharmony_ci#endif 4898c2ecf20Sopenharmony_ci /* read the image superblock and check it */ 4908c2ecf20Sopenharmony_ci rsb = kmalloc(512, GFP_KERNEL); 4918c2ecf20Sopenharmony_ci if (!rsb) 4928c2ecf20Sopenharmony_ci return -ENOMEM; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci sb->s_fs_info = (void *) 512; 4958c2ecf20Sopenharmony_ci ret = romfs_dev_read(sb, 0, rsb, 512); 4968c2ecf20Sopenharmony_ci if (ret < 0) 4978c2ecf20Sopenharmony_ci goto error_rsb; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci img_size = be32_to_cpu(rsb->size); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (sb->s_mtd && img_size > sb->s_mtd->size) 5028c2ecf20Sopenharmony_ci goto error_rsb_inval; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci sb->s_fs_info = (void *) img_size; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 || 5078c2ecf20Sopenharmony_ci img_size < ROMFH_SIZE) { 5088c2ecf20Sopenharmony_ci if (!(fc->sb_flags & SB_SILENT)) 5098c2ecf20Sopenharmony_ci errorf(fc, "VFS: Can't find a romfs filesystem on dev %s.\n", 5108c2ecf20Sopenharmony_ci sb->s_id); 5118c2ecf20Sopenharmony_ci goto error_rsb_inval; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) { 5158c2ecf20Sopenharmony_ci pr_err("bad initial checksum on dev %s.\n", sb->s_id); 5168c2ecf20Sopenharmony_ci goto error_rsb_inval; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci storage = sb->s_mtd ? "MTD" : "the block layer"; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci len = strnlen(rsb->name, ROMFS_MAXFN); 5228c2ecf20Sopenharmony_ci if (!(fc->sb_flags & SB_SILENT)) 5238c2ecf20Sopenharmony_ci pr_notice("Mounting image '%*.*s' through %s\n", 5248c2ecf20Sopenharmony_ci (unsigned) len, (unsigned) len, rsb->name, storage); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci kfree(rsb); 5278c2ecf20Sopenharmony_ci rsb = NULL; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* find the root directory */ 5308c2ecf20Sopenharmony_ci pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci root = romfs_iget(sb, pos); 5338c2ecf20Sopenharmony_ci if (IS_ERR(root)) 5348c2ecf20Sopenharmony_ci return PTR_ERR(root); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci sb->s_root = d_make_root(root); 5378c2ecf20Sopenharmony_ci if (!sb->s_root) 5388c2ecf20Sopenharmony_ci return -ENOMEM; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cierror_rsb_inval: 5438c2ecf20Sopenharmony_ci ret = -EINVAL; 5448c2ecf20Sopenharmony_cierror_rsb: 5458c2ecf20Sopenharmony_ci kfree(rsb); 5468c2ecf20Sopenharmony_ci return ret; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * get a superblock for mounting 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic int romfs_get_tree(struct fs_context *fc) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci int ret = -EINVAL; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 5578c2ecf20Sopenharmony_ci ret = get_tree_mtd(fc, romfs_fill_super); 5588c2ecf20Sopenharmony_ci#endif 5598c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 5608c2ecf20Sopenharmony_ci if (ret == -EINVAL) 5618c2ecf20Sopenharmony_ci ret = get_tree_bdev(fc, romfs_fill_super); 5628c2ecf20Sopenharmony_ci#endif 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic const struct fs_context_operations romfs_context_ops = { 5678c2ecf20Sopenharmony_ci .get_tree = romfs_get_tree, 5688c2ecf20Sopenharmony_ci .reconfigure = romfs_reconfigure, 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/* 5728c2ecf20Sopenharmony_ci * Set up the filesystem mount context. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cistatic int romfs_init_fs_context(struct fs_context *fc) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci fc->ops = &romfs_context_ops; 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* 5818c2ecf20Sopenharmony_ci * destroy a romfs superblock in the appropriate manner 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic void romfs_kill_sb(struct super_block *sb) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 5868c2ecf20Sopenharmony_ci if (sb->s_mtd) { 5878c2ecf20Sopenharmony_ci kill_mtd_super(sb); 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci#endif 5918c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 5928c2ecf20Sopenharmony_ci if (sb->s_bdev) { 5938c2ecf20Sopenharmony_ci kill_block_super(sb); 5948c2ecf20Sopenharmony_ci return; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci#endif 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic struct file_system_type romfs_fs_type = { 6008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6018c2ecf20Sopenharmony_ci .name = "romfs", 6028c2ecf20Sopenharmony_ci .init_fs_context = romfs_init_fs_context, 6038c2ecf20Sopenharmony_ci .kill_sb = romfs_kill_sb, 6048c2ecf20Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 6058c2ecf20Sopenharmony_ci}; 6068c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("romfs"); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/* 6098c2ecf20Sopenharmony_ci * inode storage initialiser 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_cistatic void romfs_i_init_once(void *_inode) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct romfs_inode_info *inode = _inode; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci inode_init_once(&inode->vfs_inode); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* 6198c2ecf20Sopenharmony_ci * romfs module initialisation 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistatic int __init init_romfs_fs(void) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci int ret; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci pr_info("ROMFS MTD (C) 2007 Red Hat, Inc.\n"); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci romfs_inode_cachep = 6288c2ecf20Sopenharmony_ci kmem_cache_create("romfs_i", 6298c2ecf20Sopenharmony_ci sizeof(struct romfs_inode_info), 0, 6308c2ecf20Sopenharmony_ci SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | 6318c2ecf20Sopenharmony_ci SLAB_ACCOUNT, romfs_i_init_once); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!romfs_inode_cachep) { 6348c2ecf20Sopenharmony_ci pr_err("Failed to initialise inode cache\n"); 6358c2ecf20Sopenharmony_ci return -ENOMEM; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci ret = register_filesystem(&romfs_fs_type); 6388c2ecf20Sopenharmony_ci if (ret) { 6398c2ecf20Sopenharmony_ci pr_err("Failed to register filesystem\n"); 6408c2ecf20Sopenharmony_ci goto error_register; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cierror_register: 6458c2ecf20Sopenharmony_ci kmem_cache_destroy(romfs_inode_cachep); 6468c2ecf20Sopenharmony_ci return ret; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/* 6508c2ecf20Sopenharmony_ci * romfs module removal 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic void __exit exit_romfs_fs(void) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci unregister_filesystem(&romfs_fs_type); 6558c2ecf20Sopenharmony_ci /* 6568c2ecf20Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 6578c2ecf20Sopenharmony_ci * destroy cache. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci rcu_barrier(); 6608c2ecf20Sopenharmony_ci kmem_cache_destroy(romfs_inode_cachep); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cimodule_init(init_romfs_fs); 6648c2ecf20Sopenharmony_cimodule_exit(exit_romfs_fs); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Direct-MTD Capable RomFS"); 6678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc."); 6688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); /* Actually dual-licensed, but it doesn't matter for */ 669