/* iobuf.c - File Handling for OpenPGP.
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
* 2009, 2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2015 g10 Code GmbH
*
* This file is part of GnuPG.
*
} sock_filter_ctx_t;
#endif /*HAVE_W32_SYSTEM*/
-/* The first partial length header block must be of size 512
- * to make it easier (and efficienter) we use a min. block size of 512
- * for all chunks (but the last one) */
+/* The first partial length header block must be of size 512 to make
+ * it easier (and more efficient) we use a min. block size of 512 for
+ * all chunks (but the last one) */
#define OP_MIN_PARTIAL_CHUNK 512
#define OP_MIN_PARTIAL_CHUNK_2POW 9
-enum
- {
- BLOCK_FILTER_INPUT=1,
- BLOCK_FILTER_OUTPUT=2,
- BLOCK_FILTER_TEMP=3
- };
-
/* The context we use for the block filter (used to handle OpenPGP
length information header). */
typedef struct
/* Global flag to tell whether special file names are enabled. See
- gpg.c for an explanation of these file names. FIXME: it does not
- belong into the iobuf subsystem. */
+ gpg.c for an explanation of these file names. FIXME: This does not
+ belong in the iobuf subsystem. */
static int special_names_enabled;
/* Local prototypes. */
-static int underflow (iobuf_t a);
+static int underflow (iobuf_t a, int clear_pending_eof);
static int translate_file_handle (int fd, int for_write);
+/* Sends any pending data to the filter's FILTER function. Note: this
+ works on the filter and not on the whole pipeline. That is,
+ iobuf_flush doesn't necessarily cause data to be written to any
+ underlying file; it just causes any data buffered at the filter A
+ to be sent to A's filter function.
+
+ If A is a IOBUF_OUTPUT_TEMP filter, then this also enlarges the
+ buffer by IOBUF_BUFFER_SIZE.
+
+ May only be called on an IOBUF_OUTPUT or IOBUF_OUTPUT_TEMP filters. */
+static int filter_flush (iobuf_t a);
+
\f
/* This is a replacement for strcmp. Under W32 it does not
}
/*
- * Do an direct_open on FNAME but first try to reuse one from the fd_cache
+ * Do a direct_open on FNAME but first try to reuse one from the fd_cache
*/
static gnupg_fd_t
fd_cache_open (const char *fname, const char *mode)
}
-/****************
- * Read data from a file into buf which has an allocated length of *LEN.
- * return the number of read bytes in *LEN. OPAQUE is the FILE * of
- * the stream. A is not used.
- * control may be:
- * IOBUFCTRL_INIT: called just before the function is linked into the
- * list of function. This can be used to prepare internal
- * data structures of the function.
- * IOBUFCTRL_FREE: called just before the function is removed from the
- * list of functions and can be used to release internal
- * data structures or close a file etc.
- * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer
- * with new stuff. *RET_LEN is the available size of the
- * buffer, and should be set to the number of bytes
- * which were put into the buffer. The function
- * returns 0 to indicate success, -1 on EOF and
- * GPG_ERR_xxxxx for other errors.
- *
- * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff.
- * *RET_LAN is the number of bytes in BUF.
- *
- * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The
- * filter may take appropriate action on this message.
- */
static int
file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
size_t * ret_len)
log_debug ("init block_filter %p\n", a);
if (a->partial)
a->count = 0;
- else if (a->use == BLOCK_FILTER_INPUT)
+ else if (a->use == IOBUF_INPUT)
a->count = a->size = 0;
else
a->count = a->size; /* force first length bytes */
}
else if (control == IOBUFCTRL_FREE)
{
- if (a->use == BLOCK_FILTER_OUTPUT)
+ if (a->use == IOBUF_OUTPUT)
{ /* write the end markers */
if (a->partial)
{
return rc;
}
+static const char *
+iobuf_desc (iobuf_t a)
+{
+ size_t dummy_len = 0;
+ const char *desc = "?";
+
+ if (! a || ! a->filter)
+ return desc;
+
+ a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
+ (byte *) & desc, &dummy_len);
+
+ return desc;
+}
static void
print_chain (iobuf_t a)
return;
for (; a; a = a->chain)
{
- size_t dummy_len = 0;
- const char *desc = "[none]";
-
- if (a->filter)
- a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
- (byte *) & desc, &dummy_len);
log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n",
- a->no, a->subno, desc?desc:"?", a->filter_eof,
+ a->no, a->subno, iobuf_desc (a), a->filter_eof,
(int) a->d.start, (int) a->d.len);
}
}
return 0;
}
-/* Allocate a new io buffer, with no function assigned.
-
- USE is the desired usage: BLOCK_FILTER_INPUT for input,
- BLOCK_FILTER_OUTPUT for output, or BLOCK_FILTER_TEMP for a temp
- buffer.
-
- BUFSIZE is a suggested buffer size.
- */
iobuf_t
iobuf_alloc (int use, size_t bufsize)
{
iobuf_t a;
static int number = 0;
- assert (use == BLOCK_FILTER_INPUT
- || use == BLOCK_FILTER_OUTPUT
- || use == BLOCK_FILTER_TEMP);
+ assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP
+ || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP);
+ if (bufsize == 0)
+ {
+ log_bug ("iobuf_alloc() passed a bufsize of 0!\n");
+ bufsize = IOBUF_BUFFER_SIZE;
+ }
a = xcalloc (1, sizeof *a);
a->use = use;
a->d.size = bufsize;
a->no = ++number;
a->subno = 0;
- a->opaque = NULL;
a->real_fname = NULL;
return a;
}
int
iobuf_close (iobuf_t a)
{
- iobuf_t a2;
+ iobuf_t a_chain;
size_t dummy_len = 0;
int rc = 0;
- if (a && a->directfp)
+ for (; a; a = a_chain)
{
- fclose (a->directfp);
- xfree (a->real_fname);
- if (DBG_IOBUF)
- log_debug ("iobuf_close -> %p\n", a->directfp);
- return 0;
- }
+ int rc2 = 0;
- for (; a && !rc; a = a2)
- {
- a2 = a->chain;
- if (a->use == BLOCK_FILTER_OUTPUT && (rc = iobuf_flush (a)))
- log_error ("iobuf_flush failed on close: %s\n", gpg_strerror (rc));
+ a_chain = a->chain;
+
+ if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a)))
+ log_error ("filter_flush failed on close: %s\n", gpg_strerror (rc));
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: close '%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
- if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_FREE,
- a->chain, NULL, &dummy_len)))
+ log_debug ("iobuf-%d.%d: close '%s'\n",
+ a->no, a->subno, iobuf_desc (a));
+
+ if (a->filter && (rc2 = a->filter (a->filter_ov, IOBUFCTRL_FREE,
+ a->chain, NULL, &dummy_len)))
log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc));
+ if (! rc && rc2)
+ /* Whoops! An error occured. Save it in RC if we haven't
+ already recorded an error. */
+ rc = rc2;
+
xfree (a->real_fname);
if (a->d.buf)
{
char *remove_name = NULL;
#endif
- if (a && a->use == BLOCK_FILTER_OUTPUT)
+ if (a && a->use == IOBUF_OUTPUT)
{
s = iobuf_get_real_fname (a);
if (s && *s)
}
-/****************
- * create a temporary iobuf, which can be used to collect stuff
- * in an iobuf and later be written by iobuf_write_temp() to another
- * iobuf.
- */
iobuf_t
-iobuf_temp ()
+iobuf_temp (void)
{
- iobuf_t a;
-
- a = iobuf_alloc (3, IOBUF_BUFFER_SIZE);
-
- return a;
+ return iobuf_alloc (IOBUF_OUTPUT_TEMP, IOBUF_BUFFER_SIZE);
}
iobuf_t
iobuf_t a;
int i;
- a = iobuf_alloc (3, length);
+ a = iobuf_alloc (IOBUF_INPUT_TEMP, length);
+ assert (length == a->d.size);
/* memcpy (a->d.buf, buffer, length); */
for (i=0; i < length; i++)
a->d.buf[i] = buffer[i];
}
-/* This fucntion returns true if FNAME indicates a PIPE (stdout or
- stderr) or a special file name if those are enabled. */
int
iobuf_is_pipe_filename (const char *fname)
{
return check_special_filename (fname) != -1;
}
-
-/* Either open the file specified by the file descriptor FD or - if FD
- is -1, the file with name FNAME. As of now MODE is assumed to be
- "rb" if FNAME is used. In contrast to iobuf_fdopen the file
- descriptor FD will not be closed during an iobuf_close. */
-iobuf_t
-iobuf_open_fd_or_name (gnupg_fd_t fd, const char *fname, const char *mode)
-{
- iobuf_t a;
-
- if (fd == GNUPG_INVALID_FD)
- a = iobuf_open (fname);
- else
- a = iobuf_fdopen_nc (FD2INT(fd), mode);
- return a;
-}
-
-
-/****************
- * Create a head iobuf for reading from a file
- * returns: NULL if an error occures and sets errno
- */
-iobuf_t
-iobuf_open (const char *fname)
+static iobuf_t
+do_open (const char *fname, int special_filenames,
+ int use, const char *opentype, int mode700)
{
iobuf_t a;
gnupg_fd_t fp;
int print_only = 0;
int fd;
- if (!fname || (*fname == '-' && !fname[1]))
+ assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT);
+
+ if (special_filenames
+ /* NULL or '-'. */
+ && (!fname || (*fname == '-' && !fname[1])))
{
- fp = FD_FOR_STDIN;
- fname = "[stdin]";
+ if (use == IOBUF_INPUT)
+ {
+ fp = FD_FOR_STDIN;
+ fname = "[stdin]";
+ }
+ else
+ {
+ fp = FD_FOR_STDOUT;
+ fname = "[stdout]";
+ }
print_only = 1;
}
- else if ((fd = check_special_filename (fname)) != -1)
- return iobuf_fdopen (translate_file_handle (fd, 0), "rb");
- else if ((fp = fd_cache_open (fname, "rb")) == GNUPG_INVALID_FD)
+ else if (!fname)
return NULL;
- a = iobuf_alloc (1, IOBUF_BUFFER_SIZE);
+ else if (special_filenames && (fd = check_special_filename (fname)) != -1)
+ return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1),
+ opentype);
+ else
+ {
+ if (use == IOBUF_INPUT)
+ fp = fd_cache_open (fname, opentype);
+ else
+ fp = direct_open (fname, opentype, mode700);
+ if (fp == GNUPG_INVALID_FD)
+ return NULL;
+ }
+
+ a = iobuf_alloc (use, IOBUF_BUFFER_SIZE);
fcx = xmalloc (sizeof *fcx + strlen (fname));
fcx->fp = fp;
fcx->print_only_name = print_only;
a->real_fname = xstrdup (fname);
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: open '%s' fd=%d\n",
- a->no, a->subno, fname, FD2INT (fcx->fp));
+ log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n",
+ a->no, a->subno, fname, iobuf_desc (a), FD2INT (fcx->fp));
return a;
}
+iobuf_t
+iobuf_open (const char *fname)
+{
+ return do_open (fname, 1, IOBUF_INPUT, "rb", 0);
+}
+
+iobuf_t
+iobuf_create (const char *fname, int mode700)
+{
+ return do_open (fname, 1, IOBUF_OUTPUT, "wb", mode700);
+}
+
+iobuf_t
+iobuf_openrw (const char *fname)
+{
+ return do_open (fname, 0, IOBUF_OUTPUT, "r+b", 0);
+}
+
static iobuf_t
do_iobuf_fdopen (int fd, const char *mode, int keep_open)
fp = INT2FD (fd);
- a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
+ a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
+ IOBUF_BUFFER_SIZE);
fcx = xmalloc (sizeof *fcx + 20);
fcx->fp = fp;
fcx->print_only_name = 1;
sprintf (fcx->fname, "[fd %d]", fd);
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: fdopen%s '%s'\n",
}
-/* Create a head iobuf for reading or writing from/to a file Returns:
- * NULL and sets ERRNO if an error occured. */
iobuf_t
iobuf_fdopen (int fd, const char *mode)
{
file_es_filter_ctx_t *fcx;
size_t len;
- a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
+ a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
+ IOBUF_BUFFER_SIZE);
fcx = xtrymalloc (sizeof *fcx + 30);
fcx->fp = estream;
fcx->print_only_name = 1;
sprintf (fcx->fname, "[fd %p]", estream);
a->filter = file_es_filter;
a->filter_ov = fcx;
- file_es_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: esopen%s '%s'\n",
sock_filter_ctx_t *scx;
size_t len;
- a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
+ a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
+ IOBUF_BUFFER_SIZE);
scx = xmalloc (sizeof *scx + 25);
scx->sock = fd;
scx->print_only_name = 1;
sprintf (scx->fname, "[sock %d]", fd);
a->filter = sock_filter;
a->filter_ov = scx;
- sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len);
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname);
return a;
}
-/****************
- * Create an iobuf for writing to a file; the file will be created.
- * With MODE700 set the file is created with that mode (Unix only).
- */
-iobuf_t
-iobuf_create (const char *fname, int mode700)
-{
- iobuf_t a;
- gnupg_fd_t fp;
- file_filter_ctx_t *fcx;
- size_t len;
- int print_only = 0;
- int fd;
-
- if (!fname || (*fname == '-' && !fname[1]))
- {
- fp = FD_FOR_STDOUT;
- fname = "[stdout]";
- print_only = 1;
- }
- else if ((fd = check_special_filename (fname)) != -1)
- return iobuf_fdopen (translate_file_handle (fd, 1), "wb");
- else if ((fp = direct_open (fname, "wb", mode700)) == GNUPG_INVALID_FD)
- return NULL;
- a = iobuf_alloc (2, IOBUF_BUFFER_SIZE);
- fcx = xmalloc (sizeof *fcx + strlen (fname));
- fcx->fp = fp;
- fcx->print_only_name = print_only;
- strcpy (fcx->fname, fname);
- if (!print_only)
- a->real_fname = xstrdup (fname);
- a->filter = file_filter;
- a->filter_ov = fcx;
- file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
- file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
- if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: create '%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
-
- return a;
-}
-
-
-iobuf_t
-iobuf_openrw (const char *fname)
-{
- iobuf_t a;
- gnupg_fd_t fp;
- file_filter_ctx_t *fcx;
- size_t len;
-
- if (!fname)
- return NULL;
- else if ((fp = direct_open (fname, "r+b", 0)) == GNUPG_INVALID_FD)
- return NULL;
- a = iobuf_alloc (2, IOBUF_BUFFER_SIZE);
- fcx = xmalloc (sizeof *fcx + strlen (fname));
- fcx->fp = fp;
- strcpy (fcx->fname, fname);
- a->real_fname = xstrdup (fname);
- a->filter = file_filter;
- a->filter_ov = fcx;
- file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
- file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
- if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: openrw '%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
-
- return a;
-}
-
-
int
iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
{
anymore. */
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n",
- a ? a->no : -1, a ? a->subno : -1,
- a && a->desc ? a->desc : "?",
+ a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a),
intval);
for (; a; a = a->chain)
if (!a->chain && a->filter == file_filter)
{
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n",
- a ? a->no : -1, a ? a->subno : -1,
- a && a->desc? a->desc : "?",
+ a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a),
intval);
for (; a; a = a->chain)
if (!a->chain && a->filter == file_filter)
size_t dummy_len = 0;
int rc = 0;
- if (a->directfp)
- BUG ();
-
- if (a->use == BLOCK_FILTER_OUTPUT && (rc = iobuf_flush (a)))
+ if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a)))
return rc;
if (a->subno >= MAX_NESTING_FILTER)
return GPG_ERR_BAD_DATA;
}
- /* make a copy of the current stream, so that
- * A is the new stream and B the original one.
- * The contents of the buffers are transferred to the
- * new stream.
- */
+ /* We want to create a new filter and put it in front of A. A
+ simple implementation would do:
+
+ b = iobuf_alloc (...);
+ b->chain = a;
+ return a;
+
+ This is a bit problematic: A is the head of the pipeline and
+ there are potentially many pointers to it. Requiring the caller
+ to update all of these pointers is a burden.
+
+ An alternative implementation would add a level of indirection.
+ For instance, we could use a pipeline object, which contains a
+ pointer to the first filter in the pipeline. This is not what we
+ do either.
+
+ Instead, we allocate a new buffer (B) and copy the first filter's
+ state into that and use the initial buffer (A) for the new
+ filter. One limitation of this approach is that it is not
+ practical to maintain a pointer to a specific filter's state.
+
+ Before:
+
+ A
+ |
+ v 0x100 0x200
+ +----------+ +----------+
+ | filter x |--------->| filter y |---->....
+ +----------+ +----------+
+
+ After: B
+ |
+ v 0x300
+ +----------+
+ A | filter x |
+ | +----------+
+ v 0x100 ^ v 0x200
+ +----------+ +----------+
+ | filter w | | filter y |---->....
+ +----------+ +----------+
+
+ Note: filter x's address changed from 0x100 to 0x300, but A still
+ points to the head of the pipeline.
+ */
+
b = xmalloc (sizeof *b);
memcpy (b, a, sizeof *b);
/* fixme: it is stupid to keep a copy of the name at every level
a->filter_ov = NULL;
a->filter_ov_owner = 0;
a->filter_eof = 0;
- if (a->use == BLOCK_FILTER_TEMP)
- /* make a write stream from a temp stream */
- a->use = BLOCK_FILTER_OUTPUT;
+ if (a->use == IOBUF_OUTPUT_TEMP)
+ /* A TEMP filter buffers any data sent to it; it does not forward
+ any data down the pipeline. If we add a new filter to the
+ pipeline, it shouldn't also buffer data. It should send it
+ downstream to be buffered. Thus, the correct type for a filter
+ added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not
+ IOBUF_OUTPUT_TEMP. */
+ {
+ a->use = IOBUF_OUTPUT;
- if (a->use == BLOCK_FILTER_OUTPUT)
- { /* allocate a fresh buffer for the
- original stream */
- b->d.buf = xmalloc (a->d.size);
- b->d.len = 0;
- b->d.start = 0;
+ /* When pipeline is written to, the temp buffer's size is
+ increased accordingly. We don't need to allocate a 10 MB
+ buffer for a non-terminal filter. Just use the default
+ size. */
+ a->d.size = IOBUF_BUFFER_SIZE;
}
- else
- { /* allocate a fresh buffer for the new
- stream */
- a->d.buf = xmalloc (a->d.size);
- a->d.len = 0;
- a->d.start = 0;
+ else if (a->use == IOBUF_INPUT_TEMP)
+ /* Same idea as above. */
+ {
+ a->use = IOBUF_INPUT;
+ a->d.size = IOBUF_BUFFER_SIZE;
}
+
+ /* The new filter (A) gets a new buffer.
+
+ If the pipeline is an output or temp pipeline, then giving the
+ buffer to the new filter means that data that was written before
+ the filter was pushed gets sent to the filter. That's clearly
+ wrong.
+
+ If the pipeline is an input pipeline, then giving the buffer to
+ the new filter (A) means that data that has read from (B), but
+ not yet read from the pipeline won't be processed by the new
+ filter (A)! That's certainly not what we want. */
+ a->d.buf = xmalloc (a->d.size);
+ a->d.len = 0;
+ a->d.start = 0;
+
/* disable nlimit for the new stream */
a->ntotal = b->ntotal + b->nbytes;
a->nlimit = a->nbytes = 0;
- a->nofast &= ~1;
+ a->nofast = 0;
/* make a link from the new stream to the original stream */
a->chain = b;
- a->opaque = b->opaque;
/* setup the function on the new stream */
a->filter = f;
a->filter_ov_owner = rel_ov;
a->subno = b->subno + 1;
- f (ov, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &dummy_len);
if (DBG_IOBUF)
{
- log_debug ("iobuf-%d.%d: push '%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
+ log_debug ("iobuf-%d.%d: push '%s'\n",
+ a->no, a->subno, iobuf_desc (a));
print_chain (a);
}
size_t dummy_len = 0;
int rc = 0;
- if (a->directfp)
- BUG ();
-
if (DBG_IOBUF)
- log_debug ("iobuf-%d.%d: pop '%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
+ log_debug ("iobuf-%d.%d: pop '%s'\n",
+ a->no, a->subno, iobuf_desc (a));
+ if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP)
+ {
+ /* This should be the last filter in the pipeline. */
+ assert (! a->chain);
+ return 0;
+ }
if (!a->filter)
{ /* this is simple */
b = a->chain;
log_bug ("pop_filter(): filter function not found\n");
/* flush this stream if it is an output stream */
- if (a->use == BLOCK_FILTER_OUTPUT && (rc = iobuf_flush (b)))
+ if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b)))
{
- log_error ("iobuf_flush failed in pop_filter: %s\n", gpg_strerror (rc));
+ log_error ("filter_flush failed in pop_filter: %s\n", gpg_strerror (rc));
return rc;
}
/* and tell the filter to free it self */
* the first byte or -1 on EOF.
*/
static int
-underflow (iobuf_t a)
+underflow (iobuf_t a, int clear_pending_eof)
{
size_t len;
int rc;
- assert (a->d.start == a->d.len);
- if (a->use == BLOCK_FILTER_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)));
+
+ if (a->use == IOBUF_INPUT_TEMP)
+ /* By definition, there isn't more data to read into the
+ buffer. */
+ return -1;
+
+ 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 (! clear_pending_eof)
+ return -1;
+
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, a->desc?a->desc:"?");
+ 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);
}
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->directfp)
+ 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. */
{
- FILE *fp = a->directfp;
-
- len = fread (a->d.buf, 1, a->d.size, fp);
- if (len < a->d.size)
- {
- if (ferror (fp))
- a->error = gpg_error_from_syserror ();
- }
- a->d.len = len;
- a->d.start = 0;
- return len ? a->d.buf[a->d.start++] : -1;
- }
-
-
- if (a->filter)
- {
- 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 == BLOCK_FILTER_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;
- a->desc = NULL;
+ xfree (a->filter_ov);
a->filter_ov = NULL;
+ a->filter = NULL;
a->filter_eof = 1;
- if (!len && a->chain)
+
+ if (clear_pending_eof && 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;
}
-int
-iobuf_flush (iobuf_t a)
+static int
+filter_flush (iobuf_t a)
{
size_t len;
int rc;
- if (a->directfp)
- return 0;
-
- if (a->use == BLOCK_FILTER_TEMP)
+ if (a->use == IOBUF_OUTPUT_TEMP)
{ /* increase the temp buffer */
- unsigned char *newbuf;
size_t newsize = a->d.size + IOBUF_BUFFER_SIZE;
if (DBG_IOBUF)
log_debug ("increasing temp iobuf from %lu to %lu\n",
(ulong) a->d.size, (ulong) newsize);
- newbuf = xmalloc (newsize);
- memcpy (newbuf, a->d.buf, a->d.len);
- xfree (a->d.buf);
- a->d.buf = newbuf;
+
+ a->d.buf = xrealloc (a->d.buf, newsize);
a->d.size = newsize;
return 0;
}
- else if (a->use != BLOCK_FILTER_OUTPUT)
+ else if (a->use != IOBUF_OUTPUT)
log_bug ("flush on non-output iobuf\n");
else if (!a->filter)
- log_bug ("iobuf_flush: no filter\n");
+ log_bug ("filter_flush: no filter\n");
len = a->d.len;
rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len);
if (!rc && len != a->d.len)
{
- log_info ("iobuf_flush did not write all!\n");
+ log_info ("filter_flush did not write all!\n");
rc = GPG_ERR_INTERNAL;
}
else if (rc)
}
-/****************
- * Read a byte from the iobuf; returns -1 on EOF
- */
int
iobuf_readbyte (iobuf_t a)
{
int c;
+ if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP)
+ {
+ log_bug ("iobuf_readbyte called on a non-INPUT pipeline!\n");
+ return -1;
+ }
+
+ assert (a->d.start <= a->d.len);
+
if (a->nlimit && a->nbytes >= a->nlimit)
return -1; /* forced EOF */
{
c = a->d.buf[a->d.start++];
}
- else if ((c = underflow (a)) == -1)
+ else if ((c = underflow (a, 1)) == -1)
return -1; /* EOF */
+ assert (a->d.start <= a->d.len);
+
+ /* Note: if underflow doesn't return EOF, then it returns the first
+ byte that was read and advances a->d.start appropriately. */
+
a->nbytes++;
return c;
}
unsigned char *buf = (unsigned char *)buffer;
int c, n;
+ if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP)
+ {
+ log_bug ("iobuf_read called on a non-INPUT pipeline!\n");
+ return -1;
+ }
+
if (a->nlimit)
{
/* Handle special cases. */
return -1; /* eof */
break;
}
- else if (buf)
- *buf = c;
+
if (buf)
- buf++;
+ {
+ *buf = c;
+ buf++;
+ }
}
return n;
}
do
{
if (n < buflen && a->d.start < a->d.len)
+ /* Drain the buffer. */
{
unsigned size = a->d.len - a->d.start;
if (size > buflen - n)
buf += size;
}
if (n < buflen)
+ /* Draining the internal buffer didn't fill BUFFER. Call
+ underflow to read more data into the filter's internal
+ buffer. */
{
- if ((c = underflow (a)) == -1)
+ if ((c = underflow (a, 1)) == -1)
+ /* EOF. If we managed to read something, don't return EOF
+ now. */
{
a->nbytes += n;
return n ? n : -1 /*EOF*/;
-/****************
- * Have a look at the iobuf.
- * NOTE: This only works in special cases.
- */
int
iobuf_peek (iobuf_t a, byte * buf, unsigned buflen)
{
int n = 0;
- if (a->filter_eof)
- return -1;
+ assert (buflen > 0);
+ assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP);
+
+ if (buflen > a->d.size)
+ /* We can't peek more than we can buffer. */
+ buflen = a->d.size;
- if (!(a->d.start < a->d.len))
+ /* Try to fill the internal buffer with enough data to satisfy the
+ request. */
+ while (buflen > a->d.len - a->d.start)
{
- if (underflow (a) == -1)
- return -1;
- /* And unget this character. */
+ if (underflow (a, 0) == -1)
+ /* EOF. We can't read any more. */
+ break;
+
+ /* Underflow consumes the first character (it's the return
+ value). unget() it by resetting the "file position". */
assert (a->d.start == 1);
a->d.start = 0;
}
- for (n = 0; n < buflen && (a->d.start + n) < a->d.len; n++, buf++)
- *buf = a->d.buf[n];
+ n = a->d.len - a->d.start;
+ if (n > buflen)
+ n = buflen;
+
+ if (n == 0)
+ /* EOF. */
+ return -1;
+
+ memcpy (buf, &a->d.buf[a->d.start], n);
+
return n;
}
{
int rc;
- if (a->directfp)
- BUG ();
+ if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
+ {
+ log_bug ("iobuf_writebyte called on an input pipeline!\n");
+ return -1;
+ }
if (a->d.len == a->d.size)
- if ((rc=iobuf_flush (a)))
+ if ((rc=filter_flush (a)))
return rc;
assert (a->d.len < a->d.size);
const unsigned char *buf = (const unsigned char *)buffer;
int rc;
- if (a->directfp)
- BUG ();
+ if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
+ {
+ log_bug ("iobuf_write called on an input pipeline!\n");
+ return -1;
+ }
do
{
}
if (buflen)
{
- rc = iobuf_flush (a);
+ rc = filter_flush (a);
if (rc)
return rc;
}
int
iobuf_writestr (iobuf_t a, const char *buf)
{
- int rc;
+ if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
+ {
+ log_bug ("iobuf_writestr called on an input pipeline!\n");
+ return -1;
+ }
- for (; *buf; buf++)
- if ((rc=iobuf_writebyte (a, *buf)))
- return rc;
- return 0;
+ return iobuf_write (a, buf, strlen (buf));
}
-/****************
- * copy the contents of TEMP to A.
- */
int
-iobuf_write_temp (iobuf_t a, iobuf_t temp)
+iobuf_write_temp (iobuf_t dest, iobuf_t source)
{
- while (temp->chain)
- pop_filter (temp, temp->filter, NULL);
- return iobuf_write (a, temp->d.buf, temp->d.len);
+ assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP);
+ assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP);
+
+ iobuf_flush_temp (source);
+ return iobuf_write (dest, source->d.buf, source->d.len);
}
-/****************
- * copy the contents of the temp io stream to BUFFER.
- */
size_t
iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen)
{
- size_t n = a->d.len;
+ size_t n;
+ while (1)
+ {
+ int rc = filter_flush (a);
+ if (rc)
+ log_bug ("Flushing iobuf %d.%d (%s) from iobuf_temp_to_buffer failed. Ignoring.\n",
+ a->no, a->subno, iobuf_desc (a));
+ if (! a->chain)
+ break;
+ a = a->chain;
+ }
+
+ n = a->d.len;
if (n > buflen)
n = buflen;
memcpy (buffer, a->d.buf, n);
}
-/****************
- * Call this function to terminate processing of the temp stream
- * without closing it. This removes all filters from the stream
- * makes sure that iobuf_get_temp_{buffer,length}() returns correct
- * values.
- */
void
iobuf_flush_temp (iobuf_t temp)
{
+ if (temp->use == IOBUF_INPUT || temp->use == IOBUF_INPUT_TEMP)
+ log_bug ("iobuf_writestr called on an input pipeline!\n");
while (temp->chain)
pop_filter (temp, temp->filter, NULL);
}
-/****************
- * Set a limit on how many bytes may be read from the input stream A.
- * Setting the limit to 0 disables this feature.
- */
void
iobuf_set_limit (iobuf_t a, off_t nlimit)
{
if (nlimit)
- a->nofast |= 1;
+ a->nofast = 1;
else
- a->nofast &= ~1;
+ a->nofast = 0;
a->nlimit = nlimit;
a->ntotal += a->nbytes;
a->nbytes = 0;
-/* Return the length of an open file A. IF OVERFLOW is not NULL it
- will be set to true if the file is larger than what off_t can cope
- with. The function return 0 on error or on overflow condition. */
off_t
iobuf_get_filelength (iobuf_t a, int *overflow)
{
if (overflow)
*overflow = 0;
- if ( a->directfp )
- {
- FILE *fp = a->directfp;
+ /* Hmmm: file_filter may have already been removed */
+ for ( ; a->chain; a = a->chain )
+ ;
- if ( !fstat(fileno(fp), &st) )
- return st.st_size;
- log_error("fstat() failed: %s\n", strerror(errno) );
- return 0;
- }
+ if (a->filter != file_filter)
+ return 0;
- /* Hmmm: file_filter may have already been removed */
- for ( ; a; a = a->chain )
- if ( !a->chain && a->filter == file_filter )
- {
- file_filter_ctx_t *b = a->filter_ov;
- gnupg_fd_t fp = b->fp;
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ gnupg_fd_t fp = b->fp;
#if defined(HAVE_W32_SYSTEM)
- ulong size;
- static int (* __stdcall get_file_size_ex) (void *handle,
- LARGE_INTEGER *r_size);
- static int get_file_size_ex_initialized;
-
- if (!get_file_size_ex_initialized)
- {
- void *handle;
-
- handle = dlopen ("kernel32.dll", RTLD_LAZY);
- if (handle)
- {
- get_file_size_ex = dlsym (handle, "GetFileSizeEx");
- if (!get_file_size_ex)
- dlclose (handle);
- }
- get_file_size_ex_initialized = 1;
- }
-
- if (get_file_size_ex)
- {
- /* This is a newer system with GetFileSizeEx; we use this
- then because it seem that GetFileSize won't return a
- proper error in case a file is larger than 4GB. */
- LARGE_INTEGER exsize;
-
- if (get_file_size_ex (fp, &exsize))
- {
- if (!exsize.u.HighPart)
- return exsize.u.LowPart;
- if (overflow)
- *overflow = 1;
- return 0;
- }
- }
- else
- {
- if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
- return size;
- }
- log_error ("GetFileSize for handle %p failed: %s\n",
- fp, w32_strerror (0));
+ ulong size;
+ static int (* __stdcall get_file_size_ex) (void *handle,
+ LARGE_INTEGER *r_size);
+ static int get_file_size_ex_initialized;
+
+ if (!get_file_size_ex_initialized)
+ {
+ void *handle;
+
+ handle = dlopen ("kernel32.dll", RTLD_LAZY);
+ if (handle)
+ {
+ get_file_size_ex = dlsym (handle, "GetFileSizeEx");
+ if (!get_file_size_ex)
+ dlclose (handle);
+ }
+ get_file_size_ex_initialized = 1;
+ }
+
+ if (get_file_size_ex)
+ {
+ /* This is a newer system with GetFileSizeEx; we use this
+ then because it seem that GetFileSize won't return a
+ proper error in case a file is larger than 4GB. */
+ LARGE_INTEGER exsize;
+
+ if (get_file_size_ex (fp, &exsize))
+ {
+ if (!exsize.u.HighPart)
+ return exsize.u.LowPart;
+ if (overflow)
+ *overflow = 1;
+ return 0;
+ }
+ }
+ else
+ {
+ if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
+ return size;
+ }
+ log_error ("GetFileSize for handle %p failed: %s\n",
+ fp, w32_strerror (0));
#else
- if ( !fstat (FD2INT (fp), &st) )
- return st.st_size;
- log_error("fstat() failed: %s\n", strerror(errno) );
+ if ( !fstat (FD2INT (fp), &st) )
+ return st.st_size;
+ log_error("fstat() failed: %s\n", strerror(errno) );
#endif
- break/*the for loop*/;
- }
+ }
- return 0;
+ return 0;
}
-/* Return the file descriptor of the underlying file or -1 if it is
- not available. */
int
iobuf_get_fd (iobuf_t a)
{
- if (a->directfp)
- return fileno ( (FILE*)a->directfp );
+ for (; a->chain; a = a->chain)
+ ;
- for ( ; a; a = a->chain )
- if (!a->chain && a->filter == file_filter)
- {
- file_filter_ctx_t *b = a->filter_ov;
- gnupg_fd_t fp = b->fp;
+ if (a->filter != file_filter)
+ return -1;
- return FD2INT (fp);
- }
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ gnupg_fd_t fp = b->fp;
- return -1;
+ return FD2INT (fp);
+ }
}
-
-/****************
- * Tell the file position, where the next read will take place
- */
off_t
iobuf_tell (iobuf_t a)
{
}
#endif
-/****************
- * This is a very limited implementation. It simply discards all internal
- * buffering and removes all filters but the first one.
- */
int
iobuf_seek (iobuf_t a, off_t newpos)
{
file_filter_ctx_t *b = NULL;
- if (a->directfp)
- {
- FILE *fp = a->directfp;
- if (fseeko (fp, newpos, SEEK_SET))
- {
- log_error ("can't seek: %s\n", strerror (errno));
- return -1;
- }
- clearerr (fp);
- }
- else if (a->use != BLOCK_FILTER_TEMP)
+ if (a->use == IOBUF_OUTPUT || a->use == IOBUF_INPUT)
{
- for (; a; a = a->chain)
- {
- if (!a->chain && a->filter == file_filter)
- {
- b = a->filter_ov;
- break;
- }
- }
- if (!a)
+ /* Find the last filter in the pipeline. */
+ for (; a->chain; a = a->chain)
+ ;
+
+ if (a->filter != file_filter)
return -1;
+
+ b = a->filter_ov;
+
#ifdef HAVE_W32_SYSTEM
if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff)
{
return -1;
}
#endif
+ /* Discard the buffer it is not a temp stream. */
+ a->d.len = 0;
}
- /* Discard the buffer unless it is a temp stream. */
- if (a->use != BLOCK_FILTER_TEMP)
- a->d.len = 0;
a->d.start = 0;
a->nbytes = 0;
a->nlimit = 0;
- a->nofast &= ~1;
+ a->nofast = 0;
a->ntotal = newpos;
a->error = 0;
+
+ /* It is impossible for A->CHAIN to be non-NULL. If A is an INPUT
+ or OUTPUT buffer, then we find the last filter, which is defined
+ as A->CHAIN being NULL. If A is a TEMP filter, then A must be
+ the only filter in the pipe: when iobuf_push_filter adds a filter
+ to the front of a pipeline, it sets the new filter to be an
+ OUTPUT filter if the pipeline is an OUTPUT or TEMP pipeline and
+ to be an INPUT filter if the pipeline is an INPUT pipeline.
+ Thus, only the last filter in a TEMP pipeline can be a */
+
/* remove filters, but the last */
if (a->chain)
log_debug ("pop_filter called in iobuf_seek - please report\n");
}
-
-
-
-
-/****************
- * Retrieve the real filename. This is the filename actually used on
- * disk and not a made up one. Returns NULL if no real filename is
- * available.
- */
const char *
iobuf_get_real_fname (iobuf_t a)
{
return NULL;
}
-
-/****************
- * Retrieve the filename. This name should only be used in diagnostics.
- */
const char *
iobuf_get_fname (iobuf_t a)
{
return NULL;
}
-/* Same as iobuf_get_fname but never returns NULL. */
const char *
iobuf_get_fname_nonnull (iobuf_t a)
{
{
block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx);
- assert (a->use == BLOCK_FILTER_INPUT || a->use == BLOCK_FILTER_OUTPUT);
ctx->use = a->use;
if (!len)
{
- if (a->use == BLOCK_FILTER_INPUT)
+ if (a->use == IOBUF_INPUT)
log_debug ("pop_filter called in set_partial_block_mode"
" - please report\n");
+ /* XXX: This pop_filter doesn't make sense. Since we haven't
+ actually added the filter to the pipeline yet, why are we
+ popping anything? Moreover, since we don't report an error,
+ the caller won't directly see an error. I think that it
+ would be better to push the filter and set a->error to
+ GPG_ERR_BAD_DATA, but Werner thinks it's impossible for len
+ to be 0 (but he doesn't want to remove the check just in
+ case). */
pop_filter (a, block_filter, NULL);
}
else
-/****************
- * Same as fgets() but if the buffer is too short a larger one will
- * be allocated up to some limit *max_length.
- * A line is considered a byte stream ending in a LF.
- * Returns the length of the line. EOF is indicated by a line of
- * length zero. The last LF may be missing due to an EOF.
- * is max_length is zero on return, the line has been truncated.
- *
- * Note: The buffer is allocated with enough space to append a CR,LF,EOL
- */
unsigned int
iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
unsigned *length_of_buffer, unsigned *max_length)
unsigned maxlen = *max_length;
char *p;
- if (!buffer)
- { /* must allocate a new buffer */
- length = 256;
- buffer = xmalloc (length);
+ /* The code assumes that we have space for at least a newline and a
+ NUL character in the buffer. This requires at least 2 bytes. We
+ don't complicate the code by handling the stupid corner case, but
+ simply assert that it can't happen. */
+ assert (length >= 2 || maxlen >= 2);
+
+ if (!buffer || length <= 1)
+ /* must allocate a new buffer */
+ {
+ length = 256 <= maxlen ? 256 : maxlen;
+ buffer = xrealloc (buffer, length);
*addr_of_buffer = (unsigned char *)buffer;
*length_of_buffer = length;
}
- length -= 3; /* reserve 3 bytes (cr,lf,eol) */
p = buffer;
while ((c = iobuf_get (a)) != -1)
{
- if (nbytes == length)
- { /* increase the buffer */
- if (length > maxlen)
- { /* this is out limit */
- /* skip the rest of the line */
+ *p++ = c;
+ nbytes++;
+ if (c == '\n')
+ break;
+
+ if (nbytes == length - 1)
+ /* We don't have enough space to add a \n and a \0. Increase
+ the buffer size. */
+ {
+ if (length == maxlen)
+ /* We reached the buffer's size limit! */
+ {
+ /* Skip the rest of the line. */
while (c != '\n' && (c = iobuf_get (a)) != -1)
;
- *p++ = '\n'; /* always append a LF (we have reserved space) */
- nbytes++;
- *max_length = 0; /* indicate truncation */
+
+ /* p is pointing at the last byte in the buffer. We
+ always terminate the line with "\n\0" so overwrite
+ the previous byte with a \n. */
+ assert (p > buffer);
+ p[-1] = '\n';
+
+ /* Indicate truncation. */
+ *max_length = 0;
break;
}
- length += 3; /* correct for the reserved byte */
+
length += length < 1024 ? 256 : 1024;
+ if (length > maxlen)
+ length = maxlen;
+
buffer = xrealloc (buffer, length);
*addr_of_buffer = (unsigned char *)buffer;
*length_of_buffer = length;
- length -= 3; /* and reserve again */
p = buffer + nbytes;
}
- *p++ = c;
- nbytes++;
- if (c == '\n')
- break;
}
- *p = 0; /* make sure the line is a string */
+ /* Add the terminating NUL. */
+ *p = 0;
+ /* Return the number of characters written to the buffer including
+ the newline, but not including the terminating NUL. */
return nbytes;
}