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