1 /*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/lock.h>
39 #include <sys/spinlock2.h>
40 #include <sys/fcntl.h>
41 #include <sys/device.h>
42 #include <sys/mount.h>
43 #include <sys/devfs.h>
44 #include <sys/devfs_rules.h>
45
46 MALLOC_DECLARE(M_DEVFS);
47
48 static d_open_t devfs_dev_open;
49 static d_close_t devfs_dev_close;
50 static d_ioctl_t devfs_dev_ioctl;
51
52 static struct devfs_rule *devfs_rule_alloc(struct devfs_rule_ioctl *);
53 static void devfs_rule_free(struct devfs_rule *);
54 static int devfs_rule_insert(struct devfs_rule_ioctl *);
55 static void devfs_rule_remove(struct devfs_rule *);
56 static int devfs_rule_clear(struct devfs_rule_ioctl *);
57 static void devfs_rule_create_link(struct devfs_node *, struct devfs_rule *);
58 static int devfs_rule_checkname(struct devfs_rule *, struct devfs_node *);
59
60 static struct objcache *devfs_rule_cache;
61 static struct lock devfs_rule_lock;
62
63 static struct objcache_malloc_args devfs_rule_malloc_args = {
64 sizeof(struct devfs_rule), M_DEVFS };
65
66 static cdev_t devfs_dev;
67 static struct devfs_rule_head devfs_rule_list =
68 TAILQ_HEAD_INITIALIZER(devfs_rule_list);
69
70 static struct dev_ops devfs_dev_ops = {
71 { "devfs", 0, D_MPSAFE },
72 .d_open = devfs_dev_open,
73 .d_close = devfs_dev_close,
74 .d_ioctl = devfs_dev_ioctl
75 };
76
77
78 static struct devfs_rule *
devfs_rule_alloc(struct devfs_rule_ioctl * templ)79 devfs_rule_alloc(struct devfs_rule_ioctl *templ)
80 {
81 struct devfs_rule *rule;
82 size_t len;
83
84 rule = objcache_get(devfs_rule_cache, M_WAITOK);
85 memset(rule, 0, sizeof(struct devfs_rule));
86
87 if (templ->mntpoint == NULL)
88 goto error_out;
89 /* NOTREACHED */
90
91 len = strlen(templ->mntpoint);
92 if (len == 0)
93 goto error_out;
94 /* NOTREACHED */
95
96 rule->mntpoint = kstrdup(templ->mntpoint, M_DEVFS);
97 rule->mntpointlen = len;
98
99 if (templ->rule_type & DEVFS_RULE_NAME) {
100 if (templ->name == NULL)
101 goto error_out;
102 /* NOTREACHED */
103
104 len = strlen(templ->name);
105 if (len == 0)
106 goto error_out;
107 /* NOTREACHED */
108
109 rule->name = kstrdup(templ->name, M_DEVFS);
110 rule->namlen = len;
111 }
112
113 if (templ->rule_cmd & DEVFS_RULE_LINK) {
114 if (templ->linkname == NULL)
115 goto error_out;
116 /* NOTREACHED */
117
118 len = strlen(templ->linkname);
119 if (len == 0)
120 goto error_out;
121 /* NOTREACHED */
122
123 rule->linkname = kstrdup(templ->linkname, M_DEVFS);
124 rule->linknamlen = len;
125 }
126
127 rule->rule_type = templ->rule_type;
128 rule->rule_cmd = templ->rule_cmd;
129 rule->dev_type = templ->dev_type;
130 rule->mode = templ->mode;
131 rule->uid = templ->uid;
132 rule->gid = templ->gid;
133
134 return rule;
135
136 error_out:
137 devfs_rule_free(rule);
138 return NULL;
139 }
140
141
142 static void
devfs_rule_free(struct devfs_rule * rule)143 devfs_rule_free(struct devfs_rule *rule)
144 {
145 if (rule->mntpoint != NULL) {
146 kfree(rule->mntpoint, M_DEVFS);
147 }
148
149 if (rule->name != NULL) {
150 kfree(rule->name, M_DEVFS);
151 }
152
153 if (rule->linkname != NULL) {
154 kfree(rule->linkname, M_DEVFS);
155 }
156 objcache_put(devfs_rule_cache, rule);
157 }
158
159
160 static int
devfs_rule_insert(struct devfs_rule_ioctl * templ)161 devfs_rule_insert(struct devfs_rule_ioctl *templ)
162 {
163 struct devfs_rule *rule;
164
165 rule = devfs_rule_alloc(templ);
166 if (rule == NULL)
167 return EINVAL;
168
169 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
170 TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link);
171 lockmgr(&devfs_rule_lock, LK_RELEASE);
172
173 return 0;
174 }
175
176
177 static void
devfs_rule_remove(struct devfs_rule * rule)178 devfs_rule_remove(struct devfs_rule *rule)
179 {
180 TAILQ_REMOVE(&devfs_rule_list, rule, link);
181 devfs_rule_free(rule);
182 }
183
184
185 static int
devfs_rule_clear(struct devfs_rule_ioctl * templ)186 devfs_rule_clear(struct devfs_rule_ioctl *templ)
187 {
188 struct devfs_rule *rule1, *rule2;
189 size_t mntpointlen;
190
191 if (templ->mntpoint == NULL)
192 return EINVAL;
193
194 mntpointlen = strlen(templ->mntpoint);
195 if (mntpointlen == 0)
196 return EINVAL;
197
198 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
199 TAILQ_FOREACH_MUTABLE(rule1, &devfs_rule_list, link, rule2) {
200 if ((templ->mntpoint[0] == '*') ||
201 ((mntpointlen == rule1->mntpointlen) &&
202 (!memcmp(templ->mntpoint, rule1->mntpoint, mntpointlen)))) {
203 devfs_rule_remove(rule1);
204 }
205 }
206 lockmgr(&devfs_rule_lock, LK_RELEASE);
207
208 return 0;
209 }
210
211
212 void *
devfs_rule_reset_node(struct devfs_node * node,void * unused)213 devfs_rule_reset_node(struct devfs_node *node, void *unused)
214 {
215 /*
216 * Don't blindly unhide all devices, some, like unix98 pty masters,
217 * haven't been hidden by a rule.
218 */
219 if (node->flags & DEVFS_RULE_HIDDEN)
220 node->flags &= ~(DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
221
222 if ((node->node_type == Nlink) && (node->flags & DEVFS_RULE_CREATED)) {
223 KKASSERT(node->link_target);
224 node->flags &= ~DEVFS_RULE_CREATED;
225 --node->link_target->nlinks;
226 devfs_gc(node);
227 } else if ((node->node_type == Ndev) && (node->d_dev)) {
228 node->uid = node->d_dev->si_uid;
229 node->gid = node->d_dev->si_gid;
230 node->mode = node->d_dev->si_perms;
231 }
232
233 return NULL;
234 }
235
236 static void
devfs_rule_create_link(struct devfs_node * node,struct devfs_rule * rule)237 devfs_rule_create_link(struct devfs_node *node, struct devfs_rule *rule)
238 {
239 size_t len = 0;
240 char *path = NULL;
241 char *name, name_buf[PATH_MAX], buf[PATH_MAX];
242
243 if (rule->name[rule->namlen-1] == '*') {
244 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
245 len = strlen(name);
246 --len;
247 ksnprintf(buf, sizeof(buf), "%s%s",
248 rule->linkname, node->d_dir.d_name+len);
249 devfs_alias_create(buf, node, 1);
250 } else {
251 devfs_alias_create(rule->linkname, node, 1);
252 }
253 }
254
255 void *
devfs_rule_check_apply(struct devfs_node * node,void * unused)256 devfs_rule_check_apply(struct devfs_node *node, void *unused)
257 {
258 struct devfs_rule *rule;
259 struct mount *mp = node->mp;
260 int locked = 0;
261
262 /* Check if it is locked already. if not, we acquire the devfs lock */
263 if ((lockstatus(&devfs_rule_lock, curthread)) != LK_EXCLUSIVE) {
264 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
265 locked = 1;
266 }
267
268 TAILQ_FOREACH(rule, &devfs_rule_list, link) {
269 /*
270 * Skip this rule if it is only intended for jailed mount points
271 * and the current mount point isn't jailed
272 */
273 if ((rule->rule_type & DEVFS_RULE_JAIL) &&
274 (!(DEVFS_MNTDATA(mp)->jailed)) )
275 continue;
276
277 /*
278 * Skip this rule if it is not intended for jailed mount points
279 * and the current mount point is jailed.
280 */
281 if (!(rule->rule_type & DEVFS_RULE_JAIL) &&
282 (DEVFS_MNTDATA(mp)->jailed))
283 continue;
284
285 /*
286 * Skip this rule if the mount point specified in the rule doesn't
287 * match the mount point of the node
288 */
289 if ((rule->mntpoint[0] != '*') &&
290 (strcmp(rule->mntpoint, mp->mnt_stat.f_mntonname)))
291 continue;
292
293 /*
294 * Skip this rule if this is a by-type rule and the device flags
295 * don't match the specified device type in the rule
296 */
297 if ((rule->rule_type & DEVFS_RULE_TYPE) &&
298 ( (rule->dev_type == 0) || (!dev_is_good(node->d_dev)) ||
299 (!(dev_dflags(node->d_dev) & rule->dev_type))) )
300 continue;
301
302 /*
303 * Skip this rule if this is a by-name rule and the node name
304 * doesn't match the wildcard string in the rule
305 */
306 if ((rule->rule_type & DEVFS_RULE_NAME) &&
307 (!devfs_rule_checkname(rule, node)) )
308 continue;
309
310 if (rule->rule_cmd & DEVFS_RULE_HIDE) {
311 /*
312 * If we should hide the device, we just apply the relevant
313 * hide flag to the node and let devfs do the rest in the
314 * vnops
315 */
316 if ((node->d_dir.d_namlen == 5) &&
317 (!memcmp(node->d_dir.d_name, "devfs", 5))) {
318 /*
319 * Magically avoid /dev/devfs from being hidden, so that one
320 * can still use the rule system even after a "* hide".
321 */
322 continue;
323 }
324 node->flags |= (DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
325 } else if (rule->rule_cmd & DEVFS_RULE_SHOW) {
326 /*
327 * Show rule just means that the node should not be hidden, so
328 * what we do is clear the hide flag from the node.
329 */
330 node->flags &= ~DEVFS_HIDDEN;
331 } else if (rule->rule_cmd & DEVFS_RULE_LINK) {
332 /*
333 * This is a LINK rule, so we tell devfs to create
334 * a link with the correct name to this node.
335 */
336 devfs_rule_create_link(node, rule);
337 #if 0
338 devfs_alias_create(rule->linkname, node, 1);
339 #endif
340 } else if (rule->rule_cmd & DEVFS_RULE_PERM) {
341 /*
342 * This is a normal ownership/permission rule. We
343 * just apply the permissions and ownership and
344 * we are done.
345 */
346 node->mode = rule->mode;
347 node->uid = rule->uid;
348 node->gid = rule->gid;
349 }
350 }
351
352 /* If we acquired the lock, we also get rid of it */
353 if (locked)
354 lockmgr(&devfs_rule_lock, LK_RELEASE);
355
356 return NULL;
357 }
358
359
360 static int
devfs_rule_checkname(struct devfs_rule * rule,struct devfs_node * node)361 devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node)
362 {
363 struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node;
364 char *path = NULL;
365 char *name, name_buf[PATH_MAX];
366 int no_match = 0;
367
368 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
369 parent = devfs_resolve_or_create_path(parent, path, 0);
370
371 if (parent == NULL)
372 return 0; /* no match */
373
374 /* Check if node is a child of the parent we found */
375 if (node->parent != parent)
376 return 0; /* no match */
377
378 #if 0
379 if (rule->rule_type & DEVFS_RULE_LINK)
380 no_match = memcmp(name, node->d_dir.d_name, strlen(name));
381 else
382 #endif
383 no_match = devfs_WildCaseCmp(name, node->d_dir.d_name);
384
385 return !no_match;
386 }
387
388
389 static int
devfs_dev_open(struct dev_open_args * ap)390 devfs_dev_open(struct dev_open_args *ap)
391 {
392 /*
393 * Only allow read-write access.
394 */
395 if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0))
396 return(EPERM);
397
398 /*
399 * We don't allow nonblocking access.
400 */
401 if ((ap->a_oflags & O_NONBLOCK) != 0) {
402 devfs_debug(DEVFS_DEBUG_SHOW,
403 "devfs_dev: can't do nonblocking access\n");
404 return(ENODEV);
405 }
406
407 return 0;
408 }
409
410
411 static int
devfs_dev_close(struct dev_close_args * ap)412 devfs_dev_close(struct dev_close_args *ap)
413 {
414 return 0;
415 }
416
417
418 static int
devfs_dev_ioctl(struct dev_ioctl_args * ap)419 devfs_dev_ioctl(struct dev_ioctl_args *ap)
420 {
421 int error;
422 struct devfs_rule_ioctl *rule;
423
424 error = 0;
425 rule = (struct devfs_rule_ioctl *)ap->a_data;
426
427 switch(ap->a_cmd) {
428 case DEVFS_RULE_ADD:
429 error = devfs_rule_insert(rule);
430 break;
431
432 case DEVFS_RULE_APPLY:
433 if (rule->mntpoint == NULL)
434 error = EINVAL;
435 else
436 devfs_apply_rules(rule->mntpoint);
437 break;
438
439 case DEVFS_RULE_CLEAR:
440 error = devfs_rule_clear(rule);
441 break;
442
443 case DEVFS_RULE_RESET:
444 if (rule->mntpoint == NULL)
445 error = EINVAL;
446 else
447 devfs_reset_rules(rule->mntpoint);
448 break;
449
450 default:
451 error = ENOTTY; /* Inappropriate ioctl for device */
452 break;
453 }
454
455 return(error);
456 }
457
458
459 static void
devfs_dev_init(void * unused)460 devfs_dev_init(void *unused)
461 {
462 lockinit(&devfs_rule_lock, "devfs_rule lock", 0, 0);
463
464 devfs_rule_cache = objcache_create("devfs-rule-cache", 0, 0,
465 NULL, NULL, NULL,
466 objcache_malloc_alloc,
467 objcache_malloc_free,
468 &devfs_rule_malloc_args);
469
470 devfs_dev = make_dev(&devfs_dev_ops, 0, UID_ROOT, GID_WHEEL,
471 0600, "devfs");
472 }
473
474
475 static void
devfs_dev_uninit(void * unused)476 devfs_dev_uninit(void *unused)
477 {
478 /* XXX: destroy all rules first */
479 destroy_dev(devfs_dev);
480 objcache_destroy(devfs_rule_cache);
481 }
482
483
484 SYSINIT(devfsdev, SI_SUB_DRIVERS, SI_ORDER_FIRST, devfs_dev_init, NULL);
485 SYSUNINIT(devfsdev, SI_SUB_DRIVERS, SI_ORDER_FIRST, devfs_dev_uninit, NULL);
486
487