Check the device name validity on device registration.

A new function prep_devname() sanitizes a device name by removing
leading and redundant sequential slashes. The function returns an error
for names which already exist or are considered invalid.

A new flag MAKEDEV_CHECKNAME for make_dev_p(9) and make_dev_credf(9)
indicates that the caller is prepared to handle an error related to the
device name. An invalid name triggers a panic if the flag is not
specified.

Document the MAKEDEV_CHECKNAME flag in the make_dev(9) manual page.

Idea from:	kib
Reviewed by:	kib
This commit is contained in:
Jaakko Heinonen 2010-10-07 18:00:55 +00:00
parent 337299c66d
commit 68f7a01392
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=213526
3 changed files with 109 additions and 39 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 5, 2010
.Dd October 7, 2010
.Dt MAKE_DEV 9
.Os
.Sh NAME
@ -131,12 +131,18 @@ argument alters the operation of
.Fn make_dev_credf .
The following values are currently accepted:
.Pp
.Bd -literal -offset indent -compact
MAKEDEV_REF reference the created device
MAKEDEV_NOWAIT do not sleep, may return NULL
MAKEDEV_WAITOK allow the function to sleep to satisfy malloc
MAKEDEV_ETERNAL created device will be never destroyed
.Ed
.Bl -tag -width "MAKEDEV_CHECKNAME" -compact -offset indent
.It MAKEDEV_REF
reference the created device
.It MAKEDEV_NOWAIT
do not sleep, may return NULL
.It MAKEDEV_WAITOK
allow the function to sleep to satisfy malloc
.It MAKEDEV_ETERNAL
created device will be never destroyed
.It MAKEDEV_CHECKNAME
return NULL if the device name is invalid or already exists
.El
.Pp
The
.Dv MAKEDEV_WAITOK
@ -166,6 +172,9 @@ For the convenience, use the
flag for the code that can be compiled into kernel or loaded
(and unloaded) as loadable module.
.Pp
A panic will occur if the MAKEDEV_CHECKNAME flag is not specified
and the device name is invalid or already exists.
.Pp
The
.Fn make_dev_cred
function is equivalent to the call

View File

@ -681,27 +681,92 @@ prep_cdevsw(struct cdevsw *devsw, int flags)
return (0);
}
static int
prep_devname(struct cdev *dev, const char *fmt, va_list ap)
{
int len;
char *from, *q, *s, *to;
mtx_assert(&devmtx, MA_OWNED);
len = vsnrprintf(dev->__si_namebuf, sizeof(dev->__si_namebuf), 32,
fmt, ap);
if (len > sizeof(dev->__si_namebuf) - 1)
return (ENAMETOOLONG);
/* Strip leading slashes. */
for (from = dev->__si_namebuf; *from == '/'; from++)
;
for (to = dev->__si_namebuf; *from != '\0'; from++, to++) {
/* Treat multiple sequential slashes as single. */
while (from[0] == '/' && from[1] == '/')
from++;
/* Trailing slash is considered invalid. */
if (from[0] == '/' && from[1] == '\0')
return (EINVAL);
*to = *from;
}
*to = '\0';
if (dev->__si_namebuf[0] == '\0')
return (EINVAL);
/* Disallow "." and ".." components. */
for (s = dev->__si_namebuf;;) {
for (q = s; *q != '/' && *q != '\0'; q++)
;
if (q - s == 1 && s[0] == '.')
return (EINVAL);
if (q - s == 2 && s[0] == '.' && s[1] == '.')
return (EINVAL);
if (*q != '/')
break;
s = q + 1;
}
if (devfs_dev_exists(dev->__si_namebuf) != 0)
return (EEXIST);
return (0);
}
static int
make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit,
struct ucred *cr, uid_t uid, gid_t gid, int mode, const char *fmt,
va_list ap)
{
struct cdev *dev;
int i, res;
struct cdev *dev, *dev_new;
int res;
KASSERT((flags & MAKEDEV_WAITOK) == 0 || (flags & MAKEDEV_NOWAIT) == 0,
("make_dev_credv: both WAITOK and NOWAIT specified"));
dev = devfs_alloc(flags);
if (dev == NULL)
dev_new = devfs_alloc(flags);
if (dev_new == NULL)
return (ENOMEM);
dev_lock();
res = prep_cdevsw(devsw, flags);
if (res != 0) {
dev_unlock();
devfs_free(dev);
devfs_free(dev_new);
return (res);
}
dev = newdev(devsw, unit, dev);
dev = newdev(devsw, unit, dev_new);
if ((dev->si_flags & SI_NAMED) == 0)
res = prep_devname(dev, fmt, ap);
if (res != 0) {
if ((flags & MAKEDEV_CHECKNAME) == 0) {
panic(
"make_dev_credv: bad si_name (error=%d, si_name=%s)",
res, dev->si_name);
}
if (dev == dev_new) {
LIST_REMOVE(dev, si_list);
dev_unlock();
devfs_free(dev);
}
return (res);
}
if (flags & MAKEDEV_REF)
dev_refl(dev);
if (flags & MAKEDEV_ETERNAL)
@ -720,13 +785,6 @@ make_dev_credv(int flags, struct cdev **dres, struct cdevsw *devsw, int unit,
KASSERT(!(dev->si_flags & SI_NAMED),
("make_dev() by driver %s on pre-existing device (min=%x, name=%s)",
devsw->d_name, dev2unit(dev), devtoname(dev)));
i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
if (i > (sizeof dev->__si_namebuf - 1)) {
printf("WARNING: Device name truncated! (%s)\n",
dev->__si_namebuf);
}
dev->si_flags |= SI_NAMED;
if (cr != NULL)
dev->si_cred = crhold(cr);
@ -756,7 +814,8 @@ make_dev(struct cdevsw *devsw, int unit, uid_t uid, gid_t gid, int mode,
res = make_dev_credv(0, &dev, devsw, unit, NULL, uid, gid, mode, fmt,
ap);
va_end(ap);
KASSERT(res == 0 && dev != NULL, ("make_dev: failed make_dev_credv"));
KASSERT(res == 0 && dev != NULL,
("make_dev: failed make_dev_credv (error=%d)", res));
return (dev);
}
@ -773,7 +832,7 @@ make_dev_cred(struct cdevsw *devsw, int unit, struct ucred *cr, uid_t uid,
va_end(ap);
KASSERT(res == 0 && dev != NULL,
("make_dev_cred: failed make_dev_credv"));
("make_dev_cred: failed make_dev_credv (error=%d)", res));
return (dev);
}
@ -790,8 +849,9 @@ make_dev_credf(int flags, struct cdevsw *devsw, int unit, struct ucred *cr,
fmt, ap);
va_end(ap);
KASSERT((flags & MAKEDEV_NOWAIT) != 0 || res == 0,
("make_dev_credf: failed make_dev_credv"));
KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) ||
((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0,
("make_dev_credf: failed make_dev_credv (error=%d)", res));
return (res == 0 ? dev : NULL);
}
@ -807,8 +867,9 @@ make_dev_p(int flags, struct cdev **cdev, struct cdevsw *devsw,
fmt, ap);
va_end(ap);
KASSERT((flags & MAKEDEV_NOWAIT) != 0 || res == 0,
("make_dev_p: failed make_dev_credv"));
KASSERT(((flags & MAKEDEV_NOWAIT) != 0 && res == ENOMEM) ||
((flags & MAKEDEV_CHECKNAME) != 0 && res != ENOMEM) || res == 0,
("make_dev_p: failed make_dev_credv (error=%d)", res));
return (res);
}
@ -836,21 +897,20 @@ make_dev_alias(struct cdev *pdev, const char *fmt, ...)
{
struct cdev *dev;
va_list ap;
int i;
int error;
KASSERT(pdev != NULL, ("NULL pdev"));
dev = devfs_alloc(MAKEDEV_WAITOK);
dev_lock();
dev->si_flags |= SI_ALIAS;
dev->si_flags |= SI_NAMED;
va_start(ap, fmt);
i = vsnrprintf(dev->__si_namebuf, sizeof dev->__si_namebuf, 32, fmt, ap);
if (i > (sizeof dev->__si_namebuf - 1)) {
printf("WARNING: Device name truncated! (%s)\n",
dev->__si_namebuf);
}
error = prep_devname(dev, fmt, ap);
va_end(ap);
if (error != 0) {
panic("make_dev_alias: bad si_name (error=%d, si_name=%s)",
error, dev->si_name);
}
dev->si_flags |= SI_NAMED;
devfs_create(dev);
dev_dependsl(pdev, dev);
clean_unrhdrl(devfs_inos);

View File

@ -263,11 +263,12 @@ struct cdev *make_dev(struct cdevsw *_devsw, int _unit, uid_t _uid, gid_t _gid,
struct cdev *make_dev_cred(struct cdevsw *_devsw, int _unit,
struct ucred *_cr, uid_t _uid, gid_t _gid, int _perms,
const char *_fmt, ...) __printflike(7, 8);
#define MAKEDEV_REF 0x01
#define MAKEDEV_WHTOUT 0x02
#define MAKEDEV_NOWAIT 0x04
#define MAKEDEV_WAITOK 0x08
#define MAKEDEV_ETERNAL 0x10
#define MAKEDEV_REF 0x01
#define MAKEDEV_WHTOUT 0x02
#define MAKEDEV_NOWAIT 0x04
#define MAKEDEV_WAITOK 0x08
#define MAKEDEV_ETERNAL 0x10
#define MAKEDEV_CHECKNAME 0x20
struct cdev *make_dev_credf(int _flags,
struct cdevsw *_devsw, int _unit,
struct ucred *_cr, uid_t _uid, gid_t _gid, int _mode,