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:
parent
c57275f347
commit
9498a983a9
@ -12,6 +12,7 @@ SUBDIR= adjkerntz \
|
||||
clri \
|
||||
comcontrol \
|
||||
conscontrol \
|
||||
devfs \
|
||||
dhclient \
|
||||
disklabel \
|
||||
dmesg \
|
||||
|
8
sbin/devfs/Makefile
Normal file
8
sbin/devfs/Makefile
Normal 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
326
sbin/devfs/devfs.8
Normal 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
140
sbin/devfs/devfs.c
Normal 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
55
sbin/devfs/extern.h
Normal 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
419
sbin/devfs/rule.c
Normal 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");
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
839
sys/fs/devfs/devfs_rule.c
Normal 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 */
|
@ -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) {
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user