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