summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man9/sbuf.957
-rw-r--r--sys/kern/subr_sbuf.c98
-rw-r--r--sys/sys/sbuf.h7
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
OpenPOWER on IntegriCloud