xref: /openbmc/linux/fs/openpromfs/inode.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1 /* $Id: inode.c,v 1.15 2001/11/12 09:43:39 davem Exp $
2  * openpromfs.c: /proc/openprom handling routines
3  *
4  * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
5  * Copyright (C) 1998      Eddie C. Dost  (ecd@skynet.be)
6  */
7 
8 #include <linux/module.h>
9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/fs.h>
12 #include <linux/openprom_fs.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #include <linux/smp_lock.h>
16 
17 #include <asm/openprom.h>
18 #include <asm/oplib.h>
19 #include <asm/uaccess.h>
20 
21 #define ALIASES_NNODES 64
22 
23 typedef struct {
24 	u16	parent;
25 	u16	next;
26 	u16	child;
27 	u16	first_prop;
28 	u32	node;
29 } openpromfs_node;
30 
31 typedef struct {
32 #define OPP_STRING	0x10
33 #define OPP_STRINGLIST	0x20
34 #define OPP_BINARY	0x40
35 #define OPP_HEXSTRING	0x80
36 #define OPP_DIRTY	0x01
37 #define OPP_QUOTED	0x02
38 #define OPP_NOTQUOTED	0x04
39 #define OPP_ASCIIZ	0x08
40 	u32	flag;
41 	u32	alloclen;
42 	u32	len;
43 	char	*value;
44 	char	name[8];
45 } openprom_property;
46 
47 static openpromfs_node *nodes;
48 static int alloced;
49 static u16 last_node;
50 static u16 first_prop;
51 static u16 options = 0xffff;
52 static u16 aliases = 0xffff;
53 static int aliases_nodes;
54 static char *alias_names [ALIASES_NNODES];
55 
56 #define OPENPROM_ROOT_INO	16
57 #define OPENPROM_FIRST_INO	OPENPROM_ROOT_INO
58 #define NODE(ino) nodes[ino - OPENPROM_FIRST_INO]
59 #define NODE2INO(node) (node + OPENPROM_FIRST_INO)
60 #define NODEP2INO(no) (no + OPENPROM_FIRST_INO + last_node)
61 
62 static int openpromfs_create (struct inode *, struct dentry *, int, struct nameidata *);
63 static int openpromfs_readdir(struct file *, void *, filldir_t);
64 static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry, struct nameidata *nd);
65 static int openpromfs_unlink (struct inode *, struct dentry *dentry);
66 
67 static ssize_t nodenum_read(struct file *file, char __user *buf,
68 			    size_t count, loff_t *ppos)
69 {
70 	struct inode *inode = file->f_dentry->d_inode;
71 	char buffer[10];
72 
73 	if (count < 0 || !inode->u.generic_ip)
74 		return -EINVAL;
75 	sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip));
76 	if (file->f_pos >= 9)
77 		return 0;
78 	if (count > 9 - file->f_pos)
79 		count = 9 - file->f_pos;
80 	if (copy_to_user(buf, buffer + file->f_pos, count))
81 		return -EFAULT;
82 	*ppos += count;
83 	return count;
84 }
85 
86 static ssize_t property_read(struct file *filp, char __user *buf,
87 			     size_t count, loff_t *ppos)
88 {
89 	struct inode *inode = filp->f_dentry->d_inode;
90 	int i, j, k;
91 	u32 node;
92 	char *p, *s;
93 	u32 *q;
94 	openprom_property *op;
95 	char buffer[64];
96 
97 	if (!filp->private_data) {
98 		node = nodes[(u16)((long)inode->u.generic_ip)].node;
99 		i = ((u32)(long)inode->u.generic_ip) >> 16;
100 		if ((u16)((long)inode->u.generic_ip) == aliases) {
101 			if (i >= aliases_nodes)
102 				p = NULL;
103 			else
104 				p = alias_names [i];
105 		} else
106 			for (p = prom_firstprop (node, buffer);
107 			     i && p && *p;
108 			     p = prom_nextprop (node, p, buffer), i--)
109 				/* nothing */ ;
110 		if (!p || !*p)
111 			return -EIO;
112 		i = prom_getproplen (node, p);
113 		if (i < 0) {
114 			if ((u16)((long)inode->u.generic_ip) == aliases)
115 				i = 0;
116 			else
117 				return -EIO;
118 		}
119 		k = i;
120 		if (i < 64) i = 64;
121 		filp->private_data = kmalloc (sizeof (openprom_property)
122 					      + (j = strlen (p)) + 2 * i,
123 					      GFP_KERNEL);
124 		if (!filp->private_data)
125 			return -ENOMEM;
126 		op = (openprom_property *)filp->private_data;
127 		op->flag = 0;
128 		op->alloclen = 2 * i;
129 		strcpy (op->name, p);
130 		op->value = (char *)(((unsigned long)(op->name + j + 4)) & ~3);
131 		op->len = k;
132 		if (k && prom_getproperty (node, p, op->value, i) < 0)
133 			return -EIO;
134 		op->value [k] = 0;
135 		if (k) {
136 			for (s = NULL, p = op->value; p < op->value + k; p++) {
137 				if ((*p >= ' ' && *p <= '~') || *p == '\n') {
138 					op->flag |= OPP_STRING;
139 					s = p;
140 					continue;
141 				}
142 				if (p > op->value && !*p && s == p - 1) {
143 					if (p < op->value + k - 1)
144 						op->flag |= OPP_STRINGLIST;
145 					else
146 						op->flag |= OPP_ASCIIZ;
147 					continue;
148 				}
149 				if (k == 1 && !*p) {
150 					op->flag |= (OPP_STRING|OPP_ASCIIZ);
151 					break;
152 				}
153 				op->flag &= ~(OPP_STRING|OPP_STRINGLIST);
154 				if (k & 3)
155 					op->flag |= OPP_HEXSTRING;
156 				else
157 					op->flag |= OPP_BINARY;
158 				break;
159 			}
160 			if (op->flag & OPP_STRINGLIST)
161 				op->flag &= ~(OPP_STRING);
162 			if (op->flag & OPP_ASCIIZ)
163 				op->len--;
164 		}
165 	} else
166 		op = (openprom_property *)filp->private_data;
167 	if (!count || !(op->len || (op->flag & OPP_ASCIIZ)))
168 		return 0;
169 	if (*ppos >= 0xffffff || count >= 0xffffff)
170 		return -EINVAL;
171 	if (op->flag & OPP_STRINGLIST) {
172 		for (k = 0, p = op->value; p < op->value + op->len; p++)
173 			if (!*p)
174 				k++;
175 		i = op->len + 4 * k + 3;
176 	} else if (op->flag & OPP_STRING) {
177 		i = op->len + 3;
178 	} else if (op->flag & OPP_BINARY) {
179 		i = (op->len * 9) >> 2;
180 	} else {
181 		i = (op->len << 1) + 1;
182 	}
183 	k = *ppos;
184 	if (k >= i) return 0;
185 	if (count > i - k) count = i - k;
186 	if (op->flag & OPP_STRING) {
187 		if (!k) {
188 			if (put_user('\'', buf))
189 				return -EFAULT;
190 			k++;
191 			count--;
192 		}
193 
194 		if (k + count >= i - 2)
195 			j = i - 2 - k;
196 		else
197 			j = count;
198 
199 		if (j >= 0) {
200 			if (copy_to_user(buf + k - *ppos,
201 					 op->value + k - 1, j))
202 				return -EFAULT;
203 			count -= j;
204 			k += j;
205 		}
206 
207 		if (count) {
208 			if (put_user('\'', &buf [k++ - *ppos]))
209 				return -EFAULT;
210 		}
211 		if (count > 1) {
212 			if (put_user('\n', &buf [k++ - *ppos]))
213 				return -EFAULT;
214 		}
215 	} else if (op->flag & OPP_STRINGLIST) {
216 		char *tmp;
217 
218 		tmp = kmalloc (i, GFP_KERNEL);
219 		if (!tmp)
220 			return -ENOMEM;
221 
222 		s = tmp;
223 		*s++ = '\'';
224 		for (p = op->value; p < op->value + op->len; p++) {
225 			if (!*p) {
226 				strcpy(s, "' + '");
227 				s += 5;
228 				continue;
229 			}
230 			*s++ = *p;
231 		}
232 		strcpy(s, "'\n");
233 
234 		if (copy_to_user(buf, tmp + k, count))
235 			return -EFAULT;
236 
237 		kfree(tmp);
238 		k += count;
239 
240 	} else if (op->flag & OPP_BINARY) {
241 		char buffer[10];
242 		u32 *first, *last;
243 		int first_off, last_cnt;
244 
245 		first = ((u32 *)op->value) + k / 9;
246 		first_off = k % 9;
247 		last = ((u32 *)op->value) + (k + count - 1) / 9;
248 		last_cnt = (k + count) % 9;
249 		if (!last_cnt) last_cnt = 9;
250 
251 		if (first == last) {
252 			sprintf (buffer, "%08x.", *first);
253 			if (copy_to_user(buf, buffer + first_off,
254 					 last_cnt - first_off))
255 				return -EFAULT;
256 			buf += last_cnt - first_off;
257 		} else {
258 			for (q = first; q <= last; q++) {
259 				sprintf (buffer, "%08x.", *q);
260 				if (q == first) {
261 					if (copy_to_user(buf, buffer + first_off,
262 							 9 - first_off))
263 						return -EFAULT;
264 					buf += 9 - first_off;
265 				} else if (q == last) {
266 					if (copy_to_user(buf, buffer, last_cnt))
267 						return -EFAULT;
268 					buf += last_cnt;
269 				} else {
270 					if (copy_to_user(buf, buffer, 9))
271 						return -EFAULT;
272 					buf += 9;
273 				}
274 			}
275 		}
276 
277 		if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) {
278 			if (put_user('\n', (buf - 1)))
279 				return -EFAULT;
280 		}
281 
282 		k += count;
283 
284 	} else if (op->flag & OPP_HEXSTRING) {
285 		char buffer[3];
286 
287 		if ((k < i - 1) && (k & 1)) {
288 			sprintf (buffer, "%02x",
289 				 (unsigned char) *(op->value + (k >> 1)) & 0xff);
290 			if (put_user(buffer[1], &buf[k++ - *ppos]))
291 				return -EFAULT;
292 			count--;
293 		}
294 
295 		for (; (count > 1) && (k < i - 1); k += 2) {
296 			sprintf (buffer, "%02x",
297 				 (unsigned char) *(op->value + (k >> 1)) & 0xff);
298 			if (copy_to_user(buf + k - *ppos, buffer, 2))
299 				return -EFAULT;
300 			count -= 2;
301 		}
302 
303 		if (count && (k < i - 1)) {
304 			sprintf (buffer, "%02x",
305 				 (unsigned char) *(op->value + (k >> 1)) & 0xff);
306 			if (put_user(buffer[0], &buf[k++ - *ppos]))
307 				return -EFAULT;
308 			count--;
309 		}
310 
311 		if (count) {
312 			if (put_user('\n', &buf [k++ - *ppos]))
313 				return -EFAULT;
314 		}
315 	}
316 	count = k - *ppos;
317 	*ppos = k;
318 	return count;
319 }
320 
321 static ssize_t property_write(struct file *filp, const char __user *buf,
322 			      size_t count, loff_t *ppos)
323 {
324 	int i, j, k;
325 	char *p;
326 	u32 *q;
327 	void *b;
328 	openprom_property *op;
329 
330 	if (*ppos >= 0xffffff || count >= 0xffffff)
331 		return -EINVAL;
332 	if (!filp->private_data) {
333 		i = property_read (filp, NULL, 0, NULL);
334 		if (i)
335 			return i;
336 	}
337 	k = *ppos;
338 	op = (openprom_property *)filp->private_data;
339 	if (!(op->flag & OPP_STRING)) {
340 		u32 *first, *last;
341 		int first_off, last_cnt;
342 		u32 mask, mask2;
343 		char tmp [9];
344 		int forcelen = 0;
345 
346 		j = k % 9;
347 		for (i = 0; i < count; i++, j++) {
348 			if (j == 9) j = 0;
349 			if (!j) {
350 				char ctmp;
351 				if (get_user(ctmp, &buf[i]))
352 					return -EFAULT;
353 				if (ctmp != '.') {
354 					if (ctmp != '\n') {
355 						if (op->flag & OPP_BINARY)
356 							return -EINVAL;
357 						else
358 							goto write_try_string;
359 					} else {
360 						count = i + 1;
361 						forcelen = 1;
362 						break;
363 					}
364 				}
365 			} else {
366 				char ctmp;
367 				if (get_user(ctmp, &buf[i]))
368 					return -EFAULT;
369 				if (ctmp < '0' ||
370 				    (ctmp > '9' && ctmp < 'A') ||
371 				    (ctmp > 'F' && ctmp < 'a') ||
372 				    ctmp > 'f') {
373 					if (op->flag & OPP_BINARY)
374 						return -EINVAL;
375 					else
376 						goto write_try_string;
377 				}
378 			}
379 		}
380 		op->flag |= OPP_BINARY;
381 		tmp [8] = 0;
382 		i = ((count + k + 8) / 9) << 2;
383 		if (op->alloclen <= i) {
384 			b = kmalloc (sizeof (openprom_property) + 2 * i,
385 				     GFP_KERNEL);
386 			if (!b)
387 				return -ENOMEM;
388 			memcpy (b, filp->private_data,
389 				sizeof (openprom_property)
390 				+ strlen (op->name) + op->alloclen);
391 			memset (((char *)b) + sizeof (openprom_property)
392 				+ strlen (op->name) + op->alloclen,
393 				0, 2 * i - op->alloclen);
394 			op = (openprom_property *)b;
395 			op->alloclen = 2*i;
396 			b = filp->private_data;
397 			filp->private_data = (void *)op;
398 			kfree (b);
399 		}
400 		first = ((u32 *)op->value) + (k / 9);
401 		first_off = k % 9;
402 		last = (u32 *)(op->value + i);
403 		last_cnt = (k + count) % 9;
404 		if (first + 1 == last) {
405 			memset (tmp, '0', 8);
406 			if (copy_from_user(tmp + first_off, buf,
407 					   (count + first_off > 8) ?
408 					   8 - first_off : count))
409 				return -EFAULT;
410 			mask = 0xffffffff;
411 			mask2 = 0xffffffff;
412 			for (j = 0; j < first_off; j++)
413 				mask >>= 1;
414 			for (j = 8 - count - first_off; j > 0; j--)
415 				mask2 <<= 1;
416 			mask &= mask2;
417 			if (mask) {
418 				*first &= ~mask;
419 				*first |= simple_strtoul (tmp, NULL, 16);
420 				op->flag |= OPP_DIRTY;
421 			}
422 		} else {
423 			op->flag |= OPP_DIRTY;
424 			for (q = first; q < last; q++) {
425 				if (q == first) {
426 					if (first_off < 8) {
427 						memset (tmp, '0', 8);
428 						if (copy_from_user(tmp + first_off,
429 								   buf,
430 								   8 - first_off))
431 							return -EFAULT;
432 						mask = 0xffffffff;
433 						for (j = 0; j < first_off; j++)
434 							mask >>= 1;
435 						*q &= ~mask;
436 						*q |= simple_strtoul (tmp,NULL,16);
437 					}
438 					buf += 9;
439 				} else if ((q == last - 1) && last_cnt
440 					   && (last_cnt < 8)) {
441 					memset (tmp, '0', 8);
442 					if (copy_from_user(tmp, buf, last_cnt))
443 						return -EFAULT;
444 					mask = 0xffffffff;
445 					for (j = 0; j < 8 - last_cnt; j++)
446 						mask <<= 1;
447 					*q &= ~mask;
448 					*q |= simple_strtoul (tmp, NULL, 16);
449 					buf += last_cnt;
450 				} else {
451 					char tchars[17]; /* XXX yuck... */
452 
453 					if (copy_from_user(tchars, buf, 16))
454 						return -EFAULT;
455 					*q = simple_strtoul (tchars, NULL, 16);
456 					buf += 9;
457 				}
458 			}
459 		}
460 		if (!forcelen) {
461 			if (op->len < i)
462 				op->len = i;
463 		} else
464 			op->len = i;
465 		*ppos += count;
466 	}
467 write_try_string:
468 	if (!(op->flag & OPP_BINARY)) {
469 		if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) {
470 			char ctmp;
471 
472 			/* No way, if somebody starts writing from the middle,
473 			 * we don't know whether he uses quotes around or not
474 			 */
475 			if (k > 0)
476 				return -EINVAL;
477 			if (get_user(ctmp, buf))
478 				return -EFAULT;
479 			if (ctmp == '\'') {
480 				op->flag |= OPP_QUOTED;
481 				buf++;
482 				count--;
483 				(*ppos)++;
484 				if (!count) {
485 					op->flag |= OPP_STRING;
486 					return 1;
487 				}
488 			} else
489 				op->flag |= OPP_NOTQUOTED;
490 		}
491 		op->flag |= OPP_STRING;
492 		if (op->alloclen <= count + *ppos) {
493 			b = kmalloc (sizeof (openprom_property)
494 				     + 2 * (count + *ppos), GFP_KERNEL);
495 			if (!b)
496 				return -ENOMEM;
497 			memcpy (b, filp->private_data,
498 				sizeof (openprom_property)
499 				+ strlen (op->name) + op->alloclen);
500 			memset (((char *)b) + sizeof (openprom_property)
501 				+ strlen (op->name) + op->alloclen,
502 				0, 2*(count - *ppos) - op->alloclen);
503 			op = (openprom_property *)b;
504 			op->alloclen = 2*(count + *ppos);
505 			b = filp->private_data;
506 			filp->private_data = (void *)op;
507 			kfree (b);
508 		}
509 		p = op->value + *ppos - ((op->flag & OPP_QUOTED) ? 1 : 0);
510 		if (copy_from_user(p, buf, count))
511 			return -EFAULT;
512 		op->flag |= OPP_DIRTY;
513 		for (i = 0; i < count; i++, p++)
514 			if (*p == '\n') {
515 				*p = 0;
516 				break;
517 			}
518 		if (i < count) {
519 			op->len = p - op->value;
520 			*ppos += i + 1;
521 			if ((p > op->value) && (op->flag & OPP_QUOTED)
522 			    && (*(p - 1) == '\''))
523 				op->len--;
524 		} else {
525 			if (p - op->value > op->len)
526 				op->len = p - op->value;
527 			*ppos += count;
528 		}
529 	}
530 	return *ppos - k;
531 }
532 
533 int property_release (struct inode *inode, struct file *filp)
534 {
535 	openprom_property *op = (openprom_property *)filp->private_data;
536 	int error;
537 	u32 node;
538 
539 	if (!op)
540 		return 0;
541 	lock_kernel();
542 	node = nodes[(u16)((long)inode->u.generic_ip)].node;
543 	if ((u16)((long)inode->u.generic_ip) == aliases) {
544 		if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) {
545 			char *p = op->name;
546 			int i = (op->value - op->name) - strlen (op->name) - 1;
547 			op->value [op->len] = 0;
548 			*(op->value - 1) = ' ';
549 			if (i) {
550 				for (p = op->value - i - 2; p >= op->name; p--)
551 					p[i] = *p;
552 				p = op->name + i;
553 			}
554 			memcpy (p - 8, "nvalias ", 8);
555 			prom_feval (p - 8);
556 		}
557 	} else if (op->flag & OPP_DIRTY) {
558 		if (op->flag & OPP_STRING) {
559 			op->value [op->len] = 0;
560 			error = prom_setprop (node, op->name,
561 					      op->value, op->len + 1);
562 			if (error <= 0)
563 				printk (KERN_WARNING "openpromfs: "
564 					"Couldn't write property %s\n",
565 					op->name);
566 		} else if ((op->flag & OPP_BINARY) || !op->len) {
567 			error = prom_setprop (node, op->name,
568 					      op->value, op->len);
569 			if (error <= 0)
570 				printk (KERN_WARNING "openpromfs: "
571 					"Couldn't write property %s\n",
572 					op->name);
573 		} else {
574 			printk (KERN_WARNING "openpromfs: "
575 				"Unknown property type of %s\n",
576 				op->name);
577 		}
578 	}
579 	unlock_kernel();
580 	kfree (filp->private_data);
581 	return 0;
582 }
583 
584 static struct file_operations openpromfs_prop_ops = {
585 	.read		= property_read,
586 	.write		= property_write,
587 	.release	= property_release,
588 };
589 
590 static struct file_operations openpromfs_nodenum_ops = {
591 	.read		= nodenum_read,
592 };
593 
594 static struct file_operations openprom_operations = {
595 	.read		= generic_read_dir,
596 	.readdir	= openpromfs_readdir,
597 };
598 
599 static struct inode_operations openprom_alias_inode_operations = {
600 	.create		= openpromfs_create,
601 	.lookup		= openpromfs_lookup,
602 	.unlink		= openpromfs_unlink,
603 };
604 
605 static struct inode_operations openprom_inode_operations = {
606 	.lookup		= openpromfs_lookup,
607 };
608 
609 static int lookup_children(u16 n, const char * name, int len)
610 {
611 	int ret;
612 	u16 node;
613 	for (; n != 0xffff; n = nodes[n].next) {
614 		node = nodes[n].child;
615 		if (node != 0xffff) {
616 			char buffer[128];
617 			int i;
618 			char *p;
619 
620 			while (node != 0xffff) {
621 				if (prom_getname (nodes[node].node,
622 						  buffer, 128) >= 0) {
623 					i = strlen (buffer);
624 					if ((len == i)
625 					    && !strncmp (buffer, name, len))
626 						return NODE2INO(node);
627 					p = strchr (buffer, '@');
628 					if (p && (len == p - buffer)
629 					    && !strncmp (buffer, name, len))
630 						return NODE2INO(node);
631 				}
632 				node = nodes[node].next;
633 			}
634 		} else
635 			continue;
636 		ret = lookup_children (nodes[n].child, name, len);
637 		if (ret) return ret;
638 	}
639 	return 0;
640 }
641 
642 static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
643 {
644 	int ino = 0;
645 #define OPFSL_DIR	0
646 #define OPFSL_PROPERTY	1
647 #define OPFSL_NODENUM	2
648 	int type = 0;
649 	char buffer[128];
650 	char *p;
651 	const char *name;
652 	u32 n;
653 	u16 dirnode;
654 	unsigned int len;
655 	int i;
656 	struct inode *inode;
657 	char buffer2[64];
658 
659 	inode = NULL;
660 	name = dentry->d_name.name;
661 	len = dentry->d_name.len;
662 	lock_kernel();
663 	if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) {
664 		ino = NODEP2INO(NODE(dir->i_ino).first_prop);
665 		type = OPFSL_NODENUM;
666 	}
667 	if (!ino) {
668 		u16 node = NODE(dir->i_ino).child;
669 		while (node != 0xffff) {
670 			if (prom_getname (nodes[node].node, buffer, 128) >= 0) {
671 				i = strlen (buffer);
672 				if (len == i && !strncmp (buffer, name, len)) {
673 					ino = NODE2INO(node);
674 					type = OPFSL_DIR;
675 					break;
676 				}
677 				p = strchr (buffer, '@');
678 				if (p && (len == p - buffer)
679 				    && !strncmp (buffer, name, len)) {
680 					ino = NODE2INO(node);
681 					type = OPFSL_DIR;
682 					break;
683 				}
684 			}
685 			node = nodes[node].next;
686 		}
687 	}
688 	n = NODE(dir->i_ino).node;
689 	dirnode = dir->i_ino - OPENPROM_FIRST_INO;
690 	if (!ino) {
691 		int j = NODEP2INO(NODE(dir->i_ino).first_prop);
692 		if (dirnode != aliases) {
693 			for (p = prom_firstprop (n, buffer2);
694 			     p && *p;
695 			     p = prom_nextprop (n, p, buffer2)) {
696 				j++;
697 				if ((len == strlen (p))
698 				    && !strncmp (p, name, len)) {
699 					ino = j;
700 					type = OPFSL_PROPERTY;
701 					break;
702 				}
703 			}
704 		} else {
705 			int k;
706 			for (k = 0; k < aliases_nodes; k++) {
707 				j++;
708 				if (alias_names [k]
709 				    && (len == strlen (alias_names [k]))
710 				    && !strncmp (alias_names [k], name, len)) {
711 					ino = j;
712 					type = OPFSL_PROPERTY;
713 					break;
714 				}
715 			}
716 		}
717 	}
718 	if (!ino) {
719 		ino = lookup_children (NODE(dir->i_ino).child, name, len);
720 		if (ino)
721 			type = OPFSL_DIR;
722 		else {
723 			unlock_kernel();
724 			return ERR_PTR(-ENOENT);
725 		}
726 	}
727 	inode = iget (dir->i_sb, ino);
728 	unlock_kernel();
729 	if (!inode)
730 		return ERR_PTR(-EINVAL);
731 	switch (type) {
732 	case OPFSL_DIR:
733 		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
734 		if (ino == OPENPROM_FIRST_INO + aliases) {
735 			inode->i_mode |= S_IWUSR;
736 			inode->i_op = &openprom_alias_inode_operations;
737 		} else
738 			inode->i_op = &openprom_inode_operations;
739 		inode->i_fop = &openprom_operations;
740 		inode->i_nlink = 2;
741 		break;
742 	case OPFSL_NODENUM:
743 		inode->i_mode = S_IFREG | S_IRUGO;
744 		inode->i_fop = &openpromfs_nodenum_ops;
745 		inode->i_nlink = 1;
746 		inode->u.generic_ip = (void *)(long)(n);
747 		break;
748 	case OPFSL_PROPERTY:
749 		if ((dirnode == options) && (len == 17)
750 		    && !strncmp (name, "security-password", 17))
751 			inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
752 		else {
753 			inode->i_mode = S_IFREG | S_IRUGO;
754 			if (dirnode == options || dirnode == aliases) {
755 				if (len != 4 || strncmp (name, "name", 4))
756 					inode->i_mode |= S_IWUSR;
757 			}
758 		}
759 		inode->i_fop = &openpromfs_prop_ops;
760 		inode->i_nlink = 1;
761 		if (inode->i_size < 0)
762 			inode->i_size = 0;
763 		inode->u.generic_ip = (void *)(long)(((u16)dirnode) |
764 			(((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16));
765 		break;
766 	}
767 
768 	inode->i_gid = 0;
769 	inode->i_uid = 0;
770 
771 	d_add(dentry, inode);
772 	return NULL;
773 }
774 
775 static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
776 {
777 	struct inode *inode = filp->f_dentry->d_inode;
778 	unsigned int ino;
779 	u32 n;
780 	int i, j;
781 	char buffer[128];
782 	u16 node;
783 	char *p;
784 	char buffer2[64];
785 
786 	lock_kernel();
787 
788 	ino = inode->i_ino;
789 	i = filp->f_pos;
790 	switch (i) {
791 	case 0:
792 		if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) goto out;
793 		i++;
794 		filp->f_pos++;
795 		/* fall thru */
796 	case 1:
797 		if (filldir(dirent, "..", 2, i,
798 			(NODE(ino).parent == 0xffff) ?
799 			OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent), DT_DIR) < 0)
800 			goto out;
801 		i++;
802 		filp->f_pos++;
803 		/* fall thru */
804 	default:
805 		i -= 2;
806 		node = NODE(ino).child;
807 		while (i && node != 0xffff) {
808 			node = nodes[node].next;
809 			i--;
810 		}
811 		while (node != 0xffff) {
812 			if (prom_getname (nodes[node].node, buffer, 128) < 0)
813 				goto out;
814 			if (filldir(dirent, buffer, strlen(buffer),
815 				    filp->f_pos, NODE2INO(node), DT_DIR) < 0)
816 				goto out;
817 			filp->f_pos++;
818 			node = nodes[node].next;
819 		}
820 		j = NODEP2INO(NODE(ino).first_prop);
821 		if (!i) {
822 			if (filldir(dirent, ".node", 5, filp->f_pos, j, DT_REG) < 0)
823 				goto out;
824 			filp->f_pos++;
825 		} else
826 			i--;
827 		n = NODE(ino).node;
828 		if (ino == OPENPROM_FIRST_INO + aliases) {
829 			for (j++; i < aliases_nodes; i++, j++) {
830 				if (alias_names [i]) {
831 					if (filldir (dirent, alias_names [i],
832 						strlen (alias_names [i]),
833 						filp->f_pos, j, DT_REG) < 0) goto out;
834 					filp->f_pos++;
835 				}
836 			}
837 		} else {
838 			for (p = prom_firstprop (n, buffer2);
839 			     p && *p;
840 			     p = prom_nextprop (n, p, buffer2)) {
841 				j++;
842 				if (i) i--;
843 				else {
844 					if (filldir(dirent, p, strlen(p),
845 						    filp->f_pos, j, DT_REG) < 0)
846 						goto out;
847 					filp->f_pos++;
848 				}
849 			}
850 		}
851 	}
852 out:
853 	unlock_kernel();
854 	return 0;
855 }
856 
857 static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode,
858 		struct nameidata *nd)
859 {
860 	char *p;
861 	struct inode *inode;
862 
863 	if (!dir)
864 		return -ENOENT;
865 	if (dentry->d_name.len > 256)
866 		return -EINVAL;
867 	p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL);
868 	if (!p)
869 		return -ENOMEM;
870 	strncpy (p, dentry->d_name.name, dentry->d_name.len);
871 	p [dentry->d_name.len] = 0;
872 	lock_kernel();
873 	if (aliases_nodes == ALIASES_NNODES) {
874 		kfree(p);
875 		unlock_kernel();
876 		return -EIO;
877 	}
878 	alias_names [aliases_nodes++] = p;
879 	inode = iget (dir->i_sb,
880 			NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes);
881 	if (!inode) {
882 		unlock_kernel();
883 		return -EINVAL;
884 	}
885 	inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
886 	inode->i_fop = &openpromfs_prop_ops;
887 	inode->i_nlink = 1;
888 	if (inode->i_size < 0) inode->i_size = 0;
889 	inode->u.generic_ip = (void *)(long)(((u16)aliases) |
890 			(((u16)(aliases_nodes - 1)) << 16));
891 	unlock_kernel();
892 	d_instantiate(dentry, inode);
893 	return 0;
894 }
895 
896 static int openpromfs_unlink (struct inode *dir, struct dentry *dentry)
897 {
898 	unsigned int len;
899 	char *p;
900 	const char *name;
901 	int i;
902 
903 	name = dentry->d_name.name;
904 	len = dentry->d_name.len;
905 	lock_kernel();
906 	for (i = 0; i < aliases_nodes; i++)
907 		if ((strlen (alias_names [i]) == len)
908 		    && !strncmp (name, alias_names[i], len)) {
909 			char buffer[512];
910 
911 			p = alias_names [i];
912 			alias_names [i] = NULL;
913 			kfree (p);
914 			strcpy (buffer, "nvunalias ");
915 			memcpy (buffer + 10, name, len);
916 			buffer [10 + len] = 0;
917 			prom_feval (buffer);
918 		}
919 	unlock_kernel();
920 	return 0;
921 }
922 
923 /* {{{ init section */
924 static int __init check_space (u16 n)
925 {
926 	unsigned long pages;
927 
928 	if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) {
929 		pages = __get_free_pages (GFP_KERNEL, alloced + 1);
930 		if (!pages)
931 			return -1;
932 
933 		if (nodes) {
934 			memcpy ((char *)pages, (char *)nodes,
935 				(1 << alloced) * PAGE_SIZE);
936 			free_pages ((unsigned long)nodes, alloced);
937 		}
938 		alloced++;
939 		nodes = (openpromfs_node *)pages;
940 	}
941 	return 0;
942 }
943 
944 static u16 __init get_nodes (u16 parent, u32 node)
945 {
946 	char *p;
947 	u16 n = last_node++, i;
948 	char buffer[64];
949 
950 	if (check_space (n) < 0)
951 		return 0xffff;
952 	nodes[n].parent = parent;
953 	nodes[n].node = node;
954 	nodes[n].next = 0xffff;
955 	nodes[n].child = 0xffff;
956 	nodes[n].first_prop = first_prop++;
957 	if (!parent) {
958 		char buffer[8];
959 		int j;
960 
961 		if ((j = prom_getproperty (node, "name", buffer, 8)) >= 0) {
962 		    buffer[j] = 0;
963 		    if (!strcmp (buffer, "options"))
964 			options = n;
965 		    else if (!strcmp (buffer, "aliases"))
966 		        aliases = n;
967 		}
968 	}
969 	if (n != aliases)
970 		for (p = prom_firstprop (node, buffer);
971 		     p && p != (char *)-1 && *p;
972 		     p = prom_nextprop (node, p, buffer))
973 			first_prop++;
974 	else {
975 		char *q;
976 		for (p = prom_firstprop (node, buffer);
977 		     p && p != (char *)-1 && *p;
978 		     p = prom_nextprop (node, p, buffer)) {
979 			if (aliases_nodes == ALIASES_NNODES)
980 				break;
981 			for (i = 0; i < aliases_nodes; i++)
982 				if (!strcmp (p, alias_names [i]))
983 					break;
984 			if (i < aliases_nodes)
985 				continue;
986 			q = kmalloc (strlen (p) + 1, GFP_KERNEL);
987 			if (!q)
988 				return 0xffff;
989 			strcpy (q, p);
990 			alias_names [aliases_nodes++] = q;
991 		}
992 		first_prop += ALIASES_NNODES;
993 	}
994 	node = prom_getchild (node);
995 	if (node) {
996 		parent = get_nodes (n, node);
997 		if (parent == 0xffff)
998 			return 0xffff;
999 		nodes[n].child = parent;
1000 		while ((node = prom_getsibling (node)) != 0) {
1001 			i = get_nodes (n, node);
1002 			if (i == 0xffff)
1003 				return 0xffff;
1004 			nodes[parent].next = i;
1005 			parent = i;
1006 		}
1007 	}
1008 	return n;
1009 }
1010 
1011 static void openprom_read_inode(struct inode * inode)
1012 {
1013 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
1014 	if (inode->i_ino == OPENPROM_ROOT_INO) {
1015 		inode->i_op = &openprom_inode_operations;
1016 		inode->i_fop = &openprom_operations;
1017 		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
1018 	}
1019 }
1020 
1021 static int openprom_remount(struct super_block *sb, int *flags, char *data)
1022 {
1023 	*flags |= MS_NOATIME;
1024 	return 0;
1025 }
1026 
1027 static struct super_operations openprom_sops = {
1028 	.read_inode	= openprom_read_inode,
1029 	.statfs		= simple_statfs,
1030 	.remount_fs	= openprom_remount,
1031 };
1032 
1033 static int openprom_fill_super(struct super_block *s, void *data, int silent)
1034 {
1035 	struct inode * root_inode;
1036 
1037 	s->s_flags |= MS_NOATIME;
1038 	s->s_blocksize = 1024;
1039 	s->s_blocksize_bits = 10;
1040 	s->s_magic = OPENPROM_SUPER_MAGIC;
1041 	s->s_op = &openprom_sops;
1042 	s->s_time_gran = 1;
1043 	root_inode = iget(s, OPENPROM_ROOT_INO);
1044 	if (!root_inode)
1045 		goto out_no_root;
1046 	s->s_root = d_alloc_root(root_inode);
1047 	if (!s->s_root)
1048 		goto out_no_root;
1049 	return 0;
1050 
1051 out_no_root:
1052 	printk("openprom_fill_super: get root inode failed\n");
1053 	iput(root_inode);
1054 	return -ENOMEM;
1055 }
1056 
1057 static struct super_block *openprom_get_sb(struct file_system_type *fs_type,
1058 	int flags, const char *dev_name, void *data)
1059 {
1060 	return get_sb_single(fs_type, flags, data, openprom_fill_super);
1061 }
1062 
1063 static struct file_system_type openprom_fs_type = {
1064 	.owner		= THIS_MODULE,
1065 	.name		= "openpromfs",
1066 	.get_sb		= openprom_get_sb,
1067 	.kill_sb	= kill_anon_super,
1068 };
1069 
1070 static int __init init_openprom_fs(void)
1071 {
1072 	nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
1073 	if (!nodes) {
1074 		printk (KERN_WARNING "openpromfs: can't get free page\n");
1075 		return -EIO;
1076 	}
1077 	if (get_nodes (0xffff, prom_root_node) == 0xffff) {
1078 		printk (KERN_WARNING "openpromfs: couldn't setup tree\n");
1079 		return -EIO;
1080 	}
1081 	nodes[last_node].first_prop = first_prop;
1082 	return register_filesystem(&openprom_fs_type);
1083 }
1084 
1085 static void __exit exit_openprom_fs(void)
1086 {
1087 	int i;
1088 	unregister_filesystem(&openprom_fs_type);
1089 	free_pages ((unsigned long)nodes, alloced);
1090 	for (i = 0; i < aliases_nodes; i++)
1091 		if (alias_names [i])
1092 			kfree (alias_names [i]);
1093 	nodes = NULL;
1094 }
1095 
1096 module_init(init_openprom_fs)
1097 module_exit(exit_openprom_fs)
1098 MODULE_LICENSE("GPL");
1099