1/* inode.c: /proc/openprom handling routines
2 *
3 * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
4 * Copyright (C) 1998      Eddie C. Dost  (ecd@skynet.be)
5 */
6
7#include <linux/module.h>
8#include <linux/types.h>
9#include <linux/string.h>
10#include <linux/fs.h>
11#include <linux/init.h>
12#include <linux/slab.h>
13#include <linux/seq_file.h>
14#include <linux/magic.h>
15
16#include <asm/openprom.h>
17#include <asm/oplib.h>
18#include <asm/prom.h>
19#include <asm/uaccess.h>
20
21static DEFINE_MUTEX(op_mutex);
22
23#define OPENPROM_ROOT_INO	0
24
25enum op_inode_type {
26	op_inode_node,
27	op_inode_prop,
28};
29
30union op_inode_data {
31	struct device_node	*node;
32	struct property		*prop;
33};
34
35struct op_inode_info {
36	struct inode		vfs_inode;
37	enum op_inode_type	type;
38	union op_inode_data	u;
39};
40
41static inline struct op_inode_info *OP_I(struct inode *inode)
42{
43	return container_of(inode, struct op_inode_info, vfs_inode);
44}
45
46static int is_string(unsigned char *p, int len)
47{
48	int i;
49
50	for (i = 0; i < len; i++) {
51		unsigned char val = p[i];
52
53		if ((i && !val) ||
54		    (val >= ' ' && val <= '~'))
55			continue;
56
57		return 0;
58	}
59
60	return 1;
61}
62
63static int property_show(struct seq_file *f, void *v)
64{
65	struct property *prop = f->private;
66	void *pval;
67	int len;
68
69	len = prop->length;
70	pval = prop->value;
71
72	if (is_string(pval, len)) {
73		while (len > 0) {
74			int n = strlen(pval);
75
76			seq_printf(f, "%s", (char *) pval);
77
78			/* Skip over the NULL byte too.  */
79			pval += n + 1;
80			len -= n + 1;
81
82			if (len > 0)
83				seq_printf(f, " + ");
84		}
85	} else {
86		if (len & 3) {
87			while (len) {
88				len--;
89				if (len)
90					seq_printf(f, "%02x.",
91						   *(unsigned char *) pval);
92				else
93					seq_printf(f, "%02x",
94						   *(unsigned char *) pval);
95				pval++;
96			}
97		} else {
98			while (len >= 4) {
99				len -= 4;
100
101				if (len)
102					seq_printf(f, "%08x.",
103						   *(unsigned int *) pval);
104				else
105					seq_printf(f, "%08x",
106						   *(unsigned int *) pval);
107				pval += 4;
108			}
109		}
110	}
111	seq_printf(f, "\n");
112
113	return 0;
114}
115
116static void *property_start(struct seq_file *f, loff_t *pos)
117{
118	if (*pos == 0)
119		return pos;
120	return NULL;
121}
122
123static void *property_next(struct seq_file *f, void *v, loff_t *pos)
124{
125	(*pos)++;
126	return NULL;
127}
128
129static void property_stop(struct seq_file *f, void *v)
130{
131	/* Nothing to do */
132}
133
134static struct seq_operations property_op = {
135	.start		= property_start,
136	.next		= property_next,
137	.stop		= property_stop,
138	.show		= property_show
139};
140
141static int property_open(struct inode *inode, struct file *file)
142{
143	struct op_inode_info *oi = OP_I(inode);
144	int ret;
145
146	BUG_ON(oi->type != op_inode_prop);
147
148	ret = seq_open(file, &property_op);
149	if (!ret) {
150		struct seq_file *m = file->private_data;
151		m->private = oi->u.prop;
152	}
153	return ret;
154}
155
156static const struct file_operations openpromfs_prop_ops = {
157	.open		= property_open,
158	.read		= seq_read,
159	.llseek		= seq_lseek,
160	.release	= seq_release,
161};
162
163static int openpromfs_readdir(struct file *, void *, filldir_t);
164
165static const struct file_operations openprom_operations = {
166	.read		= generic_read_dir,
167	.readdir	= openpromfs_readdir,
168};
169
170static struct dentry *openpromfs_lookup(struct inode *, struct dentry *, struct nameidata *);
171
172static const struct inode_operations openprom_inode_operations = {
173	.lookup		= openpromfs_lookup,
174};
175
176static struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
177{
178	struct op_inode_info *ent_oi, *oi = OP_I(dir);
179	struct device_node *dp, *child;
180	struct property *prop;
181	enum op_inode_type ent_type;
182	union op_inode_data ent_data;
183	const char *name;
184	struct inode *inode;
185	unsigned int ino;
186	int len;
187
188	BUG_ON(oi->type != op_inode_node);
189
190	dp = oi->u.node;
191
192	name = dentry->d_name.name;
193	len = dentry->d_name.len;
194
195	mutex_lock(&op_mutex);
196
197	child = dp->child;
198	while (child) {
199		int n = strlen(child->path_component_name);
200
201		if (len == n &&
202		    !strncmp(child->path_component_name, name, len)) {
203			ent_type = op_inode_node;
204			ent_data.node = child;
205			ino = child->unique_id;
206			goto found;
207		}
208		child = child->sibling;
209	}
210
211	prop = dp->properties;
212	while (prop) {
213		int n = strlen(prop->name);
214
215		if (len == n && !strncmp(prop->name, name, len)) {
216			ent_type = op_inode_prop;
217			ent_data.prop = prop;
218			ino = prop->unique_id;
219			goto found;
220		}
221
222		prop = prop->next;
223	}
224
225	mutex_unlock(&op_mutex);
226	return ERR_PTR(-ENOENT);
227
228found:
229	inode = iget(dir->i_sb, ino);
230	mutex_unlock(&op_mutex);
231	if (!inode)
232		return ERR_PTR(-EINVAL);
233	ent_oi = OP_I(inode);
234	ent_oi->type = ent_type;
235	ent_oi->u = ent_data;
236
237	switch (ent_type) {
238	case op_inode_node:
239		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
240		inode->i_op = &openprom_inode_operations;
241		inode->i_fop = &openprom_operations;
242		inode->i_nlink = 2;
243		break;
244	case op_inode_prop:
245		if (!strcmp(dp->name, "options") && (len == 17) &&
246		    !strncmp (name, "security-password", 17))
247			inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
248		else
249			inode->i_mode = S_IFREG | S_IRUGO;
250		inode->i_fop = &openpromfs_prop_ops;
251		inode->i_nlink = 1;
252		inode->i_size = ent_oi->u.prop->length;
253		break;
254	}
255
256	inode->i_gid = 0;
257	inode->i_uid = 0;
258
259	d_add(dentry, inode);
260	return NULL;
261}
262
263static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
264{
265	struct inode *inode = filp->f_path.dentry->d_inode;
266	struct op_inode_info *oi = OP_I(inode);
267	struct device_node *dp = oi->u.node;
268	struct device_node *child;
269	struct property *prop;
270	unsigned int ino;
271	int i;
272
273	mutex_lock(&op_mutex);
274
275	ino = inode->i_ino;
276	i = filp->f_pos;
277	switch (i) {
278	case 0:
279		if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
280			goto out;
281		i++;
282		filp->f_pos++;
283		/* fall thru */
284	case 1:
285		if (filldir(dirent, "..", 2, i,
286			    (dp->parent == NULL ?
287			     OPENPROM_ROOT_INO :
288			     dp->parent->unique_id), DT_DIR) < 0)
289			goto out;
290		i++;
291		filp->f_pos++;
292		/* fall thru */
293	default:
294		i -= 2;
295
296		/* First, the children nodes as directories.  */
297		child = dp->child;
298		while (i && child) {
299			child = child->sibling;
300			i--;
301		}
302		while (child) {
303			if (filldir(dirent,
304				    child->path_component_name,
305				    strlen(child->path_component_name),
306				    filp->f_pos, child->unique_id, DT_DIR) < 0)
307				goto out;
308
309			filp->f_pos++;
310			child = child->sibling;
311		}
312
313		/* Next, the properties as files.  */
314		prop = dp->properties;
315		while (i && prop) {
316			prop = prop->next;
317			i--;
318		}
319		while (prop) {
320			if (filldir(dirent, prop->name, strlen(prop->name),
321				    filp->f_pos, prop->unique_id, DT_REG) < 0)
322				goto out;
323
324			filp->f_pos++;
325			prop = prop->next;
326		}
327	}
328out:
329	mutex_unlock(&op_mutex);
330	return 0;
331}
332
333static struct kmem_cache *op_inode_cachep;
334
335static struct inode *openprom_alloc_inode(struct super_block *sb)
336{
337	struct op_inode_info *oi;
338
339	oi = kmem_cache_alloc(op_inode_cachep, GFP_KERNEL);
340	if (!oi)
341		return NULL;
342
343	return &oi->vfs_inode;
344}
345
346static void openprom_destroy_inode(struct inode *inode)
347{
348	kmem_cache_free(op_inode_cachep, OP_I(inode));
349}
350
351static void openprom_read_inode(struct inode * inode)
352{
353	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
354	if (inode->i_ino == OPENPROM_ROOT_INO) {
355		inode->i_op = &openprom_inode_operations;
356		inode->i_fop = &openprom_operations;
357		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
358	}
359}
360
361static int openprom_remount(struct super_block *sb, int *flags, char *data)
362{
363	*flags |= MS_NOATIME;
364	return 0;
365}
366
367static const struct super_operations openprom_sops = {
368	.alloc_inode	= openprom_alloc_inode,
369	.destroy_inode	= openprom_destroy_inode,
370	.read_inode	= openprom_read_inode,
371	.statfs		= simple_statfs,
372	.remount_fs	= openprom_remount,
373};
374
375static int openprom_fill_super(struct super_block *s, void *data, int silent)
376{
377	struct inode *root_inode;
378	struct op_inode_info *oi;
379
380	s->s_flags |= MS_NOATIME;
381	s->s_blocksize = 1024;
382	s->s_blocksize_bits = 10;
383	s->s_magic = OPENPROM_SUPER_MAGIC;
384	s->s_op = &openprom_sops;
385	s->s_time_gran = 1;
386	root_inode = iget(s, OPENPROM_ROOT_INO);
387	if (!root_inode)
388		goto out_no_root;
389
390	oi = OP_I(root_inode);
391	oi->type = op_inode_node;
392	oi->u.node = of_find_node_by_path("/");
393
394	s->s_root = d_alloc_root(root_inode);
395	if (!s->s_root)
396		goto out_no_root;
397	return 0;
398
399out_no_root:
400	printk("openprom_fill_super: get root inode failed\n");
401	iput(root_inode);
402	return -ENOMEM;
403}
404
405static int openprom_get_sb(struct file_system_type *fs_type,
406	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
407{
408	return get_sb_single(fs_type, flags, data, openprom_fill_super, mnt);
409}
410
411static struct file_system_type openprom_fs_type = {
412	.owner		= THIS_MODULE,
413	.name		= "openpromfs",
414	.get_sb		= openprom_get_sb,
415	.kill_sb	= kill_anon_super,
416};
417
418static void op_inode_init_once(void *data, struct kmem_cache * cachep, unsigned long flags)
419{
420	struct op_inode_info *oi = (struct op_inode_info *) data;
421
422	inode_init_once(&oi->vfs_inode);
423}
424
425static int __init init_openprom_fs(void)
426{
427	int err;
428
429	op_inode_cachep = kmem_cache_create("op_inode_cache",
430					    sizeof(struct op_inode_info),
431					    0,
432					    (SLAB_RECLAIM_ACCOUNT |
433					     SLAB_MEM_SPREAD),
434					    op_inode_init_once, NULL);
435	if (!op_inode_cachep)
436		return -ENOMEM;
437
438	err = register_filesystem(&openprom_fs_type);
439	if (err)
440		kmem_cache_destroy(op_inode_cachep);
441
442	return err;
443}
444
445static void __exit exit_openprom_fs(void)
446{
447	unregister_filesystem(&openprom_fs_type);
448	kmem_cache_destroy(op_inode_cachep);
449}
450
451module_init(init_openprom_fs)
452module_exit(exit_openprom_fs)
453MODULE_LICENSE("GPL");
454