162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* inode.c: /proc/openprom handling routines
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
562306a36Sopenharmony_ci * Copyright (C) 1998      Eddie C. Dost  (ecd@skynet.be)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/fs_context.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/seq_file.h>
1662306a36Sopenharmony_ci#include <linux/magic.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/openprom.h>
1962306a36Sopenharmony_ci#include <asm/oplib.h>
2062306a36Sopenharmony_ci#include <asm/prom.h>
2162306a36Sopenharmony_ci#include <linux/uaccess.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic DEFINE_MUTEX(op_mutex);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define OPENPROM_ROOT_INO	0
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cienum op_inode_type {
2862306a36Sopenharmony_ci	op_inode_node,
2962306a36Sopenharmony_ci	op_inode_prop,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciunion op_inode_data {
3362306a36Sopenharmony_ci	struct device_node	*node;
3462306a36Sopenharmony_ci	struct property		*prop;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct op_inode_info {
3862306a36Sopenharmony_ci	struct inode		vfs_inode;
3962306a36Sopenharmony_ci	enum op_inode_type	type;
4062306a36Sopenharmony_ci	union op_inode_data	u;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic struct inode *openprom_iget(struct super_block *sb, ino_t ino);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic inline struct op_inode_info *OP_I(struct inode *inode)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	return container_of(inode, struct op_inode_info, vfs_inode);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int is_string(unsigned char *p, int len)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int i;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
5562306a36Sopenharmony_ci		unsigned char val = p[i];
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		if ((i && !val) ||
5862306a36Sopenharmony_ci		    (val >= ' ' && val <= '~'))
5962306a36Sopenharmony_ci			continue;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		return 0;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 1;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int property_show(struct seq_file *f, void *v)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct property *prop = f->private;
7062306a36Sopenharmony_ci	void *pval;
7162306a36Sopenharmony_ci	int len;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	len = prop->length;
7462306a36Sopenharmony_ci	pval = prop->value;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (is_string(pval, len)) {
7762306a36Sopenharmony_ci		while (len > 0) {
7862306a36Sopenharmony_ci			int n = strlen(pval);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci			seq_printf(f, "%s", (char *) pval);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci			/* Skip over the NULL byte too.  */
8362306a36Sopenharmony_ci			pval += n + 1;
8462306a36Sopenharmony_ci			len -= n + 1;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci			if (len > 0)
8762306a36Sopenharmony_ci				seq_printf(f, " + ");
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	} else {
9062306a36Sopenharmony_ci		if (len & 3) {
9162306a36Sopenharmony_ci			while (len) {
9262306a36Sopenharmony_ci				len--;
9362306a36Sopenharmony_ci				if (len)
9462306a36Sopenharmony_ci					seq_printf(f, "%02x.",
9562306a36Sopenharmony_ci						   *(unsigned char *) pval);
9662306a36Sopenharmony_ci				else
9762306a36Sopenharmony_ci					seq_printf(f, "%02x",
9862306a36Sopenharmony_ci						   *(unsigned char *) pval);
9962306a36Sopenharmony_ci				pval++;
10062306a36Sopenharmony_ci			}
10162306a36Sopenharmony_ci		} else {
10262306a36Sopenharmony_ci			while (len >= 4) {
10362306a36Sopenharmony_ci				len -= 4;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci				if (len)
10662306a36Sopenharmony_ci					seq_printf(f, "%08x.",
10762306a36Sopenharmony_ci						   *(unsigned int *) pval);
10862306a36Sopenharmony_ci				else
10962306a36Sopenharmony_ci					seq_printf(f, "%08x",
11062306a36Sopenharmony_ci						   *(unsigned int *) pval);
11162306a36Sopenharmony_ci				pval += 4;
11262306a36Sopenharmony_ci			}
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	seq_printf(f, "\n");
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void *property_start(struct seq_file *f, loff_t *pos)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	if (*pos == 0)
12362306a36Sopenharmony_ci		return pos;
12462306a36Sopenharmony_ci	return NULL;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void *property_next(struct seq_file *f, void *v, loff_t *pos)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	(*pos)++;
13062306a36Sopenharmony_ci	return NULL;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void property_stop(struct seq_file *f, void *v)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	/* Nothing to do */
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct seq_operations property_op = {
13962306a36Sopenharmony_ci	.start		= property_start,
14062306a36Sopenharmony_ci	.next		= property_next,
14162306a36Sopenharmony_ci	.stop		= property_stop,
14262306a36Sopenharmony_ci	.show		= property_show
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int property_open(struct inode *inode, struct file *file)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct op_inode_info *oi = OP_I(inode);
14862306a36Sopenharmony_ci	int ret;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	BUG_ON(oi->type != op_inode_prop);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = seq_open(file, &property_op);
15362306a36Sopenharmony_ci	if (!ret) {
15462306a36Sopenharmony_ci		struct seq_file *m = file->private_data;
15562306a36Sopenharmony_ci		m->private = oi->u.prop;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci	return ret;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct file_operations openpromfs_prop_ops = {
16162306a36Sopenharmony_ci	.open		= property_open,
16262306a36Sopenharmony_ci	.read		= seq_read,
16362306a36Sopenharmony_ci	.llseek		= seq_lseek,
16462306a36Sopenharmony_ci	.release	= seq_release,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int openpromfs_readdir(struct file *, struct dir_context *);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic const struct file_operations openprom_operations = {
17062306a36Sopenharmony_ci	.read		= generic_read_dir,
17162306a36Sopenharmony_ci	.iterate_shared	= openpromfs_readdir,
17262306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct dentry *openpromfs_lookup(struct inode *, struct dentry *, unsigned int);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic const struct inode_operations openprom_inode_operations = {
17862306a36Sopenharmony_ci	.lookup		= openpromfs_lookup,
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct op_inode_info *ent_oi, *oi = OP_I(dir);
18462306a36Sopenharmony_ci	struct device_node *dp, *child;
18562306a36Sopenharmony_ci	struct property *prop;
18662306a36Sopenharmony_ci	enum op_inode_type ent_type;
18762306a36Sopenharmony_ci	union op_inode_data ent_data;
18862306a36Sopenharmony_ci	const char *name;
18962306a36Sopenharmony_ci	struct inode *inode;
19062306a36Sopenharmony_ci	unsigned int ino;
19162306a36Sopenharmony_ci	int len;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	BUG_ON(oi->type != op_inode_node);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	dp = oi->u.node;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	name = dentry->d_name.name;
19862306a36Sopenharmony_ci	len = dentry->d_name.len;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	mutex_lock(&op_mutex);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	child = dp->child;
20362306a36Sopenharmony_ci	while (child) {
20462306a36Sopenharmony_ci		const char *node_name = kbasename(child->full_name);
20562306a36Sopenharmony_ci		int n = strlen(node_name);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		if (len == n &&
20862306a36Sopenharmony_ci		    !strncmp(node_name, name, len)) {
20962306a36Sopenharmony_ci			ent_type = op_inode_node;
21062306a36Sopenharmony_ci			ent_data.node = child;
21162306a36Sopenharmony_ci			ino = child->unique_id;
21262306a36Sopenharmony_ci			goto found;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci		child = child->sibling;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	prop = dp->properties;
21862306a36Sopenharmony_ci	while (prop) {
21962306a36Sopenharmony_ci		int n = strlen(prop->name);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		if (len == n && !strncmp(prop->name, name, len)) {
22262306a36Sopenharmony_ci			ent_type = op_inode_prop;
22362306a36Sopenharmony_ci			ent_data.prop = prop;
22462306a36Sopenharmony_ci			ino = prop->unique_id;
22562306a36Sopenharmony_ci			goto found;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		prop = prop->next;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	mutex_unlock(&op_mutex);
23262306a36Sopenharmony_ci	return ERR_PTR(-ENOENT);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cifound:
23562306a36Sopenharmony_ci	inode = openprom_iget(dir->i_sb, ino);
23662306a36Sopenharmony_ci	mutex_unlock(&op_mutex);
23762306a36Sopenharmony_ci	if (IS_ERR(inode))
23862306a36Sopenharmony_ci		return ERR_CAST(inode);
23962306a36Sopenharmony_ci	if (inode->i_state & I_NEW) {
24062306a36Sopenharmony_ci		inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
24162306a36Sopenharmony_ci		ent_oi = OP_I(inode);
24262306a36Sopenharmony_ci		ent_oi->type = ent_type;
24362306a36Sopenharmony_ci		ent_oi->u = ent_data;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		switch (ent_type) {
24662306a36Sopenharmony_ci		case op_inode_node:
24762306a36Sopenharmony_ci			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
24862306a36Sopenharmony_ci			inode->i_op = &openprom_inode_operations;
24962306a36Sopenharmony_ci			inode->i_fop = &openprom_operations;
25062306a36Sopenharmony_ci			set_nlink(inode, 2);
25162306a36Sopenharmony_ci			break;
25262306a36Sopenharmony_ci		case op_inode_prop:
25362306a36Sopenharmony_ci			if (of_node_name_eq(dp, "options") && (len == 17) &&
25462306a36Sopenharmony_ci			    !strncmp (name, "security-password", 17))
25562306a36Sopenharmony_ci				inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
25662306a36Sopenharmony_ci			else
25762306a36Sopenharmony_ci				inode->i_mode = S_IFREG | S_IRUGO;
25862306a36Sopenharmony_ci			inode->i_fop = &openpromfs_prop_ops;
25962306a36Sopenharmony_ci			set_nlink(inode, 1);
26062306a36Sopenharmony_ci			inode->i_size = ent_oi->u.prop->length;
26162306a36Sopenharmony_ci			break;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci		unlock_new_inode(inode);
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return d_splice_alias(inode, dentry);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int openpromfs_readdir(struct file *file, struct dir_context *ctx)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
27262306a36Sopenharmony_ci	struct op_inode_info *oi = OP_I(inode);
27362306a36Sopenharmony_ci	struct device_node *dp = oi->u.node;
27462306a36Sopenharmony_ci	struct device_node *child;
27562306a36Sopenharmony_ci	struct property *prop;
27662306a36Sopenharmony_ci	int i;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	mutex_lock(&op_mutex);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (ctx->pos == 0) {
28162306a36Sopenharmony_ci		if (!dir_emit(ctx, ".", 1, inode->i_ino, DT_DIR))
28262306a36Sopenharmony_ci			goto out;
28362306a36Sopenharmony_ci		ctx->pos = 1;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci	if (ctx->pos == 1) {
28662306a36Sopenharmony_ci		if (!dir_emit(ctx, "..", 2,
28762306a36Sopenharmony_ci			    (dp->parent == NULL ?
28862306a36Sopenharmony_ci			     OPENPROM_ROOT_INO :
28962306a36Sopenharmony_ci			     dp->parent->unique_id), DT_DIR))
29062306a36Sopenharmony_ci			goto out;
29162306a36Sopenharmony_ci		ctx->pos = 2;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	i = ctx->pos - 2;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* First, the children nodes as directories.  */
29662306a36Sopenharmony_ci	child = dp->child;
29762306a36Sopenharmony_ci	while (i && child) {
29862306a36Sopenharmony_ci		child = child->sibling;
29962306a36Sopenharmony_ci		i--;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	while (child) {
30262306a36Sopenharmony_ci		if (!dir_emit(ctx,
30362306a36Sopenharmony_ci			    kbasename(child->full_name),
30462306a36Sopenharmony_ci			    strlen(kbasename(child->full_name)),
30562306a36Sopenharmony_ci			    child->unique_id, DT_DIR))
30662306a36Sopenharmony_ci			goto out;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		ctx->pos++;
30962306a36Sopenharmony_ci		child = child->sibling;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* Next, the properties as files.  */
31362306a36Sopenharmony_ci	prop = dp->properties;
31462306a36Sopenharmony_ci	while (i && prop) {
31562306a36Sopenharmony_ci		prop = prop->next;
31662306a36Sopenharmony_ci		i--;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci	while (prop) {
31962306a36Sopenharmony_ci		if (!dir_emit(ctx, prop->name, strlen(prop->name),
32062306a36Sopenharmony_ci			    prop->unique_id, DT_REG))
32162306a36Sopenharmony_ci			goto out;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		ctx->pos++;
32462306a36Sopenharmony_ci		prop = prop->next;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ciout:
32862306a36Sopenharmony_ci	mutex_unlock(&op_mutex);
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic struct kmem_cache *op_inode_cachep;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic struct inode *openprom_alloc_inode(struct super_block *sb)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct op_inode_info *oi;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	oi = alloc_inode_sb(sb, op_inode_cachep, GFP_KERNEL);
33962306a36Sopenharmony_ci	if (!oi)
34062306a36Sopenharmony_ci		return NULL;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return &oi->vfs_inode;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void openprom_free_inode(struct inode *inode)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	kmem_cache_free(op_inode_cachep, OP_I(inode));
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct inode *openprom_iget(struct super_block *sb, ino_t ino)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct inode *inode = iget_locked(sb, ino);
35362306a36Sopenharmony_ci	if (!inode)
35462306a36Sopenharmony_ci		inode = ERR_PTR(-ENOMEM);
35562306a36Sopenharmony_ci	return inode;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int openprom_remount(struct super_block *sb, int *flags, char *data)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	sync_filesystem(sb);
36162306a36Sopenharmony_ci	*flags |= SB_NOATIME;
36262306a36Sopenharmony_ci	return 0;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic const struct super_operations openprom_sops = {
36662306a36Sopenharmony_ci	.alloc_inode	= openprom_alloc_inode,
36762306a36Sopenharmony_ci	.free_inode	= openprom_free_inode,
36862306a36Sopenharmony_ci	.statfs		= simple_statfs,
36962306a36Sopenharmony_ci	.remount_fs	= openprom_remount,
37062306a36Sopenharmony_ci};
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int openprom_fill_super(struct super_block *s, struct fs_context *fc)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct inode *root_inode;
37562306a36Sopenharmony_ci	struct op_inode_info *oi;
37662306a36Sopenharmony_ci	int ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	s->s_flags |= SB_NOATIME;
37962306a36Sopenharmony_ci	s->s_blocksize = 1024;
38062306a36Sopenharmony_ci	s->s_blocksize_bits = 10;
38162306a36Sopenharmony_ci	s->s_magic = OPENPROM_SUPER_MAGIC;
38262306a36Sopenharmony_ci	s->s_op = &openprom_sops;
38362306a36Sopenharmony_ci	s->s_time_gran = 1;
38462306a36Sopenharmony_ci	root_inode = openprom_iget(s, OPENPROM_ROOT_INO);
38562306a36Sopenharmony_ci	if (IS_ERR(root_inode)) {
38662306a36Sopenharmony_ci		ret = PTR_ERR(root_inode);
38762306a36Sopenharmony_ci		goto out_no_root;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	root_inode->i_mtime = root_inode->i_atime = inode_set_ctime_current(root_inode);
39162306a36Sopenharmony_ci	root_inode->i_op = &openprom_inode_operations;
39262306a36Sopenharmony_ci	root_inode->i_fop = &openprom_operations;
39362306a36Sopenharmony_ci	root_inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
39462306a36Sopenharmony_ci	oi = OP_I(root_inode);
39562306a36Sopenharmony_ci	oi->type = op_inode_node;
39662306a36Sopenharmony_ci	oi->u.node = of_find_node_by_path("/");
39762306a36Sopenharmony_ci	unlock_new_inode(root_inode);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	s->s_root = d_make_root(root_inode);
40062306a36Sopenharmony_ci	if (!s->s_root)
40162306a36Sopenharmony_ci		goto out_no_root_dentry;
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ciout_no_root_dentry:
40562306a36Sopenharmony_ci	ret = -ENOMEM;
40662306a36Sopenharmony_ciout_no_root:
40762306a36Sopenharmony_ci	printk("openprom_fill_super: get root inode failed\n");
40862306a36Sopenharmony_ci	return ret;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int openpromfs_get_tree(struct fs_context *fc)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	return get_tree_single(fc, openprom_fill_super);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic const struct fs_context_operations openpromfs_context_ops = {
41762306a36Sopenharmony_ci	.get_tree	= openpromfs_get_tree,
41862306a36Sopenharmony_ci};
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int openpromfs_init_fs_context(struct fs_context *fc)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	fc->ops = &openpromfs_context_ops;
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic struct file_system_type openprom_fs_type = {
42762306a36Sopenharmony_ci	.owner		= THIS_MODULE,
42862306a36Sopenharmony_ci	.name		= "openpromfs",
42962306a36Sopenharmony_ci	.init_fs_context = openpromfs_init_fs_context,
43062306a36Sopenharmony_ci	.kill_sb	= kill_anon_super,
43162306a36Sopenharmony_ci};
43262306a36Sopenharmony_ciMODULE_ALIAS_FS("openpromfs");
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void op_inode_init_once(void *data)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct op_inode_info *oi = (struct op_inode_info *) data;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	inode_init_once(&oi->vfs_inode);
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic int __init init_openprom_fs(void)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	int err;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	op_inode_cachep = kmem_cache_create("op_inode_cache",
44662306a36Sopenharmony_ci					    sizeof(struct op_inode_info),
44762306a36Sopenharmony_ci					    0,
44862306a36Sopenharmony_ci					    (SLAB_RECLAIM_ACCOUNT |
44962306a36Sopenharmony_ci					     SLAB_MEM_SPREAD | SLAB_ACCOUNT),
45062306a36Sopenharmony_ci					    op_inode_init_once);
45162306a36Sopenharmony_ci	if (!op_inode_cachep)
45262306a36Sopenharmony_ci		return -ENOMEM;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	err = register_filesystem(&openprom_fs_type);
45562306a36Sopenharmony_ci	if (err)
45662306a36Sopenharmony_ci		kmem_cache_destroy(op_inode_cachep);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return err;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void __exit exit_openprom_fs(void)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	unregister_filesystem(&openprom_fs_type);
46462306a36Sopenharmony_ci	/*
46562306a36Sopenharmony_ci	 * Make sure all delayed rcu free inodes are flushed before we
46662306a36Sopenharmony_ci	 * destroy cache.
46762306a36Sopenharmony_ci	 */
46862306a36Sopenharmony_ci	rcu_barrier();
46962306a36Sopenharmony_ci	kmem_cache_destroy(op_inode_cachep);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cimodule_init(init_openprom_fs)
47362306a36Sopenharmony_cimodule_exit(exit_openprom_fs)
47462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
475