aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--common/iobuf.c138
1 files changed, 92 insertions, 46 deletions
diff --git a/common/iobuf.c b/common/iobuf.c
index 664db393f..fed98397a 100644
--- a/common/iobuf.c
+++ b/common/iobuf.c
@@ -1767,18 +1767,38 @@ underflow (iobuf_t a)
size_t len;
int rc;
- assert (a->d.start == a->d.len);
- if (a->use == IOBUF_TEMP)
- return -1; /* EOF because a temp buffer can't do an underflow */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: buffer size: %d; still buffered: %d => space for %d bytes\n",
+ a->no, a->subno,
+ (int) a->d.size, (int) (a->d.len - a->d.start),
+ (int) (a->d.size - (a->d.len - a->d.start)));
+
+ assert (a->use == IOBUF_INPUT);
+
+ /* If there is still some buffered data, then move it to the start
+ of the buffer and try to fill the end of the buffer. (This is
+ useful if we are called from iobuf_peek().) */
+ assert (a->d.start <= a->d.len);
+ a->d.len -= a->d.start;
+ memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len);
+ a->d.start = 0;
- if (a->filter_eof)
+ if (a->d.len == 0 && a->filter_eof)
+ /* The last time we tried to read from this filter, we got an EOF.
+ We couldn't return the EOF, because there was buffered data.
+ Since there is no longer any buffered data, return the
+ error. */
{
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n",
+ a->no, a->subno);
if (a->chain)
+ /* A filter follows this one. Free this filter. */
{
iobuf_t b = a->chain;
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: pop '%s' in underflow\n",
- a->no, a->subno, iobuf_desc (a));
+ log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n",
+ a->no, a->subno);
xfree (a->d.buf);
xfree (a->real_fname);
memcpy (a, b, sizeof *a);
@@ -1787,82 +1807,108 @@ underflow (iobuf_t a)
}
else
a->filter_eof = 0; /* for the top level filter */
- if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: underflow: eof (due to filter eof)\n",
- a->no, a->subno);
return -1; /* return one(!) EOF */
}
- if (a->error)
+
+ if (a->d.len == 0 && a->error)
+ /* The last time we tried to read from this filter, we got an
+ error. We couldn't return the error, because there was
+ buffered data. Since there is no longer any buffered data,
+ return the error. */
{
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: error\n", a->no, a->subno);
+ log_debug ("iobuf-%d.%d: pending error (%s) returned\n",
+ a->no, a->subno, gpg_strerror (a->error));
return -1;
}
- if (a->filter)
+ if (a->filter && ! a->filter_eof && ! a->error)
+ /* We have a filter function and the last time we tried to read we
+ didn't get an EOF or an error. Try to fill the buffer. */
{
- len = a->d.size;
+ /* Be careful to account for any buffered data. */
+ len = a->d.size - a->d.len;
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: underflow: req=%lu\n",
+ log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n",
a->no, a->subno, (ulong) len);
- rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
- a->d.buf, &len);
+ if (len == 0)
+ /* There is no space for more data. Don't bother calling
+ A->FILTER. */
+ rc = 0;
+ else
+ rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+ &a->d.buf[a->d.len], &len);
+ a->d.len += len;
+
if (DBG_IOBUF)
- {
- log_debug ("iobuf-%d.%d: underflow: got=%lu rc=%d\n",
- a->no, a->subno, (ulong) len, rc);
+ log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes\n",
+ a->no, a->subno,
+ rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc),
+ (ulong) len);
/* if( a->no == 1 ) */
/* log_hexdump (" data:", a->d.buf, len); */
- }
- if (a->use == IOBUF_INPUT && rc == -1)
- { /* EOF: we can remove the filter */
+
+ if (rc == -1)
+ /* EOF. */
+ {
size_t dummy_len = 0;
- /* and tell the filter to free itself */
+ /* Tell the filter to free itself */
if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain,
NULL, &dummy_len)))
log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
+
+ /* Free everything except for the internal buffer. */
if (a->filter_ov && a->filter_ov_owner)
- {
- xfree (a->filter_ov);
- a->filter_ov = NULL;
- }
- a->filter = NULL;
+ xfree (a->filter_ov);
a->filter_ov = NULL;
+ a->filter = NULL;
a->filter_eof = 1;
- if (!len && a->chain)
+
+ if (a->d.len == 0 && a->chain)
+ /* We don't need to keep this filter around at all:
+
+ - we got an EOF
+ - we have no buffered data
+ - a filter follows this one.
+
+ Unlink this filter. */
{
iobuf_t b = a->chain;
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: pop in underflow (!len)\n",
+ log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n",
a->no, a->subno);
xfree (a->d.buf);
xfree (a->real_fname);
memcpy (a, b, sizeof *a);
xfree (b);
+
print_chain (a);
+
+ return -1;
}
+ else if (a->d.len == 0)
+ /* We can't unlink this filter (it is the only one in the
+ pipeline), but we can immediately return EOF. */
+ return -1;
}
else if (rc)
- a->error = rc;
-
- if (!len)
+ /* Record the error. */
{
- if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: underflow: eof\n", a->no, a->subno);
- return -1;
+ a->error = rc;
+
+ if (a->d.len == 0)
+ /* There is no buffered data. Immediately return EOF. */
+ return -1;
}
- a->d.len = len;
- a->d.start = 0;
- return a->d.buf[a->d.start++];
- }
- else
- {
- if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: underflow: eof (no filter)\n",
- a->no, a->subno);
- return -1; /* no filter; return EOF */
}
+
+ assert (a->d.start <= a->d.len);
+ if (a->d.start < a->d.len)
+ return a->d.buf[a->d.start++];
+
+ /* EOF. */
+ return -1;
}