summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_sbuf.c
diff options
context:
space:
mode:
authorkbyanc <kbyanc@FreeBSD.org>2002-01-06 08:38:23 +0000
committerkbyanc <kbyanc@FreeBSD.org>2002-01-06 08:38:23 +0000
commit9af9cb3fe99a90dfce093ffbfbe8ad449bef02e2 (patch)
treec9f2edd994d53b72edd7bedcd3dd939c54177667 /sys/kern/subr_sbuf.c
parent719af3e61a9a9dd18c1117e10400b8bc7591f222 (diff)
downloadFreeBSD-src-9af9cb3fe99a90dfce093ffbfbe8ad449bef02e2.zip
FreeBSD-src-9af9cb3fe99a90dfce093ffbfbe8ad449bef02e2.tar.gz
* Implement SBUF_AUTOEXTEND flag; sbufs created with this flag are
automatically extended to prevent overflow. * Added sbuf_vprintf(); sbuf_printf() is now just a wrapper around sbuf_vprintf(). * Include <stdio.h> and <string.h> when building libsbuf to silence WARNS=4 warnings. Reviewed by: des
Diffstat (limited to 'sys/kern/subr_sbuf.c')
-rw-r--r--sys/kern/subr_sbuf.c131
1 files changed, 108 insertions, 23 deletions
diff --git a/sys/kern/subr_sbuf.c b/sys/kern/subr_sbuf.c
index 72e6522..e2a84e5 100644
--- a/sys/kern/subr_sbuf.c
+++ b/sys/kern/subr_sbuf.c
@@ -40,7 +40,9 @@
#else /* _KERNEL */
#include <ctype.h>
#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#endif /* _KERNEL */
#include <sys/sbuf.h>
@@ -64,6 +66,8 @@ MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED)
#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED)
#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1)
+#define SBUF_FREESPACE(s) ((s)->s_size - (s)->s_len - 1)
+#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
/*
* Set / clear flags
@@ -71,6 +75,10 @@ MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0)
#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
+#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */
+#define SBUF_MAXEXTENDSIZE PAGE_SIZE
+#define SBUF_MAXEXTENDINCR PAGE_SIZE
+
/*
* Debugging support
*/
@@ -81,7 +89,7 @@ _assert_sbuf_integrity(char *fun, struct sbuf *s)
KASSERT(s != NULL,
("%s called with a NULL sbuf pointer", fun));
KASSERT(s->s_buf != NULL,
- ("%s called with unitialized or corrupt sbuf", fun));
+ ("%s called with uninitialized or corrupt sbuf", fun));
KASSERT(s->s_len < s->s_size,
("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
}
@@ -100,6 +108,49 @@ _assert_sbuf_state(char *fun, struct sbuf *s, int state)
#define assert_sbuf_state(s, i) do { } while (0)
#endif /* _KERNEL && INVARIANTS */
+static int
+sbuf_extendsize(int size)
+{
+ int newsize;
+
+ newsize = SBUF_MINEXTENDSIZE;
+ while (newsize < size) {
+ if (newsize < SBUF_MAXEXTENDSIZE)
+ newsize *= 2;
+ else
+ newsize += SBUF_MAXEXTENDINCR;
+ }
+
+ return (newsize);
+}
+
+
+/*
+ * Extend an sbuf.
+ */
+static int
+sbuf_extend(struct sbuf *s, int addlen)
+{
+ char *newbuf;
+ int newsize;
+
+ if (!SBUF_CANEXTEND(s))
+ return (-1);
+
+ newsize = sbuf_extendsize(s->s_size + addlen);
+ newbuf = (char *)SBMALLOC(newsize);
+ if (newbuf == NULL)
+ return (-1);
+ bcopy(s->s_buf, newbuf, s->s_size);
+ if (SBUF_ISDYNAMIC(s))
+ SBFREE(s->s_buf);
+ else
+ SBUF_SETFLAG(s, SBUF_DYNAMIC);
+ s->s_buf = newbuf;
+ s->s_size = newsize;
+ return (0);
+}
+
/*
* Initialize an sbuf.
* If buf is non-NULL, it points to a static or already-allocated string
@@ -110,23 +161,28 @@ sbuf_new(struct sbuf *s, char *buf, int length, int flags)
{
KASSERT(length >= 0,
("attempt to create an sbuf of negative length (%d)", length));
- KASSERT(flags == 0,
- ("%s called with non-zero flags", __func__));
+ KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
+ ("%s called with invalid flags", __func__));
+ flags &= SBUF_USRFLAGMSK;
if (s == NULL) {
s = (struct sbuf *)SBMALLOC(sizeof *s);
if (s == NULL)
return (NULL);
bzero(s, sizeof *s);
+ s->s_flags = flags;
SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
} else {
bzero(s, sizeof *s);
+ s->s_flags = flags;
}
s->s_size = length;
if (buf) {
s->s_buf = buf;
return (s);
}
+ if (flags & SBUF_AUTOEXTEND)
+ s->s_size = sbuf_extendsize(s->s_size);
s->s_buf = (char *)SBMALLOC(s->s_size);
if (s->s_buf == NULL) {
if (SBUF_ISDYNSTRUCT(s))
@@ -166,7 +222,7 @@ sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
#endif
/*
- * Clear an sbuf and reset its position
+ * Clear an sbuf and reset its position.
*/
void
sbuf_clear(struct sbuf *s)
@@ -180,7 +236,8 @@ sbuf_clear(struct sbuf *s)
}
/*
- * Set the sbuf's position to an arbitrary value
+ * Set the sbuf's end position to an arbitrary value.
+ * Effectively truncates the sbuf at the new position.
*/
int
sbuf_setpos(struct sbuf *s, int pos)
@@ -211,8 +268,11 @@ sbuf_bcat(struct sbuf *s, const char *str, size_t len)
if (SBUF_HASOVERFLOWED(s))
return (-1);
- while (len-- && SBUF_HASROOM(s))
+ while (len--) {
+ if (!SBUF_HASROOM(s) && sbuf_extend(s, len) < 0)
+ break;
s->s_buf[s->s_len++] = *str++;
+ }
if (len) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return (-1);
@@ -235,8 +295,10 @@ sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
if (len == 0)
return (0);
- if (len > (s->s_size - s->s_len - 1))
- len = s->s_size - s->s_len - 1;
+ if (len > SBUF_FREESPACE(s)) {
+ sbuf_extend(s, len - SBUF_FREESPACE(s));
+ len = min(len, SBUF_FREESPACE(s));
+ }
if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
return (-1);
s->s_len += len;
@@ -270,8 +332,11 @@ sbuf_cat(struct sbuf *s, const char *str)
if (SBUF_HASOVERFLOWED(s))
return (-1);
- while (*str && SBUF_HASROOM(s))
+ while (*str) {
+ if (!SBUF_HASROOM(s) && sbuf_extend(s, strlen(str)) < 0)
+ break;
s->s_buf[s->s_len++] = *str++;
+ }
if (*str) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return (-1);
@@ -281,7 +346,7 @@ sbuf_cat(struct sbuf *s, const char *str)
#ifdef _KERNEL
/*
- * Copy a string from userland into an sbuf.
+ * Append a string from userland to an sbuf.
*/
int
sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
@@ -294,8 +359,12 @@ sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
if (SBUF_HASOVERFLOWED(s))
return (-1);
- if (len == 0 || len > (s->s_size - s->s_len - 1))
- len = s->s_size - s->s_len - 1;
+ if (len == 0)
+ len = SBUF_FREESPACE(s); /* XXX return 0? */
+ if (len > SBUF_FREESPACE(s)) {
+ sbuf_extend(s, len);
+ len = min(len, SBUF_FREESPACE(s));
+ }
switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
case ENAMETOOLONG:
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
@@ -325,26 +394,27 @@ sbuf_cpy(struct sbuf *s, const char *str)
}
/*
- * Format the given arguments and append the resulting string to an sbuf.
+ * Format the given argument list and append the resulting string to an sbuf.
*/
int
-sbuf_printf(struct sbuf *s, const char *fmt, ...)
+sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
{
- va_list ap;
int len;
assert_sbuf_integrity(s);
assert_sbuf_state(s, 0);
-
+
KASSERT(fmt != NULL,
("%s called with a NULL format string", __func__));
-
+
if (SBUF_HASOVERFLOWED(s))
return (-1);
- va_start(ap, fmt);
- len = vsnprintf(&s->s_buf[s->s_len], s->s_size - s->s_len, fmt, ap);
- va_end(ap);
+ do {
+ len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
+ fmt, ap);
+ } while (len > SBUF_FREESPACE(s) &&
+ sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
/*
* s->s_len is the length of the string, without the terminating nul.
@@ -355,7 +425,7 @@ sbuf_printf(struct sbuf *s, const char *fmt, ...)
* vsnprintf() returns the amount that would have been copied,
* given sufficient space, hence the min() calculation below.
*/
- s->s_len += min(len, s->s_size - s->s_len - 1);
+ s->s_len += min(len, SBUF_FREESPACE(s));
if (!SBUF_HASROOM(s))
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
@@ -368,6 +438,21 @@ sbuf_printf(struct sbuf *s, const char *fmt, ...)
}
/*
+ * Format the given arguments and append the resulting string to an sbuf.
+ */
+int
+sbuf_printf(struct sbuf *s, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = sbuf_vprintf(s, fmt, ap);
+ va_end(ap);
+ return(result);
+}
+
+/*
* Append a character to an sbuf.
*/
int
@@ -379,7 +464,7 @@ sbuf_putc(struct sbuf *s, int c)
if (SBUF_HASOVERFLOWED(s))
return (-1);
- if (!SBUF_HASROOM(s)) {
+ if (!SBUF_HASROOM(s) && sbuf_extend(s, 1) < 0) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return (-1);
}
@@ -389,7 +474,7 @@ sbuf_putc(struct sbuf *s, int c)
}
/*
- * Trim whitespace characters from an sbuf.
+ * Trim whitespace characters from end of an sbuf.
*/
int
sbuf_trim(struct sbuf *s)
OpenPOWER on IntegriCloud