devfs - Add a destroy_dev_alias, remove hotplug
[dragonfly.git] / sys / vfs / devfs / devfs_rules.c
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>
40 #include <sys/lock.h>
41 #include <sys/spinlock2.h>
42 #include <sys/fcntl.h>
43 #include <sys/device.h>
44 #include <sys/mount.h>
45 #include <sys/devfs.h>
46 #include <sys/devfs_rules.h>
47
48 MALLOC_DECLARE(M_DEVFS);
49
50 static d_open_t      devfs_dev_open;
51 static d_close_t     devfs_dev_close;
52 static d_ioctl_t     devfs_dev_ioctl;
53
54 static struct devfs_rule *devfs_rule_alloc(struct devfs_rule_ioctl *);
55 static void devfs_rule_free(struct devfs_rule *);
56 static int devfs_rule_insert(struct devfs_rule_ioctl *);
57 static void devfs_rule_remove(struct devfs_rule *);
58 static int devfs_rule_clear(struct devfs_rule_ioctl *);
59 static void devfs_rule_create_link(struct devfs_node *, struct devfs_rule *);
60 static int devfs_rule_checkname(struct devfs_rule *, struct devfs_node *);
61
62 static struct objcache  *devfs_rule_cache;
63 static struct lock              devfs_rule_lock;
64
65 static struct objcache_malloc_args devfs_rule_malloc_args = {
66         sizeof(struct devfs_rule), M_DEVFS };
67
68 static cdev_t devfs_dev;
69 static struct devfs_rule_head devfs_rule_list =
70                 TAILQ_HEAD_INITIALIZER(devfs_rule_list);
71
72 static 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
80 static struct devfs_rule *
81 devfs_rule_alloc(struct devfs_rule_ioctl *templ)
82 {
83         struct devfs_rule *rule;
84         size_t len;
85
86         rule = objcache_get(devfs_rule_cache, M_WAITOK);
87         memset(rule, 0, sizeof(struct devfs_rule));
88
89         if (templ->mntpoint == NULL)
90                 goto error_out;
91                 /* NOTREACHED */
92
93         len = strlen(templ->mntpoint);
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 */
105
106                 len = strlen(templ->name);
107                 if (len == 0)
108                         goto error_out;
109                         /* NOTREACHED */
110
111                 rule->name = kstrdup(templ->name, M_DEVFS);
112                 rule->namlen = len;
113         }
114
115         if (templ->rule_cmd & DEVFS_RULE_LINK) {
116                 if (templ->linkname == NULL)
117                         goto error_out;
118                         /* NOTREACHED */
119
120                 len = strlen(templ->linkname);
121                 if (len == 0)
122                         goto error_out;
123                         /* NOTREACHED */
124
125                 rule->linkname = kstrdup(templ->linkname, M_DEVFS);
126                 rule->linknamlen = len;
127         }
128
129         rule->rule_type = templ->rule_type;
130         rule->rule_cmd = templ->rule_cmd;
131         rule->dev_type = templ->dev_type;
132         rule->mode = templ->mode;
133         rule->uid = templ->uid;
134         rule->gid = templ->gid;
135
136         return rule;
137
138 error_out:
139         devfs_rule_free(rule);
140         return NULL;
141 }
142
143
144 static void
145 devfs_rule_free(struct devfs_rule *rule)
146 {
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         }
158         objcache_put(devfs_rule_cache, rule);
159 }
160
161
162 static int
163 devfs_rule_insert(struct devfs_rule_ioctl *templ)
164 {
165         struct devfs_rule *rule;
166
167         rule = devfs_rule_alloc(templ);
168         if (rule == NULL)
169                 return EINVAL;
170
171         lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
172         TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link);
173         lockmgr(&devfs_rule_lock, LK_RELEASE);
174
175         return 0;
176 }
177
178
179 static void
180 devfs_rule_remove(struct devfs_rule *rule)
181 {
182         TAILQ_REMOVE(&devfs_rule_list, rule, link);
183         devfs_rule_free(rule);
184 }
185
186
187 static int
188 devfs_rule_clear(struct devfs_rule_ioctl *templ)
189 {
190         struct devfs_rule *rule1, *rule2;
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;
199
200         lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
201         TAILQ_FOREACH_MUTABLE(rule1, &devfs_rule_list, link, rule2) {
202                 if ((templ->mntpoint[0] == '*') ||
203                         ( (mntpointlen == rule1->mntpointlen) &&
204                           (!memcmp(templ->mntpoint, rule1->mntpoint, mntpointlen)) )) {
205                         devfs_rule_remove(rule1);
206                 }
207         }
208         lockmgr(&devfs_rule_lock, LK_RELEASE);
209
210         return 0;
211 }
212
213
214 void *
215 devfs_rule_reset_node(struct devfs_node *node, void *unused)
216 {
217         /*
218          * Don't blindly unhide all devices, some, like unix98 pty masters,
219          * haven't been hidden by a rule.
220          */
221         if (node->flags & DEVFS_RULE_HIDDEN)
222                 node->flags &= ~(DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
223
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)) {
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
235         return NULL;
236 }
237
238 static void
239 devfs_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 }
256
257 void *
258 devfs_rule_check_apply(struct devfs_node *node, void *unused)
259 {
260         struct devfs_rule *rule;
261         struct mount *mp = node->mp;
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         }
269
270         TAILQ_FOREACH(rule, &devfs_rule_list, link) {
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                 /*
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                 /*
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] != '*') &&
292                         (strcmp(rule->mntpoint, mp->mnt_stat.f_mntonname)))
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)) ||
301                           (!(dev_dflags(node->d_dev) & rule->dev_type))) )
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
312                 if (rule->rule_cmd & DEVFS_RULE_HIDE) {
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                          */
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                         }
326                         node->flags |= (DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
327                 } else if (rule->rule_cmd & DEVFS_RULE_SHOW) {
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;
333                 } else if (rule->rule_cmd & DEVFS_RULE_LINK) {
334                         /*
335                          * This is a LINK rule, so we tell devfs to create
336                          * a link with the correct name to this node.
337                          */
338                         devfs_rule_create_link(node, rule);
339 #if 0
340                         devfs_alias_create(rule->linkname, node, 1);
341 #endif
342                 } else if (rule->rule_cmd & DEVFS_RULE_PERM) {
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;
351                 }
352         }
353
354         /* If we acquired the lock, we also get rid of it */
355         if (locked)
356                 lockmgr(&devfs_rule_lock, LK_RELEASE);
357
358         return NULL;
359 }
360
361
362 static int
363 devfs_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
380 #if 0
381         if (rule->rule_type & DEVFS_RULE_LINK)
382                 no_match = memcmp(name, node->d_dir.d_name, strlen(name));
383         else
384 #endif
385         no_match = devfs_WildCaseCmp(name, node->d_dir.d_name);
386
387         return !no_match;
388 }
389
390
391 static int
392 devfs_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) {
404                 devfs_debug(DEVFS_DEBUG_SHOW, "devfs_dev: can't do nonblocking access\n");
405                 return(ENODEV);
406         }
407
408         return 0;
409 }
410
411
412 static int
413 devfs_dev_close(struct dev_close_args *ap)
414 {
415         return 0;
416 }
417
418
419 static int
420 devfs_dev_ioctl(struct dev_ioctl_args *ap)
421 {
422         int error;
423         struct devfs_rule_ioctl *rule;
424
425         error = 0;
426         rule = (struct devfs_rule_ioctl *)ap->a_data;
427
428         switch(ap->a_cmd) {
429         case DEVFS_RULE_ADD:
430                 error = devfs_rule_insert(rule);
431                 break;
432
433         case DEVFS_RULE_APPLY:
434                 if (rule->mntpoint == NULL)
435                         error = EINVAL;
436                 else
437                         devfs_apply_rules(rule->mntpoint);
438                 break;
439
440         case DEVFS_RULE_CLEAR:
441                 error = devfs_rule_clear(rule);
442                 break;
443
444         case DEVFS_RULE_RESET:
445                 if (rule->mntpoint == NULL)
446                         error = EINVAL;
447                 else
448                         devfs_reset_rules(rule->mntpoint);
449                 break;
450
451         default:
452                 error = ENOTTY; /* Inappropriate ioctl for device */
453                 break;
454         }
455
456         return(error);
457 }
458
459
460 static void
461 devfs_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
480 static void
481 devfs_dev_uninit(void *unused)
482 {
483         /* XXX: destroy all rules first */
484     destroy_dev(devfs_dev);
485         objcache_destroy(devfs_rule_cache);
486 }
487
488
489 SYSINIT(devfsdev,SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_init,NULL)
490 SYSUNINIT(devfsdev, SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_uninit, NULL);
491