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