]>
Commit | Line | Data |
---|---|---|
a14bc59f | 1 | /* |
35f7605b | 2 | * Copyright (c) 2009, 2010 Nicira Networks. |
a14bc59f BP |
3 | * Distributed under the terms of the GNU GPL version 2. |
4 | * | |
5 | * Significant portions of this file may be copied from parts of the Linux | |
6 | * kernel, by Linus Torvalds and others. | |
7 | */ | |
8 | ||
064af421 BP |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> | |
11 | #include <linux/netdevice.h> | |
12 | #include <linux/proc_fs.h> | |
13 | #include <linux/seq_file.h> | |
14 | #include <net/genetlink.h> | |
35f7605b | 15 | #include "brc_procfs.h" |
064af421 BP |
16 | #include "openvswitch/brcompat-netlink.h" |
17 | ||
18 | /* This code implements a Generic Netlink command BRC_GENL_C_SET_PROC that can | |
19 | * be used to add, modify, and delete arbitrary files in selected | |
20 | * subdirectories of /proc. It's a horrible kluge prompted by the need to | |
21 | * simulate certain /proc/net/vlan and /proc/net/bonding files for software | |
22 | * that wants to read them, and with any luck it will go away eventually. | |
23 | * | |
24 | * The implementation is a kluge too. In particular, we want to release the | |
25 | * strings copied into the 'data' members of proc_dir_entry when the | |
26 | * proc_dir_entry structures are freed, but there doesn't appear to be a way to | |
27 | * hook that, so instead we have to rely on being the only entity modifying the | |
28 | * directories in question. | |
29 | */ | |
30 | ||
31 | static int brc_seq_show(struct seq_file *seq, void *unused) | |
32 | { | |
33 | seq_puts(seq, seq->private); | |
34 | return 0; | |
35 | } | |
36 | ||
37 | static int brc_seq_open(struct inode *inode, struct file *file) | |
38 | { | |
39 | return single_open(file, brc_seq_show, PDE(inode)->data); | |
40 | } | |
41 | ||
42 | static struct file_operations brc_fops = { | |
43 | .owner = THIS_MODULE, | |
44 | .open = brc_seq_open, | |
45 | .read = seq_read, | |
46 | .llseek = seq_lseek, | |
47 | .release = single_release, | |
48 | }; | |
49 | ||
50 | static struct proc_dir_entry *proc_vlan_dir; | |
51 | static struct proc_dir_entry *proc_bonding_dir; | |
52 | ||
35f7605b | 53 | static struct proc_dir_entry *brc_lookup_entry(struct proc_dir_entry *de, const char *name) |
064af421 BP |
54 | { |
55 | int namelen = strlen(name); | |
56 | for (de = de->subdir; de; de = de->next) { | |
57 | if (de->namelen != namelen) | |
58 | continue; | |
59 | if (!memcmp(name, de->name, de->namelen)) | |
60 | return de; | |
61 | } | |
62 | return NULL; | |
63 | } | |
64 | ||
65 | static struct proc_dir_entry *brc_open_dir(const char *dir_name, | |
66 | struct proc_dir_entry *parent, | |
67 | struct proc_dir_entry **dirp) | |
68 | { | |
69 | if (!*dirp) { | |
70 | struct proc_dir_entry *dir; | |
71 | if (brc_lookup_entry(parent, dir_name)) { | |
72 | printk(KERN_WARNING "%s proc directory exists, can't " | |
73 | "simulate--probably its real module is " | |
74 | "loaded\n", dir_name); | |
75 | return NULL; | |
76 | } | |
77 | dir = *dirp = proc_mkdir(dir_name, parent); | |
78 | } | |
79 | return *dirp; | |
80 | } | |
81 | ||
82 | /* Maximum length of the BRC_GENL_A_PROC_DIR and BRC_GENL_A_PROC_NAME strings. | |
83 | * If we could depend on supporting NLA_NUL_STRING and the .len member in | |
84 | * Generic Netlink policy, then we could just put this in brc_genl_policy (and | |
85 | * simplify brc_genl_set_proc() below too), but upstream 2.6.18 does not have | |
86 | * either. */ | |
87 | #define BRC_NAME_LEN_MAX 32 | |
88 | ||
89 | int brc_genl_set_proc(struct sk_buff *skb, struct genl_info *info) | |
90 | { | |
91 | struct proc_dir_entry *dir, *entry; | |
92 | const char *dir_name, *name; | |
93 | char *data; | |
94 | ||
95 | if (!info->attrs[BRC_GENL_A_PROC_DIR] || | |
96 | VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DIR]) || | |
97 | !info->attrs[BRC_GENL_A_PROC_NAME] || | |
98 | VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_NAME]) || | |
99 | (info->attrs[BRC_GENL_A_PROC_DATA] && | |
100 | VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DATA]))) | |
101 | return -EINVAL; | |
102 | ||
103 | dir_name = nla_data(info->attrs[BRC_GENL_A_PROC_DIR]); | |
104 | name = nla_data(info->attrs[BRC_GENL_A_PROC_NAME]); | |
105 | if (strlen(dir_name) > BRC_NAME_LEN_MAX || | |
106 | strlen(name) > BRC_NAME_LEN_MAX) | |
107 | return -EINVAL; | |
108 | ||
109 | if (!strcmp(dir_name, "net/vlan")) | |
110 | dir = brc_open_dir("vlan", proc_net, &proc_vlan_dir); | |
111 | else if (!strcmp(dir_name, "net/bonding")) | |
112 | dir = brc_open_dir("bonding", proc_net, &proc_bonding_dir); | |
113 | else | |
114 | return -EINVAL; | |
115 | if (!dir) { | |
116 | /* Probably failed because the module that really implements | |
117 | * the function in question is loaded and already owns the | |
118 | * directory in question.*/ | |
119 | return -EBUSY; | |
120 | } | |
121 | ||
122 | entry = brc_lookup_entry(dir, name); | |
123 | if (!info->attrs[BRC_GENL_A_PROC_DATA]) { | |
124 | if (!entry) | |
125 | return -ENOENT; | |
126 | ||
127 | data = entry->data; | |
128 | remove_proc_entry(name, dir); | |
129 | if (brc_lookup_entry(dir, name)) | |
130 | return -EBUSY; /* Shouldn't happen */ | |
131 | ||
132 | kfree(data); | |
133 | } else { | |
134 | data = kstrdup(nla_data(info->attrs[BRC_GENL_A_PROC_DATA]), | |
135 | GFP_KERNEL); | |
136 | if (!data) | |
137 | return -ENOMEM; | |
138 | ||
139 | if (entry) { | |
140 | char *old_data = entry->data; | |
141 | entry->data = data; | |
142 | kfree(old_data); | |
143 | return 0; | |
144 | } | |
145 | ||
146 | entry = create_proc_entry(name, S_IFREG|S_IRUSR|S_IWUSR, dir); | |
147 | if (!entry) { | |
148 | kfree(data); | |
149 | return -ENOBUFS; | |
150 | } | |
151 | entry->proc_fops = &brc_fops; | |
152 | entry->data = data; | |
153 | } | |
154 | return 0; | |
155 | } | |
156 | ||
157 | static void kill_proc_dir(const char *dir_name, | |
158 | struct proc_dir_entry *parent, | |
159 | struct proc_dir_entry *dir) | |
160 | { | |
161 | if (!dir) | |
162 | return; | |
163 | for (;;) { | |
164 | struct proc_dir_entry *e; | |
165 | char *data; | |
166 | char name[BRC_NAME_LEN_MAX + 1]; | |
167 | ||
168 | e = dir->subdir; | |
169 | if (!e) | |
170 | break; | |
171 | ||
172 | if (e->namelen >= sizeof name) { | |
173 | /* Can't happen: we prevent adding names this long by | |
174 | * limiting the BRC_GENL_A_PROC_NAME string to | |
175 | * BRC_NAME_LEN_MAX bytes. */ | |
176 | WARN_ON(1); | |
177 | break; | |
178 | } | |
179 | strcpy(name, e->name); | |
180 | ||
181 | data = e->data; | |
182 | e->data = NULL; | |
183 | kfree(data); | |
184 | ||
185 | remove_proc_entry(name, dir); | |
186 | } | |
187 | remove_proc_entry(dir_name, parent); | |
188 | } | |
189 | ||
190 | void brc_procfs_exit(void) | |
191 | { | |
192 | kill_proc_dir("vlan", proc_net, proc_vlan_dir); | |
193 | kill_proc_dir("bonding", proc_net, proc_bonding_dir); | |
194 | } |