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
This commit is contained in:
Dima Dorfman 2002-07-17 01:46:48 +00:00
parent a96f7d1a1b
commit a1dc209638
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=100206
12 changed files with 1899 additions and 1 deletions

View File

@ -12,6 +12,7 @@ SUBDIR= adjkerntz \
clri \
comcontrol \
conscontrol \
devfs \
dhclient \
disklabel \
dmesg \

8
sbin/devfs/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $FreeBSD$
PROG= devfs
SRCS= devfs.c rule.c
MAN= devfs.8
WARNS?= 4
.include <bsd.prog.mk>

326
sbin/devfs/devfs.8 Normal file
View File

@ -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

140
sbin/devfs/devfs.c Normal file
View File

@ -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);
}

55
sbin/devfs/extern.h Normal file
View File

@ -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__ */

419
sbin/devfs/rule.c Normal file
View File

@ -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");
}

View File

@ -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

View File

@ -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);

View File

@ -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);

839
sys/fs/devfs/devfs_rule.c Normal file
View File

@ -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 */

View File

@ -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) {

View File

@ -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);
@ -246,6 +247,24 @@ devfs_getattr(ap)
return (error);
}
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 /* {
@ -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 },