434336c4b47336075931debba6952ae2d55bcf08
[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 #if 0
51 static int WildCmp(const char *w, const char *s);
52 #endif
53 static int WildCaseCmp(const char *w, const char *s);
54 static int wildCmp(const char **mary, int d, const char *w, const char *s);
55 static int wildCaseCmp(const char **mary, int d, const char *w, const char *s);
56
57 static d_open_t      devfs_dev_open;
58 static d_close_t     devfs_dev_close;
59 static d_ioctl_t     devfs_dev_ioctl;
60
61 static struct devfs_rule *devfs_rule_alloc(struct devfs_rule_ioctl *);
62 static void devfs_rule_free(struct devfs_rule *);
63 static int devfs_rule_insert(struct devfs_rule_ioctl *);
64 static void devfs_rule_remove(struct devfs_rule *);
65 static int devfs_rule_clear(struct devfs_rule_ioctl *);
66 static void devfs_rule_create_link(struct devfs_node *, struct devfs_rule *);
67 static int devfs_rule_checkname(struct devfs_rule *, struct devfs_node *);
68
69 static struct objcache  *devfs_rule_cache;
70 static struct lock              devfs_rule_lock;
71
72 static struct objcache_malloc_args devfs_rule_malloc_args = {
73         sizeof(struct devfs_rule), M_DEVFS };
74
75 static cdev_t devfs_dev;
76 static struct devfs_rule_head devfs_rule_list =
77                 TAILQ_HEAD_INITIALIZER(devfs_rule_list);
78
79 static struct dev_ops devfs_dev_ops = {
80         { "devfs", 0, 0 },
81         .d_open = devfs_dev_open,
82         .d_close = devfs_dev_close,
83         .d_ioctl = devfs_dev_ioctl
84 };
85
86
87 static struct devfs_rule *
88 devfs_rule_alloc(struct devfs_rule_ioctl *templ)
89 {
90         struct devfs_rule *rule;
91         size_t len;
92
93         rule = objcache_get(devfs_rule_cache, M_WAITOK);
94         memset(rule, 0, sizeof(struct devfs_rule));
95
96         if (templ->mntpoint == NULL)
97                 goto error_out;
98                 /* NOTREACHED */
99
100         len = strlen(templ->mntpoint);
101         if (len == 0)
102                 goto error_out;
103                 /* NOTREACHED */
104
105         rule->mntpoint = kstrdup(templ->mntpoint, M_DEVFS);
106         rule->mntpointlen = len;
107
108         if (templ->rule_type & DEVFS_RULE_NAME) {
109                 if (templ->name == NULL)
110                         goto error_out;
111                         /* NOTREACHED */
112
113                 len = strlen(templ->name);
114                 if (len == 0)
115                         goto error_out;
116                         /* NOTREACHED */
117
118                 rule->name = kstrdup(templ->name, M_DEVFS);
119                 rule->namlen = len;
120         }
121
122         if (templ->rule_cmd & DEVFS_RULE_LINK) {
123                 if (templ->linkname == NULL)
124                         goto error_out;
125                         /* NOTREACHED */
126
127                 len = strlen(templ->linkname);
128                 if (len == 0)
129                         goto error_out;
130                         /* NOTREACHED */
131
132                 rule->linkname = kstrdup(templ->linkname, M_DEVFS);
133                 rule->linknamlen = len;
134         }
135
136         rule->rule_type = templ->rule_type;
137         rule->rule_cmd = templ->rule_cmd;
138         rule->dev_type = templ->dev_type;
139         rule->mode = templ->mode;
140         rule->uid = templ->uid;
141         rule->gid = templ->gid;
142
143         return rule;
144
145 error_out:
146         devfs_rule_free(rule);
147         return NULL;
148 }
149
150
151 static void
152 devfs_rule_free(struct devfs_rule *rule)
153 {
154         if (rule->mntpoint != NULL) {
155                 kfree(rule->mntpoint, M_DEVFS);
156         }
157
158         if (rule->name != NULL) {
159                 kfree(rule->name, M_DEVFS);
160         }
161
162         if (rule->linkname != NULL) {
163                 kfree(rule->linkname, M_DEVFS);
164         }
165         objcache_put(devfs_rule_cache, rule);
166 }
167
168
169 static int
170 devfs_rule_insert(struct devfs_rule_ioctl *templ)
171 {
172         struct devfs_rule *rule;
173
174         rule = devfs_rule_alloc(templ);
175         if (rule == NULL)
176                 return EINVAL;
177
178         lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
179         TAILQ_INSERT_TAIL(&devfs_rule_list, rule, link);
180         lockmgr(&devfs_rule_lock, LK_RELEASE);
181
182         return 0;
183 }
184
185
186 static void
187 devfs_rule_remove(struct devfs_rule *rule)
188 {
189         TAILQ_REMOVE(&devfs_rule_list, rule, link);
190         devfs_rule_free(rule);
191 }
192
193
194 static int
195 devfs_rule_clear(struct devfs_rule_ioctl *templ)
196 {
197         struct devfs_rule *rule1, *rule2;
198         size_t mntpointlen;
199
200         if (templ->mntpoint == NULL)
201                 return EINVAL;
202
203         mntpointlen = strlen(templ->mntpoint);
204         if (mntpointlen == 0)
205                 return EINVAL;
206
207         lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
208         TAILQ_FOREACH_MUTABLE(rule1, &devfs_rule_list, link, rule2) {
209                 if ((templ->mntpoint[0] == '*') ||
210                         ( (mntpointlen == rule1->mntpointlen) &&
211                           (!memcmp(templ->mntpoint, rule1->mntpoint, mntpointlen)) )) {
212                         devfs_rule_remove(rule1);
213                 }
214         }
215         lockmgr(&devfs_rule_lock, LK_RELEASE);
216
217         return 0;
218 }
219
220
221 void *
222 devfs_rule_reset_node(struct devfs_node *node, void *unused)
223 {
224         /*
225          * Don't blindly unhide all devices, some, like unix98 pty masters,
226          * haven't been hidden by a rule.
227          */
228         if (node->flags & DEVFS_RULE_HIDDEN)
229                 node->flags &= ~(DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
230
231         if ((node->node_type == Plink) && (node->flags & DEVFS_RULE_CREATED)) {
232                 KKASSERT(node->link_target);
233                 node->flags &= ~DEVFS_RULE_CREATED;
234                 --node->link_target->nlinks;
235                 devfs_gc(node);
236         } else if ((node->node_type == Pdev) && (node->d_dev)) {
237                 node->uid = node->d_dev->si_uid;
238                 node->gid = node->d_dev->si_gid;
239                 node->mode = node->d_dev->si_perms;
240         }
241
242         return NULL;
243 }
244
245 static void
246 devfs_rule_create_link(struct devfs_node *node, struct devfs_rule *rule)
247 {
248         size_t len = 0;
249         char *path = NULL;
250         char *name, name_buf[PATH_MAX], buf[PATH_MAX];
251
252         if (rule->name[rule->namlen-1] == '*') {
253                 devfs_resolve_name_path(rule->name, name_buf, &path, &name);
254                 len = strlen(name);
255                 --len;
256                 ksnprintf(buf, sizeof(buf), "%s%s",
257                     rule->linkname, node->d_dir.d_name+len);
258                 devfs_alias_create(buf, node, 1);
259         } else {
260                 devfs_alias_create(rule->linkname, node, 1);
261         }
262 }
263
264 void *
265 devfs_rule_check_apply(struct devfs_node *node, void *unused)
266 {
267         struct devfs_rule *rule;
268         struct mount *mp = node->mp;
269         int locked = 0;
270
271         /* Check if it is locked already. if not, we acquire the devfs lock */
272         if (!(lockstatus(&devfs_rule_lock, curthread)) == LK_EXCLUSIVE) {
273                 lockmgr(&devfs_rule_lock, LK_EXCLUSIVE);
274                 locked = 1;
275         }
276
277         TAILQ_FOREACH(rule, &devfs_rule_list, link) {
278                 /*
279                  * Skip this rule if it is only intended for jailed mount points
280                  * and the current mount point isn't jailed
281                  */
282                 if ((rule->rule_type & DEVFS_RULE_JAIL) &&
283                         (!(DEVFS_MNTDATA(mp)->jailed)) )
284                         continue;
285
286                 /*
287                  * Skip this rule if it is not intended for jailed mount points
288                  * and the current mount point is jailed.
289                  */
290                 if (!(rule->rule_type & DEVFS_RULE_JAIL) &&
291                         (DEVFS_MNTDATA(mp)->jailed))
292                     continue;
293
294                 /*
295                  * Skip this rule if the mount point specified in the rule doesn't
296                  * match the mount point of the node
297                  */
298                 if ((rule->mntpoint[0] != '*') &&
299                         (strcmp(rule->mntpoint, mp->mnt_stat.f_mntonname)))
300                         continue;
301
302                 /*
303                  * Skip this rule if this is a by-type rule and the device flags
304                  * don't match the specified device type in the rule
305                  */
306                 if ((rule->rule_type & DEVFS_RULE_TYPE) &&
307                         ( (rule->dev_type == 0) || (!dev_is_good(node->d_dev)) ||
308                           (!(dev_dflags(node->d_dev) & rule->dev_type))) )
309                         continue;
310
311                 /*
312                  * Skip this rule if this is a by-name rule and the node name
313                  * doesn't match the wildcard string in the rule
314                  */
315                 if ((rule->rule_type & DEVFS_RULE_NAME) &&
316                         (!devfs_rule_checkname(rule, node)) )
317                         continue;
318
319                 if (rule->rule_cmd & DEVFS_RULE_HIDE) {
320                         /*
321                          * If we should hide the device, we just apply the relevant
322                          * hide flag to the node and let devfs do the rest in the
323                          * vnops
324                          */
325                         if ((node->d_dir.d_namlen == 5) &&
326                                 (!memcmp(node->d_dir.d_name, "devfs", 5))) {
327                                 /*
328                                  * Magically avoid /dev/devfs from being hidden, so that one
329                                  * can still use the rule system even after a "* hide".
330                                  */
331                                  continue;
332                         }
333                         node->flags |= (DEVFS_HIDDEN | DEVFS_RULE_HIDDEN);
334                 } else if (rule->rule_cmd & DEVFS_RULE_SHOW) {
335                         /*
336                          * Show rule just means that the node should not be hidden, so
337                          * what we do is clear the hide flag from the node.
338                          */
339                         node->flags &= ~DEVFS_HIDDEN;
340                 } else if (rule->rule_cmd & DEVFS_RULE_LINK) {
341                         /*
342                          * This is a LINK rule, so we tell devfs to create
343                          * a link with the correct name to this node.
344                          */
345                         devfs_rule_create_link(node, rule);
346 #if 0
347                         devfs_alias_create(rule->linkname, node, 1);
348 #endif
349                 } else if (rule->rule_cmd & DEVFS_RULE_PERM) {
350                         /*
351                          * This is a normal ownership/permission rule. We
352                          * just apply the permissions and ownership and
353                          * we are done.
354                          */
355                         node->mode = rule->mode;
356                         node->uid = rule->uid;
357                         node->gid = rule->gid;
358                 }
359         }
360
361         /* If we acquired the lock, we also get rid of it */
362         if (locked)
363                 lockmgr(&devfs_rule_lock, LK_RELEASE);
364
365         return NULL;
366 }
367
368
369 static int
370 devfs_rule_checkname(struct devfs_rule *rule, struct devfs_node *node)
371 {
372         struct devfs_node *parent = DEVFS_MNTDATA(node->mp)->root_node;
373         char *path = NULL;
374         char *name, name_buf[PATH_MAX];
375         int no_match = 0;
376
377         devfs_resolve_name_path(rule->name, name_buf, &path, &name);
378         parent = devfs_resolve_or_create_path(parent, path, 0);
379
380         if (parent == NULL)
381                 return 0; /* no match */
382
383         /* Check if node is a child of the parent we found */
384         if (node->parent != parent)
385                 return 0; /* no match */
386
387 #if 0
388         if (rule->rule_type & DEVFS_RULE_LINK)
389                 no_match = memcmp(name, node->d_dir.d_name, strlen(name));
390         else
391 #endif
392         no_match = WildCaseCmp(name, node->d_dir.d_name);
393
394         return !no_match;
395 }
396
397
398 static int
399 devfs_dev_open(struct dev_open_args *ap)
400 {
401         /*
402          * Only allow read-write access.
403          */
404         if (((ap->a_oflags & FWRITE) == 0) || ((ap->a_oflags & FREAD) == 0))
405                 return(EPERM);
406
407         /*
408          * We don't allow nonblocking access.
409          */
410         if ((ap->a_oflags & O_NONBLOCK) != 0) {
411                 devfs_debug(DEVFS_DEBUG_SHOW, "devfs_dev: can't do nonblocking access\n");
412                 return(ENODEV);
413         }
414
415         return 0;
416 }
417
418
419 static int
420 devfs_dev_close(struct dev_close_args *ap)
421 {
422         return 0;
423 }
424
425
426 static int
427 devfs_dev_ioctl(struct dev_ioctl_args *ap)
428 {
429         int error;
430         struct devfs_rule_ioctl *rule;
431
432         error = 0;
433         rule = (struct devfs_rule_ioctl *)ap->a_data;
434
435         switch(ap->a_cmd) {
436         case DEVFS_RULE_ADD:
437                 error = devfs_rule_insert(rule);
438                 break;
439
440         case DEVFS_RULE_APPLY:
441                 if (rule->mntpoint == NULL)
442                         error = EINVAL;
443                 else
444                         devfs_apply_rules(rule->mntpoint);
445                 break;
446
447         case DEVFS_RULE_CLEAR:
448                 error = devfs_rule_clear(rule);
449                 break;
450
451         case DEVFS_RULE_RESET:
452                 if (rule->mntpoint == NULL)
453                         error = EINVAL;
454                 else
455                         devfs_reset_rules(rule->mntpoint);
456                 break;
457
458         default:
459                 error = ENOTTY; /* Inappropriate ioctl for device */
460                 break;
461         }
462
463         return(error);
464 }
465
466
467 static void
468 devfs_dev_init(void *unused)
469 {
470         lockinit(&devfs_rule_lock, "devfs_rule lock", 0, 0);
471
472     devfs_rule_cache = objcache_create("devfs-rule-cache", 0, 0,
473                         NULL, NULL, NULL,
474                         objcache_malloc_alloc,
475                         objcache_malloc_free,
476                         &devfs_rule_malloc_args );
477
478     devfs_dev = make_dev(&devfs_dev_ops,
479             0,
480             UID_ROOT,
481             GID_WHEEL,
482             0600,
483             "devfs");
484 }
485
486
487 static void
488 devfs_dev_uninit(void *unused)
489 {
490         /* XXX: destroy all rules first */
491     destroy_dev(devfs_dev);
492         objcache_destroy(devfs_rule_cache);
493 }
494
495
496 SYSINIT(devfsdev,SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_init,NULL)
497 SYSUNINIT(devfsdev, SI_SUB_DRIVERS,SI_ORDER_FIRST,devfs_dev_uninit, NULL);
498
499 #if 0
500
501 static int
502 WildCmp(const char *w, const char *s)
503 {
504     int i;
505     int c;
506     int slen = strlen(s);
507     const char **mary;
508
509     for (i = c = 0; w[i]; ++i) {
510         if (w[i] == '*')
511             ++c;
512     }
513     mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
514     for (i = 0; i < c; ++i)
515         mary[i] = s + slen;
516     i = wildCmp(mary, 0, w, s);
517     kfree(mary, M_DEVFS);
518     return(i);
519 }
520
521 #endif
522
523 static int
524 WildCaseCmp(const char *w, const char *s)
525 {
526     int i;
527     int c;
528     int slen = strlen(s);
529     const char **mary;
530
531     for (i = c = 0; w[i]; ++i) {
532         if (w[i] == '*')
533             ++c;
534     }
535     mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
536     for (i = 0; i < c; ++i)
537         mary[i] = s + slen;
538     i = wildCaseCmp(mary, 0, w, s);
539     kfree(mary, M_DEVFS);
540     return(i);
541 }
542
543 /*
544  * WildCmp() - compare wild string to sane string
545  *
546  *      Returns 0 on success, -1 on failure.
547  */
548 static int
549 wildCmp(const char **mary, int d, const char *w, const char *s)
550 {
551     int i;
552
553     /*
554      * skip fixed portion
555      */
556     for (;;) {
557         switch(*w) {
558         case '*':
559             /*
560              * optimize terminator
561              */
562             if (w[1] == 0)
563                 return(0);
564             if (w[1] != '?' && w[1] != '*') {
565                 /*
566                  * optimize * followed by non-wild
567                  */
568                 for (i = 0; s + i < mary[d]; ++i) {
569                     if (s[i] == w[1] && wildCmp(mary, d + 1, w + 1, s + i) == 0)
570                         return(0);
571                 }
572             } else {
573                 /*
574                  * less-optimal
575                  */
576                 for (i = 0; s + i < mary[d]; ++i) {
577                     if (wildCmp(mary, d + 1, w + 1, s + i) == 0)
578                         return(0);
579                 }
580             }
581             mary[d] = s;
582             return(-1);
583         case '?':
584             if (*s == 0)
585                 return(-1);
586             ++w;
587             ++s;
588             break;
589         default:
590             if (*w != *s)
591                 return(-1);
592             if (*w == 0)        /* terminator */
593                 return(0);
594             ++w;
595             ++s;
596             break;
597         }
598     }
599     /* not reached */
600     return(-1);
601 }
602
603
604 /*
605  * WildCaseCmp() - compare wild string to sane string, case insensitive
606  *
607  *      Returns 0 on success, -1 on failure.
608  */
609 static int
610 wildCaseCmp(const char **mary, int d, const char *w, const char *s)
611 {
612     int i;
613
614     /*
615      * skip fixed portion
616      */
617     for (;;) {
618         switch(*w) {
619         case '*':
620             /*
621              * optimize terminator
622              */
623             if (w[1] == 0)
624                 return(0);
625             if (w[1] != '?' && w[1] != '*') {
626                 /*
627                  * optimize * followed by non-wild
628                  */
629                 for (i = 0; s + i < mary[d]; ++i) {
630                     if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
631                         return(0);
632                 }
633             } else {
634                 /*
635                  * less-optimal
636                  */
637                 for (i = 0; s + i < mary[d]; ++i) {
638                     if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
639                         return(0);
640                 }
641             }
642             mary[d] = s;
643             return(-1);
644         case '?':
645             if (*s == 0)
646                 return(-1);
647             ++w;
648             ++s;
649             break;
650         default:
651             if (*w != *s) {
652 #define tolower(x)      ((x >= 'A' && x <= 'Z')?(x+('a'-'A')):(x))
653                 if (tolower(*w) != tolower(*s))
654                     return(-1);
655             }
656             if (*w == 0)        /* terminator */
657                 return(0);
658             ++w;
659             ++s;
660             break;
661         }
662     }
663     /* not reached */
664     return(-1);
665 }