Index: head/share/man/man9/sbuf.9 =================================================================== --- head/share/man/man9/sbuf.9 (revision 350690) +++ head/share/man/man9/sbuf.9 (revision 350691) @@ -1,748 +1,753 @@ .\"- .\" 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 May 7, 2019 +.Dd August 7, 2019 .Dt SBUF 9 .Os .Sh NAME .Nm sbuf , .Nm sbuf_new , .Nm sbuf_new_auto , .Nm sbuf_new_for_sysctl , .Nm sbuf_clear , .Nm sbuf_get_flags , .Nm sbuf_set_flags , .Nm sbuf_clear_flags , .Nm sbuf_setpos , .Nm sbuf_bcat , .Nm sbuf_bcopyin , .Nm sbuf_bcpy , .Nm sbuf_cat , .Nm sbuf_copyin , .Nm sbuf_cpy , .Nm sbuf_printf , .Nm sbuf_vprintf , .Nm sbuf_putc , .Nm sbuf_set_drain , .Nm sbuf_trim , .Nm sbuf_error , .Nm sbuf_finish , .Nm sbuf_data , .Nm sbuf_len , .Nm sbuf_done , .Nm sbuf_delete , .Nm sbuf_start_section , .Nm sbuf_end_section , .Nm sbuf_hexdump , .Nm sbuf_printf_drain , .Nm sbuf_putbuf .Nd safe string composition .Sh SYNOPSIS .In sys/types.h .In sys/sbuf.h .Ft typedef\ int ( sbuf_drain_func ) ( void\ *arg, const\ char\ *data, int\ len ) ; .Pp .Ft struct sbuf * .Fo sbuf_new .Fa "struct sbuf *s" .Fa "char *buf" .Fa "int length" .Fa "int flags" .Fc .Ft struct sbuf * .Fo sbuf_new_auto .Fa "void" .Fc .Ft void .Fo sbuf_clear .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_get_flags .Fa "struct sbuf *s" .Fc .Ft void .Fo sbuf_set_flags .Fa "struct sbuf *s" .Fa "int flags" .Fc .Ft void .Fo sbuf_clear_flags .Fa "struct sbuf *s" .Fa "int flags" .Fc .Ft int .Fo sbuf_setpos .Fa "struct sbuf *s" .Fa "int pos" .Fc .Ft int .Fo sbuf_bcat .Fa "struct sbuf *s" .Fa "const void *buf" .Fa "size_t len" .Fc .Ft int .Fo sbuf_bcpy .Fa "struct sbuf *s" .Fa "const void *buf" .Fa "size_t len" .Fc .Ft int .Fo sbuf_cat .Fa "struct sbuf *s" .Fa "const char *str" .Fc .Ft int .Fo sbuf_cpy .Fa "struct sbuf *s" .Fa "const char *str" .Fc .Ft int .Fo sbuf_printf .Fa "struct sbuf *s" .Fa "const char *fmt" "..." .Fc .Ft int .Fo sbuf_vprintf .Fa "struct sbuf *s" .Fa "const char *fmt" .Fa "va_list ap" .Fc .Ft int .Fo sbuf_putc .Fa "struct sbuf *s" .Fa "int c" .Fc .Ft void .Fo sbuf_set_drain .Fa "struct sbuf *s" .Fa "sbuf_drain_func *func" .Fa "void *arg" .Fc .Ft int .Fo sbuf_trim .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_error .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_finish .Fa "struct sbuf *s" .Fc .Ft char * .Fo sbuf_data .Fa "struct sbuf *s" .Fc .Ft ssize_t .Fo sbuf_len .Fa "struct sbuf *s" .Fc .Ft int .Fo sbuf_done .Fa "struct sbuf *s" .Fc .Ft void .Fo sbuf_delete .Fa "struct sbuf *s" .Fc .Ft void .Fo sbuf_start_section .Fa "struct sbuf *s" .Fa "ssize_t *old_lenp" .Fc .Ft ssize_t .Fo sbuf_end_section .Fa "struct sbuf *s" .Fa "ssize_t old_len" .Fa "size_t pad" .Fa "int c" .Fc .Ft void .Fo sbuf_hexdump .Fa "struct sbuf *sb" .Fa "void *ptr" .Fa "int length" .Fa "const char *hdr" .Fa "int flags" .Fc .Ft int .Fo sbuf_printf_drain .Fa "void *arg" .Fa "const char *data" .Fa "int len" .Fc .Ft void .Fo sbuf_putbuf .Fa "struct sbuf *s" .Fc .Fd #ifdef _KERNEL .In sys/types.h .In sys/sbuf.h .Ft int .Fo sbuf_bcopyin .Fa "struct sbuf *s" .Fa "const void *uaddr" .Fa "size_t len" .Fc .Ft int .Fo sbuf_copyin .Fa "struct sbuf *s" .Fa "const void *uaddr" .Fa "size_t len" .Fc .In sys/sysctl.h .Ft struct sbuf * .Fo sbuf_new_for_sysctl .Fa "struct sbuf *s" .Fa "char *buf" .Fa "int length" .Fa "struct sysctl_req *req" .Fc .Fd #endif /* _KERNEL */ .Sh DESCRIPTION The .Nm family of functions allows one to safely allocate, compose and release strings in kernel or user space. .Pp Instead of arrays of characters, these functions operate on structures called .Fa sbufs , defined in .In sys/sbuf.h . .Pp Any errors encountered during the allocation or composition of the string will be latched in the data structure, making a single error test at the end of the composition sufficient to determine success or failure of the entire process. .Pp The .Fn sbuf_new function initializes the .Fa sbuf pointed to by its first argument. If that pointer is .Dv NULL , .Fn sbuf_new allocates a .Vt struct sbuf using .Xr malloc 9 . 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 initial size of the storage buffer. The fourth argument, .Fa flags , may be comprised of the following flags: .Bl -tag -width ".Dv SBUF_AUTOEXTEND" .It Dv SBUF_FIXEDLEN The storage buffer is fixed at its initial size. Attempting to extend the sbuf beyond this size results in an overflow condition. .It Dv SBUF_AUTOEXTEND This indicates that the storage buffer may be extended as necessary, so long as resources allow, to hold additional data. .It Dv SBUF_INCLUDENUL This causes the final nulterm byte to be counted in the length of the data. .It Dv SBUF_DRAINTOEOR Treat top-level sections started with .Fn sbuf_start_section as a record boundary marker that will be used during drain operations to avoid records being split. If a record grows sufficiently large such that it fills the .Fa sbuf and therefore cannot be drained without being split, an error of .Er EDEADLK is set. +.It Dv SBUF_NOWAIT +Indicates that attempts to extend the storage buffer should fail in low memory +conditions, like +.Xr malloc 9 +.Dv M_NOWAIT . .El .Pp Note that if .Fa buf is not .Dv NULL , it must point to an array of at least .Fa length characters. The result of accessing that array directly while it is in use by the sbuf is undefined. .Pp The .Fn sbuf_new_auto function is a shortcut for creating a completely dynamic .Nm . It is the equivalent of calling .Fn sbuf_new with values .Dv NULL , .Dv NULL , .Dv 0 , and .Dv SBUF_AUTOEXTEND . .Pp The .Fn sbuf_new_for_sysctl function will set up an sbuf with a drain function to use .Fn SYSCTL_OUT when the internal buffer fills. Note that if the various functions which append to an sbuf are used while a non-sleepable lock is held, the user buffer should be wired using .Fn sysctl_wire_old_buffer . .Pp The .Fn sbuf_delete function clears the .Fa sbuf and frees any memory allocated for it. There must be a call to .Fn sbuf_delete for every call to .Fn sbuf_new . Any attempt to access the sbuf after it has been deleted will fail. .Pp The .Fn sbuf_clear function invalidates the contents of the .Fa sbuf and resets its position to zero. .Pp The .Fn sbuf_get_flags function returns the current user flags. The .Fn sbuf_set_flags and .Fn sbuf_get_flags functions set or clear one or more user flags, respectively. The user flags are described under the .Fn sbuf_new function. .Pp The .Fn sbuf_setpos function sets the .Fa sbuf Ns 's end position to .Fa pos , which is a value between zero and one less than the size of the storage buffer. This effectively truncates the sbuf at the new position. .Pp The .Fn sbuf_bcat function appends the first .Fa len bytes from the buffer .Fa buf to the .Fa sbuf . .Pp The .Fn sbuf_bcopyin function copies .Fa len bytes from the specified userland address into the .Fa sbuf . .Pp The .Fn sbuf_bcpy function replaces the contents of the .Fa sbuf with the first .Fa len bytes from the buffer .Fa buf . .Pp The .Fn sbuf_cat function appends the NUL-terminated string .Fa str to the .Fa sbuf at the current position. .Pp The .Fn sbuf_set_drain function sets a drain function .Fa func for the .Fa sbuf , and records a pointer .Fa arg to be passed to the drain on callback. The drain function cannot be changed while .Fa sbuf_len is non-zero. .Pp The registered drain function .Vt sbuf_drain_func will be called with the argument .Fa arg provided to .Fn sbuf_set_drain , a pointer .Fa data to a byte string that is the contents of the sbuf, and the length .Fa len of the data. If the drain function exists, it will be called when the sbuf internal buffer is full, or on behalf of .Fn sbuf_finish . The drain function may drain some or all of the data, but must drain at least 1 byte. The return value from the drain function, if positive, indicates how many bytes were drained. If negative, the return value indicates the negative error code which will be returned from this or a later call to .Fn sbuf_finish . If the returned drained length is 0, an error of .Er EDEADLK is set. To do unbuffered draining, initialize the sbuf with a two-byte buffer. The drain will be called for every byte added to the sbuf. The .Fn sbuf_bcopyin , .Fn sbuf_copyin , .Fn sbuf_trim , and .Fn sbuf_data functions cannot be used on an sbuf with a drain. .Pp The .Fn sbuf_copyin function copies a NUL-terminated string from the specified userland address into the .Fa sbuf . If the .Fa len argument is non-zero, no more than .Fa len characters (not counting the terminating NUL) are copied; otherwise the entire string, or as much of it as can fit in the .Fa sbuf , is copied. .Pp The .Fn sbuf_cpy function replaces the contents of the .Fa sbuf with those of the NUL-terminated 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_clear or .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_vprintf function behaves the same as .Fn sbuf_printf except that the arguments are obtained from the variable-length argument list .Fa ap . .Pp The .Fn sbuf_putc function appends the character .Fa c to the .Fa sbuf at the current position. .Pp The .Fn sbuf_trim function removes trailing whitespace from the .Fa sbuf . .Pp The .Fn sbuf_error function returns any error value that the .Fa sbuf may have accumulated, either from the drain function, or .Er ENOMEM if the .Fa sbuf overflowed. This function is generally not needed and instead the error code from .Fn sbuf_finish is the preferred way to discover whether an sbuf had an error. .Pp The .Fn sbuf_finish function will call the attached drain function if one exists until all the data in the .Fa sbuf is flushed. If there is no attached drain, .Fn sbuf_finish NUL-terminates the .Fa sbuf . In either case it marks the .Fa sbuf as finished, which means that it may no longer be modified using .Fn sbuf_setpos , .Fn sbuf_cat , .Fn sbuf_cpy , .Fn sbuf_printf or .Fn sbuf_putc , until .Fn sbuf_clear is used to reset the sbuf. .Pp The .Fn sbuf_data function returns the actual string; .Fn sbuf_data only works on a finished .Fa sbuf . The .Fn sbuf_len function returns the length of the string. For an .Fa sbuf with an attached drain, .Fn sbuf_len returns the length of the un-drained data. .Fn sbuf_done returns non-zero if the .Fa sbuf is finished. .Pp The .Fn sbuf_start_section and .Fn sbuf_end_section functions may be used for automatic section alignment. The arguments .Fa pad and .Fa c specify the padding size and a character used for padding. The arguments .Fa old_lenp and .Fa old_len are to save and restore the current section length when nested sections are used. For the top level section .Dv NULL and \-1 can be specified for .Fa old_lenp and .Fa old_len respectively. .Pp The .Fn sbuf_hexdump function prints an array of bytes to the supplied sbuf, along with an ASCII representation of the bytes if possible. See the .Xr hexdump 3 man page for more details on the interface. .Pp The .Fn sbuf_printf_drain function is a drain function that will call printf, or log to the console. The argument .Fa arg must be either .Dv NULL , or a valid pointer to a .Vt size_t . If .Fa arg is not .Dv NULL , the total bytes drained will be added to the value pointed to by .Fa arg . .Pp The .Fn sbuf_putbuf function printfs the sbuf to stdout if in userland, and to the console and log if in the kernel. The .Fa sbuf must be finished before calling .Fn sbuf_putbuf . It does not drain the buffer or update any pointers. .Sh NOTES If an operation caused an .Fa sbuf to overflow, most subsequent operations on it will fail until the .Fa sbuf is finished using .Fn sbuf_finish or reset using .Fn sbuf_clear , or its 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 . .Pp Drains in user-space will not always function as indicated. While the drain function will be called immediately on overflow from the .Fa sbuf_putc , .Fa sbuf_bcat , .Fa sbuf_cat functions, .Fa sbuf_printf and .Fa sbuf_vprintf currently have no way to determine whether there will be an overflow until after it occurs, and cannot do a partial expansion of the format string. Thus when using libsbuf the buffer may be extended to allow completion of a single printf call, even though a drain is attached. .Sh RETURN VALUES The .Fn sbuf_new function returns .Dv NULL if it failed to allocate a storage buffer, and a pointer to the new .Fa sbuf otherwise. .Pp The .Fn sbuf_setpos function returns \-1 if .Fa pos was invalid, and zero otherwise. .Pp The .Fn sbuf_bcat , .Fn sbuf_cat , .Fn sbuf_cpy , .Fn sbuf_printf , .Fn sbuf_putc , and .Fn sbuf_trim functions all return \-1 if the buffer overflowed, and zero otherwise. .Pp The .Fn sbuf_error function returns a non-zero value if the buffer has an overflow or drain error, and zero otherwise. .Pp The .Fn sbuf_len function returns \-1 if the buffer overflowed. .Pp The .Fn sbuf_copyin function returns \-1 if copying string from userland failed, and number of bytes copied otherwise. .Pp The .Fn sbuf_end_section function returns the section length or \-1 if the buffer has an error. .Pp The .Fn sbuf_finish 9 function (the kernel version) returns .Er ENOMEM if the sbuf overflowed before being finished, or returns the error code from the drain if one is attached. .Pp The .Fn sbuf_finish 3 function (the userland version) will return zero for success and \-1 and set errno on error. .Sh EXAMPLES .Bd -literal -compact #include #include struct sbuf *sb; sb = sbuf_new_auto(); sbuf_cat(sb, "Customers found:\en"); TAILQ_FOREACH(foo, &foolist, list) { sbuf_printf(sb, " %4d %s\en", foo->index, foo->name); sbuf_printf(sb, " Address: %s\en", foo->address); sbuf_printf(sb, " Zip: %s\en", foo->zipcode); } if (sbuf_finish(sb) != 0) /* Check for any and all errors */ err(1, "Could not generate message"); transmit_msg(sbuf_data(sb), sbuf_len(sb)); sbuf_delete(sb); .Ed .Sh SEE ALSO .Xr hexdump 3 , .Xr printf 3 , .Xr strcat 3 , .Xr strcpy 3 , .Xr copyin 9 , .Xr copyinstr 9 , .Xr printf 9 .Sh HISTORY The .Nm family of functions first appeared in .Fx 4.4 . .Sh AUTHORS .An -nosplit The .Nm family of functions was designed by .An Poul-Henning Kamp Aq Mt phk@FreeBSD.org and implemented by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . Additional improvements were suggested by .An Justin T. Gibbs Aq Mt gibbs@FreeBSD.org . Auto-extend support added by .An Kelly Yancey Aq Mt kbyanc@FreeBSD.org . Drain functionality added by .An Matthew Fleming Aq Mt mdf@FreeBSD.org . .Pp This manual page was written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . Index: head/sys/kern/subr_sbuf.c =================================================================== --- head/sys/kern/subr_sbuf.c (revision 350690) +++ head/sys/kern/subr_sbuf.c (revision 350691) @@ -1,899 +1,901 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000-2008 Poul-Henning Kamp * Copyright (c) 2000-2008 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. * * 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. */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #else /* _KERNEL */ #include #include #include #include #include #include #include #endif /* _KERNEL */ #include #ifdef _KERNEL static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers"); -#define SBMALLOC(size) malloc(size, M_SBUF, M_WAITOK|M_ZERO) +#define SBMALLOC(size, flags) malloc(size, M_SBUF, (flags) | M_ZERO) #define SBFREE(buf) free(buf, M_SBUF) #else /* _KERNEL */ #define KASSERT(e, m) -#define SBMALLOC(size) calloc(1, size) +#define SBMALLOC(size, flags) calloc(1, size) #define SBFREE(buf) free(buf) #endif /* _KERNEL */ /* * Predicates */ #define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC) #define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT) #define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED) #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) #define SBUF_ISSECTION(s) ((s)->s_flags & SBUF_INSECTION) #define SBUF_NULINCLUDED(s) ((s)->s_flags & SBUF_INCLUDENUL) #define SBUF_ISDRAINTOEOR(s) ((s)->s_flags & SBUF_DRAINTOEOR) #define SBUF_DODRAINTOEOR(s) (SBUF_ISSECTION(s) && SBUF_ISDRAINTOEOR(s)) +#define SBUF_MALLOCFLAG(s) \ + (((s)->s_flags & SBUF_NOWAIT) ? M_NOWAIT : M_WAITOK) /* * Set / clear flags */ #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_MINSIZE 2 /* Min is 1 byte + nulterm. */ #define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */ #ifdef PAGE_SIZE #define SBUF_MAXEXTENDSIZE PAGE_SIZE #define SBUF_MAXEXTENDINCR PAGE_SIZE #else #define SBUF_MAXEXTENDSIZE 4096 #define SBUF_MAXEXTENDINCR 4096 #endif /* * Debugging support */ #if defined(_KERNEL) && defined(INVARIANTS) static void _assert_sbuf_integrity(const char *fun, struct sbuf *s) { KASSERT(s != NULL, ("%s called with a NULL sbuf pointer", fun)); KASSERT(s->s_buf != NULL, ("%s called with uninitialized or corrupt sbuf", fun)); if (SBUF_ISFINISHED(s) && SBUF_NULINCLUDED(s)) { KASSERT(s->s_len <= s->s_size, ("wrote past end of sbuf (%jd >= %jd)", (intmax_t)s->s_len, (intmax_t)s->s_size)); } else { KASSERT(s->s_len < s->s_size, ("wrote past end of sbuf (%jd >= %jd)", (intmax_t)s->s_len, (intmax_t)s->s_size)); } } static void _assert_sbuf_state(const char *fun, struct sbuf *s, int state) { KASSERT((s->s_flags & SBUF_FINISHED) == state, ("%s called with %sfinished or corrupt sbuf", fun, (state ? "un" : ""))); } #define assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s)) #define assert_sbuf_state(s, i) _assert_sbuf_state(__func__, (s), (i)) #else /* _KERNEL && INVARIANTS */ #define assert_sbuf_integrity(s) do { } while (0) #define assert_sbuf_state(s, i) do { } while (0) #endif /* _KERNEL && INVARIANTS */ #ifdef CTASSERT CTASSERT(powerof2(SBUF_MAXEXTENDSIZE)); CTASSERT(powerof2(SBUF_MAXEXTENDINCR)); #endif static int sbuf_extendsize(int size) { int newsize; if (size < (int)SBUF_MAXEXTENDSIZE) { newsize = SBUF_MINEXTENDSIZE; while (newsize < size) newsize *= 2; } else { newsize = roundup2(size, SBUF_MAXEXTENDINCR); } KASSERT(newsize >= size, ("%s: %d < %d\n", __func__, newsize, size)); 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 = SBMALLOC(newsize); + newbuf = SBMALLOC(newsize, SBUF_MALLOCFLAG(s)); if (newbuf == NULL) return (-1); memcpy(newbuf, s->s_buf, 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 the internals of an sbuf. * If buf is non-NULL, it points to a static or already-allocated string * big enough to hold at least length characters. */ static struct sbuf * sbuf_newbuf(struct sbuf *s, char *buf, int length, int flags) { memset(s, 0, sizeof(*s)); s->s_flags = flags; s->s_size = length; s->s_buf = buf; - if ((s->s_flags & SBUF_AUTOEXTEND) == 0) { + if (!SBUF_CANEXTEND(s)) { KASSERT(s->s_size >= SBUF_MINSIZE, ("attempt to create an sbuf smaller than %d bytes", SBUF_MINSIZE)); } if (s->s_buf != NULL) return (s); - if ((flags & SBUF_AUTOEXTEND) != 0) + if (SBUF_CANEXTEND(s)) s->s_size = sbuf_extendsize(s->s_size); - s->s_buf = SBMALLOC(s->s_size); + s->s_buf = SBMALLOC(s->s_size, SBUF_MALLOCFLAG(s)); if (s->s_buf == NULL) return (NULL); SBUF_SETFLAG(s, SBUF_DYNAMIC); return (s); } /* * 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. */ struct sbuf * 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 & ~SBUF_USRFLAGMSK) == 0, ("%s called with invalid flags", __func__)); flags &= SBUF_USRFLAGMSK; if (s != NULL) return (sbuf_newbuf(s, buf, length, flags)); - s = SBMALLOC(sizeof(*s)); + s = SBMALLOC(sizeof(*s), (flags & SBUF_NOWAIT) ? M_NOWAIT : M_WAITOK); if (s == NULL) return (NULL); if (sbuf_newbuf(s, buf, length, flags) == NULL) { SBFREE(s); return (NULL); } SBUF_SETFLAG(s, SBUF_DYNSTRUCT); return (s); } #ifdef _KERNEL /* * Create an sbuf with uio data */ struct sbuf * sbuf_uionew(struct sbuf *s, struct uio *uio, int *error) { KASSERT(uio != NULL, ("%s called with NULL uio pointer", __func__)); KASSERT(error != NULL, ("%s called with NULL error pointer", __func__)); s = sbuf_new(s, NULL, uio->uio_resid + 1, 0); if (s == NULL) { *error = ENOMEM; return (NULL); } *error = uiomove(s->s_buf, uio->uio_resid, uio); if (*error != 0) { sbuf_delete(s); return (NULL); } s->s_len = s->s_size - 1; if (SBUF_ISSECTION(s)) s->s_sect_len = s->s_size - 1; *error = 0; return (s); } #endif int sbuf_get_flags(struct sbuf *s) { return (s->s_flags & SBUF_USRFLAGMSK); } void sbuf_clear_flags(struct sbuf *s, int flags) { s->s_flags &= ~(flags & SBUF_USRFLAGMSK); } void sbuf_set_flags(struct sbuf *s, int flags) { s->s_flags |= (flags & SBUF_USRFLAGMSK); } /* * Clear an sbuf and reset its position. */ void sbuf_clear(struct sbuf *s) { assert_sbuf_integrity(s); /* don't care if it's finished or not */ SBUF_CLEARFLAG(s, SBUF_FINISHED); s->s_error = 0; s->s_len = 0; s->s_rec_off = 0; s->s_sect_len = 0; } /* * Set the sbuf's end position to an arbitrary value. * Effectively truncates the sbuf at the new position. */ int sbuf_setpos(struct sbuf *s, ssize_t pos) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(pos >= 0, ("attempt to seek to a negative position (%jd)", (intmax_t)pos)); KASSERT(pos < s->s_size, ("attempt to seek past end of sbuf (%jd >= %jd)", (intmax_t)pos, (intmax_t)s->s_size)); KASSERT(!SBUF_ISSECTION(s), ("attempt to seek when in a section")); if (pos < 0 || pos > s->s_len) return (-1); s->s_len = pos; return (0); } /* * Drain into a counter. Counts amount of data without producing output. * Useful for cases like sysctl, where user may first request only size. * This allows to avoid pointless allocation/freeing of large buffers. */ int sbuf_count_drain(void *arg, const char *data __unused, int len) { size_t *sizep; sizep = (size_t *)arg; *sizep += len; return (len); } /* * Set up a drain function and argument on an sbuf to flush data to * when the sbuf buffer overflows. */ void sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx) { assert_sbuf_state(s, 0); assert_sbuf_integrity(s); KASSERT(func == s->s_drain_func || s->s_len == 0, ("Cannot change drain to %p on non-empty sbuf %p", func, s)); s->s_drain_func = func; s->s_drain_arg = ctx; } /* * Call the drain and process the return. */ static int sbuf_drain(struct sbuf *s) { int len; KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s)); KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s)); if (SBUF_DODRAINTOEOR(s) && s->s_rec_off == 0) return (s->s_error = EDEADLK); len = s->s_drain_func(s->s_drain_arg, s->s_buf, SBUF_DODRAINTOEOR(s) ? s->s_rec_off : s->s_len); if (len <= 0) { s->s_error = len ? -len : EDEADLK; return (s->s_error); } KASSERT(len > 0 && len <= s->s_len, ("Bad drain amount %d for sbuf %p", len, s)); s->s_len -= len; s->s_rec_off -= len; /* * Fast path for the expected case where all the data was * drained. */ if (s->s_len == 0) return (0); /* * Move the remaining characters to the beginning of the * string. */ memmove(s->s_buf, s->s_buf + len, s->s_len); return (0); } /* * Append bytes to an sbuf. This is the core function for appending * to an sbuf and is the main place that deals with extending the * buffer and marking overflow. */ static void sbuf_put_bytes(struct sbuf *s, const char *buf, size_t len) { size_t n; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); if (s->s_error != 0) return; while (len > 0) { if (SBUF_FREESPACE(s) <= 0) { /* * If there is a drain, use it, otherwise extend the * buffer. */ if (s->s_drain_func != NULL) (void)sbuf_drain(s); else if (sbuf_extend(s, len > INT_MAX ? INT_MAX : len) < 0) s->s_error = ENOMEM; if (s->s_error != 0) return; } n = SBUF_FREESPACE(s); if (len < n) n = len; memcpy(&s->s_buf[s->s_len], buf, n); s->s_len += n; if (SBUF_ISSECTION(s)) s->s_sect_len += n; len -= n; buf += n; } } static void sbuf_put_byte(struct sbuf *s, char c) { sbuf_put_bytes(s, &c, 1); } /* * Append a byte string to an sbuf. */ int sbuf_bcat(struct sbuf *s, const void *buf, size_t len) { sbuf_put_bytes(s, buf, len); if (s->s_error != 0) return (-1); return (0); } #ifdef _KERNEL /* * Copy a byte string from userland into an sbuf. */ int sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(s->s_drain_func == NULL, ("Nonsensical copyin to sbuf %p with a drain", s)); if (s->s_error != 0) return (-1); if (len == 0) return (0); if (len > SBUF_FREESPACE(s)) { sbuf_extend(s, len - SBUF_FREESPACE(s)); if (SBUF_FREESPACE(s) < len) len = SBUF_FREESPACE(s); } if (copyin(uaddr, s->s_buf + s->s_len, len) != 0) return (-1); s->s_len += len; return (0); } #endif /* * Copy a byte string into an sbuf. */ int sbuf_bcpy(struct sbuf *s, const void *buf, size_t len) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); sbuf_clear(s); return (sbuf_bcat(s, buf, len)); } /* * Append a string to an sbuf. */ int sbuf_cat(struct sbuf *s, const char *str) { size_t n; n = strlen(str); sbuf_put_bytes(s, str, n); if (s->s_error != 0) return (-1); return (0); } #ifdef _KERNEL /* * Append a string from userland to an sbuf. */ int sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len) { size_t done; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(s->s_drain_func == NULL, ("Nonsensical copyin to sbuf %p with a drain", s)); if (s->s_error != 0) return (-1); if (len == 0) len = SBUF_FREESPACE(s); /* XXX return 0? */ if (len > SBUF_FREESPACE(s)) { sbuf_extend(s, len); if (SBUF_FREESPACE(s) < len) len = SBUF_FREESPACE(s); } switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) { case ENAMETOOLONG: s->s_error = ENOMEM; /* fall through */ case 0: s->s_len += done - 1; if (SBUF_ISSECTION(s)) s->s_sect_len += done - 1; break; default: return (-1); /* XXX */ } return (done); } #endif /* * Copy a string into an sbuf. */ int sbuf_cpy(struct sbuf *s, const char *str) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); sbuf_clear(s); return (sbuf_cat(s, str)); } /* * Format the given argument list and append the resulting string to an sbuf. */ #ifdef _KERNEL /* * Append a non-NUL character to an sbuf. This prototype signature is * suitable for use with kvprintf(9). */ static void sbuf_putc_func(int c, void *arg) { if (c != '\0') sbuf_put_byte(arg, c); } int sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(fmt != NULL, ("%s called with a NULL format string", __func__)); (void)kvprintf(fmt, sbuf_putc_func, s, 10, ap); if (s->s_error != 0) return (-1); return (0); } #else /* !_KERNEL */ int sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap) { va_list ap_copy; int error, len; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(fmt != NULL, ("%s called with a NULL format string", __func__)); if (s->s_error != 0) return (-1); /* * For the moment, there is no way to get vsnprintf(3) to hand * back a character at a time, to push everything into * sbuf_putc_func() as was done for the kernel. * * In userspace, while drains are useful, there's generally * not a problem attempting to malloc(3) on out of space. So * expand a userland sbuf if there is not enough room for the * data produced by sbuf_[v]printf(3). */ error = 0; do { va_copy(ap_copy, ap); len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1, fmt, ap_copy); if (len < 0) { s->s_error = errno; return (-1); } va_end(ap_copy); if (SBUF_FREESPACE(s) >= len) break; /* Cannot print with the current available space. */ if (s->s_drain_func != NULL && s->s_len > 0) error = sbuf_drain(s); /* sbuf_drain() sets s_error. */ else if (sbuf_extend(s, len - SBUF_FREESPACE(s)) != 0) s->s_error = error = ENOMEM; } while (error == 0); /* * s->s_len is the length of the string, without the terminating nul. * When updating s->s_len, we must subtract 1 from the length that * we passed into vsnprintf() because that length includes the * terminating nul. * * vsnprintf() returns the amount that would have been copied, * given sufficient space, so don't over-increment s_len. */ if (SBUF_FREESPACE(s) < len) len = SBUF_FREESPACE(s); s->s_len += len; if (SBUF_ISSECTION(s)) s->s_sect_len += len; KASSERT(s->s_len < s->s_size, ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size)); if (s->s_error != 0) return (-1); return (0); } #endif /* _KERNEL */ /* * 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 sbuf_putc(struct sbuf *s, int c) { sbuf_put_byte(s, c); if (s->s_error != 0) return (-1); return (0); } /* * Trim whitespace characters from end of an sbuf. */ int sbuf_trim(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); if (s->s_error != 0) return (-1); while (s->s_len > 0 && isspace(s->s_buf[s->s_len-1])) { --s->s_len; if (SBUF_ISSECTION(s)) s->s_sect_len--; } return (0); } /* * Check if an sbuf has an error. */ int sbuf_error(const struct sbuf *s) { return (s->s_error); } /* * Finish off an sbuf. */ int sbuf_finish(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); s->s_buf[s->s_len] = '\0'; if (SBUF_NULINCLUDED(s)) s->s_len++; if (s->s_drain_func != NULL) { while (s->s_len > 0 && s->s_error == 0) s->s_error = sbuf_drain(s); } SBUF_SETFLAG(s, SBUF_FINISHED); #ifdef _KERNEL return (s->s_error); #else if (s->s_error != 0) { errno = s->s_error; return (-1); } return (0); #endif } /* * Return a pointer to the sbuf data. */ char * sbuf_data(struct sbuf *s) { assert_sbuf_integrity(s); assert_sbuf_state(s, SBUF_FINISHED); KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); return (s->s_buf); } /* * Return the length of the sbuf data. */ ssize_t sbuf_len(struct sbuf *s) { assert_sbuf_integrity(s); /* don't care if it's finished or not */ KASSERT(s->s_drain_func == NULL, ("%s makes no sense on sbuf %p with drain", __func__, s)); if (s->s_error != 0) return (-1); /* If finished, nulterm is already in len, else add one. */ if (SBUF_NULINCLUDED(s) && !SBUF_ISFINISHED(s)) return (s->s_len + 1); return (s->s_len); } /* * Clear an sbuf, free its buffer if necessary. */ void sbuf_delete(struct sbuf *s) { int isdyn; assert_sbuf_integrity(s); /* don't care if it's finished or not */ if (SBUF_ISDYNAMIC(s)) SBFREE(s->s_buf); isdyn = SBUF_ISDYNSTRUCT(s); memset(s, 0, sizeof(*s)); if (isdyn) SBFREE(s); } /* * Check if an sbuf has been finished. */ int sbuf_done(const struct sbuf *s) { return (SBUF_ISFINISHED(s)); } /* * Start a section. */ void sbuf_start_section(struct sbuf *s, ssize_t *old_lenp) { assert_sbuf_integrity(s); assert_sbuf_state(s, 0); if (!SBUF_ISSECTION(s)) { KASSERT(s->s_sect_len == 0, ("s_sect_len != 0 when starting a section")); if (old_lenp != NULL) *old_lenp = -1; s->s_rec_off = s->s_len; SBUF_SETFLAG(s, SBUF_INSECTION); } else { KASSERT(old_lenp != NULL, ("s_sect_len should be saved when starting a subsection")); *old_lenp = s->s_sect_len; s->s_sect_len = 0; } } /* * End the section padding to the specified length with the specified * character. */ ssize_t sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c) { ssize_t len; assert_sbuf_integrity(s); assert_sbuf_state(s, 0); KASSERT(SBUF_ISSECTION(s), ("attempt to end a section when not in a section")); if (pad > 1) { len = roundup(s->s_sect_len, pad) - s->s_sect_len; for (; s->s_error == 0 && len > 0; len--) sbuf_put_byte(s, c); } len = s->s_sect_len; if (old_len == -1) { s->s_rec_off = s->s_sect_len = 0; SBUF_CLEARFLAG(s, SBUF_INSECTION); } else { s->s_sect_len += old_len; } if (s->s_error != 0) return (-1); return (len); } Index: head/sys/sys/sbuf.h =================================================================== --- head/sys/sys/sbuf.h (revision 350690) +++ head/sys/sys/sbuf.h (revision 350691) @@ -1,118 +1,119 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000-2008 Poul-Henning Kamp * Copyright (c) 2000-2008 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. * * 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$ */ #ifndef _SYS_SBUF_H_ #define _SYS_SBUF_H_ #include struct sbuf; typedef int (sbuf_drain_func)(void *, const char *, int); /* * Structure definition */ struct sbuf { char *s_buf; /* storage buffer */ sbuf_drain_func *s_drain_func; /* drain function */ void *s_drain_arg; /* user-supplied drain argument */ int s_error; /* current error code */ ssize_t s_size; /* size of storage buffer */ ssize_t s_len; /* current length of string */ #define SBUF_FIXEDLEN 0x00000000 /* fixed length buffer (default) */ #define SBUF_AUTOEXTEND 0x00000001 /* automatically extend buffer */ #define SBUF_INCLUDENUL 0x00000002 /* nulterm byte is counted in len */ #define SBUF_DRAINTOEOR 0x00000004 /* use section 0 as drain EOR marker */ +#define SBUF_NOWAIT 0x00000008 /* Extend with non-blocking malloc */ #define SBUF_USRFLAGMSK 0x0000ffff /* mask of flags the user may specify */ #define SBUF_DYNAMIC 0x00010000 /* s_buf must be freed */ #define SBUF_FINISHED 0x00020000 /* set by sbuf_finish() */ #define SBUF_DYNSTRUCT 0x00080000 /* sbuf must be freed */ #define SBUF_INSECTION 0x00100000 /* set by sbuf_start_section() */ int s_flags; /* flags */ ssize_t s_sect_len; /* current length of section */ ssize_t s_rec_off; /* current record start offset */ }; #ifndef HD_COLUMN_MASK #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 #define HD_OMIT_COUNT (1 << 16) #define HD_OMIT_HEX (1 << 17) #define HD_OMIT_CHARS (1 << 18) #endif /* HD_COLUMN_MASK */ __BEGIN_DECLS /* * API functions */ struct sbuf *sbuf_new(struct sbuf *, char *, int, int); #define sbuf_new_auto() \ sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND) int sbuf_get_flags(struct sbuf *); void sbuf_clear_flags(struct sbuf *, int); void sbuf_set_flags(struct sbuf *, int); void sbuf_clear(struct sbuf *); int sbuf_setpos(struct sbuf *, ssize_t); int sbuf_bcat(struct sbuf *, const void *, size_t); int sbuf_bcpy(struct sbuf *, const void *, size_t); int sbuf_cat(struct sbuf *, const char *); int sbuf_cpy(struct sbuf *, const char *); int sbuf_printf(struct sbuf *, const char *, ...) __printflike(2, 3); int sbuf_vprintf(struct sbuf *, const char *, __va_list) __printflike(2, 0); int sbuf_putc(struct sbuf *, int); void sbuf_set_drain(struct sbuf *, sbuf_drain_func *, void *); int sbuf_trim(struct sbuf *); int sbuf_error(const struct sbuf *); int sbuf_finish(struct sbuf *); char *sbuf_data(struct sbuf *); ssize_t sbuf_len(struct sbuf *); int sbuf_done(const struct sbuf *); void sbuf_delete(struct sbuf *); void sbuf_start_section(struct sbuf *, ssize_t *); ssize_t sbuf_end_section(struct sbuf *, ssize_t, size_t, int); void sbuf_hexdump(struct sbuf *, const void *, int, const char *, int); int sbuf_count_drain(void *arg, const char *data, int len); int sbuf_printf_drain(void *arg, const char *data, int len); void sbuf_putbuf(struct sbuf *); #ifdef _KERNEL struct uio; struct sbuf *sbuf_uionew(struct sbuf *, struct uio *, int *); int sbuf_bcopyin(struct sbuf *, const void *, size_t); int sbuf_copyin(struct sbuf *, const void *, size_t); #endif __END_DECLS #endif