Linux Audio

Check our new training course

Loading...
  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 struct inode *openprom_iget(struct super_block *sb, ino_t ino);
 42
 43static inline struct op_inode_info *OP_I(struct inode *inode)
 44{
 45	return container_of(inode, struct op_inode_info, vfs_inode);
 46}
 47
 48static int is_string(unsigned char *p, int len)
 49{
 50	int i;
 51
 52	for (i = 0; i < len; i++) {
 53		unsigned char val = p[i];
 54
 55		if ((i && !val) ||
 56		    (val >= ' ' && val <= '~'))
 57			continue;
 58
 59		return 0;
 60	}
 61
 62	return 1;
 63}
 64
 65static int property_show(struct seq_file *f, void *v)
 66{
 67	struct property *prop = f->private;
 68	void *pval;
 69	int len;
 70
 71	len = prop->length;
 72	pval = prop->value;
 73
 74	if (is_string(pval, len)) {
 75		while (len > 0) {
 76			int n = strlen(pval);
 77
 78			seq_printf(f, "%s", (char *) pval);
 79
 80			/* Skip over the NULL byte too.  */
 81			pval += n + 1;
 82			len -= n + 1;
 83
 84			if (len > 0)
 85				seq_printf(f, " + ");
 86		}
 87	} else {
 88		if (len & 3) {
 89			while (len) {
 90				len--;
 91				if (len)
 92					seq_printf(f, "%02x.",
 93						   *(unsigned char *) pval);
 94				else
 95					seq_printf(f, "%02x",
 96						   *(unsigned char *) pval);
 97				pval++;
 98			}
 99		} else {
100			while (len >= 4) {
101				len -= 4;
102
103				if (len)
104					seq_printf(f, "%08x.",
105						   *(unsigned int *) pval);
106				else
107					seq_printf(f, "%08x",
108						   *(unsigned int *) pval);
109				pval += 4;
110			}
111		}
112	}
113	seq_printf(f, "\n");
114
115	return 0;
116}
117
118static void *property_start(struct seq_file *f, loff_t *pos)
119{
120	if (*pos == 0)
121		return pos;
122	return NULL;
123}
124
125static void *property_next(struct seq_file *f, void *v, loff_t *pos)
126{
127	(*pos)++;
128	return NULL;
129}
130
131static void property_stop(struct seq_file *f, void *v)
132{
133	/* Nothing to do */
134}
135
136static const struct seq_operations property_op = {
137	.start		= property_start,
138	.next		= property_next,
139	.stop		= property_stop,
140	.show		= property_show
141};
142
143static int property_open(struct inode *inode, struct file *file)
144{
145	struct op_inode_info *oi = OP_I(inode);
146	int ret;
147
148	BUG_ON(oi->type != op_inode_prop);
149
150	ret = seq_open(file, &property_op);
151	if (!ret) {
152		struct seq_file *m = file->private_data;
153		m->private = oi->u.prop;
154	}
155	return ret;
156}
157
158static const struct file_operations openpromfs_prop_ops = {
159	.open		= property_open,
160	.read		= seq_read,
161	.llseek		= seq_lseek,
162	.release	= seq_release,
163};
164
165static int openpromfs_readdir(struct file *, void *, filldir_t);
166
167static const struct file_operations openprom_operations = {
168	.read		= generic_read_dir,
169	.readdir	= openpromfs_readdir,
170	.llseek		= generic_file_llseek,
171};
172
173static struct dentry *openpromfs_lookup(struct inode *, struct dentry *, struct nameidata *);
174
175static const struct inode_operations openprom_inode_operations = {
176	.lookup		= openpromfs_lookup,
177};
178
179static struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
180{
181	struct op_inode_info *ent_oi, *oi = OP_I(dir);
182	struct device_node *dp, *child;
183	struct property *prop;
184	enum op_inode_type ent_type;
185	union op_inode_data ent_data;
186	const char *name;
187	struct inode *inode;
188	unsigned int ino;
189	int len;
190	
191	BUG_ON(oi->type != op_inode_node);
192
193	dp = oi->u.node;
194
195	name = dentry->d_name.name;
196	len = dentry->d_name.len;
197
198	mutex_lock(&op_mutex);
199
200	child = dp->child;
201	while (child) {
202		int n = strlen(child->path_component_name);
203
204		if (len == n &&
205		    !strncmp(child->path_component_name, name, len)) {
206			ent_type = op_inode_node;
207			ent_data.node = child;
208			ino = child->unique_id;
209			goto found;
210		}
211		child = child->sibling;
212	}
213
214	prop = dp->properties;
215	while (prop) {
216		int n = strlen(prop->name);
217
218		if (len == n && !strncmp(prop->name, name, len)) {
219			ent_type = op_inode_prop;
220			ent_data.prop = prop;
221			ino = prop->unique_id;
222			goto found;
223		}
224
225		prop = prop->next;
226	}
227
228	mutex_unlock(&op_mutex);
229	return ERR_PTR(-ENOENT);
230
231found:
232	inode = openprom_iget(dir->i_sb, ino);
233	mutex_unlock(&op_mutex);
234	if (IS_ERR(inode))
235		return ERR_CAST(inode);
236	ent_oi = OP_I(inode);
237	ent_oi->type = ent_type;
238	ent_oi->u = ent_data;
239
240	switch (ent_type) {
241	case op_inode_node:
242		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
243		inode->i_op = &openprom_inode_operations;
244		inode->i_fop = &openprom_operations;
245		set_nlink(inode, 2);
246		break;
247	case op_inode_prop:
248		if (!strcmp(dp->name, "options") && (len == 17) &&
249		    !strncmp (name, "security-password", 17))
250			inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
251		else
252			inode->i_mode = S_IFREG | S_IRUGO;
253		inode->i_fop = &openpromfs_prop_ops;
254		set_nlink(inode, 1);
255		inode->i_size = ent_oi->u.prop->length;
256		break;
257	}
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_i_callback(struct rcu_head *head)
347{
348	struct inode *inode = container_of(head, struct inode, i_rcu);
349	kmem_cache_free(op_inode_cachep, OP_I(inode));
350}
351
352static void openprom_destroy_inode(struct inode *inode)
353{
354	call_rcu(&inode->i_rcu, openprom_i_callback);
355}
356
357static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
358{
359	struct inode *inode;
360
361	inode = iget_locked(sb, ino);
362	if (!inode)
363		return ERR_PTR(-ENOMEM);
364	if (inode->i_state & I_NEW) {
365		inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
366		if (inode->i_ino == OPENPROM_ROOT_INO) {
367			inode->i_op = &openprom_inode_operations;
368			inode->i_fop = &openprom_operations;
369			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
370		}
371		unlock_new_inode(inode);
372	}
373	return inode;
374}
375
376static int openprom_remount(struct super_block *sb, int *flags, char *data)
377{
378	*flags |= MS_NOATIME;
379	return 0;
380}
381
382static const struct super_operations openprom_sops = {
383	.alloc_inode	= openprom_alloc_inode,
384	.destroy_inode	= openprom_destroy_inode,
385	.statfs		= simple_statfs,
386	.remount_fs	= openprom_remount,
387};
388
389static int openprom_fill_super(struct super_block *s, void *data, int silent)
390{
391	struct inode *root_inode;
392	struct op_inode_info *oi;
393	int ret;
394
395	s->s_flags |= MS_NOATIME;
396	s->s_blocksize = 1024;
397	s->s_blocksize_bits = 10;
398	s->s_magic = OPENPROM_SUPER_MAGIC;
399	s->s_op = &openprom_sops;
400	s->s_time_gran = 1;
401	root_inode = openprom_iget(s, OPENPROM_ROOT_INO);
402	if (IS_ERR(root_inode)) {
403		ret = PTR_ERR(root_inode);
404		goto out_no_root;
405	}
406
407	oi = OP_I(root_inode);
408	oi->type = op_inode_node;
409	oi->u.node = of_find_node_by_path("/");
410
411	s->s_root = d_make_root(root_inode);
412	if (!s->s_root)
413		goto out_no_root_dentry;
414	return 0;
415
416out_no_root_dentry:
417	ret = -ENOMEM;
418out_no_root:
419	printk("openprom_fill_super: get root inode failed\n");
420	return ret;
421}
422
423static struct dentry *openprom_mount(struct file_system_type *fs_type,
424	int flags, const char *dev_name, void *data)
425{
426	return mount_single(fs_type, flags, data, openprom_fill_super);
427}
428
429static struct file_system_type openprom_fs_type = {
430	.owner		= THIS_MODULE,
431	.name		= "openpromfs",
432	.mount		= openprom_mount,
433	.kill_sb	= kill_anon_super,
434};
435
436static void op_inode_init_once(void *data)
437{
438	struct op_inode_info *oi = (struct op_inode_info *) data;
439
440	inode_init_once(&oi->vfs_inode);
441}
442
443static int __init init_openprom_fs(void)
444{
445	int err;
446
447	op_inode_cachep = kmem_cache_create("op_inode_cache",
448					    sizeof(struct op_inode_info),
449					    0,
450					    (SLAB_RECLAIM_ACCOUNT |
451					     SLAB_MEM_SPREAD),
452					    op_inode_init_once);
453	if (!op_inode_cachep)
454		return -ENOMEM;
455
456	err = register_filesystem(&openprom_fs_type);
457	if (err)
458		kmem_cache_destroy(op_inode_cachep);
459
460	return err;
461}
462
463static void __exit exit_openprom_fs(void)
464{
465	unregister_filesystem(&openprom_fs_type);
466	kmem_cache_destroy(op_inode_cachep);
467}
468
469module_init(init_openprom_fs)
470module_exit(exit_openprom_fs)
471MODULE_LICENSE("GPL");