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