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:
parent
337299c66d
commit
68f7a01392
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=213526
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user