Code changes from sbuf-20001211b.diff: - rename sbuf_malloc_type to M_SBUF. - replace s_dynamic and s_done with flags. - move s_next up to avoid alignment problems. - clarify the difference between an sbuf that has overflowed (but the overflow was caught) and one which apparently has had data written past the end of its storage buffer (which shouldn't happen, and is therfore only checked in KASSERTs). - remove the assertion that an sbuf's storage buffer is always null- terminated (which sbuf_setpos() violated, in any case). Now only sbuf_finish() null-terminates an sbuf. - gather the most commonly used assertions into two functions that the API functions can call to assert the integrity and state of the sbuf they are operating on. Man page changes from sbuf-20001211b.diff: - add a note about overflowed sbufs and what to do with them. - use .Fn instead of .Nm for function names. - simplify some of the language. Notes: - possible uses for the flags parameter to sbuf_new(): - specify that an sbuf is to auto-extend its buffer rather than overflow - make sbuf_new() use M_NOWAIT instead of M_WAITOK if the caller is able to get along without the sbuf (perhaps by outputting or returning unformatted data) in low-memory situations. -- DES Index: sys/sys/sbuf.h =================================================================== RCS file: sbuf.h diff -N sbuf.h --- /dev/null Tue Dec 12 20:56:33 2000 +++ sbuf.h Tue Dec 12 20:57:12 2000 @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 _SYS_SBUF_H_ +#define _SYS_SBUF_H_ + +/* + * Structure definition + */ +struct sbuf { + char *s_buf; /* storage buffer */ + struct sbuf *s_next; /* next in chain */ + size_t s_size; /* size of storage buffer */ + size_t s_len; /* current length of string */ +#define SBUF_AUTOEXTEND 0x00000001 /* automatically extend buffer */ +#define SBUF_DYNAMIC 0x00010000 /* s_buf must be freed */ +#define SBUF_FINISHED 0x00020000 /* set by sbuf_finish() */ +#define SBUF_OVERFLOWED 0x00040000 /* sbuf overflowed */ + int s_flags; /* flags */ +}; + +/* + * Predicates + */ +#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC) +#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) + +/* + * Other macros + */ +#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0) + +/* + * API functions + */ +int sbuf_new(struct sbuf *s, char *buf, size_t length, int flags); +int sbuf_setpos(struct sbuf *s, size_t pos); +int sbuf_cat(struct sbuf *s, char *str); +int sbuf_cpy(struct sbuf *s, char *str); +int sbuf_printf(struct sbuf *s, char *fmt, ...); +int sbuf_putc(struct sbuf *s, int c); +int sbuf_finish(struct sbuf *s); +char *sbuf_data(struct sbuf *s); +size_t sbuf_len(struct sbuf *s); +void sbuf_delete(struct sbuf *s); + +#endif Index: sys/kern/subr_sbuf.c =================================================================== RCS file: subr_sbuf.c diff -N subr_sbuf.c --- /dev/null Tue Dec 12 20:56:33 2000 +++ subr_sbuf.c Tue Dec 12 20:58:44 2000 @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$ + */ + +#include +#include +#include +#include +#include + +#include + +MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); + +#ifdef INVARIANTS +static void +assert_sbuf_integrity(struct sbuf *s) +{ + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + KASSERT(s->s_buf != NULL, + (__FUNCTION__ " called with unitialized or corrupt sbuf")); + KASSERT(s->s_len < s->s_size, + ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); +} + +static void +assert_sbuf_state(struct sbuf *s, int state) +{ + KASSERT((s->s_flags & SBUF_FINISHED) == state, + (__FUNCTION__ " called with %sfinished or corrupt sbuf", + (state ? "un" : ""))); +} +#else +#define assert_sbuf_integrity(s) do { } while (0) +#define assert_sbuf_state(s, i) do { } while (0) +#endif + +/* + * Initialize an sbuf. + * If buf is non-NULL, it points to a static or already-allocated string + * big enough to hold at least length characters. + */ +int +sbuf_new(struct sbuf *s, char *buf, size_t length, int flags) +{ + KASSERT(flags == 0, + (__FUNCTION__ " called with non-zero flags")); + KASSERT(s != NULL, + (__FUNCTION__ " called with a NULL sbuf pointer")); + + bzero(s, sizeof *s); + s->s_size = length; + if (buf) { + s->s_buf = buf; + return (0); + } + s->s_buf = malloc(s->s_size, M_SBUF, M_WAITOK); + if (s->s_buf == NULL) + return (-1); + SBUF_SETFLAG(s, SBUF_DYNAMIC); + return (0); +} + +/* + * Set the sbuf's position to an arbitrary value + */ +int +sbuf_setpos(struct sbuf *s, size_t pos) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + KASSERT(pos >= 0, + ("attempt to seek to a negative position (%d)", pos)); + KASSERT(pos < s->s_size, + ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size)); + + if (pos < 0 || pos > s->s_len) + return (-1); + s->s_len = pos; + return (0); +} + +/* + * Append a string to an sbuf. + */ +int +sbuf_cat(struct sbuf *s, char *str) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + + while (*str && SBUF_HASROOM(s)) + s->s_buf[s->s_len++] = *str++; + if (*str) { + SBUF_SETFLAG(s, SBUF_OVERFLOWED); + return (-1); + } + return (0); +} + +/* + * Copy a string into an sbuf. + */ +int +sbuf_cpy(struct sbuf *s, char *str) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + s->s_len = 0; + return (sbuf_cat(s, str)); +} + +/* + * PCHAR function for sbuf_printf() + */ +static void +_sbuf_pchar(int c, void *v) +{ + struct sbuf *s = (struct sbuf *)v; + + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + if (SBUF_HASOVERFLOWED(s)) + return; + if (SBUF_HASROOM(s)) + s->s_buf[s->s_len++] = c; + else + SBUF_SETFLAG(s, SBUF_OVERFLOWED); +} + +/* + * Format the given arguments and append the resulting string to an sbuf. + */ +int +sbuf_printf(struct sbuf *s, char *fmt, ...) +{ + va_list ap; + size_t len; + + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + KASSERT(fmt != NULL, + (__FUNCTION__ " called with a NULL format string")); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + + va_start(ap, fmt); + len = kvprintf(fmt, _sbuf_pchar, s, 10, ap); + va_end(ap); + + KASSERT(s->s_len < s->s_size, + ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + return (0); +} + +/* + * Append a character to an sbuf. + */ +int +sbuf_putc(struct sbuf *s, int c) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + + if (!SBUF_HASROOM(s)) { + SBUF_SETFLAG(s, SBUF_OVERFLOWED); + return (-1); + } + s->s_buf[s->s_len++] = c; + return (0); +} + +/* + * Finish off an sbuf. + */ +int +sbuf_finish(struct sbuf *s) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + + s->s_buf[s->s_len++] = '\0'; + SBUF_SETFLAG(s, SBUF_FINISHED); + return (0); +} + +/* + * Return a pointer to the sbuf data. + */ +char * +sbuf_data(struct sbuf *s) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, SBUF_FINISHED); + + if (SBUF_HASOVERFLOWED(s)) + return (NULL); + return s->s_buf; +} + +/* + * Return the length of the sbuf data. + */ +size_t +sbuf_len(struct sbuf *s) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, SBUF_FINISHED); + + if (SBUF_HASOVERFLOWED(s)) + return (0); + return s->s_len; +} + +/* + * Clear an sbuf, free its buffer if necessary. + */ +void +sbuf_delete(struct sbuf *s) +{ + assert_sbuf_integrity(s); + /* don't care if it's finished or not */ + + if (SBUF_ISDYNAMIC(s)) + free(s->s_buf, M_SBUF); + bzero(s, sizeof *s); +} Index: sys/conf/files =================================================================== RCS file: /home/ncvs/src/sys/conf/files,v retrieving revision 1.448 diff -u -r1.448 files --- sys/conf/files 2000/12/02 05:42:30 1.448 +++ sys/conf/files 2000/12/10 16:18:38 @@ -648,6 +648,7 @@ kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rman.c standard +kern/subr_sbuf.c standard kern/subr_scanf.c standard kern/subr_taskqueue.c standard kern/subr_xxx.c standard Index: share/man/man9/Makefile =================================================================== RCS file: /home/ncvs/src/share/man/man9/Makefile,v retrieving revision 1.87 diff -u -r1.87 Makefile --- share/man/man9/Makefile 2000/11/22 03:44:55 1.87 +++ share/man/man9/Makefile 2000/12/10 19:50:11 @@ -48,6 +48,7 @@ bus_alloc_resource.9 bus_release_resource.9 \ VOP_ACLCHECK.9 VOP_GETACL.9 VOP_GETEXTATTR.9 VOP_SETACL.9 \ VOP_SETEXTATTR.9 acl.9 extattr.9 kobj.9 taskqueue.9 accept_filter.9 \ + sbuf.9 \ sysctl_add_oid.9 sysctl_ctx_init.9 MLINKS+=MD5.9 MD5Init.9 MD5.9 MD5Transform.9 @@ -179,5 +180,16 @@ MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_add.9 MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_del.9 MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_find.9 + +MLINKS+=sbuf.9 sbuf_new.9 +MLINKS+=sbuf.9 sbuf_setpos.9 +MLINKS+=sbuf.9 sbuf_cat.9 +MLINKS+=sbuf.9 sbuf_cpy.9 +MLINKS+=sbuf.9 sbuf_printf.9 +MLINKS+=sbuf.9 sbuf_putc.9 +MLINKS+=sbuf.9 sbuf_finish.9 +MLINKS+=sbuf.9 sbuf_data.9 +MLINKS+=sbuf.9 sbuf_len.9 +MLINKS+=sbuf.9 sbuf_delete.9 .include Index: share/man/man9/sbuf.9 =================================================================== RCS file: sbuf.9 diff -N sbuf.9 --- /dev/null Tue Dec 12 20:56:33 2000 +++ sbuf.9 Tue Dec 12 21:06:20 2000 @@ -0,0 +1,233 @@ +.\"- +.\" Copyright (c) 2000 Poul Henning Kamp and Dag-Erling Coïdan Smørgrav +.\" 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$ +.\" +.Dd December 10th, 2000 +.Dt sbuf 9 +.Os FreeBSD +.Sh NAME +.Nm sbuf_new , +.Nm sbuf_setpos , +.Nm sbuf_cat , +.Nm sbuf_cpy , +.Nm sbuf_printf , +.Nm sbuf_putc , +.Nm sbuf_finish , +.Nm sbuf_data , +.Nm sbuf_len , +.Nm sbuf_delete +.Nd safe string formatting +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn sbuf_new "struct sbuf *s" "char *buf" "size_t length" "int flags" +.Ft int +.Fn sbuf_setpos "struct sbuf *s" "size_t pos" +.Ft int +.Fn sbuf_cat "struct sbuf *s" "char *str" +.Ft int +.Fn sbuf_cpy "struct sbuf *s" "char *str" +.Ft int +.Fn sbuf_printf "struct sbuf *s" "char *fmt" "..." +.Ft int +.Fn sbuf_putc "struct sbuf *s" "int c" +.Ft int +.Fn sbuf_finish "struct sbuf *s" +.Ft char * +.Fn sbuf_data "struct sbuf *s" +.Ft size_t +.Fn sbuf_len "struct sbuf *s" +.Ft void +.Fn sbuf_delete "struct sbuf *s" +.Sh DESCRIPTION +The +.Nm sbuf +family of functions allow one to safely allocate, construct and +release bounded null-terminated strings in kernel space. +Instead of arrays of characters, these functions operate on structures +called +.Fa sbufs , +defined in +.Aq Pa sys/sbuf.h . +.Pp +The +.Fn sbuf_new +function initializes the +.Fa sbuf +pointed to by its first argument. +The +.Fa buf +argument is a pointer to a buffer in which to store the actual string; +if it is +.Dv NULL , +.Fn sbuf_new +will allocate one using +.Xr malloc 9 . +The +.Fa length +is the intended size of the storage buffer. +The fourth argument, +.Fa flags , +is currently unused and should always be set to zero. +.Pp +Note that if +.Fa buf +is not +.Dv NULL , +it must point to an array of at least +.Fa length +characters. +.Pp +The +.Fn sbuf_setpos +function sets the +.Fa sbuf 's +current position to +.Fa pos , +which is a value between zero and one less than the size of the +storage buffer. +.Pp +The +.Fn sbuf_cat +function appends the string +.Fa str +to the +.Fa sbuf +at the current position. +.Pp +The +.Fn sbuf_cpy +function replaces the contents of the +.Fa sbuf +with those of the string +.Fa str . +This is equivalent to calling +.Fn sbuf_cat +with a fresh +.Fa sbuf +or one which position has been reset to zero with +.Fn sbuf_setpos . +.Pp +The +.Fn sbuf_printf +function formats its arguments according to the format string pointed +to by +.Fa fmt +and appends the resulting string to the +.Fa sbuf +at the current position. +.Pp +The +.Fn sbuf_putc +function appends the character +.Fa c +to the +.Fa sbuf +at the current position. +.Pp +The +.Fn sbuf_finish +function null-terminates the +.Fa sbuf +and marks it as finished, which means that it may no longer be +modified using +.Fn sbuf_setpos , +.Fn sbuf_cat , +.Fn sbuf_cpu , +.Fn sbuf_printf +or +.Fn sbuf_putc . +.Pp +The +.Fn sbuf_data +and +.Fn sbuf_len +functions return the actual string and its length, respectively, and +only work on a finished and non-overflowed +.Fa sbuf . +.Pp +Finally, the +.Fn sbuf_delete +function clears the +.Fa sbuf +and frees its storage buffer if it was allocated by +.Fn sbuf_new . +.Sh NOTES +If an operation caused an +.Fa sbuf +to overflow, most subsequent operations (including +.Fn sbuf_finish ) +on it will fail until the +.Fa sbuf 's +position is reset to a value between 0 and one less than the size of +its storage buffer using +.Fn sbuf_setpos , +or it is reinitialized to a sufficiently short string using +.Fn sbuf_cpy . +.Sh RETURN VALUES +.Fn sbuf_new +returns \-1 if it failed to allocate a storage buffer, and zero +otherwise. +.Pp +.Fn sbuf_setpos +returns \-1 if +.Fa pos +was invalid, and zero otherwise. +.Pp +.Fn sbuf_cat , +.Fn sbuf_cpy , +.Fn sbuf_printf , +.Fn sbuf_putc , +and +.Fn sbuf_finish +all return \-1 if the buffer overflowed, and zero otherwise. +.Pp +.Fn sbuf_data +and +.Fn sbuf_len +return +.Fa NULL +and 0, respectively, if the buffer overflowed. +.Sh SEE ALSO +.Xr printf 3 , +.Xr strcat 3 , +.Xr strcpy 3 +.Sh HISTORY +The +.Nm sbuf +family of functions first appeared in +.Fx 4.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm sbuf +family of functions was designed by +.An Poul-Henning Kamp Aq phk@FreeBSD.org +and implemented by +.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org . +.Pp +This manual page was written by +.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org .