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