aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man9/sbuf.910
-rw-r--r--sys/kern/subr_sbuf.c12
-rw-r--r--sys/sys/sbuf.h2
3 files changed, 21 insertions, 3 deletions
diff --git a/share/man/man9/sbuf.9 b/share/man/man9/sbuf.9
index e9de2c8f27bd..8af80ed968f5 100644
--- a/share/man/man9/sbuf.9
+++ b/share/man/man9/sbuf.9
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 5, 2017
+.Dd August 17, 2017
.Dt SBUF 9
.Os
.Sh NAME
@@ -271,6 +271,14 @@ 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 EDEADLK is set.
.El
.Pp
Note that if
diff --git a/sys/kern/subr_sbuf.c b/sys/kern/subr_sbuf.c
index d7465f9dd758..80c95dc917d2 100644
--- a/sys/kern/subr_sbuf.c
+++ b/sys/kern/subr_sbuf.c
@@ -73,6 +73,8 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#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))
/*
* Set / clear flags
@@ -308,6 +310,7 @@ sbuf_clear(struct sbuf *s)
SBUF_CLEARFLAG(s, SBUF_FINISHED);
s->s_error = 0;
s->s_len = 0;
+ s->s_rec_off = 0;
s->s_sect_len = 0;
}
@@ -362,7 +365,10 @@ sbuf_drain(struct sbuf *s)
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));
- len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
+ 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;
return (s->s_error);
@@ -370,6 +376,7 @@ sbuf_drain(struct sbuf *s)
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.
@@ -835,6 +842,7 @@ sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
("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,
@@ -865,7 +873,7 @@ sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
}
len = s->s_sect_len;
if (old_len == -1) {
- s->s_sect_len = 0;
+ s->s_rec_off = s->s_sect_len = 0;
SBUF_CLEARFLAG(s, SBUF_INSECTION);
} else {
s->s_sect_len += old_len;
diff --git a/sys/sys/sbuf.h b/sys/sys/sbuf.h
index c05bafd1e598..d0f908be7b6b 100644
--- a/sys/sys/sbuf.h
+++ b/sys/sys/sbuf.h
@@ -49,6 +49,7 @@ struct sbuf {
#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_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() */
@@ -56,6 +57,7 @@ struct sbuf {
#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