162306a36Sopenharmony_ci/* Block- or MTD-based romfs 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright © 2007 Red Hat, Inc. All Rights Reserved. 462306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Derived from: ROMFS file system, Linux implementation 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright © 1997-1999 Janos Farkas <chexum@shadow.banki.hu> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Using parts of the minix filesystem 1162306a36Sopenharmony_ci * Copyright © 1991, 1992 Linus Torvalds 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * and parts of the affs filesystem additionally 1462306a36Sopenharmony_ci * Copyright © 1993 Ray Burr 1562306a36Sopenharmony_ci * Copyright © 1996 Hans-Joachim Widmaier 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Changes 1862306a36Sopenharmony_ci * Changed for 2.1.19 modules 1962306a36Sopenharmony_ci * Jan 1997 Initial release 2062306a36Sopenharmony_ci * Jun 1997 2.1.43+ changes 2162306a36Sopenharmony_ci * Proper page locking in read_folio 2262306a36Sopenharmony_ci * Changed to work with 2.1.45+ fs 2362306a36Sopenharmony_ci * Jul 1997 Fixed follow_link 2462306a36Sopenharmony_ci * 2.1.47 2562306a36Sopenharmony_ci * lookup shouldn't return -ENOENT 2662306a36Sopenharmony_ci * from Horst von Brand: 2762306a36Sopenharmony_ci * fail on wrong checksum 2862306a36Sopenharmony_ci * double unlock_super was possible 2962306a36Sopenharmony_ci * correct namelen for statfs 3062306a36Sopenharmony_ci * spotted by Bill Hawes: 3162306a36Sopenharmony_ci * readlink shouldn't iput() 3262306a36Sopenharmony_ci * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir() 3362306a36Sopenharmony_ci * exposed a problem in readdir 3462306a36Sopenharmony_ci * 2.1.107 code-freeze spellchecker run 3562306a36Sopenharmony_ci * Aug 1998 2.1.118+ VFS changes 3662306a36Sopenharmony_ci * Sep 1998 2.1.122 another VFS change (follow_link) 3762306a36Sopenharmony_ci * Apr 1999 2.2.7 no more EBADF checking in 3862306a36Sopenharmony_ci * lookup/readdir, use ERR_PTR 3962306a36Sopenharmony_ci * Jun 1999 2.3.6 d_alloc_root use changed 4062306a36Sopenharmony_ci * 2.3.9 clean up usage of ENOENT/negative 4162306a36Sopenharmony_ci * dentries in lookup 4262306a36Sopenharmony_ci * clean up page flags setting 4362306a36Sopenharmony_ci * (error, uptodate, locking) in 4462306a36Sopenharmony_ci * in read_folio 4562306a36Sopenharmony_ci * use init_special_inode for 4662306a36Sopenharmony_ci * fifos/sockets (and streamline) in 4762306a36Sopenharmony_ci * read_inode, fix _ops table order 4862306a36Sopenharmony_ci * Aug 1999 2.3.16 __initfunc() => __init change 4962306a36Sopenharmony_ci * Oct 1999 2.3.24 page->owner hack obsoleted 5062306a36Sopenharmony_ci * Nov 1999 2.3.27 2.3.25+ page->offset => index change 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 5462306a36Sopenharmony_ci * modify it under the terms of the GNU General Public Licence 5562306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 5662306a36Sopenharmony_ci * 2 of the Licence, or (at your option) any later version. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#include <linux/module.h> 6262306a36Sopenharmony_ci#include <linux/string.h> 6362306a36Sopenharmony_ci#include <linux/fs.h> 6462306a36Sopenharmony_ci#include <linux/time.h> 6562306a36Sopenharmony_ci#include <linux/slab.h> 6662306a36Sopenharmony_ci#include <linux/init.h> 6762306a36Sopenharmony_ci#include <linux/blkdev.h> 6862306a36Sopenharmony_ci#include <linux/fs_context.h> 6962306a36Sopenharmony_ci#include <linux/mount.h> 7062306a36Sopenharmony_ci#include <linux/namei.h> 7162306a36Sopenharmony_ci#include <linux/statfs.h> 7262306a36Sopenharmony_ci#include <linux/mtd/super.h> 7362306a36Sopenharmony_ci#include <linux/ctype.h> 7462306a36Sopenharmony_ci#include <linux/highmem.h> 7562306a36Sopenharmony_ci#include <linux/pagemap.h> 7662306a36Sopenharmony_ci#include <linux/uaccess.h> 7762306a36Sopenharmony_ci#include <linux/major.h> 7862306a36Sopenharmony_ci#include "internal.h" 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic struct kmem_cache *romfs_inode_cachep; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const umode_t romfs_modemap[8] = { 8362306a36Sopenharmony_ci 0, /* hard link */ 8462306a36Sopenharmony_ci S_IFDIR | 0644, /* directory */ 8562306a36Sopenharmony_ci S_IFREG | 0644, /* regular file */ 8662306a36Sopenharmony_ci S_IFLNK | 0777, /* symlink */ 8762306a36Sopenharmony_ci S_IFBLK | 0600, /* blockdev */ 8862306a36Sopenharmony_ci S_IFCHR | 0600, /* chardev */ 8962306a36Sopenharmony_ci S_IFSOCK | 0644, /* socket */ 9062306a36Sopenharmony_ci S_IFIFO | 0644 /* FIFO */ 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic const unsigned char romfs_dtype_table[] = { 9462306a36Sopenharmony_ci DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct inode *romfs_iget(struct super_block *sb, unsigned long pos); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * read a page worth of data from the image 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic int romfs_read_folio(struct file *file, struct folio *folio) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct page *page = &folio->page; 10562306a36Sopenharmony_ci struct inode *inode = page->mapping->host; 10662306a36Sopenharmony_ci loff_t offset, size; 10762306a36Sopenharmony_ci unsigned long fillsize, pos; 10862306a36Sopenharmony_ci void *buf; 10962306a36Sopenharmony_ci int ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci buf = kmap(page); 11262306a36Sopenharmony_ci if (!buf) 11362306a36Sopenharmony_ci return -ENOMEM; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 32 bit warning -- but not for us :) */ 11662306a36Sopenharmony_ci offset = page_offset(page); 11762306a36Sopenharmony_ci size = i_size_read(inode); 11862306a36Sopenharmony_ci fillsize = 0; 11962306a36Sopenharmony_ci ret = 0; 12062306a36Sopenharmony_ci if (offset < size) { 12162306a36Sopenharmony_ci size -= offset; 12262306a36Sopenharmony_ci fillsize = size > PAGE_SIZE ? PAGE_SIZE : size; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pos = ROMFS_I(inode)->i_dataoffset + offset; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = romfs_dev_read(inode->i_sb, pos, buf, fillsize); 12762306a36Sopenharmony_ci if (ret < 0) { 12862306a36Sopenharmony_ci SetPageError(page); 12962306a36Sopenharmony_ci fillsize = 0; 13062306a36Sopenharmony_ci ret = -EIO; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (fillsize < PAGE_SIZE) 13562306a36Sopenharmony_ci memset(buf + fillsize, 0, PAGE_SIZE - fillsize); 13662306a36Sopenharmony_ci if (ret == 0) 13762306a36Sopenharmony_ci SetPageUptodate(page); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci flush_dcache_page(page); 14062306a36Sopenharmony_ci kunmap(page); 14162306a36Sopenharmony_ci unlock_page(page); 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct address_space_operations romfs_aops = { 14662306a36Sopenharmony_ci .read_folio = romfs_read_folio 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * read the entries from a directory 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic int romfs_readdir(struct file *file, struct dir_context *ctx) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct inode *i = file_inode(file); 15562306a36Sopenharmony_ci struct romfs_inode ri; 15662306a36Sopenharmony_ci unsigned long offset, maxoff; 15762306a36Sopenharmony_ci int j, ino, nextfh; 15862306a36Sopenharmony_ci char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ 15962306a36Sopenharmony_ci int ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci maxoff = romfs_maxsize(i->i_sb); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci offset = ctx->pos; 16462306a36Sopenharmony_ci if (!offset) { 16562306a36Sopenharmony_ci offset = i->i_ino & ROMFH_MASK; 16662306a36Sopenharmony_ci ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); 16762306a36Sopenharmony_ci if (ret < 0) 16862306a36Sopenharmony_ci goto out; 16962306a36Sopenharmony_ci offset = be32_to_cpu(ri.spec) & ROMFH_MASK; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Not really failsafe, but we are read-only... */ 17362306a36Sopenharmony_ci for (;;) { 17462306a36Sopenharmony_ci if (!offset || offset >= maxoff) { 17562306a36Sopenharmony_ci offset = maxoff; 17662306a36Sopenharmony_ci ctx->pos = offset; 17762306a36Sopenharmony_ci goto out; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci ctx->pos = offset; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Fetch inode info */ 18262306a36Sopenharmony_ci ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); 18362306a36Sopenharmony_ci if (ret < 0) 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE, 18762306a36Sopenharmony_ci sizeof(fsname) - 1); 18862306a36Sopenharmony_ci if (j < 0) 18962306a36Sopenharmony_ci goto out; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j); 19262306a36Sopenharmony_ci if (ret < 0) 19362306a36Sopenharmony_ci goto out; 19462306a36Sopenharmony_ci fsname[j] = '\0'; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ino = offset; 19762306a36Sopenharmony_ci nextfh = be32_to_cpu(ri.next); 19862306a36Sopenharmony_ci if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) 19962306a36Sopenharmony_ci ino = be32_to_cpu(ri.spec); 20062306a36Sopenharmony_ci if (!dir_emit(ctx, fsname, j, ino, 20162306a36Sopenharmony_ci romfs_dtype_table[nextfh & ROMFH_TYPE])) 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci offset = nextfh & ROMFH_MASK; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ciout: 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * look up an entry in a directory 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry, 21462306a36Sopenharmony_ci unsigned int flags) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci unsigned long offset, maxoff; 21762306a36Sopenharmony_ci struct inode *inode = NULL; 21862306a36Sopenharmony_ci struct romfs_inode ri; 21962306a36Sopenharmony_ci const char *name; /* got from dentry */ 22062306a36Sopenharmony_ci int len, ret; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci offset = dir->i_ino & ROMFH_MASK; 22362306a36Sopenharmony_ci ret = romfs_dev_read(dir->i_sb, offset, &ri, ROMFH_SIZE); 22462306a36Sopenharmony_ci if (ret < 0) 22562306a36Sopenharmony_ci goto error; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* search all the file entries in the list starting from the one 22862306a36Sopenharmony_ci * pointed to by the directory's special data */ 22962306a36Sopenharmony_ci maxoff = romfs_maxsize(dir->i_sb); 23062306a36Sopenharmony_ci offset = be32_to_cpu(ri.spec) & ROMFH_MASK; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci name = dentry->d_name.name; 23362306a36Sopenharmony_ci len = dentry->d_name.len; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci for (;;) { 23662306a36Sopenharmony_ci if (!offset || offset >= maxoff) 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret = romfs_dev_read(dir->i_sb, offset, &ri, sizeof(ri)); 24062306a36Sopenharmony_ci if (ret < 0) 24162306a36Sopenharmony_ci goto error; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* try to match the first 16 bytes of name */ 24462306a36Sopenharmony_ci ret = romfs_dev_strcmp(dir->i_sb, offset + ROMFH_SIZE, name, 24562306a36Sopenharmony_ci len); 24662306a36Sopenharmony_ci if (ret < 0) 24762306a36Sopenharmony_ci goto error; 24862306a36Sopenharmony_ci if (ret == 1) { 24962306a36Sopenharmony_ci /* Hard link handling */ 25062306a36Sopenharmony_ci if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) 25162306a36Sopenharmony_ci offset = be32_to_cpu(ri.spec) & ROMFH_MASK; 25262306a36Sopenharmony_ci inode = romfs_iget(dir->i_sb, offset); 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* next entry */ 25762306a36Sopenharmony_ci offset = be32_to_cpu(ri.next) & ROMFH_MASK; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 26162306a36Sopenharmony_cierror: 26262306a36Sopenharmony_ci return ERR_PTR(ret); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic const struct file_operations romfs_dir_operations = { 26662306a36Sopenharmony_ci .read = generic_read_dir, 26762306a36Sopenharmony_ci .iterate_shared = romfs_readdir, 26862306a36Sopenharmony_ci .llseek = generic_file_llseek, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct inode_operations romfs_dir_inode_operations = { 27262306a36Sopenharmony_ci .lookup = romfs_lookup, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/* 27662306a36Sopenharmony_ci * get a romfs inode based on its position in the image (which doubles as the 27762306a36Sopenharmony_ci * inode number) 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic struct inode *romfs_iget(struct super_block *sb, unsigned long pos) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct romfs_inode_info *inode; 28262306a36Sopenharmony_ci struct romfs_inode ri; 28362306a36Sopenharmony_ci struct inode *i; 28462306a36Sopenharmony_ci unsigned long nlen; 28562306a36Sopenharmony_ci unsigned nextfh; 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci umode_t mode; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* we might have to traverse a chain of "hard link" file entries to get 29062306a36Sopenharmony_ci * to the actual file */ 29162306a36Sopenharmony_ci for (;;) { 29262306a36Sopenharmony_ci ret = romfs_dev_read(sb, pos, &ri, sizeof(ri)); 29362306a36Sopenharmony_ci if (ret < 0) 29462306a36Sopenharmony_ci goto error; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* XXX: do romfs_checksum here too (with name) */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci nextfh = be32_to_cpu(ri.next); 29962306a36Sopenharmony_ci if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci pos = be32_to_cpu(ri.spec) & ROMFH_MASK; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* determine the length of the filename */ 30662306a36Sopenharmony_ci nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN); 30762306a36Sopenharmony_ci if (IS_ERR_VALUE(nlen)) 30862306a36Sopenharmony_ci goto eio; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* get an inode for this image position */ 31162306a36Sopenharmony_ci i = iget_locked(sb, pos); 31262306a36Sopenharmony_ci if (!i) 31362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!(i->i_state & I_NEW)) 31662306a36Sopenharmony_ci return i; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* precalculate the data offset */ 31962306a36Sopenharmony_ci inode = ROMFS_I(i); 32062306a36Sopenharmony_ci inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK; 32162306a36Sopenharmony_ci inode->i_dataoffset = pos + inode->i_metasize; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci set_nlink(i, 1); /* Hard to decide.. */ 32462306a36Sopenharmony_ci i->i_size = be32_to_cpu(ri.size); 32562306a36Sopenharmony_ci i->i_mtime = i->i_atime = inode_set_ctime(i, 0, 0); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* set up mode and ops */ 32862306a36Sopenharmony_ci mode = romfs_modemap[nextfh & ROMFH_TYPE]; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci switch (nextfh & ROMFH_TYPE) { 33162306a36Sopenharmony_ci case ROMFH_DIR: 33262306a36Sopenharmony_ci i->i_size = ROMFS_I(i)->i_metasize; 33362306a36Sopenharmony_ci i->i_op = &romfs_dir_inode_operations; 33462306a36Sopenharmony_ci i->i_fop = &romfs_dir_operations; 33562306a36Sopenharmony_ci if (nextfh & ROMFH_EXEC) 33662306a36Sopenharmony_ci mode |= S_IXUGO; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case ROMFH_REG: 33962306a36Sopenharmony_ci i->i_fop = &romfs_ro_fops; 34062306a36Sopenharmony_ci i->i_data.a_ops = &romfs_aops; 34162306a36Sopenharmony_ci if (nextfh & ROMFH_EXEC) 34262306a36Sopenharmony_ci mode |= S_IXUGO; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case ROMFH_SYM: 34562306a36Sopenharmony_ci i->i_op = &page_symlink_inode_operations; 34662306a36Sopenharmony_ci inode_nohighmem(i); 34762306a36Sopenharmony_ci i->i_data.a_ops = &romfs_aops; 34862306a36Sopenharmony_ci mode |= S_IRWXUGO; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci default: 35162306a36Sopenharmony_ci /* depending on MBZ for sock/fifos */ 35262306a36Sopenharmony_ci nextfh = be32_to_cpu(ri.spec); 35362306a36Sopenharmony_ci init_special_inode(i, mode, MKDEV(nextfh >> 16, 35462306a36Sopenharmony_ci nextfh & 0xffff)); 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci i->i_mode = mode; 35962306a36Sopenharmony_ci i->i_blocks = (i->i_size + 511) >> 9; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci unlock_new_inode(i); 36262306a36Sopenharmony_ci return i; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cieio: 36562306a36Sopenharmony_ci ret = -EIO; 36662306a36Sopenharmony_cierror: 36762306a36Sopenharmony_ci pr_err("read error for inode 0x%lx\n", pos); 36862306a36Sopenharmony_ci return ERR_PTR(ret); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * allocate a new inode 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic struct inode *romfs_alloc_inode(struct super_block *sb) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct romfs_inode_info *inode; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci inode = alloc_inode_sb(sb, romfs_inode_cachep, GFP_KERNEL); 37962306a36Sopenharmony_ci return inode ? &inode->vfs_inode : NULL; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* 38362306a36Sopenharmony_ci * return a spent inode to the slab cache 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_cistatic void romfs_free_inode(struct inode *inode) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* 39162306a36Sopenharmony_ci * get filesystem statistics 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_cistatic int romfs_statfs(struct dentry *dentry, struct kstatfs *buf) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 39662306a36Sopenharmony_ci u64 id = 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* When calling huge_encode_dev(), 39962306a36Sopenharmony_ci * use sb->s_bdev->bd_dev when, 40062306a36Sopenharmony_ci * - CONFIG_ROMFS_ON_BLOCK defined 40162306a36Sopenharmony_ci * use sb->s_dev when, 40262306a36Sopenharmony_ci * - CONFIG_ROMFS_ON_BLOCK undefined and 40362306a36Sopenharmony_ci * - CONFIG_ROMFS_ON_MTD defined 40462306a36Sopenharmony_ci * leave id as 0 when, 40562306a36Sopenharmony_ci * - CONFIG_ROMFS_ON_BLOCK undefined and 40662306a36Sopenharmony_ci * - CONFIG_ROMFS_ON_MTD undefined 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci if (sb->s_bdev) 40962306a36Sopenharmony_ci id = huge_encode_dev(sb->s_bdev->bd_dev); 41062306a36Sopenharmony_ci else if (sb->s_dev) 41162306a36Sopenharmony_ci id = huge_encode_dev(sb->s_dev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci buf->f_type = ROMFS_MAGIC; 41462306a36Sopenharmony_ci buf->f_namelen = ROMFS_MAXFN; 41562306a36Sopenharmony_ci buf->f_bsize = ROMBSIZE; 41662306a36Sopenharmony_ci buf->f_bfree = buf->f_bavail = buf->f_ffree; 41762306a36Sopenharmony_ci buf->f_blocks = 41862306a36Sopenharmony_ci (romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS; 41962306a36Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* 42462306a36Sopenharmony_ci * remounting must involve read-only 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic int romfs_reconfigure(struct fs_context *fc) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci sync_filesystem(fc->root->d_sb); 42962306a36Sopenharmony_ci fc->sb_flags |= SB_RDONLY; 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic const struct super_operations romfs_super_ops = { 43462306a36Sopenharmony_ci .alloc_inode = romfs_alloc_inode, 43562306a36Sopenharmony_ci .free_inode = romfs_free_inode, 43662306a36Sopenharmony_ci .statfs = romfs_statfs, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * checksum check on part of a romfs filesystem 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_cistatic __u32 romfs_checksum(const void *data, int size) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci const __be32 *ptr = data; 44562306a36Sopenharmony_ci __u32 sum; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci sum = 0; 44862306a36Sopenharmony_ci size >>= 2; 44962306a36Sopenharmony_ci while (size > 0) { 45062306a36Sopenharmony_ci sum += be32_to_cpu(*ptr++); 45162306a36Sopenharmony_ci size--; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci return sum; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/* 45762306a36Sopenharmony_ci * fill in the superblock 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic int romfs_fill_super(struct super_block *sb, struct fs_context *fc) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct romfs_super_block *rsb; 46262306a36Sopenharmony_ci struct inode *root; 46362306a36Sopenharmony_ci unsigned long pos, img_size; 46462306a36Sopenharmony_ci const char *storage; 46562306a36Sopenharmony_ci size_t len; 46662306a36Sopenharmony_ci int ret; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci#ifdef CONFIG_BLOCK 46962306a36Sopenharmony_ci if (!sb->s_mtd) { 47062306a36Sopenharmony_ci sb_set_blocksize(sb, ROMBSIZE); 47162306a36Sopenharmony_ci } else { 47262306a36Sopenharmony_ci sb->s_blocksize = ROMBSIZE; 47362306a36Sopenharmony_ci sb->s_blocksize_bits = blksize_bits(ROMBSIZE); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci#endif 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci sb->s_maxbytes = 0xFFFFFFFF; 47862306a36Sopenharmony_ci sb->s_magic = ROMFS_MAGIC; 47962306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY | SB_NOATIME; 48062306a36Sopenharmony_ci sb->s_time_min = 0; 48162306a36Sopenharmony_ci sb->s_time_max = 0; 48262306a36Sopenharmony_ci sb->s_op = &romfs_super_ops; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 48562306a36Sopenharmony_ci /* Use same dev ID from the underlying mtdblock device */ 48662306a36Sopenharmony_ci if (sb->s_mtd) 48762306a36Sopenharmony_ci sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index); 48862306a36Sopenharmony_ci#endif 48962306a36Sopenharmony_ci /* read the image superblock and check it */ 49062306a36Sopenharmony_ci rsb = kmalloc(512, GFP_KERNEL); 49162306a36Sopenharmony_ci if (!rsb) 49262306a36Sopenharmony_ci return -ENOMEM; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci sb->s_fs_info = (void *) 512; 49562306a36Sopenharmony_ci ret = romfs_dev_read(sb, 0, rsb, 512); 49662306a36Sopenharmony_ci if (ret < 0) 49762306a36Sopenharmony_ci goto error_rsb; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci img_size = be32_to_cpu(rsb->size); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (sb->s_mtd && img_size > sb->s_mtd->size) 50262306a36Sopenharmony_ci goto error_rsb_inval; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci sb->s_fs_info = (void *) img_size; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 || 50762306a36Sopenharmony_ci img_size < ROMFH_SIZE) { 50862306a36Sopenharmony_ci if (!(fc->sb_flags & SB_SILENT)) 50962306a36Sopenharmony_ci errorf(fc, "VFS: Can't find a romfs filesystem on dev %s.\n", 51062306a36Sopenharmony_ci sb->s_id); 51162306a36Sopenharmony_ci goto error_rsb_inval; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) { 51562306a36Sopenharmony_ci pr_err("bad initial checksum on dev %s.\n", sb->s_id); 51662306a36Sopenharmony_ci goto error_rsb_inval; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci storage = sb->s_mtd ? "MTD" : "the block layer"; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci len = strnlen(rsb->name, ROMFS_MAXFN); 52262306a36Sopenharmony_ci if (!(fc->sb_flags & SB_SILENT)) 52362306a36Sopenharmony_ci pr_notice("Mounting image '%*.*s' through %s\n", 52462306a36Sopenharmony_ci (unsigned) len, (unsigned) len, rsb->name, storage); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci kfree(rsb); 52762306a36Sopenharmony_ci rsb = NULL; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* find the root directory */ 53062306a36Sopenharmony_ci pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci root = romfs_iget(sb, pos); 53362306a36Sopenharmony_ci if (IS_ERR(root)) 53462306a36Sopenharmony_ci return PTR_ERR(root); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci sb->s_root = d_make_root(root); 53762306a36Sopenharmony_ci if (!sb->s_root) 53862306a36Sopenharmony_ci return -ENOMEM; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cierror_rsb_inval: 54362306a36Sopenharmony_ci ret = -EINVAL; 54462306a36Sopenharmony_cierror_rsb: 54562306a36Sopenharmony_ci kfree(rsb); 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* 55062306a36Sopenharmony_ci * get a superblock for mounting 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_cistatic int romfs_get_tree(struct fs_context *fc) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci int ret = -EINVAL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 55762306a36Sopenharmony_ci ret = get_tree_mtd(fc, romfs_fill_super); 55862306a36Sopenharmony_ci#endif 55962306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 56062306a36Sopenharmony_ci if (ret == -EINVAL) 56162306a36Sopenharmony_ci ret = get_tree_bdev(fc, romfs_fill_super); 56262306a36Sopenharmony_ci#endif 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic const struct fs_context_operations romfs_context_ops = { 56762306a36Sopenharmony_ci .get_tree = romfs_get_tree, 56862306a36Sopenharmony_ci .reconfigure = romfs_reconfigure, 56962306a36Sopenharmony_ci}; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * Set up the filesystem mount context. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_cistatic int romfs_init_fs_context(struct fs_context *fc) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci fc->ops = &romfs_context_ops; 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* 58162306a36Sopenharmony_ci * destroy a romfs superblock in the appropriate manner 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_cistatic void romfs_kill_sb(struct super_block *sb) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci generic_shutdown_super(sb); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 58862306a36Sopenharmony_ci if (sb->s_mtd) { 58962306a36Sopenharmony_ci put_mtd_device(sb->s_mtd); 59062306a36Sopenharmony_ci sb->s_mtd = NULL; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci#endif 59362306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 59462306a36Sopenharmony_ci if (sb->s_bdev) { 59562306a36Sopenharmony_ci sync_blockdev(sb->s_bdev); 59662306a36Sopenharmony_ci blkdev_put(sb->s_bdev, sb); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci#endif 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic struct file_system_type romfs_fs_type = { 60262306a36Sopenharmony_ci .owner = THIS_MODULE, 60362306a36Sopenharmony_ci .name = "romfs", 60462306a36Sopenharmony_ci .init_fs_context = romfs_init_fs_context, 60562306a36Sopenharmony_ci .kill_sb = romfs_kill_sb, 60662306a36Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 60762306a36Sopenharmony_ci}; 60862306a36Sopenharmony_ciMODULE_ALIAS_FS("romfs"); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/* 61162306a36Sopenharmony_ci * inode storage initialiser 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_cistatic void romfs_i_init_once(void *_inode) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct romfs_inode_info *inode = _inode; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci inode_init_once(&inode->vfs_inode); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* 62162306a36Sopenharmony_ci * romfs module initialisation 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_cistatic int __init init_romfs_fs(void) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci int ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci pr_info("ROMFS MTD (C) 2007 Red Hat, Inc.\n"); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci romfs_inode_cachep = 63062306a36Sopenharmony_ci kmem_cache_create("romfs_i", 63162306a36Sopenharmony_ci sizeof(struct romfs_inode_info), 0, 63262306a36Sopenharmony_ci SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | 63362306a36Sopenharmony_ci SLAB_ACCOUNT, romfs_i_init_once); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!romfs_inode_cachep) { 63662306a36Sopenharmony_ci pr_err("Failed to initialise inode cache\n"); 63762306a36Sopenharmony_ci return -ENOMEM; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci ret = register_filesystem(&romfs_fs_type); 64062306a36Sopenharmony_ci if (ret) { 64162306a36Sopenharmony_ci pr_err("Failed to register filesystem\n"); 64262306a36Sopenharmony_ci goto error_register; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cierror_register: 64762306a36Sopenharmony_ci kmem_cache_destroy(romfs_inode_cachep); 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* 65262306a36Sopenharmony_ci * romfs module removal 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_cistatic void __exit exit_romfs_fs(void) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci unregister_filesystem(&romfs_fs_type); 65762306a36Sopenharmony_ci /* 65862306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 65962306a36Sopenharmony_ci * destroy cache. 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci rcu_barrier(); 66262306a36Sopenharmony_ci kmem_cache_destroy(romfs_inode_cachep); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cimodule_init(init_romfs_fs); 66662306a36Sopenharmony_cimodule_exit(exit_romfs_fs); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ciMODULE_DESCRIPTION("Direct-MTD Capable RomFS"); 66962306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc."); 67062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); /* Actually dual-licensed, but it doesn't matter for */ 671