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