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