diff options
-rw-r--r-- | share/man/man9/sbuf.9 | 57 | ||||
-rw-r--r-- | sys/kern/subr_sbuf.c | 98 | ||||
-rw-r--r-- | sys/sys/sbuf.h | 7 |
3 files changed, 160 insertions, 2 deletions
diff --git a/share/man/man9/sbuf.9 b/share/man/man9/sbuf.9 index 6c5b112..049aa7b 100644 --- a/share/man/man9/sbuf.9 +++ b/share/man/man9/sbuf.9 @@ -32,7 +32,11 @@ .Nm sbuf_new , .Nm sbuf_clear , .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_putc , @@ -52,8 +56,16 @@ .Ft int .Fn sbuf_setpos "struct sbuf *s" "int pos" .Ft int +.Fn sbuf_bcat "struct sbuf *s" "const char *str" "size_t len" +.Ft int +.Fn sbuf_bcopyin "struct sbuf *s" "const void *uaddr" "size_t len" +.Ft int +.Fn sbuf_bcpy "struct sbuf *s" "const char *str" "size_t len" +.Ft int .Fn sbuf_cat "struct sbuf *s" "const char *str" .Ft int +.Fn sbuf_copyin "struct sbuf *s" "const void *uaddr" "size_t len" +.Ft int .Fn sbuf_cpy "struct sbuf *s" "const char *str" .Ft int .Fn sbuf_printf "struct sbuf *s" "char *fmt" "..." @@ -131,18 +143,57 @@ which is a value between zero and one less than the size of the storage buffer. .Pp The +.Fn sbuf_bcat +function appends the first +.Fa len +bytes from the byte string +.Fa str +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 byte string +.Fa str . +.Pp +The .Fn sbuf_cat -function appends the string +function appends the NUL-terminated string .Fa str to the .Fa sbuf at the current position. .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 string +with those of the NUL-terminated string .Fa str . This is equivalent to calling .Fn sbuf_cat @@ -248,6 +299,8 @@ return .Dv NULL and \-1, respectively, if the buffer overflowed. .Sh SEE ALSO +.Xr copyin 9 , +.Xr copyinstr 9 , .Xr printf 3 , .Xr strcat 3 , .Xr strcpy 3 diff --git a/sys/kern/subr_sbuf.c b/sys/kern/subr_sbuf.c index 6de352d..0493aef 100644 --- a/sys/kern/subr_sbuf.c +++ b/sys/kern/subr_sbuf.c @@ -167,6 +167,72 @@ sbuf_setpos(struct sbuf *s, int pos) } /* + * Append a byte string to an sbuf. + */ +int +sbuf_bcat(struct sbuf *s, const char *str, size_t len) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + + while (len-- && SBUF_HASROOM(s)) + s->s_buf[s->s_len++] = *str++; + if (len) { + SBUF_SETFLAG(s, SBUF_OVERFLOWED); + 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); + + if (SBUF_HASOVERFLOWED(s)) + return (-1); + + if (len == 0) + return (0); + if (len > (s->s_size - s->s_len - 1)) + len = s->s_size - s->s_len - 1; + switch (copyin(uaddr, s->s_buf + s->s_len, len)) { + case ENAMETOOLONG: + SBUF_SETFLAG(s, SBUF_OVERFLOWED); + /* fall through */ + case 0: + s->s_len += len; + break; + default: + return (-1); /* XXX */ + } + + return (0); +} +#endif + +/* + * Copy a byte string into an sbuf. + */ +int +sbuf_bcpy(struct sbuf *s, const char *str, size_t len) +{ + assert_sbuf_integrity(s); + assert_sbuf_state(s, 0); + + sbuf_clear(s); + return (sbuf_bcat(s, str, len)); +} + +/* * Append a string to an sbuf. */ int @@ -187,6 +253,38 @@ sbuf_cat(struct sbuf *s, const char *str) return (0); } +#ifdef _KERNEL +/* + * Copy a string from userland into 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); + + 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; + switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) { + case ENAMETOOLONG: + SBUF_SETFLAG(s, SBUF_OVERFLOWED); + /* fall through */ + case 0: + s->s_len += done - 1; + break; + default: + return (-1); /* XXX */ + } + + return (0); +} +#endif + /* * Copy a string into an sbuf. */ diff --git a/sys/sys/sbuf.h b/sys/sys/sbuf.h index 0e0d7d5..80d413b 100644 --- a/sys/sys/sbuf.h +++ b/sys/sys/sbuf.h @@ -54,6 +54,8 @@ __BEGIN_DECLS struct sbuf *sbuf_new(struct sbuf *s, char *buf, int length, int flags); void sbuf_clear(struct sbuf *s); int sbuf_setpos(struct sbuf *s, int pos); +int sbuf_bcat(struct sbuf *s, const char *str, size_t len); +int sbuf_bcpy(struct sbuf *s, const char *str, size_t len); int sbuf_cat(struct sbuf *s, const char *str); int sbuf_cpy(struct sbuf *s, const char *str); int sbuf_printf(struct sbuf *s, char *fmt, ...); @@ -63,6 +65,11 @@ void sbuf_finish(struct sbuf *s); char *sbuf_data(struct sbuf *s); int sbuf_len(struct sbuf *s); void sbuf_delete(struct sbuf *s); + +#ifdef _KERNEL +int sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len); +int sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len); +#endif __END_DECLS #endif |