]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/staging/dgrp/dgrp_specproc.c
staging: dgrp: fix potential call to strncpy with a negative number
[karo-tx-linux.git] / drivers / staging / dgrp / dgrp_specproc.c
1 /*
2  *
3  * Copyright 1999 Digi International (www.digi.com)
4  *     James Puzzo  <jamesp at digi dot com>
5  *
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.
10  *
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.
15  *
16  */
17
18 /*
19  *
20  *  Filename:
21  *
22  *     dgrp_specproc.c
23  *
24  *  Description:
25  *
26  *     Handle the "config" proc entry for the linux realport device driver
27  *     and provide slots for the "net" and "mon" devices
28  *
29  *  Author:
30  *
31  *     James A. Puzzo
32  *
33  */
34
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
43 #include "dgrp_common.h"
44
45 static struct dgrp_proc_entry dgrp_table[];
46 static struct proc_dir_entry *dgrp_proc_dir_entry;
47
48 static int dgrp_add_id(long id);
49 static int dgrp_remove_nd(struct nd_struct *nd);
50 static void unregister_dgrp_device(struct proc_dir_entry *de);
51 static void register_dgrp_device(struct nd_struct *node,
52                                  struct proc_dir_entry *root,
53                                  void (*register_hook)(struct proc_dir_entry *de));
54
55 /* File operation declarations */
56 static int dgrp_gen_proc_open(struct inode *, struct file *);
57 static int dgrp_gen_proc_close(struct inode *, struct file *);
58 static int parse_write_config(char *);
59
60
61 static const struct file_operations dgrp_proc_file_ops = {
62         .owner   = THIS_MODULE,
63         .open    = dgrp_gen_proc_open,
64         .release = dgrp_gen_proc_close,
65 };
66
67 static struct inode_operations proc_inode_ops = {
68         .permission = dgrp_inode_permission
69 };
70
71
72 static void register_proc_table(struct dgrp_proc_entry *,
73                                 struct proc_dir_entry *);
74 static void unregister_proc_table(struct dgrp_proc_entry *,
75                                   struct proc_dir_entry *);
76
77 static struct dgrp_proc_entry dgrp_net_table[];
78 static struct dgrp_proc_entry dgrp_mon_table[];
79 static struct dgrp_proc_entry dgrp_ports_table[];
80 static struct dgrp_proc_entry dgrp_dpa_table[];
81
82 static ssize_t config_proc_write(struct file *file, const char __user *buffer,
83                                  size_t count, loff_t *pos);
84
85 static int nodeinfo_proc_open(struct inode *inode, struct file *file);
86 static int info_proc_open(struct inode *inode, struct file *file);
87 static int config_proc_open(struct inode *inode, struct file *file);
88
89 static struct file_operations config_proc_file_ops = {
90         .owner   = THIS_MODULE,
91         .open    = config_proc_open,
92         .read    = seq_read,
93         .llseek  = seq_lseek,
94         .release = seq_release,
95         .write   = config_proc_write
96 };
97
98 static struct file_operations info_proc_file_ops = {
99         .owner   = THIS_MODULE,
100         .open    = info_proc_open,
101         .read    = seq_read,
102         .llseek  = seq_lseek,
103         .release = seq_release,
104 };
105
106 static struct file_operations nodeinfo_proc_file_ops = {
107         .owner   = THIS_MODULE,
108         .open    = nodeinfo_proc_open,
109         .read    = seq_read,
110         .llseek  = seq_lseek,
111         .release = seq_release,
112 };
113
114 static struct dgrp_proc_entry dgrp_table[] = {
115         {
116                 .id = DGRP_CONFIG,
117                 .name = "config",
118                 .mode = 0644,
119                 .proc_file_ops = &config_proc_file_ops,
120         },
121         {
122                 .id = DGRP_INFO,
123                 .name = "info",
124                 .mode = 0644,
125                 .proc_file_ops = &info_proc_file_ops,
126         },
127         {
128                 .id = DGRP_NODEINFO,
129                 .name = "nodeinfo",
130                 .mode = 0644,
131                 .proc_file_ops = &nodeinfo_proc_file_ops,
132         },
133         {
134                 .id = DGRP_NETDIR,
135                 .name = "net",
136                 .mode = 0500,
137                 .child = dgrp_net_table
138         },
139         {
140                 .id = DGRP_MONDIR,
141                 .name = "mon",
142                 .mode = 0500,
143                 .child = dgrp_mon_table
144         },
145         {
146                 .id = DGRP_PORTSDIR,
147                 .name = "ports",
148                 .mode = 0500,
149                 .child = dgrp_ports_table
150         },
151         {
152                 .id = DGRP_DPADIR,
153                 .name = "dpa",
154                 .mode = 0500,
155                 .child = dgrp_dpa_table
156         }
157 };
158
159 static struct proc_dir_entry *net_entry_pointer;
160 static struct proc_dir_entry *mon_entry_pointer;
161 static struct proc_dir_entry *dpa_entry_pointer;
162 static struct proc_dir_entry *ports_entry_pointer;
163
164 static struct dgrp_proc_entry dgrp_net_table[] = {
165         {0}
166 };
167
168 static struct dgrp_proc_entry dgrp_mon_table[] = {
169         {0}
170 };
171
172 static struct dgrp_proc_entry dgrp_ports_table[] = {
173         {0}
174 };
175
176 static struct dgrp_proc_entry dgrp_dpa_table[] = {
177         {0}
178 };
179
180 void dgrp_unregister_proc(void)
181 {
182         unregister_proc_table(dgrp_table, dgrp_proc_dir_entry);
183         net_entry_pointer = NULL;
184         mon_entry_pointer = NULL;
185         dpa_entry_pointer = NULL;
186         ports_entry_pointer = NULL;
187
188         if (dgrp_proc_dir_entry) {
189                 remove_proc_entry(dgrp_proc_dir_entry->name,
190                                   dgrp_proc_dir_entry->parent);
191                 dgrp_proc_dir_entry = NULL;
192         }
193
194 }
195
196 void dgrp_register_proc(void)
197 {
198         /*
199          *      Register /proc/dgrp
200          */
201         dgrp_proc_dir_entry = proc_create("dgrp", S_IFDIR, NULL,
202                                           &dgrp_proc_file_ops);
203         register_proc_table(dgrp_table, dgrp_proc_dir_entry);
204 }
205
206 /*
207  * /proc/sys support
208  */
209 static int dgrp_proc_match(int len, const char *name, struct proc_dir_entry *de)
210 {
211         if (!de || !de->low_ino)
212                 return 0;
213         if (de->namelen != len)
214                 return 0;
215         return !memcmp(name, de->name, len);
216 }
217
218
219 /*
220  *  Scan the entries in table and add them all to /proc at the position
221  *  referred to by "root"
222  */
223 static void register_proc_table(struct dgrp_proc_entry *table,
224                                 struct proc_dir_entry *root)
225 {
226         struct proc_dir_entry *de;
227         int len;
228         mode_t mode;
229
230         for (; table->id; table++) {
231                 /* Can't do anything without a proc name. */
232                 if (!table->name)
233                         continue;
234
235                 /* Maybe we can't do anything with it... */
236                 if (!table->proc_file_ops &&
237                     !table->child) {
238                         pr_warn("dgrp: Can't register %s\n",
239                                 table->name);
240                         continue;
241                 }
242
243                 len = strlen(table->name);
244                 mode = table->mode;
245
246                 de = NULL;
247                 if (!table->child)
248                         mode |= S_IFREG;
249                 else {
250                         mode |= S_IFDIR;
251                         for (de = root->subdir; de; de = de->next) {
252                                 if (dgrp_proc_match(len, table->name, de))
253                                         break;
254                         }
255                         /* If the subdir exists already, de is non-NULL */
256                 }
257
258                 if (!de) {
259                         de = create_proc_entry(table->name, mode, root);
260                         if (!de)
261                                 continue;
262                         de->data = (void *) table;
263                         if (!table->child) {
264                                 de->proc_iops = &proc_inode_ops;
265                                 if (table->proc_file_ops)
266                                         de->proc_fops = table->proc_file_ops;
267                                 else
268                                         de->proc_fops = &dgrp_proc_file_ops;
269                         }
270                 }
271                 table->de = de;
272                 if (de->mode & S_IFDIR)
273                         register_proc_table(table->child, de);
274
275                 if (table->id == DGRP_NETDIR)
276                         net_entry_pointer = de;
277
278                 if (table->id == DGRP_MONDIR)
279                         mon_entry_pointer = de;
280
281                 if (table->id == DGRP_DPADIR)
282                         dpa_entry_pointer = de;
283
284                 if (table->id == DGRP_PORTSDIR)
285                         ports_entry_pointer = de;
286         }
287 }
288
289 /*
290  * Unregister a /proc sysctl table and any subdirectories.
291  */
292 static void unregister_proc_table(struct dgrp_proc_entry *table,
293                                   struct proc_dir_entry *root)
294 {
295         struct proc_dir_entry *de;
296         struct nd_struct *tmp;
297
298         list_for_each_entry(tmp, &nd_struct_list, list) {
299                 if ((table == dgrp_net_table) && (tmp->nd_net_de)) {
300                         unregister_dgrp_device(tmp->nd_net_de);
301                         dgrp_remove_node_class_sysfs_files(tmp);
302                 }
303
304                 if ((table == dgrp_mon_table) && (tmp->nd_mon_de))
305                         unregister_dgrp_device(tmp->nd_mon_de);
306
307                 if ((table == dgrp_dpa_table) && (tmp->nd_dpa_de))
308                         unregister_dgrp_device(tmp->nd_dpa_de);
309
310                 if ((table == dgrp_ports_table) && (tmp->nd_ports_de))
311                         unregister_dgrp_device(tmp->nd_ports_de);
312         }
313
314         for (; table->id; table++) {
315                 de = table->de;
316
317                 if (!de)
318                         continue;
319                 if (de->mode & S_IFDIR) {
320                         if (!table->child) {
321                                 pr_alert("dgrp: malformed sysctl tree on free\n");
322                                 continue;
323                         }
324                         unregister_proc_table(table->child, de);
325
326         /* Don't unregister directories which still have entries */
327                         if (de->subdir)
328                                 continue;
329                 }
330
331                 /* Don't unregister proc entries that are still being used.. */
332                 if ((atomic_read(&de->count)) != 1) {
333                         pr_alert("proc entry %s in use, not removing\n",
334                                 de->name);
335                         continue;
336                 }
337
338                 remove_proc_entry(de->name, de->parent);
339                 table->de = NULL;
340         }
341 }
342
343 static int dgrp_gen_proc_open(struct inode *inode, struct file *file)
344 {
345         struct proc_dir_entry *de;
346         struct dgrp_proc_entry *entry;
347         int ret = 0;
348
349         de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
350         if (!de || !de->data) {
351                 ret = -ENXIO;
352                 goto done;
353         }
354
355         entry = (struct dgrp_proc_entry *) de->data;
356         if (!entry) {
357                 ret = -ENXIO;
358                 goto done;
359         }
360
361         down(&entry->excl_sem);
362
363         if (entry->excl_cnt)
364                 ret = -EBUSY;
365         else
366                 entry->excl_cnt++;
367
368         up(&entry->excl_sem);
369
370 done:
371         return ret;
372 }
373
374 static int dgrp_gen_proc_close(struct inode *inode, struct file *file)
375 {
376         struct proc_dir_entry *de;
377         struct dgrp_proc_entry *entry;
378
379         de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
380         if (!de || !de->data)
381                 goto done;
382
383         entry = (struct dgrp_proc_entry *) de->data;
384         if (!entry)
385                 goto done;
386
387         down(&entry->excl_sem);
388
389         if (entry->excl_cnt)
390                 entry->excl_cnt = 0;
391
392         up(&entry->excl_sem);
393
394 done:
395         return 0;
396 }
397
398 static void *config_proc_start(struct seq_file *m, loff_t *pos)
399 {
400         return seq_list_start_head(&nd_struct_list, *pos);
401 }
402
403 static void *config_proc_next(struct seq_file *p, void *v, loff_t *pos)
404 {
405         return seq_list_next(v, &nd_struct_list, pos);
406 }
407
408 static void config_proc_stop(struct seq_file *m, void *v)
409 {
410 }
411
412 static int config_proc_show(struct seq_file *m, void *v)
413 {
414         struct nd_struct *nd;
415         char tmp_id[4];
416
417         if (v == &nd_struct_list) {
418                 seq_puts(m, "#-----------------------------------------------------------------------------\n");
419                 seq_puts(m, "#                        Avail\n");
420                 seq_puts(m, "# ID  Major  State       Ports\n");
421                 return 0;
422         }
423
424         nd = list_entry(v, struct nd_struct, list);
425
426         ID_TO_CHAR(nd->nd_ID, tmp_id);
427
428         seq_printf(m, "  %-2.2s  %-5ld  %-10.10s  %-5d\n",
429                    tmp_id,
430                    nd->nd_major,
431                    ND_STATE_STR(nd->nd_state),
432                    nd->nd_chan_count);
433
434         return 0;
435 }
436
437 static const struct seq_operations proc_config_ops = {
438         .start = config_proc_start,
439         .next  = config_proc_next,
440         .stop  = config_proc_stop,
441         .show  = config_proc_show
442 };
443
444 static int config_proc_open(struct inode *inode, struct file *file)
445 {
446         return seq_open(file, &proc_config_ops);
447 }
448
449
450 /*
451  *  When writing configuration information, each "record" (i.e. each
452  *  write) is treated as an independent request.  See the "parse"
453  *  description for more details.
454  */
455 static ssize_t config_proc_write(struct file *file, const char __user *buffer,
456                                  size_t count, loff_t *pos)
457 {
458         ssize_t retval;
459         char *inbuf, *sp;
460         char *line, *ldelim;
461
462         if (count > 32768)
463                 return -EINVAL;
464
465         inbuf = sp = vzalloc(count + 1);
466         if (!inbuf)
467                 return -ENOMEM;
468
469         if (copy_from_user(inbuf, buffer, count)) {
470                 retval = -EFAULT;
471                 goto done;
472         }
473
474         inbuf[count] = 0;
475
476         ldelim = "\n";
477
478         line = strpbrk(sp, ldelim);
479         while (line) {
480                 *line = 0;
481                 retval = parse_write_config(sp);
482                 if (retval)
483                         goto done;
484
485                 sp = line + 1;
486                 line = strpbrk(sp, ldelim);
487         }
488
489         retval = count;
490 done:
491         vfree(inbuf);
492         return retval;
493 }
494
495 /*
496  *  ------------------------------------------------------------------------
497  *
498  *  The following are the functions to parse input
499  *
500  *  ------------------------------------------------------------------------
501  */
502 static inline char *skip_past_ws(const char *str)
503 {
504         while ((*str) && !isspace(*str))
505                 ++str;
506
507         return skip_spaces(str);
508 }
509
510 static int parse_id(char **c, char *cID)
511 {
512         int tmp = **c;
513
514         if (isalnum(tmp) || (tmp == '_'))
515                 cID[0] = tmp;
516         else
517                 return -EINVAL;
518
519         (*c)++; tmp = **c;
520
521         if (isalnum(tmp) || (tmp == '_')) {
522                 cID[1] = tmp;
523                 (*c)++;
524         } else
525                 cID[1] = 0;
526
527         return 0;
528 }
529
530 static int parse_add_config(char *buf)
531 {
532         char *c = buf;
533         int  retval;
534         char cID[2];
535         long ID;
536
537         c = skip_past_ws(c);
538
539         retval = parse_id(&c, cID);
540         if (retval < 0)
541                 return retval;
542
543         ID = CHAR_TO_ID(cID);
544
545         c = skip_past_ws(c);
546
547         return dgrp_add_id(ID);
548 }
549
550 static int parse_del_config(char *buf)
551 {
552         char *c = buf;
553         int  retval;
554         struct nd_struct *nd;
555         char cID[2];
556         long ID;
557         long major;
558
559         c = skip_past_ws(c);
560
561         retval = parse_id(&c, cID);
562         if (retval < 0)
563                 return retval;
564
565         ID = CHAR_TO_ID(cID);
566
567         c = skip_past_ws(c);
568
569         retval = kstrtol(c, 10, &major);
570         if (retval)
571                 return retval;
572
573         nd = nd_struct_get(major);
574         if (!nd)
575                 return -EINVAL;
576
577         if ((nd->nd_major != major) || (nd->nd_ID != ID))
578                 return -EINVAL;
579
580         return dgrp_remove_nd(nd);
581 }
582
583 static int parse_chg_config(char *buf)
584 {
585         return -EINVAL;
586 }
587
588 /*
589  *  The passed character buffer represents a single configuration request.
590  *  If the first character is a "+", it is parsed as a request to add a
591  *     PortServer
592  *  If the first character is a "-", it is parsed as a request to delete a
593  *     PortServer
594  *  If the first character is a "*", it is parsed as a request to change a
595  *     PortServer
596  *  Any other character (including whitespace) causes the record to be
597  *     ignored.
598  */
599 static int parse_write_config(char *buf)
600 {
601         int retval;
602
603         switch (buf[0]) {
604         case '+':
605                 retval = parse_add_config(buf);
606                 break;
607         case '-':
608                 retval = parse_del_config(buf);
609                 break;
610         case '*':
611                 retval = parse_chg_config(buf);
612                 break;
613         default:
614                 retval = -EINVAL;
615         }
616
617         return retval;
618 }
619
620 static int info_proc_show(struct seq_file *m, void *v)
621 {
622         seq_printf(m, "version: %s\n", DIGI_VERSION);
623         seq_puts(m, "register_with_sysfs: 1\n");
624         seq_printf(m, "rawreadok: 0x%08x\t(%d)\n",
625                    dgrp_rawreadok, dgrp_rawreadok);
626         seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
627                    dgrp_poll_tick, dgrp_poll_tick);
628
629         return 0;
630 }
631
632 static int info_proc_open(struct inode *inode, struct file *file)
633 {
634         return single_open(file, info_proc_show, NULL);
635 }
636
637
638 static void *nodeinfo_start(struct seq_file *m, loff_t *pos)
639 {
640         return seq_list_start_head(&nd_struct_list, *pos);
641 }
642
643 static void *nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
644 {
645         return seq_list_next(v, &nd_struct_list, pos);
646 }
647
648 static void nodeinfo_stop(struct seq_file *m, void *v)
649 {
650 }
651
652 static int nodeinfo_show(struct seq_file *m, void *v)
653 {
654         struct nd_struct *nd;
655         char hwver[8];
656         char swver[8];
657         char tmp_id[4];
658
659         if (v == &nd_struct_list) {
660                 seq_puts(m, "#-----------------------------------------------------------------------------\n");
661                 seq_puts(m, "#                 HW       HW   SW\n");
662                 seq_puts(m, "# ID  State       Version  ID   Version  Description\n");
663                 return 0;
664         }
665
666         nd = list_entry(v, struct nd_struct, list);
667
668         ID_TO_CHAR(nd->nd_ID, tmp_id);
669
670         if (nd->nd_state == NS_READY) {
671                 sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
672                         nd->nd_hw_ver & 0xff);
673                 sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
674                         nd->nd_sw_ver & 0xff);
675                 seq_printf(m, "  %-2.2s  %-10.10s  %-7.7s  %-3d  %-7.7s  %-35.35s\n",
676                            tmp_id,
677                            ND_STATE_STR(nd->nd_state),
678                            hwver,
679                            nd->nd_hw_id,
680                            swver,
681                            nd->nd_ps_desc);
682
683         } else {
684                 seq_printf(m, "  %-2.2s  %-10.10s\n",
685                            tmp_id,
686                            ND_STATE_STR(nd->nd_state));
687         }
688
689         return 0;
690 }
691
692
693 static const struct seq_operations nodeinfo_ops = {
694         .start = nodeinfo_start,
695         .next  = nodeinfo_next,
696         .stop  = nodeinfo_stop,
697         .show  = nodeinfo_show
698 };
699
700 static int nodeinfo_proc_open(struct inode *inode, struct file *file)
701 {
702         return seq_open(file, &nodeinfo_ops);
703 }
704
705 /**
706  * dgrp_add_id() -- creates new nd struct and adds it to list
707  * @id: id of device to add
708  */
709 static int dgrp_add_id(long id)
710 {
711         struct nd_struct *nd;
712         int ret;
713         int i;
714
715         nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
716         if (!nd)
717                 return -ENOMEM;
718
719         nd->nd_major = 0;
720         nd->nd_ID = id;
721
722         spin_lock_init(&nd->nd_lock);
723
724         init_waitqueue_head(&nd->nd_tx_waitq);
725         init_waitqueue_head(&nd->nd_mon_wqueue);
726         init_waitqueue_head(&nd->nd_dpa_wqueue);
727         for (i = 0; i < SEQ_MAX; i++)
728                 init_waitqueue_head(&nd->nd_seq_wque[i]);
729
730         /* setup the structures to get the major number */
731         ret = dgrp_tty_init(nd);
732         if (ret)
733                 goto error_out;
734
735         nd->nd_major = nd->nd_serial_ttdriver->major;
736
737         ret = nd_struct_add(nd);
738         if (ret)
739                 goto error_out;
740
741         register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
742         register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
743         register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
744         register_dgrp_device(nd, ports_entry_pointer,
745                               dgrp_register_ports_hook);
746
747         return 0;
748
749 error_out:
750         kfree(nd);
751         return ret;
752
753 }
754
755 static int dgrp_remove_nd(struct nd_struct *nd)
756 {
757         int ret;
758
759         /* Check to see if the selected structure is in use */
760         if (nd->nd_tty_ref_cnt)
761                 return -EBUSY;
762
763         if (nd->nd_net_de) {
764                 unregister_dgrp_device(nd->nd_net_de);
765                 dgrp_remove_node_class_sysfs_files(nd);
766         }
767
768         if (nd->nd_mon_de)
769                 unregister_dgrp_device(nd->nd_mon_de);
770
771         if (nd->nd_ports_de)
772                 unregister_dgrp_device(nd->nd_ports_de);
773
774         if (nd->nd_dpa_de)
775                 unregister_dgrp_device(nd->nd_dpa_de);
776
777         dgrp_tty_uninit(nd);
778
779         ret = nd_struct_del(nd);
780         if (ret)
781                 return ret;
782
783         kfree(nd);
784         return 0;
785 }
786
787 static void register_dgrp_device(struct nd_struct *node,
788                                  struct proc_dir_entry *root,
789                                  void (*register_hook)(struct proc_dir_entry *de))
790 {
791         char buf[3];
792         struct proc_dir_entry *de;
793
794         ID_TO_CHAR(node->nd_ID, buf);
795
796         de = create_proc_entry(buf, 0600 | S_IFREG, root);
797         if (!de)
798                 return;
799
800         de->data = (void *) node;
801
802         if (register_hook)
803                 register_hook(de);
804
805 }
806
807 static void unregister_dgrp_device(struct proc_dir_entry *de)
808 {
809         if (!de)
810                 return;
811
812         /* Don't unregister proc entries that are still being used.. */
813         if ((atomic_read(&de->count)) != 1) {
814                 pr_alert("%s - proc entry %s in use. Not removing.\n",
815                          __func__, de->name);
816                 return;
817         }
818
819         remove_proc_entry(de->name, de->parent);
820         de = NULL;
821 }