devfs - Add a destroy_dev_alias, remove hotplug
[dragonfly.git] / sys / vfs / devfs / devfs_rules.c
CommitLineData
21864bc5
MD
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/types.h>
38#include <sys/param.h>
39#include <sys/ioccom.h>
4464e289
AH
40#include <sys/lock.h>
41#include <sys/spinlock2.h>
21864bc5
MD
42#include <sys/fcntl.h>
43#include <sys/device.h>
44#include <sys/mount.h>
2c1e28dd
AH
45#include <sys/devfs.h>
46#include <sys/devfs_rules.h>
21864bc5
MD
47
48MALLOC_DECLARE(M_DEVFS);
49
21864bc5
MD
50static d_open_t devfs_dev_open;
51static d_close_t devfs_dev_close;
52static d_ioctl_t devfs_dev_ioctl;
53
5704df1d 54static struct devfs_rule *devfs_rule_alloc(struct devfs_rule_ioctl *);
21864bc5 55static void devfs_rule_free(struct devfs_rule *);
dd8bea0a 56static int devfs_rule_insert(struct devfs_rule_ioctl *);
21864bc5 57static void devfs_rule_remove(struct devfs_rule *);
dd8bea0a 58static int devfs_rule_clear(struct devfs_rule_ioctl *);
84ba2ba8 59static void devfs_rule_create_link(struct devfs_node *, struct devfs_rule *);
21864bc5
MD
60static int devfs_rule_checkname(struct devfs_rule *, struct devfs_node *);
61
62static struct objcache *devfs_rule_cache;
63static struct lock devfs_rule_lock;
64
65static struct objcache_malloc_args devfs_rule_malloc_args = {
66 sizeof(struct devfs_rule), M_DEVFS };
67
68static cdev_t devfs_dev;
bc185c5a
AH
69static struct devfs_rule_head devfs_rule_list =
70 TAILQ_HEAD_INITIALIZER(devfs_rule_list);
21864bc5
MD
71
72static struct dev_ops devfs_dev_ops = {
73 { "devfs", 0, 0 },
74 .d_open = devfs_dev_open,
75 .d_close = devfs_dev_close,
76 .d_ioctl = devfs_dev_ioctl
77};
78
79
80static struct devfs_rule *
5704df1d 81devfs_rule_alloc(struct devfs_rule_ioctl *templ)
21864bc5 82{
894bbb25 83 struct devfs_rule *rule;
5704df1d 84 size_t len;
894bbb25
AH
85
86 rule = objcache_get(devfs_rule_cache, M_WAITOK);
87 memset(rule, 0, sizeof(struct devfs_rule));
88
dd8bea0a
AH
89 if (templ->mntpoint == NULL)
90 goto error_out;
91 /* NOTREACHED */
92
5704df1d 93 len = strlen(templ->mntpoint);
dd8bea0a
AH
94 if (len == 0)
95 goto error_out;
96 /* NOTREACHED */
97
98 rule->mntpoint = kstrdup(templ->mntpoint, M_DEVFS);
99 rule->mntpointlen = len;
100
101 if (templ->rule_type & DEVFS_RULE_NAME) {
102 if (templ->name == NULL)
103 goto error_out;
104 /* NOTREACHED */
894bbb25 105
5704df1d 106 len = strlen(templ->name);
dd8bea0a
AH
107 if (len == 0)
108 goto error_out;
109 /* NOTREACHED */
110
111 rule->name = kstrdup(templ->name, M_DEVFS);
112 rule->namlen = len;
894bbb25
AH
113 }
114
dd8bea0a
AH
115 if (templ->rule_cmd & DEVFS_RULE_LINK) {
116 if (templ->linkname == NULL)
117 goto error_out;
118 /* NOTREACHED */
119
5704df1d 120 len = strlen(templ->linkname);
dd8bea0a
AH
121 if (len == 0)
122 goto error_out;
123 /* NOTREACHED */
124
125 rule->linkname = kstrdup(templ->linkname, M_DEVFS);
126 rule->linknamlen = len;
894bbb25
AH
127 }
128
129 rule->rule_type = templ->rule_type;
5704df1d 130 rule->rule_cmd = templ->rule_cmd;
894bbb25
AH
131 rule->dev_type = templ->dev_type;
132 rule->mode = templ->mode;
133 rule->uid = templ->uid;
134 rule->gid = templ->gid;
21864bc5 135
21864bc5 136 return rule;
dd8bea0a
AH
137
138error_out:
139 devfs_rule_free(rule);
140 return NULL;
21864bc5
MD
141}
142
143
144static void
145devfs_rule_free(struct devfs_rule *rule)
146{
894bbb25
AH
147 if (rule->mntpoint != NULL) {
148 kfree(rule->mntpoint, M_DEVFS);
149 }
150
151 if (rule->name != NULL) {
152 kfree(rule->name, M_DEVFS);
153 }
154
155 if (rule->linkname != NULL) {
156 kfree(rule->linkname, M_DEVFS);
157 }
21864bc5
MD
158 objcache_put(devfs_rule_cache, rule);
159}
160
161
dd8bea0a 162static int
5704df1d 163devfs_rule_insert(struct devfs_rule_ioctl *templ)
21864bc5 164{
894bbb25
AH
165 struct devfs_rule *rule;
166
167 rule = devfs_rule_alloc(templ);
dd8bea0a
AH
168 if (rule == NULL)
169 return EINVAL;
894bbb25 170
21864bc5 171 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
21864bc5
MD
172 TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link);
173 lockmgr(&devfs_rule_lock, LK_RELEASE);
dd8bea0a
AH
174
175 return 0;
21864bc5
MD
176}
177
178
179static void
180devfs_rule_remove(struct devfs_rule *rule)
181{
182 TAILQ_REMOVE(&devfs_rule_list, rule, link);
183 devfs_rule_free(rule);
184}
185
186
dd8bea0a 187static int
5704df1d 188devfs_rule_clear(struct devfs_rule_ioctl *templ)
21864bc5 189{
5704df1d 190 struct devfs_rule *rule1, *rule2;
dd8bea0a
AH
191 size_t mntpointlen;
192
193 if (templ->mntpoint == NULL)
194 return EINVAL;
195
196 mntpointlen = strlen(templ->mntpoint);
197 if (mntpointlen == 0)
198 return EINVAL;
21864bc5
MD
199
200 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
201 TAILQ_FOREACH_MUTABLE(rule1, &devfs_rule_list, link, rule2) {
5704df1d
AH
202 if ((templ->mntpoint[0] == '*') ||
203 ( (mntpointlen == rule1->mntpointlen) &&
204 (!memcmp(templ->mntpoint, rule1->mntpoint, mntpointlen)) )) {
21864bc5
MD
205 devfs_rule_remove(rule1);
206 }
207 }
208 lockmgr(&devfs_rule_lock, LK_RELEASE);
dd8bea0a
AH
209
210 return 0;
21864bc5
MD
211}
212
213
66abefa5
AH
214void *
215devfs_rule_reset_node(struct devfs_node *node, void *unused)
21864bc5 216{
1cb12919 217 /*
0182b316
AH
218 * Don't blindly unhide all devices, some, like unix98 pty masters,
219 * haven't been hidden by a rule.
1cb12919 220 */
0182b316
AH
221 if (node->flags & DEVFS_RULE_HIDDEN)
222 node->flags &= ~(DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
21864bc5 223
1cb12919
AH
224 if ((node->node_type == Plink) && (node->flags & DEVFS_RULE_CREATED)) {
225 KKASSERT(node->link_target);
226 node->flags &= ~DEVFS_RULE_CREATED;
227 --node->link_target->nlinks;
228 devfs_gc(node);
229 } else if ((node->node_type == Pdev) && (node->d_dev)) {
21864bc5
MD
230 node->uid = node->d_dev->si_uid;
231 node->gid = node->d_dev->si_gid;
232 node->mode = node->d_dev->si_perms;
233 }
234
66abefa5 235 return NULL;
21864bc5
MD
236}
237
84ba2ba8
AH
238static void
239devfs_rule_create_link(struct devfs_node *node, struct devfs_rule *rule)
240{
241 size_t len = 0;
242 char *path = NULL;
243 char *name, name_buf[PATH_MAX], buf[PATH_MAX];
244
245 if (rule->name[rule->namlen-1] == '*') {
246 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
247 len = strlen(name);
248 --len;
249 ksnprintf(buf, sizeof(buf), "%s%s",
250 rule->linkname, node->d_dir.d_name+len);
251 devfs_alias_create(buf, node, 1);
252 } else {
253 devfs_alias_create(rule->linkname, node, 1);
254 }
255}
21864bc5 256
66abefa5
AH
257void *
258devfs_rule_check_apply(struct devfs_node *node, void *unused)
21864bc5
MD
259{
260 struct devfs_rule *rule;
261 struct mount *mp = node->mp;
4464e289
AH
262 int locked = 0;
263
264 /* Check if it is locked already. if not, we acquire the devfs lock */
265 if (!(lockstatus(&devfs_rule_lock, curthread)) == LK_EXCLUSIVE) {
266 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
267 locked = 1;
268 }
21864bc5 269
21864bc5 270 TAILQ_FOREACH(rule, &devfs_rule_list, link) {
21864bc5
MD
271 /*
272 * Skip this rule if it is only intended for jailed mount points
273 * and the current mount point isn't jailed
274 */
275 if ((rule->rule_type & DEVFS_RULE_JAIL) &&
276 (!(DEVFS_MNTDATA(mp)->jailed)) )
277 continue;
278
279 /*
a4aa064c
AH
280 * Skip this rule if it is not intended for jailed mount points
281 * and the current mount point is jailed.
282 */
283 if (!(rule->rule_type & DEVFS_RULE_JAIL) &&
284 (DEVFS_MNTDATA(mp)->jailed))
285 continue;
286
287 /*
21864bc5
MD
288 * Skip this rule if the mount point specified in the rule doesn't
289 * match the mount point of the node
290 */
291 if ((rule->mntpoint[0] != '*') &&
9cf39e57 292 (strcmp(rule->mntpoint, mp->mnt_stat.f_mntonname)))
21864bc5
MD
293 continue;
294
295 /*
296 * Skip this rule if this is a by-type rule and the device flags
297 * don't match the specified device type in the rule
298 */
299 if ((rule->rule_type & DEVFS_RULE_TYPE) &&
300 ( (rule->dev_type == 0) || (!dev_is_good(node->d_dev)) ||
4464e289 301 (!(dev_dflags(node->d_dev) & rule->dev_type))) )
21864bc5
MD
302 continue;
303
304 /*
305 * Skip this rule if this is a by-name rule and the node name
306 * doesn't match the wildcard string in the rule
307 */
308 if ((rule->rule_type & DEVFS_RULE_NAME) &&
309 (!devfs_rule_checkname(rule, node)) )
310 continue;
311
5704df1d 312 if (rule->rule_cmd & DEVFS_RULE_HIDE) {
21864bc5
MD
313 /*
314 * If we should hide the device, we just apply the relevant
315 * hide flag to the node and let devfs do the rest in the
316 * vnops
317 */
801ada31
AH
318 if ((node->d_dir.d_namlen == 5) &&
319 (!memcmp(node->d_dir.d_name, "devfs", 5))) {
320 /*
321 * Magically avoid /dev/devfs from being hidden, so that one
322 * can still use the rule system even after a "* hide".
323 */
324 continue;
325 }
0182b316 326 node->flags |= (DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
5704df1d 327 } else if (rule->rule_cmd & DEVFS_RULE_SHOW) {
21864bc5
MD
328 /*
329 * Show rule just means that the node should not be hidden, so
330 * what we do is clear the hide flag from the node.
331 */
332 node->flags &= ~DEVFS_HIDDEN;
9cf39e57 333 } else if (rule->rule_cmd & DEVFS_RULE_LINK) {
21864bc5
MD
334 /*
335 * This is a LINK rule, so we tell devfs to create
336 * a link with the correct name to this node.
337 */
84ba2ba8
AH
338 devfs_rule_create_link(node, rule);
339#if 0
1cb12919 340 devfs_alias_create(rule->linkname, node, 1);
84ba2ba8 341#endif
5704df1d 342 } else if (rule->rule_cmd & DEVFS_RULE_PERM) {
21864bc5
MD
343 /*
344 * This is a normal ownership/permission rule. We
345 * just apply the permissions and ownership and
346 * we are done.
347 */
348 node->mode = rule->mode;
349 node->uid = rule->uid;
350 node->gid = rule->gid;
21864bc5
MD
351 }
352 }
4464e289
AH
353
354 /* If we acquired the lock, we also get rid of it */
355 if (locked)
356 lockmgr(&devfs_rule_lock, LK_RELEASE);
357
66abefa5 358 return NULL;
21864bc5
MD
359}
360
361
362static int
363devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node)
364{
365 struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node;
366 char *path = NULL;
367 char *name, name_buf[PATH_MAX];
368 int no_match = 0;
369
370 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
371 parent = devfs_resolve_or_create_path(parent, path, 0);
372
373 if (parent == NULL)
374 return 0; /* no match */
375
376 /* Check if node is a child of the parent we found */
377 if (node->parent != parent)
378 return 0; /* no match */
379
84ba2ba8 380#if 0
21864bc5
MD
381 if (rule->rule_type & DEVFS_RULE_LINK)
382 no_match = memcmp(name, node->d_dir.d_name, strlen(name));
383 else
84ba2ba8 384#endif
8312ca30 385 no_match = devfs_WildCaseCmp(name, node->d_dir.d_name);
21864bc5
MD
386
387 return !no_match;
388}
389
390
391static int
392devfs_dev_open(struct dev_open_args *ap)
393{
394 /*
395 * Only allow read-write access.
396 */
397 if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0))
398 return(EPERM);
399
400 /*
401 * We don't allow nonblocking access.
402 */
403 if ((ap->a_oflags & O_NONBLOCK) != 0) {
bc185c5a 404 devfs_debug(DEVFS_DEBUG_SHOW, "devfs_dev: can't do nonblocking access\n");
21864bc5
MD
405 return(ENODEV);
406 }
407
408 return 0;
409}
410
411
412static int
413devfs_dev_close(struct dev_close_args *ap)
414{
415 return 0;
416}
417
418
419static int
420devfs_dev_ioctl(struct dev_ioctl_args *ap)
421{
422 int error;
5704df1d 423 struct devfs_rule_ioctl *rule;
21864bc5
MD
424
425 error = 0;
5704df1d 426 rule = (struct devfs_rule_ioctl *)ap->a_data;
21864bc5
MD
427
428 switch(ap->a_cmd) {
429 case DEVFS_RULE_ADD:
dd8bea0a 430 error = devfs_rule_insert(rule);
21864bc5
MD
431 break;
432
433 case DEVFS_RULE_APPLY:
dd8bea0a
AH
434 if (rule->mntpoint == NULL)
435 error = EINVAL;
436 else
437 devfs_apply_rules(rule->mntpoint);
21864bc5
MD
438 break;
439
440 case DEVFS_RULE_CLEAR:
dd8bea0a 441 error = devfs_rule_clear(rule);
21864bc5
MD
442 break;
443
444 case DEVFS_RULE_RESET:
dd8bea0a
AH
445 if (rule->mntpoint == NULL)
446 error = EINVAL;
447 else
448 devfs_reset_rules(rule->mntpoint);
21864bc5
MD
449 break;
450
451 default:
452 error = ENOTTY; /* Inappropriate ioctl for device */
453 break;
454 }
455
456 return(error);
457}
458
459
460static void
461devfs_dev_init(void *unused)
462{
463 lockinit(&devfs_rule_lock, "devfs_rule lock", 0, 0);
464
465 devfs_rule_cache = objcache_create("devfs-rule-cache", 0, 0,
466 NULL, NULL, NULL,
467 objcache_malloc_alloc,
468 objcache_malloc_free,
469 &devfs_rule_malloc_args );
470
471 devfs_dev = make_dev(&devfs_dev_ops,
472 0,
473 UID_ROOT,
474 GID_WHEEL,
475 0600,
476 "devfs");
477}
478
479
480static void
481devfs_dev_uninit(void *unused)
482{
894bbb25 483 /* XXX: destroy all rules first */
21864bc5
MD
484 destroy_dev(devfs_dev);
485 objcache_destroy(devfs_rule_cache);
486}
487
488
489SYSINIT(devfsdev,SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_init,NULL)
490SYSUNINIT(devfsdev, SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_uninit, NULL);
491