summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man9/make_dev.923
-rw-r--r--sys/kern/kern_conf.c114
-rw-r--r--sys/sys/conf.h11
3 files changed, 109 insertions, 39 deletions
diff --git a/share/man/man9/make_dev.9 b/share/man/man9/make_dev.9
index 9cc755f..2e35f5f 100644
--- a/share/man/man9/make_dev.9
+++ b/share/man/man9/make_dev.9
@@ -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
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index 12d1a8d..509ce775f 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -682,26 +682,91 @@ prep_cdevsw(struct cdevsw *devsw, int flags)
}
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);
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 6c60601..5f1ef18 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -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,
OpenPOWER on IntegriCloud