Merge with 2.3.99-pre9.
[linux-2.6/linux-mips.git] / fs / openpromfs / inode.c
blob3b875a4452e2988f169432f3da48e16322719435
1 /* $Id: inode.c,v 1.11 2000/05/22 07:29:42 davem Exp $
2 * openpromfs.c: /proc/openprom handling routines
4 * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com)
5 * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
6 */
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/locks.h>
14 #include <linux/init.h>
15 #include <linux/malloc.h>
17 #include <asm/openprom.h>
18 #include <asm/oplib.h>
19 #include <asm/uaccess.h>
21 #define ALIASES_NNODES 64
23 typedef struct {
24 u16 parent;
25 u16 next;
26 u16 child;
27 u16 first_prop;
28 u32 node;
29 } openpromfs_node;
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;
47 static openpromfs_node *nodes = NULL;
48 static int alloced = 0;
49 static u16 last_node = 0;
50 static u16 first_prop = 0;
51 static u16 options = 0xffff;
52 static u16 aliases = 0xffff;
53 static int aliases_nodes = 0;
54 static char *alias_names [ALIASES_NNODES];
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)
62 static int openpromfs_create (struct inode *, struct dentry *, int);
63 static int openpromfs_readdir(struct file *, void *, filldir_t);
64 static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry);
65 static int openpromfs_unlink (struct inode *, struct dentry *dentry);
67 static ssize_t nodenum_read(struct file *file, char *buf,
68 size_t count, loff_t *ppos)
70 struct inode *inode = file->f_dentry->d_inode;
71 char buffer[10];
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 copy_to_user(buf, buffer + file->f_pos, count);
81 file->f_pos += count;
82 return count;
85 static ssize_t property_read(struct file *filp, char *buf,
86 size_t count, loff_t *ppos)
88 struct inode *inode = filp->f_dentry->d_inode;
89 int i, j, k;
90 u32 node;
91 char *p, *s;
92 u32 *q;
93 openprom_property *op;
94 char buffer[64];
96 if (filp->f_pos >= 0xffffff)
97 return -EINVAL;
98 if (!filp->private_data) {
99 node = nodes[(u16)((long)inode->u.generic_ip)].node;
100 i = ((u32)(long)inode->u.generic_ip) >> 16;
101 if ((u16)((long)inode->u.generic_ip) == aliases) {
102 if (i >= aliases_nodes)
103 p = 0;
104 else
105 p = alias_names [i];
106 } else
107 for (p = prom_firstprop (node, buffer);
108 i && p && *p;
109 p = prom_nextprop (node, p, buffer), i--)
110 /* nothing */ ;
111 if (!p || !*p)
112 return -EIO;
113 i = prom_getproplen (node, p);
114 if (i < 0) {
115 if ((u16)((long)inode->u.generic_ip) == aliases)
116 i = 0;
117 else
118 return -EIO;
120 k = i;
121 if (i < 64) i = 64;
122 filp->private_data = kmalloc (sizeof (openprom_property)
123 + (j = strlen (p)) + 2 * i,
124 GFP_KERNEL);
125 if (!filp->private_data)
126 return -ENOMEM;
127 op = (openprom_property *)filp->private_data;
128 op->flag = 0;
129 op->alloclen = 2 * i;
130 strcpy (op->name, p);
131 op->value = (char *)(((unsigned long)(op->name + j + 4)) & ~3);
132 op->len = k;
133 if (k && prom_getproperty (node, p, op->value, i) < 0)
134 return -EIO;
135 op->value [k] = 0;
136 if (k) {
137 for (s = 0, p = op->value; p < op->value + k; p++) {
138 if ((*p >= ' ' && *p <= '~') || *p == '\n') {
139 op->flag |= OPP_STRING;
140 s = p;
141 continue;
143 if (p > op->value && !*p && s == p - 1) {
144 if (p < op->value + k - 1)
145 op->flag |= OPP_STRINGLIST;
146 else
147 op->flag |= OPP_ASCIIZ;
148 continue;
150 if (k == 1 && !*p) {
151 op->flag |= (OPP_STRING|OPP_ASCIIZ);
152 break;
154 op->flag &= ~(OPP_STRING|OPP_STRINGLIST);
155 if (k & 3)
156 op->flag |= OPP_HEXSTRING;
157 else
158 op->flag |= OPP_BINARY;
159 break;
161 if (op->flag & OPP_STRINGLIST)
162 op->flag &= ~(OPP_STRING);
163 if (op->flag & OPP_ASCIIZ)
164 op->len--;
166 } else
167 op = (openprom_property *)filp->private_data;
168 if (!count || !(op->len || (op->flag & OPP_ASCIIZ)))
169 return 0;
170 if (op->flag & OPP_STRINGLIST) {
171 for (k = 0, p = op->value; p < op->value + op->len; p++)
172 if (!*p)
173 k++;
174 i = op->len + 4 * k + 3;
175 } else if (op->flag & OPP_STRING) {
176 i = op->len + 3;
177 } else if (op->flag & OPP_BINARY) {
178 i = (op->len * 9) >> 2;
179 } else {
180 i = (op->len << 1) + 1;
182 k = filp->f_pos;
183 if (k >= i) return 0;
184 if (count > i - k) count = i - k;
185 if (op->flag & OPP_STRING) {
186 if (!k) {
187 __put_user('\'', buf);
188 k++;
189 count--;
192 if (k + count >= i - 2)
193 j = i - 2 - k;
194 else
195 j = count;
197 if (j >= 0) {
198 copy_to_user(buf + k - filp->f_pos,
199 op->value + k - 1, j);
200 count -= j;
201 k += j;
204 if (count)
205 __put_user('\'', &buf [k++ - filp->f_pos]);
206 if (count > 1)
207 __put_user('\n', &buf [k++ - filp->f_pos]);
209 } else if (op->flag & OPP_STRINGLIST) {
210 char *tmp;
212 tmp = kmalloc (i, GFP_KERNEL);
213 if (!tmp)
214 return -ENOMEM;
216 s = tmp;
217 *s++ = '\'';
218 for (p = op->value; p < op->value + op->len; p++) {
219 if (!*p) {
220 strcpy(s, "' + '");
221 s += 5;
222 continue;
224 *s++ = *p;
226 strcpy(s, "'\n");
228 copy_to_user(buf, tmp + k, count);
230 kfree(tmp);
231 k += count;
233 } else if (op->flag & OPP_BINARY) {
234 char buffer[10];
235 u32 *first, *last;
236 int first_off, last_cnt;
238 first = ((u32 *)op->value) + k / 9;
239 first_off = k % 9;
240 last = ((u32 *)op->value) + (k + count - 1) / 9;
241 last_cnt = (k + count) % 9;
242 if (!last_cnt) last_cnt = 9;
244 if (first == last) {
245 sprintf (buffer, "%08x.", *first);
246 copy_to_user (buf, buffer + first_off, last_cnt - first_off);
247 buf += last_cnt - first_off;
248 } else {
249 for (q = first; q <= last; q++) {
250 sprintf (buffer, "%08x.", *q);
251 if (q == first) {
252 copy_to_user (buf, buffer + first_off,
253 9 - first_off);
254 buf += 9 - first_off;
255 } else if (q == last) {
256 copy_to_user (buf, buffer, last_cnt);
257 buf += last_cnt;
258 } else {
259 copy_to_user (buf, buffer, 9);
260 buf += 9;
265 if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9)
266 __put_user('\n', (buf - 1));
268 k += count;
270 } else if (op->flag & OPP_HEXSTRING) {
271 char buffer[2];
273 if ((k < i - 1) && (k & 1)) {
274 sprintf (buffer, "%02x", *(op->value + (k >> 1)));
275 __put_user(buffer[1], &buf[k++ - filp->f_pos]);
276 count--;
279 for (; (count > 1) && (k < i - 1); k += 2) {
280 sprintf (buffer, "%02x", *(op->value + (k >> 1)));
281 copy_to_user (buf + k - filp->f_pos, buffer, 2);
282 count -= 2;
285 if (count && (k < i - 1)) {
286 sprintf (buffer, "%02x", *(op->value + (k >> 1)));
287 __put_user(buffer[0], &buf[k++ - filp->f_pos]);
288 count--;
291 if (count)
292 __put_user('\n', &buf [k++ - filp->f_pos]);
294 count = k - filp->f_pos;
295 filp->f_pos = k;
296 return count;
299 static ssize_t property_write(struct file *filp, const char *buf,
300 size_t count, loff_t *ppos)
302 int i, j, k;
303 char *p;
304 u32 *q;
305 void *b;
306 openprom_property *op;
308 if (filp->f_pos >= 0xffffff)
309 return -EINVAL;
310 if (!filp->private_data) {
311 i = property_read (filp, NULL, 0, 0);
312 if (i)
313 return i;
315 k = filp->f_pos;
316 op = (openprom_property *)filp->private_data;
317 if (!(op->flag & OPP_STRING)) {
318 u32 *first, *last;
319 int first_off, last_cnt;
320 u32 mask, mask2;
321 char tmp [9];
322 int forcelen = 0;
324 j = k % 9;
325 for (i = 0; i < count; i++, j++) {
326 if (j == 9) j = 0;
327 if (!j) {
328 char ctmp;
329 __get_user(ctmp, &buf[i]);
330 if (ctmp != '.') {
331 if (ctmp != '\n') {
332 if (op->flag & OPP_BINARY)
333 return -EINVAL;
334 else
335 goto write_try_string;
336 } else {
337 count = i + 1;
338 forcelen = 1;
339 break;
342 } else {
343 char ctmp;
344 __get_user(ctmp, &buf[i]);
345 if (ctmp < '0' ||
346 (ctmp > '9' && ctmp < 'A') ||
347 (ctmp > 'F' && ctmp < 'a') ||
348 ctmp > 'f') {
349 if (op->flag & OPP_BINARY)
350 return -EINVAL;
351 else
352 goto write_try_string;
356 op->flag |= OPP_BINARY;
357 tmp [8] = 0;
358 i = ((count + k + 8) / 9) << 2;
359 if (op->alloclen <= i) {
360 b = kmalloc (sizeof (openprom_property) + 2 * i,
361 GFP_KERNEL);
362 if (!b)
363 return -ENOMEM;
364 memcpy (b, filp->private_data,
365 sizeof (openprom_property)
366 + strlen (op->name) + op->alloclen);
367 memset (((char *)b) + sizeof (openprom_property)
368 + strlen (op->name) + op->alloclen,
369 0, 2 * i - op->alloclen);
370 op = (openprom_property *)b;
371 op->alloclen = 2*i;
372 b = filp->private_data;
373 filp->private_data = (void *)op;
374 kfree (b);
376 first = ((u32 *)op->value) + (k / 9);
377 first_off = k % 9;
378 last = (u32 *)(op->value + i);
379 last_cnt = (k + count) % 9;
380 if (first + 1 == last) {
381 memset (tmp, '0', 8);
382 copy_from_user (tmp + first_off, buf,
383 (count + first_off > 8) ? 8 - first_off : count);
384 mask = 0xffffffff;
385 mask2 = 0xffffffff;
386 for (j = 0; j < first_off; j++)
387 mask >>= 1;
388 for (j = 8 - count - first_off; j > 0; j--)
389 mask2 <<= 1;
390 mask &= mask2;
391 if (mask) {
392 *first &= ~mask;
393 *first |= simple_strtoul (tmp, 0, 16);
394 op->flag |= OPP_DIRTY;
396 } else {
397 op->flag |= OPP_DIRTY;
398 for (q = first; q < last; q++) {
399 if (q == first) {
400 if (first_off < 8) {
401 memset (tmp, '0', 8);
402 copy_from_user (tmp + first_off, buf,
403 8 - first_off);
404 mask = 0xffffffff;
405 for (j = 0; j < first_off; j++)
406 mask >>= 1;
407 *q &= ~mask;
408 *q |= simple_strtoul (tmp,0,16);
410 buf += 9;
411 } else if ((q == last - 1) && last_cnt
412 && (last_cnt < 8)) {
413 memset (tmp, '0', 8);
414 copy_from_user (tmp, buf, last_cnt);
415 mask = 0xffffffff;
416 for (j = 0; j < 8 - last_cnt; j++)
417 mask <<= 1;
418 *q &= ~mask;
419 *q |= simple_strtoul (tmp, 0, 16);
420 buf += last_cnt;
421 } else {
422 char tchars[17]; /* XXX yuck... */
424 copy_from_user(tchars, buf, 16);
425 *q = simple_strtoul (tchars, 0, 16);
426 buf += 9;
430 if (!forcelen) {
431 if (op->len < i)
432 op->len = i;
433 } else
434 op->len = i;
435 filp->f_pos += count;
437 write_try_string:
438 if (!(op->flag & OPP_BINARY)) {
439 if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) {
440 char ctmp;
442 /* No way, if somebody starts writing from the middle,
443 * we don't know whether he uses quotes around or not
445 if (k > 0)
446 return -EINVAL;
447 __get_user(ctmp, buf);
448 if (ctmp == '\'') {
449 op->flag |= OPP_QUOTED;
450 buf++;
451 count--;
452 filp->f_pos++;
453 if (!count) {
454 op->flag |= OPP_STRING;
455 return 1;
457 } else
458 op->flag |= OPP_NOTQUOTED;
460 op->flag |= OPP_STRING;
461 if (op->alloclen <= count + filp->f_pos) {
462 b = kmalloc (sizeof (openprom_property)
463 + 2 * (count + filp->f_pos), GFP_KERNEL);
464 if (!b)
465 return -ENOMEM;
466 memcpy (b, filp->private_data,
467 sizeof (openprom_property)
468 + strlen (op->name) + op->alloclen);
469 memset (((char *)b) + sizeof (openprom_property)
470 + strlen (op->name) + op->alloclen,
471 0, 2*(count - filp->f_pos) - op->alloclen);
472 op = (openprom_property *)b;
473 op->alloclen = 2*(count + filp->f_pos);
474 b = filp->private_data;
475 filp->private_data = (void *)op;
476 kfree (b);
478 p = op->value + filp->f_pos - ((op->flag & OPP_QUOTED) ? 1 : 0);
479 copy_from_user (p, buf, count);
480 op->flag |= OPP_DIRTY;
481 for (i = 0; i < count; i++, p++)
482 if (*p == '\n') {
483 *p = 0;
484 break;
486 if (i < count) {
487 op->len = p - op->value;
488 filp->f_pos += i + 1;
489 if ((p > op->value) && (op->flag & OPP_QUOTED)
490 && (*(p - 1) == '\''))
491 op->len--;
492 } else {
493 if (p - op->value > op->len)
494 op->len = p - op->value;
495 filp->f_pos += count;
498 return filp->f_pos - k;
501 int property_release (struct inode *inode, struct file *filp)
503 openprom_property *op = (openprom_property *)filp->private_data;
504 unsigned long flags;
505 int error;
506 u32 node;
508 if (!op)
509 return 0;
510 node = nodes[(u16)((long)inode->u.generic_ip)].node;
511 if ((u16)((long)inode->u.generic_ip) == aliases) {
512 if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) {
513 char *p = op->name;
514 int i = (op->value - op->name) - strlen (op->name) - 1;
515 op->value [op->len] = 0;
516 *(op->value - 1) = ' ';
517 if (i) {
518 for (p = op->value - i - 2; p >= op->name; p--)
519 p[i] = *p;
520 p = op->name + i;
522 memcpy (p - 8, "nvalias ", 8);
523 prom_feval (p - 8);
525 } else if (op->flag & OPP_DIRTY) {
526 if (op->flag & OPP_STRING) {
527 op->value [op->len] = 0;
528 save_and_cli (flags);
529 error = prom_setprop (node, op->name,
530 op->value, op->len + 1);
531 restore_flags (flags);
532 if (error <= 0)
533 printk (KERN_WARNING "openpromfs: "
534 "Couldn't write property %s\n",
535 op->name);
536 } else if ((op->flag & OPP_BINARY) || !op->len) {
537 save_and_cli (flags);
538 error = prom_setprop (node, op->name,
539 op->value, op->len);
540 restore_flags (flags);
541 if (error <= 0)
542 printk (KERN_WARNING "openpromfs: "
543 "Couldn't write property %s\n",
544 op->name);
545 } else {
546 printk (KERN_WARNING "openpromfs: "
547 "Unknown property type of %s\n",
548 op->name);
551 kfree (filp->private_data);
552 return 0;
555 static struct file_operations openpromfs_prop_ops = {
556 read: property_read,
557 write: property_write,
558 release: property_release,
561 static struct file_operations openpromfs_nodenum_ops = {
562 read: nodenum_read,
565 static struct file_operations openprom_operations = {
566 read: generic_read_dir,
567 readdir: openpromfs_readdir,
570 static struct inode_operations openprom_alias_inode_operations = {
571 create: openpromfs_create,
572 lookup: openpromfs_lookup,
573 unlink: openpromfs_unlink,
576 static struct inode_operations openprom_inode_operations = {
577 lookup: openpromfs_lookup,
580 static int lookup_children(u16 n, const char * name, int len)
582 int ret;
583 u16 node;
584 for (; n != 0xffff; n = nodes[n].next) {
585 node = nodes[n].child;
586 if (node != 0xffff) {
587 char buffer[128];
588 int i;
589 char *p;
591 while (node != 0xffff) {
592 if (prom_getname (nodes[node].node,
593 buffer, 128) >= 0) {
594 i = strlen (buffer);
595 if ((len == i)
596 && !strncmp (buffer, name, len))
597 return NODE2INO(node);
598 p = strchr (buffer, '@');
599 if (p && (len == p - buffer)
600 && !strncmp (buffer, name, len))
601 return NODE2INO(node);
603 node = nodes[node].next;
605 } else
606 continue;
607 ret = lookup_children (nodes[n].child, name, len);
608 if (ret) return ret;
610 return 0;
613 static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry)
615 int ino = 0;
616 #define OPFSL_DIR 0
617 #define OPFSL_PROPERTY 1
618 #define OPFSL_NODENUM 2
619 int type = 0;
620 char buffer[128];
621 char *p;
622 const char *name;
623 u32 n;
624 u16 dirnode;
625 unsigned int len;
626 int i;
627 struct inode *inode;
628 char buffer2[64];
630 inode = NULL;
631 name = dentry->d_name.name;
632 len = dentry->d_name.len;
633 if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) {
634 ino = NODEP2INO(NODE(dir->i_ino).first_prop);
635 type = OPFSL_NODENUM;
637 if (!ino) {
638 u16 node = NODE(dir->i_ino).child;
639 while (node != 0xffff) {
640 if (prom_getname (nodes[node].node, buffer, 128) >= 0) {
641 i = strlen (buffer);
642 if (len == i && !strncmp (buffer, name, len)) {
643 ino = NODE2INO(node);
644 type = OPFSL_DIR;
645 break;
647 p = strchr (buffer, '@');
648 if (p && (len == p - buffer)
649 && !strncmp (buffer, name, len)) {
650 ino = NODE2INO(node);
651 type = OPFSL_DIR;
652 break;
655 node = nodes[node].next;
658 n = NODE(dir->i_ino).node;
659 dirnode = dir->i_ino - OPENPROM_FIRST_INO;
660 if (!ino) {
661 int j = NODEP2INO(NODE(dir->i_ino).first_prop);
662 if (dirnode != aliases) {
663 for (p = prom_firstprop (n, buffer2);
664 p && *p;
665 p = prom_nextprop (n, p, buffer2)) {
666 j++;
667 if ((len == strlen (p))
668 && !strncmp (p, name, len)) {
669 ino = j;
670 type = OPFSL_PROPERTY;
671 break;
674 } else {
675 int k;
676 for (k = 0; k < aliases_nodes; k++) {
677 j++;
678 if (alias_names [k]
679 && (len == strlen (alias_names [k]))
680 && !strncmp (alias_names [k], name, len)) {
681 ino = j;
682 type = OPFSL_PROPERTY;
683 break;
688 if (!ino) {
689 ino = lookup_children (NODE(dir->i_ino).child, name, len);
690 if (ino)
691 type = OPFSL_DIR;
692 else
693 return ERR_PTR(-ENOENT);
695 inode = iget (dir->i_sb, ino);
696 if (!inode)
697 return ERR_PTR(-EINVAL);
698 switch (type) {
699 case OPFSL_DIR:
700 inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
701 if (ino == OPENPROM_FIRST_INO + aliases) {
702 inode->i_mode |= S_IWUSR;
703 inode->i_op = &openprom_alias_inode_operations;
704 } else
705 inode->i_op = &openprom_inode_operations;
706 inode->i_fop = &openprom_operations;
707 inode->i_nlink = 2;
708 break;
709 case OPFSL_NODENUM:
710 inode->i_mode = S_IFREG | S_IRUGO;
711 inode->i_fop = &openpromfs_nodenum_ops;
712 inode->i_nlink = 1;
713 inode->u.generic_ip = (void *)(long)(n);
714 break;
715 case OPFSL_PROPERTY:
716 if ((dirnode == options) && (len == 17)
717 && !strncmp (name, "security-password", 17))
718 inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
719 else {
720 inode->i_mode = S_IFREG | S_IRUGO;
721 if (dirnode == options || dirnode == aliases) {
722 if (len != 4 || strncmp (name, "name", 4))
723 inode->i_mode |= S_IWUSR;
726 inode->i_fop = &openpromfs_prop_ops;
727 inode->i_nlink = 1;
728 if (inode->i_size < 0)
729 inode->i_size = 0;
730 inode->u.generic_ip = (void *)(long)(((u16)dirnode) |
731 (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16));
732 break;
735 inode->i_gid = 0;
736 inode->i_uid = 0;
738 d_add(dentry, inode);
739 return NULL;
742 static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
744 struct inode *inode = filp->f_dentry->d_inode;
745 unsigned int ino;
746 u32 n;
747 int i, j;
748 char buffer[128];
749 u16 node;
750 char *p;
751 char buffer2[64];
753 ino = inode->i_ino;
754 i = filp->f_pos;
755 switch (i) {
756 case 0:
757 if (filldir(dirent, ".", 1, i, ino) < 0) return 0;
758 i++;
759 filp->f_pos++;
760 /* fall thru */
761 case 1:
762 if (filldir(dirent, "..", 2, i,
763 (NODE(ino).parent == 0xffff) ?
764 OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent)) < 0)
765 return 0;
766 i++;
767 filp->f_pos++;
768 /* fall thru */
769 default:
770 i -= 2;
771 node = NODE(ino).child;
772 while (i && node != 0xffff) {
773 node = nodes[node].next;
774 i--;
776 while (node != 0xffff) {
777 if (prom_getname (nodes[node].node, buffer, 128) < 0)
778 return 0;
779 if (filldir(dirent, buffer, strlen(buffer),
780 filp->f_pos, NODE2INO(node)) < 0)
781 return 0;
782 filp->f_pos++;
783 node = nodes[node].next;
785 j = NODEP2INO(NODE(ino).first_prop);
786 if (!i) {
787 if (filldir(dirent, ".node", 5, filp->f_pos, j) < 0)
788 return 0;
789 filp->f_pos++;
790 } else
791 i--;
792 n = NODE(ino).node;
793 if (ino == OPENPROM_FIRST_INO + aliases) {
794 for (j++; i < aliases_nodes; i++, j++) {
795 if (alias_names [i]) {
796 if (filldir (dirent, alias_names [i],
797 strlen (alias_names [i]),
798 filp->f_pos, j) < 0) return 0;
799 filp->f_pos++;
802 } else {
803 for (p = prom_firstprop (n, buffer2);
804 p && *p;
805 p = prom_nextprop (n, p, buffer2)) {
806 j++;
807 if (i) i--;
808 else {
809 if (filldir(dirent, p, strlen(p),
810 filp->f_pos, j) < 0)
811 return 0;
812 filp->f_pos++;
817 return 0;
820 static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode)
822 char *p;
823 struct inode *inode;
825 if (!dir)
826 return -ENOENT;
827 if (dentry->d_name.len > 256)
828 return -EINVAL;
829 if (aliases_nodes == ALIASES_NNODES)
830 return -EIO;
831 p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL);
832 if (!p)
833 return -ENOMEM;
834 strncpy (p, dentry->d_name.name, dentry->d_name.len);
835 p [dentry->d_name.len] = 0;
836 alias_names [aliases_nodes++] = p;
837 inode = iget (dir->i_sb,
838 NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes);
839 if (!inode)
840 return -EINVAL;
841 inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
842 inode->i_fop = &openpromfs_prop_ops;
843 inode->i_nlink = 1;
844 if (inode->i_size < 0) inode->i_size = 0;
845 inode->u.generic_ip = (void *)(long)(((u16)aliases) |
846 (((u16)(aliases_nodes - 1)) << 16));
847 d_instantiate(dentry, inode);
848 return 0;
851 static int openpromfs_unlink (struct inode *dir, struct dentry *dentry)
853 unsigned int len;
854 char *p;
855 const char *name;
856 int i;
858 name = dentry->d_name.name;
859 len = dentry->d_name.len;
860 for (i = 0; i < aliases_nodes; i++)
861 if ((strlen (alias_names [i]) == len)
862 && !strncmp (name, alias_names[i], len)) {
863 char buffer[512];
865 p = alias_names [i];
866 alias_names [i] = NULL;
867 kfree (p);
868 strcpy (buffer, "nvunalias ");
869 memcpy (buffer + 10, name, len);
870 buffer [10 + len] = 0;
871 prom_feval (buffer);
873 return 0;
876 /* {{{ init section */
877 #ifndef MODULE
878 static int __init check_space (u16 n)
879 #else
880 static int check_space (u16 n)
881 #endif
883 unsigned long pages;
885 if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) {
886 pages = __get_free_pages (GFP_KERNEL, alloced + 1);
887 if (!pages)
888 return -1;
890 if (nodes) {
891 memcpy ((char *)pages, (char *)nodes,
892 (1 << alloced) * PAGE_SIZE);
893 free_pages ((unsigned long)nodes, alloced);
895 alloced++;
896 nodes = (openpromfs_node *)pages;
898 return 0;
901 #ifndef MODULE
902 static u16 __init get_nodes (u16 parent, u32 node)
903 #else
904 static u16 get_nodes (u16 parent, u32 node)
905 #endif
907 char *p;
908 u16 n = last_node++, i;
909 char buffer[64];
911 if (check_space (n) < 0)
912 return 0xffff;
913 nodes[n].parent = parent;
914 nodes[n].node = node;
915 nodes[n].next = 0xffff;
916 nodes[n].child = 0xffff;
917 nodes[n].first_prop = first_prop++;
918 if (!parent) {
919 char buffer[8];
920 int j;
922 if ((j = prom_getproperty (node, "name", buffer, 8)) >= 0) {
923 buffer[j] = 0;
924 if (!strcmp (buffer, "options"))
925 options = n;
926 else if (!strcmp (buffer, "aliases"))
927 aliases = n;
930 if (n != aliases)
931 for (p = prom_firstprop (node, buffer);
932 p && p != (char *)-1 && *p;
933 p = prom_nextprop (node, p, buffer))
934 first_prop++;
935 else {
936 char *q;
937 for (p = prom_firstprop (node, buffer);
938 p && p != (char *)-1 && *p;
939 p = prom_nextprop (node, p, buffer)) {
940 if (aliases_nodes == ALIASES_NNODES)
941 break;
942 for (i = 0; i < aliases_nodes; i++)
943 if (!strcmp (p, alias_names [i]))
944 break;
945 if (i < aliases_nodes)
946 continue;
947 q = kmalloc (strlen (p) + 1, GFP_KERNEL);
948 if (!q)
949 return 0xffff;
950 strcpy (q, p);
951 alias_names [aliases_nodes++] = q;
953 first_prop += ALIASES_NNODES;
955 node = prom_getchild (node);
956 if (node) {
957 parent = get_nodes (n, node);
958 if (parent == 0xffff)
959 return 0xffff;
960 nodes[n].child = parent;
961 while ((node = prom_getsibling (node)) != 0) {
962 i = get_nodes (n, node);
963 if (i == 0xffff)
964 return 0xffff;
965 nodes[parent].next = i;
966 parent = i;
969 return n;
972 static void openprom_read_inode(struct inode * inode)
974 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
975 if (inode->i_ino == OPENPROM_ROOT_INO) {
976 inode->i_op = &openprom_inode_operations;
977 inode->i_fop = &openprom_operations;
978 inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
982 static int openprom_statfs(struct super_block *sb, struct statfs *buf)
984 buf->f_type = OPENPROM_SUPER_MAGIC;
985 buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
986 buf->f_bfree = 0;
987 buf->f_bavail = 0;
988 buf->f_ffree = 0;
989 buf->f_namelen = NAME_MAX;
990 return 0;
993 static struct super_operations openprom_sops = {
994 read_inode: openprom_read_inode,
995 statfs: openprom_statfs,
998 struct super_block *openprom_read_super(struct super_block *s,void *data,
999 int silent)
1001 struct inode * root_inode;
1003 s->s_blocksize = 1024;
1004 s->s_blocksize_bits = 10;
1005 s->s_magic = OPENPROM_SUPER_MAGIC;
1006 s->s_op = &openprom_sops;
1007 root_inode = iget(s, OPENPROM_ROOT_INO);
1008 if (!root_inode)
1009 goto out_no_root;
1010 s->s_root = d_alloc_root(root_inode);
1011 if (!s->s_root)
1012 goto out_no_root;
1013 return s;
1015 out_no_root:
1016 printk("openprom_read_super: get root inode failed\n");
1017 iput(root_inode);
1018 return NULL;
1021 static DECLARE_FSTYPE(openprom_fs_type, "openpromfs", openprom_read_super, 0);
1023 static int __init init_openprom_fs(void)
1025 nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
1026 if (!nodes) {
1027 printk (KERN_WARNING "openpromfs: can't get free page\n");
1028 return -EIO;
1030 if (get_nodes (0xffff, prom_root_node) == 0xffff) {
1031 printk (KERN_WARNING "openpromfs: couldn't setup tree\n");
1032 return -EIO;
1034 nodes[last_node].first_prop = first_prop;
1035 return register_filesystem(&openprom_fs_type);
1038 static void __exit exit_openprom_fs(void)
1040 int i;
1041 unregister_filesystem(&openprom_fs_type);
1042 free_pages ((unsigned long)nodes, alloced);
1043 for (i = 0; i < aliases_nodes; i++)
1044 if (alias_names [i])
1045 kfree (alias_names [i]);
1046 nodes = NULL;
1049 EXPORT_NO_SYMBOLS;
1051 module_init(init_openprom_fs)
1052 module_exit(exit_openprom_fs)