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