staging: dgrp: info leak in dgrp_dpa_ioctl()
[linux-2.6.git] / drivers / staging / dgrp / dgrp_specproc.c
blobd66712c8aa9453f4e6b2f8b22b3fcc4ebaf6628e
1 /*
3 * Copyright 1999 Digi International (www.digi.com)
4 * James Puzzo <jamesp at digi dot com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
13 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
20 * Filename:
22 * dgrp_specproc.c
24 * Description:
26 * Handle the "config" proc entry for the linux realport device driver
27 * and provide slots for the "net" and "mon" devices
29 * Author:
31 * James A. Puzzo
35 #include <linux/module.h>
36 #include <linux/tty.h>
37 #include <linux/sched.h>
38 #include <linux/cred.h>
39 #include <linux/proc_fs.h>
40 #include <linux/ctype.h>
41 #include <linux/seq_file.h>
42 #include <linux/uaccess.h>
43 #include <linux/vmalloc.h>
45 #include "dgrp_common.h"
47 static struct dgrp_proc_entry dgrp_table[];
48 static struct proc_dir_entry *dgrp_proc_dir_entry;
50 static int dgrp_add_id(long id);
51 static int dgrp_remove_nd(struct nd_struct *nd);
52 static void unregister_dgrp_device(struct proc_dir_entry *de);
53 static void register_dgrp_device(struct nd_struct *node,
54 struct proc_dir_entry *root,
55 void (*register_hook)(struct proc_dir_entry *de));
57 /* File operation declarations */
58 static int dgrp_gen_proc_open(struct inode *, struct file *);
59 static int dgrp_gen_proc_close(struct inode *, struct file *);
60 static int parse_write_config(char *);
63 static const struct file_operations dgrp_proc_file_ops = {
64 .owner = THIS_MODULE,
65 .open = dgrp_gen_proc_open,
66 .release = dgrp_gen_proc_close,
69 static struct inode_operations proc_inode_ops = {
70 .permission = dgrp_inode_permission
74 static void register_proc_table(struct dgrp_proc_entry *,
75 struct proc_dir_entry *);
76 static void unregister_proc_table(struct dgrp_proc_entry *,
77 struct proc_dir_entry *);
79 static struct dgrp_proc_entry dgrp_net_table[];
80 static struct dgrp_proc_entry dgrp_mon_table[];
81 static struct dgrp_proc_entry dgrp_ports_table[];
82 static struct dgrp_proc_entry dgrp_dpa_table[];
84 static ssize_t dgrp_config_proc_write(struct file *file,
85 const char __user *buffer,
86 size_t count, loff_t *pos);
88 static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file);
89 static int dgrp_info_proc_open(struct inode *inode, struct file *file);
90 static int dgrp_config_proc_open(struct inode *inode, struct file *file);
92 static struct file_operations config_proc_file_ops = {
93 .owner = THIS_MODULE,
94 .open = dgrp_config_proc_open,
95 .read = seq_read,
96 .llseek = seq_lseek,
97 .release = seq_release,
98 .write = dgrp_config_proc_write,
101 static struct file_operations info_proc_file_ops = {
102 .owner = THIS_MODULE,
103 .open = dgrp_info_proc_open,
104 .read = seq_read,
105 .llseek = seq_lseek,
106 .release = single_release,
109 static struct file_operations nodeinfo_proc_file_ops = {
110 .owner = THIS_MODULE,
111 .open = dgrp_nodeinfo_proc_open,
112 .read = seq_read,
113 .llseek = seq_lseek,
114 .release = seq_release,
117 static struct dgrp_proc_entry dgrp_table[] = {
119 .id = DGRP_CONFIG,
120 .name = "config",
121 .mode = 0644,
122 .proc_file_ops = &config_proc_file_ops,
125 .id = DGRP_INFO,
126 .name = "info",
127 .mode = 0644,
128 .proc_file_ops = &info_proc_file_ops,
131 .id = DGRP_NODEINFO,
132 .name = "nodeinfo",
133 .mode = 0644,
134 .proc_file_ops = &nodeinfo_proc_file_ops,
137 .id = DGRP_NETDIR,
138 .name = "net",
139 .mode = 0500,
140 .child = dgrp_net_table
143 .id = DGRP_MONDIR,
144 .name = "mon",
145 .mode = 0500,
146 .child = dgrp_mon_table
149 .id = DGRP_PORTSDIR,
150 .name = "ports",
151 .mode = 0500,
152 .child = dgrp_ports_table
155 .id = DGRP_DPADIR,
156 .name = "dpa",
157 .mode = 0500,
158 .child = dgrp_dpa_table
162 static struct proc_dir_entry *net_entry_pointer;
163 static struct proc_dir_entry *mon_entry_pointer;
164 static struct proc_dir_entry *dpa_entry_pointer;
165 static struct proc_dir_entry *ports_entry_pointer;
167 static struct dgrp_proc_entry dgrp_net_table[] = {
171 static struct dgrp_proc_entry dgrp_mon_table[] = {
175 static struct dgrp_proc_entry dgrp_ports_table[] = {
179 static struct dgrp_proc_entry dgrp_dpa_table[] = {
183 void dgrp_unregister_proc(void)
185 net_entry_pointer = NULL;
186 mon_entry_pointer = NULL;
187 dpa_entry_pointer = NULL;
188 ports_entry_pointer = NULL;
190 if (dgrp_proc_dir_entry) {
191 unregister_proc_table(dgrp_table, dgrp_proc_dir_entry);
192 remove_proc_entry(dgrp_proc_dir_entry->name,
193 dgrp_proc_dir_entry->parent);
194 dgrp_proc_dir_entry = NULL;
199 void dgrp_register_proc(void)
202 * Register /proc/dgrp
204 dgrp_proc_dir_entry = proc_create("dgrp", S_IFDIR, NULL,
205 &dgrp_proc_file_ops);
206 register_proc_table(dgrp_table, dgrp_proc_dir_entry);
210 * /proc/sys support
212 static int dgrp_proc_match(int len, const char *name, struct proc_dir_entry *de)
214 if (!de || !de->low_ino)
215 return 0;
216 if (de->namelen != len)
217 return 0;
218 return !memcmp(name, de->name, len);
223 * Scan the entries in table and add them all to /proc at the position
224 * referred to by "root"
226 static void register_proc_table(struct dgrp_proc_entry *table,
227 struct proc_dir_entry *root)
229 struct proc_dir_entry *de;
230 int len;
231 mode_t mode;
233 if (table == NULL)
234 return;
235 if (root == NULL)
236 return;
238 for (; table->id; table++) {
239 /* Can't do anything without a proc name. */
240 if (!table->name)
241 continue;
243 /* Maybe we can't do anything with it... */
244 if (!table->proc_file_ops &&
245 !table->child) {
246 pr_warn("dgrp: Can't register %s\n",
247 table->name);
248 continue;
251 len = strlen(table->name);
252 mode = table->mode;
254 de = NULL;
255 if (!table->child)
256 mode |= S_IFREG;
257 else {
258 mode |= S_IFDIR;
259 for (de = root->subdir; de; de = de->next) {
260 if (dgrp_proc_match(len, table->name, de))
261 break;
263 /* If the subdir exists already, de is non-NULL */
266 if (!de) {
267 de = create_proc_entry(table->name, mode, root);
268 if (!de)
269 continue;
270 de->data = (void *) table;
271 if (!table->child) {
272 de->proc_iops = &proc_inode_ops;
273 if (table->proc_file_ops)
274 rcu_assign_pointer(de->proc_fops,
275 table->proc_file_ops);
276 else
277 rcu_assign_pointer(de->proc_fops,
278 &dgrp_proc_file_ops);
281 table->de = de;
282 if (de->mode & S_IFDIR)
283 register_proc_table(table->child, de);
285 if (table->id == DGRP_NETDIR)
286 net_entry_pointer = de;
288 if (table->id == DGRP_MONDIR)
289 mon_entry_pointer = de;
291 if (table->id == DGRP_DPADIR)
292 dpa_entry_pointer = de;
294 if (table->id == DGRP_PORTSDIR)
295 ports_entry_pointer = de;
300 * Unregister a /proc sysctl table and any subdirectories.
302 static void unregister_proc_table(struct dgrp_proc_entry *table,
303 struct proc_dir_entry *root)
305 struct proc_dir_entry *de;
306 struct nd_struct *tmp;
308 if (table == NULL)
309 return;
311 list_for_each_entry(tmp, &nd_struct_list, list) {
312 if ((table == dgrp_net_table) && (tmp->nd_net_de)) {
313 unregister_dgrp_device(tmp->nd_net_de);
314 dgrp_remove_node_class_sysfs_files(tmp);
317 if ((table == dgrp_mon_table) && (tmp->nd_mon_de))
318 unregister_dgrp_device(tmp->nd_mon_de);
320 if ((table == dgrp_dpa_table) && (tmp->nd_dpa_de))
321 unregister_dgrp_device(tmp->nd_dpa_de);
323 if ((table == dgrp_ports_table) && (tmp->nd_ports_de))
324 unregister_dgrp_device(tmp->nd_ports_de);
327 for (; table->id; table++) {
328 de = table->de;
330 if (!de)
331 continue;
332 if (de->mode & S_IFDIR) {
333 if (!table->child) {
334 pr_alert("dgrp: malformed sysctl tree on free\n");
335 continue;
337 unregister_proc_table(table->child, de);
339 /* Don't unregister directories which still have entries */
340 if (de->subdir)
341 continue;
344 /* Don't unregister proc entries that are still being used.. */
345 if ((atomic_read(&de->count)) != 1) {
346 pr_alert("proc entry %s in use, not removing\n",
347 de->name);
348 continue;
351 remove_proc_entry(de->name, de->parent);
352 table->de = NULL;
356 static int dgrp_gen_proc_open(struct inode *inode, struct file *file)
358 struct proc_dir_entry *de;
359 struct dgrp_proc_entry *entry;
360 int ret = 0;
362 de = (struct proc_dir_entry *) PDE(file_inode(file));
363 if (!de || !de->data) {
364 ret = -ENXIO;
365 goto done;
368 entry = (struct dgrp_proc_entry *) de->data;
369 if (!entry) {
370 ret = -ENXIO;
371 goto done;
374 down(&entry->excl_sem);
376 if (entry->excl_cnt)
377 ret = -EBUSY;
378 else
379 entry->excl_cnt++;
381 up(&entry->excl_sem);
383 done:
384 return ret;
387 static int dgrp_gen_proc_close(struct inode *inode, struct file *file)
389 struct proc_dir_entry *de;
390 struct dgrp_proc_entry *entry;
392 de = (struct proc_dir_entry *) PDE(file_inode(file));
393 if (!de || !de->data)
394 goto done;
396 entry = (struct dgrp_proc_entry *) de->data;
397 if (!entry)
398 goto done;
400 down(&entry->excl_sem);
402 if (entry->excl_cnt)
403 entry->excl_cnt = 0;
405 up(&entry->excl_sem);
407 done:
408 return 0;
411 static void *dgrp_config_proc_start(struct seq_file *m, loff_t *pos)
413 return seq_list_start_head(&nd_struct_list, *pos);
416 static void *dgrp_config_proc_next(struct seq_file *p, void *v, loff_t *pos)
418 return seq_list_next(v, &nd_struct_list, pos);
421 static void dgrp_config_proc_stop(struct seq_file *m, void *v)
425 static int dgrp_config_proc_show(struct seq_file *m, void *v)
427 struct nd_struct *nd;
428 char tmp_id[4];
430 if (v == &nd_struct_list) {
431 seq_puts(m, "#-----------------------------------------------------------------------------\n");
432 seq_puts(m, "# Avail\n");
433 seq_puts(m, "# ID Major State Ports\n");
434 return 0;
437 nd = list_entry(v, struct nd_struct, list);
439 ID_TO_CHAR(nd->nd_ID, tmp_id);
441 seq_printf(m, " %-2.2s %-5ld %-10.10s %-5d\n",
442 tmp_id,
443 nd->nd_major,
444 ND_STATE_STR(nd->nd_state),
445 nd->nd_chan_count);
447 return 0;
450 static const struct seq_operations proc_config_ops = {
451 .start = dgrp_config_proc_start,
452 .next = dgrp_config_proc_next,
453 .stop = dgrp_config_proc_stop,
454 .show = dgrp_config_proc_show,
457 static int dgrp_config_proc_open(struct inode *inode, struct file *file)
459 return seq_open(file, &proc_config_ops);
464 * When writing configuration information, each "record" (i.e. each
465 * write) is treated as an independent request. See the "parse"
466 * description for more details.
468 static ssize_t dgrp_config_proc_write(struct file *file,
469 const char __user *buffer,
470 size_t count, loff_t *pos)
472 ssize_t retval;
473 char *inbuf, *sp;
474 char *line, *ldelim;
476 if (count > 32768)
477 return -EINVAL;
479 inbuf = sp = vzalloc(count + 1);
480 if (!inbuf)
481 return -ENOMEM;
483 if (copy_from_user(inbuf, buffer, count)) {
484 retval = -EFAULT;
485 goto done;
488 inbuf[count] = 0;
490 ldelim = "\n";
492 line = strpbrk(sp, ldelim);
493 while (line) {
494 *line = 0;
495 retval = parse_write_config(sp);
496 if (retval)
497 goto done;
499 sp = line + 1;
500 line = strpbrk(sp, ldelim);
503 retval = count;
504 done:
505 vfree(inbuf);
506 return retval;
510 * ------------------------------------------------------------------------
512 * The following are the functions to parse input
514 * ------------------------------------------------------------------------
516 static inline char *skip_past_ws(const char *str)
518 while ((*str) && !isspace(*str))
519 ++str;
521 return skip_spaces(str);
524 static int parse_id(char **c, char *cID)
526 int tmp = **c;
528 if (isalnum(tmp) || (tmp == '_'))
529 cID[0] = tmp;
530 else
531 return -EINVAL;
533 (*c)++; tmp = **c;
535 if (isalnum(tmp) || (tmp == '_')) {
536 cID[1] = tmp;
537 (*c)++;
538 } else
539 cID[1] = 0;
541 return 0;
544 static int parse_add_config(char *buf)
546 char *c = buf;
547 int retval;
548 char cID[2];
549 long ID;
551 c = skip_past_ws(c);
553 retval = parse_id(&c, cID);
554 if (retval < 0)
555 return retval;
557 ID = CHAR_TO_ID(cID);
559 c = skip_past_ws(c);
561 return dgrp_add_id(ID);
564 static int parse_del_config(char *buf)
566 char *c = buf;
567 int retval;
568 struct nd_struct *nd;
569 char cID[2];
570 long ID;
571 long major;
573 c = skip_past_ws(c);
575 retval = parse_id(&c, cID);
576 if (retval < 0)
577 return retval;
579 ID = CHAR_TO_ID(cID);
581 c = skip_past_ws(c);
583 retval = kstrtol(c, 10, &major);
584 if (retval)
585 return retval;
587 nd = nd_struct_get(major);
588 if (!nd)
589 return -EINVAL;
591 if ((nd->nd_major != major) || (nd->nd_ID != ID))
592 return -EINVAL;
594 return dgrp_remove_nd(nd);
597 static int parse_chg_config(char *buf)
599 return -EINVAL;
603 * The passed character buffer represents a single configuration request.
604 * If the first character is a "+", it is parsed as a request to add a
605 * PortServer
606 * If the first character is a "-", it is parsed as a request to delete a
607 * PortServer
608 * If the first character is a "*", it is parsed as a request to change a
609 * PortServer
610 * Any other character (including whitespace) causes the record to be
611 * ignored.
613 static int parse_write_config(char *buf)
615 int retval;
617 switch (buf[0]) {
618 case '+':
619 retval = parse_add_config(buf);
620 break;
621 case '-':
622 retval = parse_del_config(buf);
623 break;
624 case '*':
625 retval = parse_chg_config(buf);
626 break;
627 default:
628 retval = -EINVAL;
631 return retval;
634 static int dgrp_info_proc_show(struct seq_file *m, void *v)
636 seq_printf(m, "version: %s\n", DIGI_VERSION);
637 seq_puts(m, "register_with_sysfs: 1\n");
638 seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
639 dgrp_poll_tick, dgrp_poll_tick);
641 return 0;
644 static int dgrp_info_proc_open(struct inode *inode, struct file *file)
646 return single_open(file, dgrp_info_proc_show, NULL);
650 static void *dgrp_nodeinfo_start(struct seq_file *m, loff_t *pos)
652 return seq_list_start_head(&nd_struct_list, *pos);
655 static void *dgrp_nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
657 return seq_list_next(v, &nd_struct_list, pos);
660 static void dgrp_nodeinfo_stop(struct seq_file *m, void *v)
664 static int dgrp_nodeinfo_show(struct seq_file *m, void *v)
666 struct nd_struct *nd;
667 char hwver[8];
668 char swver[8];
669 char tmp_id[4];
671 if (v == &nd_struct_list) {
672 seq_puts(m, "#-----------------------------------------------------------------------------\n");
673 seq_puts(m, "# HW HW SW\n");
674 seq_puts(m, "# ID State Version ID Version Description\n");
675 return 0;
678 nd = list_entry(v, struct nd_struct, list);
680 ID_TO_CHAR(nd->nd_ID, tmp_id);
682 if (nd->nd_state == NS_READY) {
683 sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
684 nd->nd_hw_ver & 0xff);
685 sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
686 nd->nd_sw_ver & 0xff);
687 seq_printf(m, " %-2.2s %-10.10s %-7.7s %-3d %-7.7s %-35.35s\n",
688 tmp_id,
689 ND_STATE_STR(nd->nd_state),
690 hwver,
691 nd->nd_hw_id,
692 swver,
693 nd->nd_ps_desc);
695 } else {
696 seq_printf(m, " %-2.2s %-10.10s\n",
697 tmp_id,
698 ND_STATE_STR(nd->nd_state));
701 return 0;
705 static const struct seq_operations nodeinfo_ops = {
706 .start = dgrp_nodeinfo_start,
707 .next = dgrp_nodeinfo_next,
708 .stop = dgrp_nodeinfo_stop,
709 .show = dgrp_nodeinfo_show,
712 static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file)
714 return seq_open(file, &nodeinfo_ops);
718 * dgrp_add_id() -- creates new nd struct and adds it to list
719 * @id: id of device to add
721 static int dgrp_add_id(long id)
723 struct nd_struct *nd;
724 int ret;
725 int i;
727 nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
728 if (!nd)
729 return -ENOMEM;
731 nd->nd_major = 0;
732 nd->nd_ID = id;
734 spin_lock_init(&nd->nd_lock);
736 init_waitqueue_head(&nd->nd_tx_waitq);
737 init_waitqueue_head(&nd->nd_mon_wqueue);
738 init_waitqueue_head(&nd->nd_dpa_wqueue);
739 for (i = 0; i < SEQ_MAX; i++)
740 init_waitqueue_head(&nd->nd_seq_wque[i]);
742 /* setup the structures to get the major number */
743 ret = dgrp_tty_init(nd);
744 if (ret)
745 goto error_out;
747 nd->nd_major = nd->nd_serial_ttdriver->major;
749 ret = nd_struct_add(nd);
750 if (ret)
751 goto error_out;
753 register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
754 register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
755 register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
756 register_dgrp_device(nd, ports_entry_pointer,
757 dgrp_register_ports_hook);
759 return 0;
761 /* FIXME this guy should free the tty driver stored in nd and destroy
762 * all channel ports */
763 error_out:
764 kfree(nd);
765 return ret;
769 static int dgrp_remove_nd(struct nd_struct *nd)
771 int ret;
773 /* Check to see if the selected structure is in use */
774 if (nd->nd_tty_ref_cnt)
775 return -EBUSY;
777 if (nd->nd_net_de) {
778 unregister_dgrp_device(nd->nd_net_de);
779 dgrp_remove_node_class_sysfs_files(nd);
782 unregister_dgrp_device(nd->nd_mon_de);
784 unregister_dgrp_device(nd->nd_ports_de);
786 unregister_dgrp_device(nd->nd_dpa_de);
788 dgrp_tty_uninit(nd);
790 ret = nd_struct_del(nd);
791 if (ret)
792 return ret;
794 kfree(nd);
795 return 0;
798 static void register_dgrp_device(struct nd_struct *node,
799 struct proc_dir_entry *root,
800 void (*register_hook)(struct proc_dir_entry *de))
802 char buf[3];
803 struct proc_dir_entry *de;
805 ID_TO_CHAR(node->nd_ID, buf);
807 de = create_proc_entry(buf, 0600 | S_IFREG, root);
808 if (!de)
809 return;
811 de->data = (void *) node;
813 if (register_hook)
814 register_hook(de);
818 static void unregister_dgrp_device(struct proc_dir_entry *de)
820 if (!de)
821 return;
823 /* Don't unregister proc entries that are still being used.. */
824 if ((atomic_read(&de->count)) != 1) {
825 pr_alert("%s - proc entry %s in use. Not removing.\n",
826 __func__, de->name);
827 return;
830 remove_proc_entry(de->name, de->parent);
831 de = NULL;