summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordd <dd@FreeBSD.org>2002-07-17 01:46:48 +0000
committerdd <dd@FreeBSD.org>2002-07-17 01:46:48 +0000
commit9498a983a938cec96851b64642f0b62bba7d1827 (patch)
tree403633d67f5bcbe849c414892c2fea74d94f0b09
parentc57275f3471899132e94d39ef870d25599ec6f95 (diff)
downloadFreeBSD-src-9498a983a938cec96851b64642f0b62bba7d1827.zip
FreeBSD-src-9498a983a938cec96851b64642f0b62bba7d1827.tar.gz
Introduce the DEVFS "rule" subsystem. DEVFS rules permit the
administrator to define certain properties of new devfs nodes before they become visible to the userland. Both static (e.g., /dev/speaker) and dynamic (e.g., /dev/bpf*, some removable devices) nodes are supported. Each DEVFS mount may have a different ruleset assigned to it, permitting different policies to be implemented for things like jails. Approved by: phk
-rw-r--r--sbin/Makefile1
-rw-r--r--sbin/devfs/Makefile8
-rw-r--r--sbin/devfs/devfs.8326
-rw-r--r--sbin/devfs/devfs.c140
-rw-r--r--sbin/devfs/extern.h55
-rw-r--r--sbin/devfs/rule.c419
-rw-r--r--sys/conf/files1
-rw-r--r--sys/fs/devfs/devfs.h88
-rw-r--r--sys/fs/devfs/devfs_devs.c2
-rw-r--r--sys/fs/devfs/devfs_rule.c839
-rw-r--r--sys/fs/devfs/devfs_vfsops.c1
-rw-r--r--sys/fs/devfs/devfs_vnops.c20
12 files changed, 1899 insertions, 1 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
index b9c8473..1dd8fdc 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -12,6 +12,7 @@ SUBDIR= adjkerntz \
clri \
comcontrol \
conscontrol \
+ devfs \
dhclient \
disklabel \
dmesg \
diff --git a/sbin/devfs/Makefile b/sbin/devfs/Makefile
new file mode 100644
index 0000000..59c0178
--- /dev/null
+++ b/sbin/devfs/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= devfs
+SRCS= devfs.c rule.c
+MAN= devfs.8
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/sbin/devfs/devfs.8 b/sbin/devfs/devfs.8
new file mode 100644
index 0000000..48e63c6
--- /dev/null
+++ b/sbin/devfs/devfs.8
@@ -0,0 +1,326 @@
+.\"
+.\" Copyright (c) 2002 Dima Dorfman.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dt DEVFS 8
+.Dd July 1, 2002
+.Os
+.Sh NAME
+.Nm devfs
+.Nd "DEVFS control"
+.Sh SYNOPSIS
+.Nm
+.Op Fl m Ar mount-point
+.Cm keyword
+.Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an interface to manipulate properties of
+.Xr devfs 5
+mounts.
+.Pp
+The first keyword after the program name determines the context for
+the rest of the arguments.
+For example,
+most of the commands related to the rule subsystem must be preceded by the
+.Cm rule
+keyword.
+The following flags are common to all keywords:
+.Bl -tag -offset indent
+.It Fl m Ar mount-point
+Operate on
+.Ar mount-point ,
+which is expected to be a
+.Xr devfs 5
+mount.
+If this option is not specified,
+.Nm
+operates on
+.Pa /dev .
+.El
+.Sh RULE SUBSYSTEM
+The
+.Xr devfs 5
+rule subsystem provides a way for the administrator of a system to control
+the attributes of DEVFS nodes.
+.\" XXX devfs node? entry? what?
+Each DEVFS mount-point has a
+.Dq ruleset ,
+or a list of rules,
+associated with it.
+When a device driver creates a new node,
+all the rules in the ruleset associated with each mount-point are applied
+(see below) before the node becomes visible to the userland.
+This permits the administrator to change the properties,
+including the visibility,
+of certain nodes.
+For example, one might want to hide all disk nodes in a
+.Xr jail 2 Ns 's
+.Pa /dev .
+.Ss Rule Manipulation
+Rule manipulation commands follow the
+.Cm rule
+keyword.
+The following flags are common to all of the rule manipulation commands:
+.Bl -tag -offset indent
+.It Fl s Ar ruleset
+Operate on the ruleset with the number
+.Ar ruleset .
+If this is not specified,
+the commands operate on the ruleset currently associated with the
+specified mount-point.
+.El
+.Pp
+The following commands are recognized:
+.Bl -tag -offset indent
+.It Cm rule add Oo Ar rulenum Oc Ar rulespec
+Add the rule described by
+.Ar rulespec
+(defined below)
+to the ruleset.
+The rule has the number
+.Ar rulenum
+if it is explicitly specified;
+otherwise, the rule number is automatically determined by the kernel.
+.It Cm rule apply Ar rulenum | Ar rulespec
+Apply rule number
+.Ar rulenum
+or the rule described by
+.Ar rulespec
+to the mount-point.
+Rules that are "applied" have their conditions checked against all nodes
+in the mount-point, and the actions taken if they match.
+.It Cm rule applyset
+Apply all the rules in the ruleset to the mount-point
+(see above for the definition of "apply").
+.It Cm rule del Ar rulenum
+Delete rule number
+.Ar rulenum
+from the ruleset.
+.It Cm rule delset
+Delete all rules from the ruleset.
+.It Cm rule show Op Ar rulenum
+Display the rule number
+.Ar rulenum ,
+or all the rules in the ruleset.
+The output lines (one line per rule) are expected to be valid
+.Ar rulespec Ns s .
+.It Cm rule showsets
+Report the numbers of existing rulesets.
+.It Cm ruleset Ar ruleset
+Set ruleset number
+.Ar ruleset
+as the current ruleset for the mount-point.
+.El
+.Ss Rule Specification
+Rules have two parts: the conditions and the actions.
+The conditions determine which DEVFS nodes the rule matches,
+and the actions determine what should be done when a rule matches a node.
+For example, a rule can be written that sets the GID to
+.Li games
+for all devices with major number 53.
+.Pp
+The following conditions are recognized.
+Conditions are ANDed together when matching a device;
+if OR is desired, multiple rules can be written.
+.Bl -tag -offset indent
+.It Cm major Ar majdev
+Matches any node with a major number equal to
+.Ar majdev .
+.It Cm path Ar pattern
+Matches any node with a path that matches
+.Ar pattern .
+The latter is interpreted as a
+.Xr glob 3 Ns -style
+pattern.
+(Note: Pattern matching is currently unimplemented;
+the only wildcard recognized is an asterisk at the end of the string.
+This will be corrected in the future.)
+.It Cm type Ar devtype
+Matches any node that is of type
+.Ar devtype .
+Valid types are
+.Li disk , mem , tape
+and
+.Li tty .
+.El
+.Pp
+The following actions are recognized.
+Although there is no explicit delimiter between conditions and actions,
+they may not be intermixed.
+.Bl -tag -offset indent
+.It Cm group Ar gid
+Set the GID of the node to
+.Ar gid ,
+which may be a group name
+(looked up in
+.Pa /etc/group )
+or number.
+.It Cm hide
+Hide the node.
+Nodes may later be revived manually with
+.Xr mknod 8 ,
+or with the
+.Cm unhide
+action.
+.It Cm include Ar ruleset
+Apply all the rules in ruleset number
+.Ar ruleset
+to the node.
+This does not necessarily result in any changes to the node
+(e.g., if none of the rules in the included ruleset match).
+.It Cm mode Ar filemode
+Set the file mode to
+.Ar filemode ,
+which is interpreted in octal.
+.It Cm user Ar uid
+Set the UID to
+.Ar uid ,
+which may be a user name
+(looked up in
+.Pa /etc/passwd )
+or number.
+.It Cm unhide
+Unhide the node.
+.El
+.Ss Notes
+.Bl -bullet -offset indent
+.It
+Rulesets are created by the kernel at the first reference,
+and destroyed when the last reference disappears.
+E.g., a ruleset is created when a rule is added to it or when it is set
+as the current ruleset for a mount-point;
+a ruleset is destroyed when the last rule in it is deleted,
+and no other references to it exist
+(i.e., it is not included by any rules, and it is not the current ruleset
+for any mount-point).
+.It
+Ruleset number 0 is the default ruleset for all new mount-points.
+It is always empty, cannot be modified or deleted, and does not show up
+in the output of
+.Cm showsets .
+.It
+Rules and rulesets are unique to the entire system,
+not a particular mount-point.
+I.e., a
+.Cm showsets
+will return the same information regardless of the mount-point specified with
+.Fl m .
+The mount-point is only relevant when changing what its current ruleset is,
+or when using one of the apply commands.
+.El
+.Ss Examples
+When the system boots,
+the only ruleset that exists is ruleset number 0;
+since the latter may not be modified, we have to create another ruleset
+before adding rules.
+Note that since most of the following examples don't specify
+.Fl m ,
+the operations are performed on
+.Pa /dev
+(this only matters for things that might change the properties of nodes).
+.Pp
+.Dl devfs ruleset 10
+.Pp
+Specify that ruleset 10 should be the current ruleset for
+.Pa /dev
+(if it does not already exist, it is created).
+.Pp
+.Dl devfs rule add path speaker mode 666
+.Pp
+Add a rule that causes all nodes that have a path that matches
+"speaker"
+(this is only
+.Pa /dev/speaker )
+to have the file mode 666 (read and write for all).
+Note that if any such nodes already exist, their mode will not be changed
+unless this rule (or ruleset) is explicitly applied (see below).
+The mode
+.Em will
+be changed if the node is created
+.Em after
+the rule is added
+(e.g., the
+.Pa atspeaker
+module is loaded after the above rule is added).
+.Pp
+.Dl devfs rule applyset
+.Pp
+Apply all the rules in the current ruleset to all the existing nodes.
+E.g., if the above rule was added after
+.Pa /dev/speaker
+was created,
+this command will cause its file mode to be changed to 666,
+as rule rule prescribes.
+.Pp
+.Dl devfs rule add path "snp*" mode 660 group snoopers
+.Pp
+(Quoting the argument to
+.Cm path
+is often necessary to disable the shell's globbing features.)
+For all devices with a path that matches "snp*",
+set the file more to 660, and the GID to
+.Li snoopers .
+This permits users in the
+.Li snoopers
+group to use the
+.Xr snp 4
+devices.
+.Pp
+.Dl devfs rule -s 20 add major 53 group games
+.Pp
+Add a rule to ruleset number 20.
+Since this ruleset is not the current ruleset for any mount-points,
+this rule is never applied automatically (unless ruleset 20 becomes
+a current ruleset for some mount-point at a later time).
+However, it can be applied explicitly, as such:
+.Pp
+.Dl devfs -m /my/jail/dev rule -s 20 applyset
+.Pp
+This will apply all rules in ruleset number 20 to the DEVFS mount on
+.Pa /my/jail/dev .
+It doesn't matter that ruleset 20 is not the current ruleset for that
+mount-point; the rules are applied regardless.
+.Pp
+.Dl devfs rule apply hide
+.Pp
+Since this rule has no conditions, the action
+.Pq Cm hide
+will be applied to all nodes.
+Since hiding all nodes isn't very useful, we can undo like so:
+.Pp
+.Dl devfs rule apply unhide
+.Sh SEE ALSO
+.Xr jail 2 ,
+.Xr glob 3 ,
+.Xr devfs 5 ,
+.Xr chmod 8 ,
+.Xr chown 8 ,
+.Xr jail 8 ,
+.Xr mknod 8
+.Sh AUTHORS
+.An Dima Dorfman
diff --git a/sbin/devfs/devfs.c b/sbin/devfs/devfs.c
new file mode 100644
index 0000000..4622bb9
--- /dev/null
+++ b/sbin/devfs/devfs.c
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * DEVFS control.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int mpfd;
+
+static ctbl_t ctbl_main = {
+ { "rule", rule_main },
+ { "ruleset", ruleset_main },
+ { NULL, NULL }
+};
+
+int
+main(int ac, char **av)
+{
+ const char *mountpt;
+ struct cmd *c;
+ char ch;
+
+ mountpt = NULL;
+ while ((ch = getopt(ac, av, "m:")) != -1)
+ switch (ch) {
+ case 'm':
+ mountpt = optarg;
+ break;
+ default:
+ usage();
+ }
+ ac -= optind;
+ av += optind;
+ if (ac < 1)
+ usage();
+
+ if (mountpt == NULL)
+ mountpt = _PATH_DEV;
+ mpfd = open(mountpt, O_RDONLY);
+ if (mpfd == -1)
+ err(1, "open: %s", mountpt);
+
+ for (c = ctbl_main; c->name != NULL; ++c)
+ if (strcmp(c->name, av[0]) == 0)
+ exit((*c->handler)(ac, av));
+ errx(1, "unknown command: %s", av[0]);
+}
+
+/*
+ * Convert an integer to a "number" (ruleset numbers and rule numbers
+ * are 16-bit). If the conversion is successful, num contains the
+ * integer representation of s and 1 is returned; otherwise, 0 is
+ * returned and num is unchanged.
+ */
+int
+atonum(const char *s, uint16_t *num)
+{
+ unsigned long ul;
+ char *cp;
+
+ ul = strtoul(s, &cp, 10);
+ if (ul > UINT16_MAX || *cp != '\0')
+ return (0);
+ *num = (uint16_t)ul;
+ return (1);
+}
+
+/*
+ * Convert user input in ASCII to an integer.
+ */
+int
+eatoi(const char *s)
+{
+ char *cp;
+ long l;
+
+ l = strtol(s, &cp, 10);
+ if (l > INT_MAX || *cp != '\0')
+ errx(1, "error converting to integer: %s", s);
+ return ((int)l);
+}
+
+/*
+ * As atonum(), but the result of failure is death.
+ */
+uint16_t
+eatonum(const char *s)
+{
+ uint16_t num;
+
+ if (!atonum(s, &num))
+ errx(1, "error converting to number: %s", s); /* XXX clarify */
+ return (num);
+}
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "usage: devfs rule|ruleset arguments\n");
+ exit(1);
+}
diff --git a/sbin/devfs/extern.h b/sbin/devfs/extern.h
new file mode 100644
index 0000000..9814b15
--- /dev/null
+++ b/sbin/devfs/extern.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __DEVFS_H__
+#define __DEVFS_H__
+
+#include <fs/devfs/devfs.h>
+
+struct intstr {
+ const char *s;
+ int i;
+};
+
+typedef int (command_t)(int, char **);
+typedef struct cmd ctbl_t[];
+struct cmd {
+ const char *name;
+ command_t *handler;
+};
+
+command_t rule_main, ruleset_main;
+
+int atonum(const char *, uint16_t *);
+int eatoi(const char *);
+uint16_t eatonum(const char *);
+void usage(void) __dead2;
+
+extern int mpfd; /* Mount-point file descriptor. */
+
+#endif /* !__DEVFS_H__ */
diff --git a/sbin/devfs/rule.c b/sbin/devfs/rule.c
new file mode 100644
index 0000000..27ad2a6
--- /dev/null
+++ b/sbin/devfs/rule.c
@@ -0,0 +1,419 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Rule subsystem manipulation.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
+ devfs_rsnum rsnum);
+static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
+
+static command_t rule_add, rule_apply, rule_applyset;
+static command_t rule_del, rule_delset, rule_show, rule_showsets;
+
+static ctbl_t ctbl_rule = {
+ { "add", rule_add },
+ { "apply", rule_apply },
+ { "applyset", rule_applyset },
+ { "del", rule_del },
+ { "delset", rule_delset },
+ { "show", rule_show },
+ { "showsets", rule_showsets },
+ { NULL, NULL }
+};
+
+static struct intstr ist_type[] = {
+ { "disk", D_DISK },
+ { "mem", D_MEM },
+ { "tape", D_TAPE },
+ { "tty", D_TTY },
+ { NULL, -1 }
+};
+
+devfs_rsnum in_rsnum;
+
+int
+rule_main(int ac, char **av)
+{
+ struct cmd *c;
+ char ch;
+
+ setprogname("devfs rule");
+ optreset = optind = 1;
+ while ((ch = getopt(ac, av, "s:")) != -1)
+ switch (ch) {
+ case 's':
+ in_rsnum = eatonum(optarg);
+ break;
+ default:
+ usage();
+ }
+ ac -= optind;
+ av += optind;
+ if (ac < 1)
+ usage();
+
+ for (c = ctbl_rule; c->name != NULL; ++c)
+ if (strcmp(c->name, av[0]) == 0)
+ exit((*c->handler)(ac, av));
+ errx(1, "unknown command: %s", av[0]);
+}
+
+static int
+rule_add(int ac, char **av)
+{
+ struct devfs_rule dr;
+ int rv;
+
+ if (ac < 2)
+ usage();
+ rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
+ rv = ioctl(mpfd, DEVFSIO_RADD, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RADD");
+ return (0);
+}
+
+static int
+rule_apply(int ac __unused, char **av __unused)
+{
+ struct devfs_rule dr;
+ devfs_rnum rnum;
+ devfs_rid rid;
+ int rv;
+
+ if (ac < 2)
+ usage();
+ if (!atonum(av[1], &rnum)) {
+ rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
+ rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RAPPLY");
+ } else {
+ rid = mkrid(in_rsnum, rnum);
+ rv = ioctl(mpfd, DEVFSIO_RAPPLYID, &rid);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RAPPLYID");
+ }
+ return (0);
+}
+
+static int
+rule_applyset(int ac, char **av __unused)
+{
+ int rv;
+
+ if (ac != 1)
+ usage();
+ rv = ioctl(mpfd, DEVFSIO_SAPPLY, &in_rsnum);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_SAPPLY");
+ return (0);
+}
+
+static int
+rule_del(int ac __unused, char **av)
+{
+ devfs_rid rid;
+ int rv;
+
+ if (av[1] == NULL)
+ usage();
+ rid = mkrid(in_rsnum, eatoi(av[1]));
+ rv = ioctl(mpfd, DEVFSIO_RDEL, &rid);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RDEL");
+ return (0);
+}
+
+static int
+rule_delset(int ac, char **av __unused)
+{
+ struct devfs_rule dr;
+ int rv;
+
+ if (ac != 1)
+ usage();
+ memset(&dr, '\0', sizeof(dr));
+ dr.dr_magic = DEVFS_MAGIC;
+ dr.dr_id = mkrid(in_rsnum, 0);
+ while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1) {
+ rv = ioctl(mpfd, DEVFSIO_RDEL, &dr.dr_id);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RDEL");
+ }
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ return (0);
+}
+
+static int
+rule_show(int ac __unused, char **av)
+{
+ struct devfs_rule dr;
+ devfs_rnum rnum;
+ int rv;
+
+ memset(&dr, '\0', sizeof(dr));
+ dr.dr_magic = DEVFS_MAGIC;
+ if (av[1] != NULL) {
+ rnum = eatoi(av[1]);
+ dr.dr_id = mkrid(in_rsnum, rnum - 1);
+ rv = ioctl(mpfd, DEVFSIO_RGETNEXT, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ if (rid2rn(dr.dr_id) == rnum)
+ rulespec_outfp(stdout, &dr);
+ } else {
+ dr.dr_id = mkrid(in_rsnum, 0);
+ while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1)
+ rulespec_outfp(stdout, &dr);
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ }
+ return (0);
+}
+
+static int
+rule_showsets(int ac, char **av __unused)
+{
+ devfs_rsnum rsnum;
+
+ if (ac != 1)
+ usage();
+ rsnum = 0;
+ while (ioctl(mpfd, DEVFSIO_SGETNEXT, &rsnum) != -1)
+ printf("%d\n", rsnum);
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_SGETNEXT");
+ return (0);
+}
+
+int
+ruleset_main(int ac, char **av)
+{
+ devfs_rsnum rsnum;
+ int rv;
+
+ setprogname("devfs ruleset");
+ if (ac < 2)
+ usage();
+ rsnum = eatonum(av[1]);
+ rv = ioctl(mpfd, DEVFSIO_SUSE, &rsnum);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_SUSE");
+ return (0);
+}
+
+
+/*
+ * Construct a /struct devfs_rule/ from ac and av.
+ */
+static void
+rulespec_intok(struct devfs_rule *dr, int ac __unused, char **av,
+ devfs_rsnum rsnum)
+{
+ struct intstr *is;
+ struct passwd *pw;
+ struct group *gr;
+ devfs_rnum rnum;
+ char *cp;
+ long l;
+
+ memset(dr, '\0', sizeof(*dr));
+
+ /*
+ * We don't maintain ac hereinafter.
+ */
+ if (av[0] == NULL)
+ errx(1, "unexpected end of rulespec");
+
+ /* If the first argument is an integer, treat it as a rule number. */
+ if (!atonum(av[0], &rnum))
+ rnum = 0; /* auto-number */
+ else
+ ++av;
+
+ /*
+ * These aren't table-driven since that would result in more
+ * tiny functions than I care to deal with.
+ */
+ for (;;) {
+ if (av[0] == NULL)
+ break;
+ else if (strcmp(av[0], "type") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for type");
+ for (is = ist_type; is->s != NULL; ++is)
+ if (strcmp(av[1], is->s) == 0) {
+ dr->dr_dswflags |= is->i;
+ break;
+ }
+ if (is->s == NULL)
+ errx(1, "unknown type: %s", av[1]);
+ dr->dr_icond |= DRC_DSWFLAGS;
+ av += 2;
+ } else if (strcmp(av[0], "path") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for path");
+ if (strlcpy(dr->dr_pathptrn, av[1], DEVFS_MAXPTRNLEN)
+ >= DEVFS_MAXPTRNLEN)
+ warnx("pattern specified too long; truncated");
+ dr->dr_icond |= DRC_PATHPTRN;
+ av += 2;
+ } else if (strcmp(av[0], "major") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for major");
+ dr->dr_major = eatoi(av[1]);
+ dr->dr_icond |= DRC_MAJOR;
+ av += 2;
+ } else
+ break;
+ }
+ for (;;) {
+ if (av[0] == NULL)
+ break;
+ else if (strcmp(av[0], "hide") == 0) {
+ dr->dr_iacts |= DRA_BACTS;
+ dr->dr_bacts |= DRB_HIDE;
+ ++av;
+ } else if (strcmp(av[0], "unhide") == 0) {
+ dr->dr_iacts |= DRA_BACTS;
+ dr->dr_bacts |= DRB_UNHIDE;
+ ++av;
+ } else if (strcmp(av[0], "user") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for user");
+ dr->dr_iacts |= DRA_UID;
+ pw = getpwnam(av[1]);
+ if (pw != NULL)
+ dr->dr_uid = pw->pw_uid;
+ else
+ dr->dr_uid = eatoi(av[1]); /* XXX overflow */
+ av += 2;
+ } else if (strcmp(av[0], "group") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for group");
+ dr->dr_iacts |= DRA_GID;
+ gr = getgrnam(av[1]);
+ if (gr != NULL)
+ dr->dr_gid = gr->gr_gid;
+ else
+ dr->dr_gid = eatoi(av[1]); /* XXX overflow */
+ av += 2;
+ } else if (strcmp(av[0], "mode") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for mode");
+ dr->dr_iacts |= DRA_MODE;
+ l = strtol(av[1], &cp, 8);
+ if (l > (1 << (sizeof(dr->dr_mode) * 8)) - 1 ||
+ *cp != '\0')
+ errx(1, "invalid mode: %s", av[1]);
+ dr->dr_mode = l;
+ av += 2;
+ } else if (strcmp(av[0], "include") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for include");
+ dr->dr_iacts |= DRA_INCSET;
+ dr->dr_incset = eatonum(av[1]);
+ av += 2;
+ } else
+ errx(1, "unknown argument: %s", av[0]);
+ }
+
+ dr->dr_id = mkrid(rsnum, rnum);
+ dr->dr_magic = DEVFS_MAGIC;
+}
+
+/*
+ * Write a human-readable (and machine-parsable, by rulespec_in*())
+ * representation of dr to bufp. *bufp should be free(3)'d when the
+ * caller is finished with it.
+ */
+static void
+rulespec_outfp(FILE *fp, struct devfs_rule *dr)
+{
+ struct intstr *is;
+ struct passwd *pw;
+ struct group *gr;
+
+ fprintf(fp, "%d", rid2rn(dr->dr_id));
+
+ if (dr->dr_icond & DRC_DSWFLAGS)
+ for (is = ist_type; is->s != NULL; ++is)
+ if (dr->dr_dswflags & is->i)
+ fprintf(fp, " type %s", is->s);
+ if (dr->dr_icond & DRC_PATHPTRN)
+ fprintf(fp, " path %s", dr->dr_pathptrn);
+ if (dr->dr_icond & DRC_MAJOR)
+ fprintf(fp, " major %d", dr->dr_major);
+
+ if (dr->dr_iacts & DRA_BACTS) {
+ if (dr->dr_bacts & DRB_HIDE)
+ fprintf(fp, " hide");
+ if (dr->dr_bacts & DRB_UNHIDE)
+ fprintf(fp, " unhide");
+ }
+ if (dr->dr_iacts & DRA_UID) {
+ pw = getpwuid(dr->dr_uid);
+ if (pw == NULL)
+ fprintf(fp, " user %d", dr->dr_uid);
+ else
+ fprintf(fp, " user %s", pw->pw_name);
+ }
+ if (dr->dr_iacts & DRA_GID) {
+ gr = getgrgid(dr->dr_gid);
+ if (gr == NULL)
+ fprintf(fp, " group %d", dr->dr_gid);
+ else
+ fprintf(fp, " group %s", gr->gr_name);
+ }
+ if (dr->dr_iacts & DRA_MODE)
+ fprintf(fp, " mode %o", dr->dr_mode);
+ if (dr->dr_iacts & DRA_INCSET)
+ fprintf(fp, " include %d", dr->dr_incset);
+
+ fprintf(fp, "\n");
+}
diff --git a/sys/conf/files b/sys/conf/files
index 97ae16e..7a596ce 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -677,6 +677,7 @@ dev/xe/if_xe_pccard.c optional xe card
dev/xe/if_xe_pccard.c optional xe pccard
fs/deadfs/dead_vnops.c standard
fs/devfs/devfs_devs.c standard
+fs/devfs/devfs_rule.c standard
fs/devfs/devfs_vfsops.c standard
fs/devfs/devfs_vnops.c standard
fs/fdescfs/fdesc_vfsops.c optional fdescfs
diff --git a/sys/fs/devfs/devfs.h b/sys/fs/devfs/devfs.h
index 137797f..2f0dc76 100644
--- a/sys/fs/devfs/devfs.h
+++ b/sys/fs/devfs/devfs.h
@@ -3,6 +3,8 @@
* The Regents of the University of California. All rights reserved.
* Copyright (c) 2000
* Poul-Henning Kamp. All rights reserved.
+ * Copyright (c) 2002
+ * Dima Dorfman. All rights reserved.
*
* This code is derived from software donated to Berkeley by
* Jan-Simon Pendry.
@@ -37,7 +39,86 @@
#ifndef _FS_DEVFS_DEVFS_H_
#define _FS_DEVFS_DEVFS_H_
-#ifdef _KERNEL /* No userland stuff in here... */
+#define DEVFS_MAGIC 0xdb0a087a
+
+/*
+ * Identifiers. The ruleset and rule numbers are 16-bit values. The
+ * "rule ID" is a combination of the ruleset and rule number; it
+ * should be able to univocally describe a rule in the system. In
+ * this implementation, the upper 16 bits of the rule ID is the
+ * ruleset number; the lower 16 bits, the rule number within the
+ * aforementioned ruleset.
+ */
+typedef uint16_t devfs_rnum;
+typedef uint16_t devfs_rsnum;
+typedef uint32_t devfs_rid;
+
+/*
+ * Identifier manipulators.
+ */
+#define rid2rsn(rid) ((rid) >> 16)
+#define rid2rn(rid) ((rid) & 0xffff)
+#define mkrid(rsn, rn) ((rn) | ((rsn) << 16))
+
+/*
+ * Plain DEVFS rule. This gets shared between kernel and userland
+ * verbatim, so it shouldn't contain any pointers or other kernel- or
+ * userland-specific values.
+ */
+struct devfs_rule {
+ uint32_t dr_magic; /* Magic number. */
+ devfs_rid dr_id; /* Identifier. */
+
+ /*
+ * Conditions under which this rule should be applied. These
+ * are ANDed together since OR can be simulated by using
+ * multiple rules. dr_icond determines which of the other
+ * variables we should process.
+ */
+ int dr_icond;
+#define DRC_DSWFLAGS 0x001
+#define DRC_PATHPTRN 0x002
+#define DRC_MAJOR 0x004
+ int dr_dswflags; /* cdevsw flags to match. */
+#define DEVFS_MAXPTRNLEN 200
+ char dr_pathptrn[DEVFS_MAXPTRNLEN]; /* Pattern to match path. */
+ int dr_major; /* Device major number. */
+
+ /*
+ * Things to change. dr_iacts determines which of the other
+ * variables we should process.
+ */
+ int dr_iacts;
+#define DRA_BACTS 0x001
+#define DRA_UID 0x002
+#define DRA_GID 0x004
+#define DRA_MODE 0x008
+#define DRA_INCSET 0x010
+ int dr_bacts; /* Boolean (on/off) action. */
+#define DRB_HIDE 0x001 /* Hide entry (DE_WHITEOUT). */
+#define DRB_UNHIDE 0x002 /* Unhide entry. */
+ uid_t dr_uid;
+ gid_t dr_gid;
+ mode_t dr_mode;
+ devfs_rsnum dr_incset; /* Included ruleset. */
+};
+
+/*
+ * Rule-related ioctls.
+ */
+#define DEVFSIO_RADD _IOWR('D', 0, struct devfs_rule)
+#define DEVFSIO_RDEL _IOW('D', 1, devfs_rid)
+#define DEVFSIO_RAPPLY _IOW('D', 2, struct devfs_rule)
+#define DEVFSIO_RAPPLYID _IOW('D', 3, devfs_rid)
+#define DEVFSIO_RGETNEXT _IOWR('D', 4, struct devfs_rule)
+
+#define DEVFSIO_SUSE _IOW('D', 10, devfs_rsnum)
+#define DEVFSIO_SAPPLY _IOW('D', 11, devfs_rsnum)
+#define DEVFSIO_SGETNEXT _IOWR('D', 12, devfs_rsnum)
+
+/* XXX: DEVFSIO_RS_GET_INFO for refcount, active if any, etc. */
+
+#ifdef _KERNEL
/*
* These are default sizes for the DEVFS inode table and the overflow
@@ -94,6 +175,7 @@ struct devfs_mount {
struct devfs_dirent **dm_overflow;
int dm_inode;
struct lock dm_lock;
+ devfs_rsnum dm_ruleset;
};
/*
@@ -106,6 +188,10 @@ struct devfs_mount {
extern vop_t **devfs_vnodeop_p;
extern vop_t **devfs_specop_p;
+void devfs_rules_apply(struct devfs_mount *dm, struct devfs_dirent *de);
+void devfs_rules_init(void);
+int devfs_rules_ioctl(struct mount *mp, int cmd, caddr_t data, struct thread *td);
+void devfs_rules_newmount(struct devfs_mount *dm, struct thread *td);
int devfs_allocv (struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct thread *td);
dev_t *devfs_itod (int inode);
struct devfs_dirent **devfs_itode (struct devfs_mount *dm, int inode);
diff --git a/sys/fs/devfs/devfs_devs.c b/sys/fs/devfs/devfs_devs.c
index ef9827c..fd378b8 100644
--- a/sys/fs/devfs/devfs_devs.c
+++ b/sys/fs/devfs/devfs_devs.c
@@ -352,6 +352,7 @@ devfs_populate(struct devfs_mount *dm)
}
*dep = de;
de->de_dir = dd;
+ devfs_rules_apply(dm, de);
TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
#if 0
printf("Add ino%d %s\n", i, dev->si_name);
@@ -435,6 +436,7 @@ devfs_init(void *junk)
devfs_create_hook = devfs_create;
devfs_destroy_hook = devfs_destroy;
devfs_present = 1;
+ devfs_rules_init();
}
SYSINIT(devfs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_init, NULL);
diff --git a/sys/fs/devfs/devfs_rule.c b/sys/fs/devfs/devfs_rule.c
new file mode 100644
index 0000000..3f19da2
--- /dev/null
+++ b/sys/fs/devfs/devfs_rule.c
@@ -0,0 +1,839 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * DEVFS ruleset implementation.
+ *
+ * A note on terminology: To "run" a rule on a dirent is to take the
+ * prescribed action; to "apply" a rule is to check whether it matches
+ * a dirent and run if if it does.
+ *
+ * A note on locking: Only foreign entry points (non-static functions)
+ * should deal with locking. Everything else assumes we already hold
+ * the required kind of lock.
+ *
+ * A note on namespace: devfs_rules_* are the non-static functions for
+ * the entire "ruleset" subsystem, devfs_rule_* are the static
+ * functions that operate on rules, and devfs_ruleset_* are the static
+ * functions that operate on rulesets. The line between the last two
+ * isn't always clear, but the guideline is still useful.
+ *
+ * A note on "special" identifiers: Ruleset 0 is the NULL, or empty,
+ * ruleset; it cannot be deleted or changed in any way. This may be
+ * assumed inside the code; e.g., a ruleset of 0 may be interpeted to
+ * mean "no ruleset". The interpretation of rule 0 is
+ * command-dependent, but in no case is there a real rule with number
+ * 0.
+ *
+ * A note on errno codes: To make it easier for the userland to tell
+ * what went wrong, we sometimes use errno codes that are not entirely
+ * appropriate for the error but that would be less ambiguous than the
+ * appropriate "generic" code. For example, when we can't find a
+ * ruleset, we return ESRCH instead of ENOENT (except in
+ * DEVFSIO_{R,S}GETNEXT, where a nonexistent ruleset means "end of
+ * list", and the userland expects ENOENT to be this indicator); this
+ * way, when an operation fails, it's clear that what couldn't be
+ * found is a ruleset and not a rule (well, it's clear to those who
+ * know the convention).
+ */
+
+#include "opt_devfs.h"
+#ifndef NODEVFS
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/ioccom.h>
+
+#include <fs/devfs/devfs.h>
+
+
+/*
+ * Kernel version of devfs_rule.
+ */
+struct devfs_krule {
+ SLIST_ENTRY(devfs_krule) dk_list;
+ struct devfs_ruleset *dk_ruleset;
+ struct devfs_rule dk_rule;
+};
+
+/*
+ * Structure to describe a ruleset.
+ */
+struct devfs_ruleset {
+ SLIST_ENTRY(devfs_ruleset) ds_list;
+ devfs_rsnum ds_number;
+ SLIST_HEAD(, devfs_krule) ds_rules;
+ int ds_refcount;
+ int ds_flags;
+#define DS_IMMUTABLE 0x001
+};
+
+static devfs_rid devfs_rid_input(devfs_rid rid, struct devfs_mount *dm);
+
+static void devfs_rule_applyde(struct devfs_krule *dk,struct devfs_dirent *de);
+static void devfs_rule_applyde_recursive(struct devfs_krule *dk,
+ struct devfs_dirent *de);
+static void devfs_rule_applydm(struct devfs_krule *dk, struct devfs_mount *dm);
+static int devfs_rule_autonumber(struct devfs_ruleset *ds, devfs_rnum *rnp);
+static struct devfs_krule *devfs_rule_byid(devfs_rid rid);
+static int devfs_rule_delete(struct devfs_krule **dkp);
+static dev_t devfs_rule_getdev(struct devfs_dirent *de);
+static int devfs_rule_input(struct devfs_rule *dr, struct devfs_mount *dm);
+static int devfs_rule_insert(struct devfs_rule *dr);
+static int devfs_rule_match(struct devfs_krule *dk, struct devfs_dirent *de);
+static int devfs_rule_matchpath(struct devfs_krule *dk,
+ struct devfs_dirent *de);
+static void devfs_rule_run(struct devfs_krule *dk, struct devfs_dirent *de);
+
+static void devfs_ruleset_applyde(struct devfs_ruleset *ds,
+ struct devfs_dirent *de);
+static void devfs_ruleset_applydm(struct devfs_ruleset *ds,
+ struct devfs_mount *dm);
+static struct devfs_ruleset *devfs_ruleset_bynum(devfs_rsnum rsnum);
+static struct devfs_ruleset *devfs_ruleset_create(devfs_rsnum rsnum);
+static void devfs_ruleset_destroy(struct devfs_ruleset **dsp);
+static void devfs_ruleset_reap(struct devfs_ruleset **dsp);
+static int devfs_ruleset_use(devfs_rsnum rsnum, struct devfs_mount *dm);
+
+static SLIST_HEAD(, devfs_ruleset) devfs_rulesets;
+
+/*
+ * Called to apply the proper rules for de before the latter can be
+ * exposed to the userland. This should be called with an exclusive
+ * lock on dm in case we need to run anything.
+ */
+void
+devfs_rules_apply(struct devfs_mount *dm, struct devfs_dirent *de)
+{
+ struct devfs_ruleset *ds;
+
+ ds = devfs_ruleset_bynum(dm->dm_ruleset);
+ KASSERT(ds != NULL, ("mount-point has NULL ruleset"));
+ devfs_ruleset_applyde(ds, de);
+}
+
+/*
+ * Rule subsystem SYSINIT hook.
+ */
+void
+devfs_rules_init(void)
+{
+ struct devfs_ruleset *ds;
+
+ SLIST_INIT(&devfs_rulesets);
+
+ ds = devfs_ruleset_create(0);
+ ds->ds_flags |= DS_IMMUTABLE;
+ ds->ds_refcount = 1; /* Prevent reaping. */
+}
+
+/*
+ * Rule subsystem ioctl hook.
+ */
+int
+devfs_rules_ioctl(struct mount *mp, int cmd, caddr_t data, struct thread *td)
+{
+ struct devfs_mount *dm = VFSTODEVFS(mp);
+ struct devfs_ruleset *ds;
+ struct devfs_krule *dk;
+ struct devfs_rule *dr;
+ devfs_rsnum rsnum;
+ devfs_rnum rnum;
+ devfs_rid rid;
+ int error;
+
+ /*
+ * XXX: This returns an error regardless of whether we
+ * actually support the cmd or not.
+ */
+ error = suser(td);
+ if (error != 0)
+ return (error);
+
+ lockmgr(&dm->dm_lock, LK_SHARED, 0, td);
+
+ switch (cmd) {
+ case DEVFSIO_RADD:
+ dr = (struct devfs_rule *)data;
+ error = devfs_rule_input(dr, dm);
+ if (error != 0)
+ goto out;
+ dk = devfs_rule_byid(dr->dr_id);
+ if (dk != NULL) {
+ error = EEXIST;
+ goto out;
+ }
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, td);
+ error = devfs_rule_insert(dr);
+ break;
+ case DEVFSIO_RAPPLY:
+ dr = (struct devfs_rule *)data;
+ error = devfs_rule_input(dr, dm);
+ if (error != 0)
+ goto out;
+
+ /*
+ * This is one of many possible hackish
+ * implementations. The primary contender is an
+ * implementation where the rule we read in is
+ * temporarily inserted into some ruleset, perhaps
+ * with a hypothetical DRO_NOAUTO flag so that it
+ * doesn't get used where it isn't intended, and
+ * applied in the normal way. This can be done in the
+ * userland (DEVFSIO_ADD, DEVFSIO_APPLYID,
+ * DEVFSIO_DEL) or in the kernel; either way it breaks
+ * some corner case assumptions in other parts of the
+ * code (not that this implementation doesn't do
+ * that).
+ */
+ if (dr->dr_iacts & DRA_INCSET &&
+ devfs_ruleset_bynum(dr->dr_incset) == NULL) {
+ error = ESRCH;
+ goto out;
+ }
+ dk = malloc(sizeof(*dk), M_TEMP, M_WAITOK | M_ZERO);
+ memcpy(&dk->dk_rule, dr, sizeof(*dr));
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, td);
+ devfs_rule_applydm(dk, dm);
+ lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, td);
+ free(dk, M_TEMP);
+ error = 0;
+ break;
+ case DEVFSIO_RAPPLYID:
+ rid = *(devfs_rid *)data;
+ rid = devfs_rid_input(rid, dm);
+ dk = devfs_rule_byid(rid);
+ if (dk == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, td);
+ devfs_rule_applydm(dk, dm);
+ error = 0;
+ break;
+ case DEVFSIO_RDEL:
+ rid = *(devfs_rid *)data;
+ rid = devfs_rid_input(rid, dm);
+ dk = devfs_rule_byid(rid);
+ if (dk == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ ds = dk->dk_ruleset;
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, td);
+ error = devfs_rule_delete(&dk);
+ devfs_ruleset_reap(&ds);
+ break;
+ case DEVFSIO_RGETNEXT:
+ dr = (struct devfs_rule *)data;
+ error = devfs_rule_input(dr, dm);
+ if (error != 0)
+ goto out;
+ /*
+ * We can't use devfs_rule_byid() here since that
+ * requires the rule specified to exist, but we want
+ * getnext(N) to work whether there is a rule N or not
+ * (specifically, getnext(0) must work, but we should
+ * never have a rule 0 since the add command
+ * interprets 0 to mean "auto-number").
+ */
+ ds = devfs_ruleset_bynum(rid2rsn(dr->dr_id));
+ if (ds == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ rnum = rid2rn(dr->dr_id);
+ SLIST_FOREACH(dk, &ds->ds_rules, dk_list) {
+ if (rid2rn(dk->dk_rule.dr_id) > rnum)
+ break;
+ }
+ if (dk == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ memcpy(dr, &dk->dk_rule, sizeof(*dr));
+ error = 0;
+ break;
+ case DEVFSIO_SUSE:
+ rsnum = *(devfs_rsnum *)data;
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, td);
+ error = devfs_ruleset_use(rsnum, dm);
+ break;
+ case DEVFSIO_SAPPLY:
+ rsnum = *(devfs_rsnum *)data;
+ rsnum = rid2rsn(devfs_rid_input(mkrid(rsnum, 0), dm));
+ ds = devfs_ruleset_bynum(rsnum);
+ if (ds == NULL) {
+ error = ESRCH;
+ goto out;
+ }
+ lockmgr(&dm->dm_lock, LK_UPGRADE, 0, td);
+ devfs_ruleset_applydm(ds, dm);
+ error = 0;
+ break;
+ case DEVFSIO_SGETNEXT:
+ rsnum = *(devfs_rsnum *)data;
+ SLIST_FOREACH(ds, &devfs_rulesets, ds_list) {
+ if (ds->ds_number > rsnum)
+ break;
+ }
+ if (ds == NULL)
+ error = ENOENT;
+ else {
+ *(devfs_rsnum *)data = ds->ds_number;
+ error = 0;
+ }
+ break;
+ default:
+ error = ENOIOCTL;
+ break;
+ }
+
+out:
+ lockmgr(&dm->dm_lock, LK_RELEASE, 0, td);
+ return (error);
+}
+
+/*
+ * Called to initialize dm_ruleset when there is a new mount-point.
+ */
+void
+devfs_rules_newmount(struct devfs_mount *dm, struct thread *td)
+{
+ struct devfs_ruleset *ds;
+
+ lockmgr(&dm->dm_lock, LK_EXCLUSIVE, 0, td);
+ /*
+ * We can't use devfs_ruleset_use() since it will try to
+ * decrement the refcount for the old ruleset, and there is no
+ * old ruleset. Making some value of ds_ruleset "special" to
+ * mean "don't decrement refcount" is uglier than this.
+ */
+ ds = devfs_ruleset_bynum(0);
+ KASSERT(ds != NULL, ("no ruleset 0"));
+ ++ds->ds_refcount;
+ dm->dm_ruleset = 0;
+ lockmgr(&dm->dm_lock, LK_RELEASE, 0, td);
+}
+
+/*
+ * Adjust the rule identifier to use the ruleset of dm if one isn't
+ * explicitly specified.
+ *
+ * Note that after this operation, rid2rsn(rid) might still be 0, and
+ * that's okay; ruleset 0 is a valid ruleset, but when it's read in
+ * from the userland, it means "current ruleset for this mount-point".
+ */
+static devfs_rid
+devfs_rid_input(devfs_rid rid, struct devfs_mount *dm)
+{
+
+ if (rid2rsn(rid) == 0)
+ return (mkrid(dm->dm_ruleset, rid2rn(rid)));
+ else
+ return (rid);
+}
+
+/*
+ * Apply dk to de.
+ */
+static void
+devfs_rule_applyde(struct devfs_krule *dk, struct devfs_dirent *de)
+{
+
+ if (devfs_rule_match(dk, de))
+ devfs_rule_run(dk, de);
+}
+
+/*
+ * Apply dk to de and everything under de.
+ *
+ * XXX: This method needs a function call for every nested
+ * subdirectory in a devfs mount. If we plan to have many of these,
+ * we might eventually run out of kernel stack space.
+ */
+static void
+devfs_rule_applyde_recursive(struct devfs_krule *dk, struct devfs_dirent *de)
+{
+ struct devfs_dirent *de2;
+
+ /* XXX: Should we apply to ourselves first or last? Does it matter? */
+ TAILQ_FOREACH(de2, &de->de_dlist, de_list) {
+ devfs_rule_applyde_recursive(dk, de2);
+ }
+ devfs_rule_applyde(dk, de);
+}
+
+/*
+ * Apply dk to all entires in dm.
+ */
+static void
+devfs_rule_applydm(struct devfs_krule *dk, struct devfs_mount *dm)
+{
+
+ devfs_rule_applyde_recursive(dk, dm->dm_basedir);
+}
+
+/*
+ * Automatically select a number for a new rule in ds, and write the
+ * result into rnump.
+ */
+static int
+devfs_rule_autonumber(struct devfs_ruleset *ds, devfs_rnum *rnump)
+{
+ struct devfs_krule *dk;
+
+ /* Find the last rule. */
+ SLIST_FOREACH(dk, &ds->ds_rules, dk_list) {
+ if (SLIST_NEXT(dk, dk_list) == NULL)
+ break;
+ }
+ if (dk == NULL)
+ *rnump = 100;
+ else {
+ *rnump = rid2rn(dk->dk_rule.dr_id) + 100;
+ /* Detect overflow. */
+ if (*rnump < rid2rn(dk->dk_rule.dr_id))
+ return (ERANGE);
+ }
+ KASSERT(devfs_rule_byid(mkrid(ds->ds_number, *rnump)) == NULL,
+ ("autonumbering resulted in an already existing rule"));
+ return (0);
+}
+
+/*
+ * Find a krule by id.
+ */
+static struct devfs_krule *
+devfs_rule_byid(devfs_rid rid)
+{
+ struct devfs_ruleset *ds;
+ struct devfs_krule *dk;
+ devfs_rnum rn;
+
+ rn = rid2rn(rid);
+ ds = devfs_ruleset_bynum(rid2rsn(rid));
+ if (ds == NULL)
+ return (NULL);
+ SLIST_FOREACH(dk, &ds->ds_rules, dk_list) {
+ if (rid2rn(dk->dk_rule.dr_id) == rn)
+ return (dk);
+ else if (rid2rn(dk->dk_rule.dr_id) > rn)
+ break;
+ }
+ return (NULL);
+}
+
+/*
+ * Remove dkp from any lists it may be on and remove memory associated
+ * with it.
+ */
+static int
+devfs_rule_delete(struct devfs_krule **dkp)
+{
+ struct devfs_krule *dk = *dkp;
+ struct devfs_ruleset *ds;
+
+ if (dk->dk_rule.dr_iacts & DRA_INCSET) {
+ ds = devfs_ruleset_bynum(dk->dk_rule.dr_incset);
+ KASSERT(ds != NULL, ("DRA_INCSET but bad dr_incset"));
+ --ds->ds_refcount;
+ devfs_ruleset_reap(&ds);
+ }
+ SLIST_REMOVE(&dk->dk_ruleset->ds_rules, dk, devfs_krule, dk_list);
+ free(dk, M_DEVFS);
+ *dkp = NULL;
+ return (0);
+}
+
+/*
+ * Get a dev_t corresponding to de so we can try to match rules based
+ * on it. If this routine returns NULL, there is no dev_t associated
+ * with the dirent (symlinks and directories don't have dev_ts), and
+ * the caller should assume that any critera dependent on a dev_t
+ * don't match.
+ */
+static dev_t
+devfs_rule_getdev(struct devfs_dirent *de)
+{
+ dev_t *devp, dev;
+
+ devp = devfs_itod(de->de_inode);
+ if (devp != NULL)
+ dev = *devp;
+ else
+ dev = NULL;
+ /* If we think this dirent should have a dev_t, alert the user. */
+ if (dev == NULL && de->de_dirent->d_type != DT_LNK &&
+ de->de_dirent->d_type != DT_DIR)
+ printf("Warning: no dev_t for %s\n", de->de_dirent->d_name);
+ return (dev);
+}
+
+/*
+ * Do what we need to do to a rule that we just loaded from the
+ * userland. In particular, we need to check the magic, and adjust
+ * the ruleset appropriate if desired.
+ */
+static int
+devfs_rule_input(struct devfs_rule *dr, struct devfs_mount *dm)
+{
+
+ if (dr->dr_magic != DEVFS_MAGIC)
+ return (ERPCMISMATCH);
+ dr->dr_id = devfs_rid_input(dr->dr_id, dm);
+ return (0);
+}
+
+/*
+ * Import dr into the appropriate place in the kernel (i.e., make a
+ * krule). The value of dr is copied, so the pointer may be destroyed
+ * after this call completes.
+ */
+static int
+devfs_rule_insert(struct devfs_rule *dr)
+{
+ struct devfs_ruleset *ds, *dsi;
+ struct devfs_krule *k1, *k2;
+ struct devfs_krule *dk;
+ devfs_rsnum rsnum;
+ devfs_rnum dkrn;
+ int error;
+
+ /*
+ * This stuff seems out of place here, but we want to do it as
+ * soon as possible so that if it fails, we don't have to roll
+ * back any changes we already made (e.g., ruleset creation).
+ */
+ if (dr->dr_iacts & DRA_INCSET) {
+ dsi = devfs_ruleset_bynum(dr->dr_incset);
+ if (dsi == NULL)
+ return (ESRCH);
+ } else
+ dsi = NULL;
+
+ rsnum = rid2rsn(dr->dr_id);
+ ds = devfs_ruleset_bynum(rsnum);
+ if (ds == NULL)
+ ds = devfs_ruleset_create(rsnum);
+ if (ds->ds_flags & DS_IMMUTABLE)
+ return (EIO);
+ dkrn = rid2rn(dr->dr_id);
+ if (dkrn == 0) {
+ error = devfs_rule_autonumber(ds, &dkrn);
+ if (error != 0)
+ return (error);
+ }
+
+ dk = malloc(sizeof(*dk), M_DEVFS, M_WAITOK);
+ dk->dk_ruleset = ds;
+ if (dsi != NULL)
+ ++dsi->ds_refcount;
+ /* XXX: Inspect dr? */
+ memcpy(&dk->dk_rule, dr, sizeof(*dr));
+ dk->dk_rule.dr_id = mkrid(rid2rsn(dk->dk_rule.dr_id), dkrn);
+
+ k1 = SLIST_FIRST(&ds->ds_rules);
+ if (k1 == NULL || rid2rn(k1->dk_rule.dr_id) > dkrn)
+ SLIST_INSERT_HEAD(&ds->ds_rules, dk, dk_list);
+ else {
+ SLIST_FOREACH(k1, &ds->ds_rules, dk_list) {
+ k2 = SLIST_NEXT(k1, dk_list);
+ if (k2 == NULL || rid2rn(k2->dk_rule.dr_id) > dkrn) {
+ SLIST_INSERT_AFTER(k1, dk, dk_list);
+ break;
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Determine whether dk matches de. Returns 1 if dk should be run on
+ * de; 0, otherwise.
+ */
+static int
+devfs_rule_match(struct devfs_krule *dk, struct devfs_dirent *de)
+{
+ struct devfs_rule *dr = &dk->dk_rule;
+ dev_t dev;
+
+ dev = devfs_rule_getdev(de);
+ /*
+ * At this point, if dev is NULL, we should assume that any
+ * criteria that depend on it don't match. We should *not*
+ * just ignore them (i.e., act like they weren't specified),
+ * since that makes a rule that only has criteria dependent on
+ * the dev_t match all symlinks and directories.
+ *
+ * Note also that the following tests are somewhat reversed:
+ * They're actually testing to see whether the condition does
+ * *not* match, since the default is to assume the rule should
+ * be run (such as if there are no conditions).
+ */
+ if (dr->dr_icond & DRC_DSWFLAGS)
+ if (dev == NULL ||
+ (dev->si_devsw->d_flags & dr->dr_dswflags) == 0)
+ goto nomatch;
+ if (dr->dr_icond & DRC_PATHPTRN)
+ if (!devfs_rule_matchpath(dk, de))
+ goto nomatch;
+ if (dr->dr_icond & DRC_MAJOR)
+ if (dev == NULL || major(dev) != dr->dr_major)
+ goto nomatch;
+
+ return (1);
+
+nomatch:
+ return (0);
+}
+
+/*
+ * Determine whether dk matches de on account of dr_pathptrn.
+ */
+static int
+devfs_rule_matchpath(struct devfs_krule *dk, struct devfs_dirent *de)
+{
+ struct devfs_rule *dr = &dk->dk_rule;
+ char *pname;
+ dev_t dev;
+ int plen;
+
+ dev = devfs_rule_getdev(de);
+ if (dev != NULL)
+ pname = dev->si_name;
+ /* XXX: Support symlinks (check d_type == DT_LNK here). */
+ else
+ return (0);
+ KASSERT(pname != NULL, ("devfs_rule_matchpath: NULL pname"));
+
+ /*
+ * XXX: Interpret dr_pathptrn as a real pattern (support '*',
+ * '?', and perhaps brace expansion). For now, we only
+ * support one trailing asterisk.
+ */
+ plen = strlen(dr->dr_pathptrn);
+ if (dr->dr_pathptrn[plen - 1] == '*') {
+ if (strlen(pname) >= plen - 1 &&
+ strncmp(dr->dr_pathptrn, pname, plen - 1) == 0)
+ return (1);
+ } else
+ if (strcmp(dr->dr_pathptrn, pname) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * Run dk on de.
+ */
+static void
+devfs_rule_run(struct devfs_krule *dk, struct devfs_dirent *de)
+{
+ struct devfs_rule *dr = &dk->dk_rule;
+ struct devfs_ruleset *ds;
+
+ if (dr->dr_iacts & DRA_BACTS) {
+ if (dr->dr_bacts & DRB_HIDE)
+ de->de_flags |= DE_WHITEOUT;
+ if (dr->dr_bacts & DRB_UNHIDE)
+ de->de_flags &= ~DE_WHITEOUT;
+ }
+ if (dr->dr_iacts & DRA_UID)
+ de->de_uid = dr->dr_uid;
+ if (dr->dr_iacts & DRA_GID)
+ de->de_gid = dr->dr_gid;
+ if (dr->dr_iacts & DRA_MODE)
+ de->de_mode = dr->dr_mode;
+ if (dr->dr_iacts & DRA_INCSET) {
+ ds = devfs_ruleset_bynum(dk->dk_rule.dr_incset);
+ KASSERT(ds != NULL, ("DRA_INCSET but bad dr_incset"));
+ if (dk->dk_ruleset == ds) {
+ /* XXX: Do a better job of detecting loops. */
+ printf("Warning: Ruleset %d including itself!\n",
+ dk->dk_ruleset->ds_number);
+ } else
+ devfs_ruleset_applyde(ds, de);
+ }
+}
+
+/*
+ * Apply all the rules in ds to de.
+ */
+static void
+devfs_ruleset_applyde(struct devfs_ruleset *ds, struct devfs_dirent *de)
+{
+ struct devfs_krule *dk;
+
+ SLIST_FOREACH(dk, &ds->ds_rules, dk_list) {
+ devfs_rule_applyde(dk, de);
+ }
+}
+
+/*
+ * Apply all the rules in ds to all the entires in dm.
+ */
+static void
+devfs_ruleset_applydm(struct devfs_ruleset *ds, struct devfs_mount *dm)
+{
+ struct devfs_krule *dk;
+
+ /*
+ * XXX: Does it matter whether we do
+ *
+ * foreach(dk in ds)
+ * foreach(de in dm)
+ * apply(dk to de)
+ *
+ * as opposed to
+ *
+ * foreach(de in dm)
+ * foreach(dk in ds)
+ * apply(dk to de)
+ *
+ * The end result is obviously the same, but does the order
+ * matter?
+ */
+ SLIST_FOREACH(dk, &ds->ds_rules, dk_list) {
+ devfs_rule_applydm(dk, dm);
+ }
+}
+
+/*
+ * Find a ruleset by number.
+ */
+static struct devfs_ruleset *
+devfs_ruleset_bynum(devfs_rsnum rsnum)
+{
+ struct devfs_ruleset *ds;
+
+ SLIST_FOREACH(ds, &devfs_rulesets, ds_list) {
+ if (ds->ds_number == rsnum)
+ return (ds);
+ }
+ return (NULL);
+}
+
+/*
+ * Create a new ruleset.
+ */
+static struct devfs_ruleset *
+devfs_ruleset_create(devfs_rsnum rsnum)
+{
+ struct devfs_ruleset *s1, *s2;
+ struct devfs_ruleset *ds;
+
+ KASSERT(devfs_ruleset_bynum(rsnum) == NULL,
+ ("creating already existent ruleset %d", rsnum));
+
+ ds = malloc(sizeof(*ds), M_DEVFS, M_WAITOK | M_ZERO);
+ ds->ds_number = rsnum;
+ ds->ds_refcount = ds->ds_flags = 0;
+ SLIST_INIT(&ds->ds_rules);
+
+ s1 = SLIST_FIRST(&devfs_rulesets);
+ if (s1 == NULL || s1->ds_number > rsnum)
+ SLIST_INSERT_HEAD(&devfs_rulesets, ds, ds_list);
+ else {
+ SLIST_FOREACH(s1, &devfs_rulesets, ds_list) {
+ s2 = SLIST_NEXT(s1, ds_list);
+ if (s2 == NULL || s2->ds_number > rsnum) {
+ SLIST_INSERT_AFTER(s1, ds, ds_list);
+ break;
+ }
+ }
+ }
+
+ return (ds);
+}
+
+/*
+ * Remove a ruleset form the system. The ruleset specified must be
+ * empty and not in use.
+ */
+static void
+devfs_ruleset_destroy(struct devfs_ruleset **dsp)
+{
+ struct devfs_ruleset *ds = *dsp;
+
+ KASSERT(SLIST_EMPTY(&ds->ds_rules), ("destroying non-empty ruleset"));
+ KASSERT(ds->ds_refcount == 0, ("destroying busy ruleset"));
+ KASSERT((ds->ds_flags & DS_IMMUTABLE) == 0,
+ ("destroying immutable ruleset"));
+
+ SLIST_REMOVE(&devfs_rulesets, ds, devfs_ruleset, ds_list);
+ free(ds, M_DEVFS);
+ *dsp = NULL;
+}
+
+/*
+ * Remove a ruleset from the system if it's empty and not used
+ * anywhere. This should be called after every time a rule is deleted
+ * from this ruleset or the reference count is decremented.
+ */
+static void
+devfs_ruleset_reap(struct devfs_ruleset **dsp)
+{
+ struct devfs_ruleset *ds = *dsp;
+
+ if (SLIST_EMPTY(&ds->ds_rules) && ds->ds_refcount == 0) {
+ devfs_ruleset_destroy(&ds);
+ *dsp = ds;
+ }
+}
+
+/*
+ * Make rsnum the active ruleset for dm.
+ */
+static int
+devfs_ruleset_use(devfs_rsnum rsnum, struct devfs_mount *dm)
+{
+ struct devfs_ruleset *cds, *ds;
+
+ ds = devfs_ruleset_bynum(rsnum);
+ if (ds == NULL)
+ ds = devfs_ruleset_create(rsnum);
+ cds = devfs_ruleset_bynum(dm->dm_ruleset);
+ KASSERT(cds != NULL, ("mount-point has NULL ruleset"));
+
+ /* These should probably be made atomic somehow. */
+ --cds->ds_refcount;
+ ++ds->ds_refcount;
+ dm->dm_ruleset = rsnum;
+
+ devfs_ruleset_reap(&cds);
+ return (0);
+}
+
+#endif /* !NODEVFS */
diff --git a/sys/fs/devfs/devfs_vfsops.c b/sys/fs/devfs/devfs_vfsops.c
index 4b2d770..a46b0c4 100644
--- a/sys/fs/devfs/devfs_vfsops.c
+++ b/sys/fs/devfs/devfs_vfsops.c
@@ -91,6 +91,7 @@ devfs_mount(mp, ndp, td)
fmp->dm_rootdir = devfs_vmkdir("(root)", 6, NULL);
fmp->dm_rootdir->de_inode = 2;
fmp->dm_basedir = fmp->dm_rootdir;
+ devfs_rules_newmount(fmp, td);
error = devfs_root(mp, &rvp);
if (error) {
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 8779c96..069d224 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -62,6 +62,7 @@
static int devfs_access(struct vop_access_args *ap);
static int devfs_getattr(struct vop_getattr_args *ap);
+static int devfs_ioctl(struct vop_ioctl_args *ap);
static int devfs_lookupx(struct vop_lookup_args *ap);
static int devfs_mknod(struct vop_mknod_args *ap);
static int devfs_pathconf(struct vop_pathconf_args *ap);
@@ -247,6 +248,24 @@ devfs_getattr(ap)
}
static int
+devfs_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct thread *a_td;
+ } */ *ap;
+{
+ int error;
+
+ error = devfs_rules_ioctl(ap->a_vp->v_mount, ap->a_command, ap->a_data,
+ ap->a_td);
+ return (error);
+}
+
+static int
devfs_lookupx(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
@@ -808,6 +827,7 @@ static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
{ &vop_default_desc, (vop_t *) vop_defaultop },
{ &vop_access_desc, (vop_t *) devfs_access },
{ &vop_getattr_desc, (vop_t *) devfs_getattr },
+ { &vop_ioctl_desc, (vop_t *) devfs_ioctl },
{ &vop_islocked_desc, (vop_t *) vop_stdislocked },
{ &vop_lock_desc, (vop_t *) vop_stdlock },
{ &vop_lookup_desc, (vop_t *) devfs_lookup },
OpenPOWER on IntegriCloud