common/iobuf: Improve documentation and code comments.
authorNeal H. Walfield <neal@g10code.com>
Mon, 17 Aug 2015 10:30:04 +0000 (12:30 +0200)
committerNeal H. Walfield <neal@g10code.com>
Thu, 20 Aug 2015 12:16:25 +0000 (14:16 +0200)
common/iobuf.h: Improve documentation and code comments.
common/iobuf.c: Likewise.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>.
common/iobuf.c
common/iobuf.h

index 7d75e33..e859a5c 100644 (file)
@@ -133,9 +133,9 @@ typedef struct
 } 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
 
@@ -156,8 +156,8 @@ block_filter_ctx_t;
 
 
 /* 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.  */
@@ -401,7 +401,7 @@ fd_cache_close (const char *fname, gnupg_fd_t fp)
 }
 
 /*
- * 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)
@@ -440,30 +440,6 @@ 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)
@@ -1117,13 +1093,6 @@ iobuf_print_chain (iobuf_t a)
   return 0;
 }
 
-/* Allocate a new io buffer, with no function assigned.
-
-   USE is the desired usage: IOBUF_INPUT for input, IOBUF_OUTPUT for
-   output, or IOBUF_TEMP for a temp buffer.
-
-   BUFSIZE is a suggested buffer size.
- */
 iobuf_t
 iobuf_alloc (int use, size_t bufsize)
 {
@@ -1233,19 +1202,10 @@ iobuf_cancel (iobuf_t a)
 }
 
 
-/****************
- * 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 (IOBUF_TEMP, IOBUF_BUFFER_SIZE);
-
-  return a;
+  return iobuf_alloc (IOBUF_TEMP, IOBUF_BUFFER_SIZE);
 }
 
 iobuf_t
@@ -1290,8 +1250,6 @@ check_special_filename (const char *fname)
 }
 
 
-/* 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)
 {
@@ -1359,10 +1317,6 @@ do_open (const char *fname, int special_filenames,
   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)
 {
@@ -1410,8 +1364,6 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open)
 }
 
 
-/* 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)
 {
@@ -1587,11 +1539,51 @@ iobuf_push_filter2 (iobuf_t a,
       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
@@ -1938,9 +1930,6 @@ filter_flush (iobuf_t a)
 }
 
 
-/****************
- * Read a byte from the iobuf; returns -1 on EOF
- */
 int
 iobuf_readbyte (iobuf_t a)
 {
@@ -1956,6 +1945,9 @@ iobuf_readbyte (iobuf_t a)
   else if ((c = underflow (a, 1)) == -1)
     return -1;                 /* EOF */
 
+  /* 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;
 }
@@ -1990,6 +1982,7 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
   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)
@@ -2002,8 +1995,13 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
            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)) == -1)
+           /* EOF.  If we managed to read something, don't return EOF
+              now.  */
            {
              a->nbytes += n;
              return n ? n : -1 /*EOF*/;
@@ -2120,9 +2118,6 @@ iobuf_writestr (iobuf_t a, const char *buf)
 
 
 
-/****************
- * copy the contents of TEMP to A.
- */
 int
 iobuf_write_temp (iobuf_t a, iobuf_t temp)
 {
@@ -2131,9 +2126,6 @@ iobuf_write_temp (iobuf_t a, iobuf_t temp)
   return iobuf_write (a, temp->d.buf, temp->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)
 {
@@ -2158,12 +2150,6 @@ iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen)
 }
 
 
-/****************
- * 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)
 {
@@ -2172,10 +2158,6 @@ iobuf_flush_temp (iobuf_t temp)
 }
 
 
-/****************
- * 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)
 {
@@ -2190,9 +2172,6 @@ iobuf_set_limit (iobuf_t a, off_t nlimit)
 
 
 
-/* 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)
 {
@@ -2263,8 +2242,6 @@ iobuf_get_filelength (iobuf_t a, int *overflow)
 }
 
 
-/* Return the file descriptor of the underlying file or -1 if it is
-   not available.  */
 int
 iobuf_get_fd (iobuf_t a)
 {
@@ -2281,10 +2258,6 @@ iobuf_get_fd (iobuf_t a)
 }
 
 
-
-/****************
- * Tell the file position, where the next read will take place
- */
 off_t
 iobuf_tell (iobuf_t a)
 {
@@ -2322,10 +2295,6 @@ fseeko (FILE * stream, off_t newpos, int whence)
 }
 #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)
 {
@@ -2367,6 +2336,16 @@ iobuf_seek (iobuf_t a, off_t newpos)
   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");
@@ -2381,11 +2360,6 @@ iobuf_seek (iobuf_t a, off_t newpos)
 
 
 
-/****************
- * 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)
 {
@@ -2404,9 +2378,6 @@ iobuf_get_real_fname (iobuf_t a)
 }
 
 
-/****************
- * Retrieve the filename.  This name should only be used in diagnostics.
- */
 const char *
 iobuf_get_fname (iobuf_t a)
 {
@@ -2419,7 +2390,6 @@ 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)
 {
@@ -2446,6 +2416,14 @@ iobuf_set_partial_block_mode (iobuf_t a, size_t len)
       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
@@ -2459,16 +2437,6 @@ iobuf_set_partial_block_mode (iobuf_t a, size_t len)
 
 
 
-/****************
- * 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)
index 5cfccb8..7157e0f 100644 (file)
 #ifndef GNUPG_COMMON_IOBUF_H
 #define GNUPG_COMMON_IOBUF_H
 
+/* An iobuf is basically a filter in a pipeline.
+
+   Consider the following command, which consists of three filters
+   that are chained together:
+
+     $ cat file | base64 --decode | gunzip
+
+   The first filter reads the file from the file system and sends that
+   data to the second filter.  The second filter decodes
+   base64-encoded data and sends the data to the third and last
+   filter.  The last filter decompresses the data and the result is
+   displayed on the terminal.  The iobuf system works in the same way
+   where each iobuf is a filter and the individual iobufs can be
+   chained together.
+
+   There are number of predefined filters.  iobuf_open(), for
+   instance, creates a filter that reads from a specified file.  And,
+   iobuf_temp_with_content() creates a filter that returns some
+   specified contents.  There are also filters for writing content.
+   iobuf_openrw opens a file for writing.  iobuf_temp creates a filter
+   that writes data to a fixed-sized buffer.
+
+   To chain filters together, you use the iobuf_push_filter()
+   function.  The filters are chained together using the chain field
+   in the iobuf_t.
+
+   A pipeline can only be used for reading (IOBUF_INPUT) or for
+   writing (IOBUF_OUTPUT / IOBUF_TEMP).  When reading, data flows from
+   the last filter towards the first.  That is, the user calls
+   iobuf_read(), the module reads from the first filter, which gets
+   its input from the second filter, etc.  When writing, data flows
+   from the first filter towards the last.  In this case, when the
+   user calls iobuf_write(), the data is written to the first filter,
+   which writes the transformed data to the second filter, etc.
+
+   An iobuf_t contains some state about the filter.  For instance, it
+   indicates if the filter has already returned EOF (filter_eof) and
+   the next filter in the pipeline, if any (chain).  It also contains
+   a function pointer, filter.  This is a generic function.  It is
+   called when input is needed or output is available.  In this case
+   it is passed a pointer to some filter-specific persistent state
+   (filter_ov), the actual operation, the next filter in the chain, if
+   any, and a buffer that either contains the contents to write, if
+   the pipeline is setup to write data, or is the place to store data,
+   if the pipeline is setup to read data.
+
+
+   Unlike a Unix pipeline, an IOBUF pipeline can return EOF multiple
+   times.  This is similar to the following:
+
+     { cat file1; cat file2; } | grep foo
+
+   However, instead of grep seeing a single stream, grep would see
+   each byte stream followed by an EOF marker.  (When a filter returns
+   EOF, the EOF is returned to the user exactly once and then the
+   filter is removed from the pipeline.)  */
+
 /* For estream_t.  */
 #include <gpg-error.h>
 
 #define DBG_IOBUF   iobuf_debug_mode
 
 /* Filter control modes.  */
-#define IOBUFCTRL_INIT     1
-#define IOBUFCTRL_FREE     2
-#define IOBUFCTRL_UNDERFLOW 3
-#define IOBUFCTRL_FLUSH     4
-#define IOBUFCTRL_DESC     5
-#define IOBUFCTRL_CANCEL    6
-#define IOBUFCTRL_USER     16
+enum
+  {
+    IOBUFCTRL_INIT     = 1,
+    IOBUFCTRL_FREE     = 2,
+    IOBUFCTRL_UNDERFLOW = 3,
+    IOBUFCTRL_FLUSH     = 4,
+    IOBUFCTRL_DESC     = 5,
+    IOBUFCTRL_CANCEL    = 6,
+    IOBUFCTRL_USER     = 16
+  };
 
 
 /* Command codes for iobuf_ioctl.  */
@@ -60,8 +120,17 @@ typedef enum
 
 enum
   {
+    /* Pipeline is in input mode.  The data flows from the end to the
+       beginning.  That is, when reading from the pipeline, the first
+       filter gets its input from the second filter, etc.  */
     IOBUF_INPUT=1,
+    /* Pipeline is in output mode.  The data flows from the beginning
+       to the end.  That is, when writing to the pipeline, the user
+       writes to the first filter, which transforms the data and sends
+       it to the second filter, etc.  */
     IOBUF_OUTPUT=2,
+    /* Pipeline is in output mode.  The last filter in the pipeline is
+       a temporary buffer that grows as necessary.  */
     IOBUF_TEMP=3
   };
 
@@ -75,33 +144,105 @@ struct iobuf_struct
   /* The type of filter.  Either IOBUF_INPUT, IOBUF_OUTPUT or
      IOBUF_TEMP.  */
   int use;
+
+  /* nlimit can be changed using iobuf_set_limit.  If non-zero, it is
+     the number of additional bytes that can be read from the filter
+     before EOF is forcefully returned.  */
   off_t nlimit;
-  off_t nbytes;                        /* Used together with nlimit. */
-  off_t ntotal;                        /* Total bytes read (position of stream). */
+  /* nbytes if the number of bytes that have been read (using
+     iobuf_get / iobuf_readbyte / iobuf_read) since the last call to
+     iobuf_set_limit.  */
+  off_t nbytes;
+
+  /* The number of bytes read prior to the last call to
+     iobuf_set_limit.  Thus, the total bytes read (i.e., the position
+     of stream) is ntotal + nbytes. */
+  off_t ntotal;
 
   /* Whether we need to read from the filter one byte at a time or
      whether we can do bulk reads.  We need to read one byte at a time
      if a limit (set via iobuf_set_limit) is active.  */
   int nofast;
+
+  /* A buffer for unread/unwritten data.
+
+     For an output pipeline (IOBUF_OUTPUT), this is the data that has
+     not yet been written to the filter.  Consider a simple pipeline
+     consisting of a single stage, which writes to a file.  When you
+     write to the pipeline (iobuf_writebyte or iobuf_write), the data
+     is first stored in this buffer.  Only when the buffer is full or
+     you call iobuf_flush() is FILTER actually called and the data
+     written to the file.
+
+     For an input pipeline (IOBUF_INPUT), this is the data that has
+     been read from this filter, but not yet been read from the
+     preceding filter (or the user, if this filter is the head of the
+     pipeline).  Again, consider a simple pipeline consisting of a
+     single stage.  This stage reads from a file.  If you read a
+     single byte (iobuf_get) and the buffer is empty, then FILTER is
+     called to fill the buffer.  In this case, a single byte is not
+     requested, but the whole buffer is filled (if possible).  */
   struct
   {
-    size_t size;               /* Allocated size */
-    size_t start;              /* Number of invalid bytes at the
-                                   begin of the buffer */
-    size_t len;                        /* Currently filled to this size */
+    /* Size of the buffer.  */
+    size_t size;
+    /* Number of bytes at the beginning of the buffer that have
+       already been consumed.  (In other words: the index of the first
+       byte that hasn't been consumed.)  This is only non-zero for
+       input filters.  */
+    size_t start;
+    /* The number of bytes in the buffer including any bytes that have
+       been consumed.  */
+    size_t len;
+    /* The buffer itself.  */
     byte *buf;
   } d;
 
+  /* When FILTER is called to read some data, it may read some data
+     and then return EOF.  We can't return the EOF immediately.
+     Instead, we note that we observed the EOF and when the buffer is
+     finally empty, we return the EOF.  */
   int filter_eof;
+  /* Like filter_eof, when FILTER is called to read some data, it may
+     read some data and then return an error.  We can't return the
+     error (in the form of an EOF) immediately.  Instead, we note that
+     we observed the error and when the buffer is finally empty, we
+     return the EOF.  */
   int error;
+
+  /* The callback function to read data from the filter, etc.  See
+     iobuf_filter_push for details.  */
   int (*filter) (void *opaque, int control,
                 iobuf_t chain, byte * buf, size_t * len);
-  void *filter_ov;             /* Value for opaque */
+  /* An opaque pointer that can be used for local filter state.  This
+     is passed as the first parameter to FILTER.  */
+  void *filter_ov;
+  /* Whether the iobuf code should free(filter_ov) when destroying the
+     filter.  */
   int filter_ov_owner;
+
+  /* When using iobuf_open, iobuf_create, iobuf_openrw to open a file,
+     the file's name is saved here.  This is used to delete the file
+     when an output pipeline (IOBUF_OUPUT) is canceled
+     (iobuf_cancel).  */
   char *real_fname;
-  iobuf_t chain;               /* Next iobuf used for i/o if any
-                                   (passed to filter) */
-  int no, subno;
+
+  /* The next filter in the pipeline.  */
+  iobuf_t chain;
+
+  /* This field is for debugging.  Each time a filter is allocated
+     (via iobuf_alloc()), a monotonically increasing counter is
+     incremented and this field is set to the new value.  This field
+     should only be accessed via the iobuf_io macro.  */
+  int no;
+
+  /* The number of filters in the pipeline following (not including)
+     this one.  When you call iobuf_push_filter or iobuf_push_filter2,
+     this value is used to check the length of the pipeline if the
+     pipeline already contains 65 stages then these functions fail.
+     This amount of nesting typically indicates corrupted data or an
+     active denial of service attack.  */
+  int subno;
 };
 
 #ifndef EXTERN_UNLESS_MAIN_MODULE
@@ -113,78 +254,350 @@ struct iobuf_struct
 #endif
 EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode;
 
+/* Whether iobuf_open, iobuf_create and iobuf_is_pipefilename
+   recognize special filenames.  Special filenames are of the form
+   "-&nnnn" where n is a positive integer.  The integer corresponds to
+   a file descriptor.  Note: these functions always recognize the
+   special filename '-', which corresponds to standard input.  */
 void iobuf_enable_special_filenames (int yes);
+
+/* Returns whether the specified filename corresponds to a pipe.  In
+   particular, this function checks if FNAME is "-" and, if special
+   filenames are enabled (see iobuf_enable_special_filenames), whether
+   FNAME is a special filename.  */
 int  iobuf_is_pipe_filename (const char *fname);
+
+/* Allocate a new filter.  This filter doesn't have a function
+   assigned to it.  Thus you need to manually set IOBUF->FILTER and
+   IOBUF->FILTER_OV, if required.  This function is intended to help
+   create a new primary source or primary sink, i.e., the last filter
+   in the pipeline.
+
+   USE is IOBUF_INPUT, IOBUF_OUTPUT or IOBUF_TEMP.
+
+   BUFSIZE is the desired internal buffer size (that is, the size of
+   the typical read / write request).  */
 iobuf_t iobuf_alloc (int use, size_t bufsize);
+
+/* Create an output filter that simply buffers data written to it.
+   This is useful for collecting data for later processing.  The
+   buffer can be written to in the usual way (iobuf_write, etc.).  The
+   data can later be extracted using iobuf_write_temp() or
+   iobuf_temp_to_buffer().  */
 iobuf_t iobuf_temp (void);
+
+/* Create an input filter that contains some data for reading.  */
 iobuf_t iobuf_temp_with_content (const char *buffer, size_t length);
+
+/* Create an input file filter that reads from a file.  If FNAME is
+   '-', reads from stdin.  If special filenames are enabled
+   (iobuf_enable_special_filenames), then interprets special
+   filenames.  */
 iobuf_t iobuf_open (const char *fname);
+
+/* Create an output file filter that writes to a file.  If FNAME is
+   NULL or '-', writes to stdout.  If special filenames are enabled
+   (iobuf_enable_special_filenames), then interprets special
+   filenames.  If FNAME is not NULL, '-' or a special filename, the
+   file is opened for writing.  If the file exists, it is truncated.
+   If MODE700 is TRUE, the file is created with mode 600.  Otherwise,
+   mode 666 is used.  */
+iobuf_t iobuf_create (const char *fname, int mode700);
+
+/* Create an output file filter that writes to a specified file.
+   Neither '-' nor special file names are recognized.  */
+iobuf_t iobuf_openrw (const char *fname);
+
+/* Create a file filter using an existing file descriptor.  If MODE
+   contains the letter 'w', creates an output filter.  Otherwise,
+   creates an input filter.  Note: MODE must reflect the file
+   descriptors actual mode!  When the filter is destroyed, the file
+   descriptor is closed.  */
 iobuf_t iobuf_fdopen (int fd, const char *mode);
+
+/* Like iobuf_fdopen, but doesn't close the file descriptor when the
+   filter is destroyed.  */
 iobuf_t iobuf_fdopen_nc (int fd, const char *mode);
+
+/* Create a filter using an existing estream.  If MODE contains the
+   letter 'w', creates an output filter.  Otherwise, creates an input
+   filter.  If KEEP_OPEN is TRUE, then the stream is not closed when
+   the filter is destroyed.  Otherwise, the stream is closed when the
+   filter is destroyed.  */
 iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open);
+
+/* Create a filter using an existing socket.  On Windows creates a
+   special socket filter.  On non-Windows systems simply, this simply
+   calls iobuf_fdopen.  */
 iobuf_t iobuf_sockopen (int fd, const char *mode);
-iobuf_t iobuf_create (const char *fname, int mode700);
-iobuf_t iobuf_openrw (const char *fname);
+
+/* Set various options / perform different actions on a PIPELINE.  See
+   the IOBUF_IOCTL_* macros above.  */
 int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval);
+
+/* Close a pipeline.  The filters in the pipeline are first flushed
+   using iobuf_flush, if they are output filters, and then
+   IOBUFCTRL_FREE is called on each filter.
+
+   If any filter returns a non-zero value in response to the
+   IOBUFCTRL_FREE, that first such non-zero value is returned.  Note:
+   processing is not aborted in this case.  If all filters are freed
+   successfully, 0 is returned.  */
 int iobuf_close (iobuf_t iobuf);
+
+/* Calls IOBUFCTRL_CANCEL on each filter in the pipeline.  Then calls
+   io_close() on the pipeline.  Finally, if the pipeline is an output
+   pipeline, deletes the file.  Returns the result of calling
+   iobuf_close on the pipeline.  */
 int iobuf_cancel (iobuf_t iobuf);
 
+/* Add a new filter to the front of a pipeline.  A is the head of the
+   pipeline.  F is the filter implementation.  OV is an opaque pointer
+   that is passed to F and is normally used to hold any internal
+   state, such as a file pointer.
+
+   Note: you may only maintain a reference to an iobuf_t as a
+   reference to the head of the pipeline.  That is, don't think about
+   setting a pointer in OV to point to the filter's iobuf_t.  This is
+   because when we add a new filter to a pipeline, we memcpy the state
+   in A into new buffer.  This has the advantage that there is no need
+   to update any references to the pipeline when a filter is added or
+   removed, but it also means that a filter's state moves around in
+   memory.
+
+   The behavior of the filter function is determined by the value of
+   the control parameter:
+
+     IOBUFCTRL_INIT: Called this value just before the filter is
+       linked into the pipeline. This can be used to initialize
+       internal data structures.
+
+     IOBUFCTRL_FREE: Called with this value just before the filter is
+       removed from the pipeline.  Normally used to release internal
+       data structures, close a file handle, etc.
+
+     IOBUFCTRL_UNDERFLOW: Called with this value to fill the passed
+       buffer with more data. *LEN is the size of the buffer.  Before
+       returning, it should be set to the number of bytes which were
+       written into the buffer.  The function must return 0 to
+       indicate success, -1 on EOF and a GPG_ERR_xxxxx code for any
+       error.
+
+       Note: this function may both return data and indicate an error
+       or EOF.  In this case, it simply writes the data to BUF, sets
+       *LEN and returns the appropriate return code.  The implication
+       is that if an error occurs and no data has yet been written, it
+       is essential that *LEN be set to 0!
+
+     IOBUFCTRL_FLUSH: Called with this value to write out any
+       collected data.  *LEN is the number of bytes in BUF that need
+       to be written out.  Returns 0 on success and a GPG_ERR_* code
+       otherwise.  *LEN must be set to the number of bytes that were
+       written out.
+
+     IOBUFCTRL_CANCEL: Called with this value when iobuf_cancel() is
+       called on the pipeline.
+
+     IOBUFCTRL_DESC: Called with this value to get a human-readable
+       description of the filter.  * (char **) BUF should set to the
+       NUL-terminated string.  Note: you need to keep track of this
+       value and, if necessary, free it when the filter function is
+       called with control set to IOBUFCTRL_FREE.
+  */
 int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control,
-                                         iobuf_t chain, byte * buf,
-                                         size_t * len), void *ov);
+                                           iobuf_t chain, byte * buf,
+                                           size_t * len), void *ov);
+/* This variant of iobuf_push_filter allows the called to indicate
+   that OV should be freed when this filter is freed.  That is, if
+   REL_OV is TRUE, then when the filter is popped or freed OV will be
+   freed after the filter function is called with control set to
+   IOBUFCTRL_FREE.  */
 int iobuf_push_filter2 (iobuf_t a,
                        int (*f) (void *opaque, int control, iobuf_t chain,
                                  byte * buf, size_t * len), void *ov,
                        int rel_ov);
+
+/* Used for debugging.  Prints out the chain using log_debug if
+   IOBUF_DEBUG_MODE is not 0.  */
+int iobuf_print_chain (iobuf_t a);
+
+/* Indicate that some error occured on the specified filter.  */
 #define iobuf_set_error(a)    do { (a)->error = 1; } while(0)
+
+/* Return any pending error on filter A.  */
 #define iobuf_error(a)       ((a)->error)
 
+/* Limit the amount of additional data that may be read from the
+   filter.  That is, if you've already read 100 bytes from A and you
+   set the limit to 50, then you can read up to an additional 50 bytes
+   (i.e., a total of 150 bytes) before EOF is forcefully returned.
+   Setting NLIMIT to 0 removes any active limit.
+
+   Note: using iobuf_seek removes any currently enforced limit!  */
 void iobuf_set_limit (iobuf_t a, off_t nlimit);
 
+/* Returns the number of bytes that have been read from the pipeline.
+   Note: the result is undefined for IOBUF_OUTPUT and IOBUF_TEMP
+   pipelines!  */
 off_t iobuf_tell (iobuf_t a);
+
+/* There are two cases:
+
+   - If A is an INPUT or OUTPUT pipeline, then the last filter in the
+     pipeline is found.  If that is not a file filter, -1 is returned.
+     Otherwise, an fseek(..., SEEK_SET) is performed on the file
+     descriptor.
+
+   - If A is a TEMP pipeline and the *first* (and thus only filter) is
+     a TEMP filter, then the "file position" is effectively unchanged.
+     That is, data is appended to the buffer and the seek does not
+     cause the size of the buffer to grow.
+
+   If no error occured, then any limit previous set by
+   iobuf_set_limit() is cleared.  Further, any error on the filter
+   (the file filter or the temp filter) is cleared.
+
+   Returns 0 on success and -1 if an error occurs.  */
 int iobuf_seek (iobuf_t a, off_t newpos);
 
+/* Read a single byte.  If a filter has no more data, returns -1 to
+   indicate the EOF.  Generally, you don't want to use this function,
+   but instead prefer the iobuf_get macro, which is faster if there is
+   data in the internal buffer.  */
 int iobuf_readbyte (iobuf_t a);
+
+/* Get a byte from the iobuf; must check for eof prior to this
+   function.  This function returns values in the range 0 .. 255 or -1
+   to indicate EOF.  iobuf_get_noeof() does not return -1 to indicate
+   EOF, but masks the returned value to be in the range 0 .. 255.  */
+#define iobuf_get(a)  \
+     ( ((a)->nofast || (a)->d.start >= (a)->d.len )?  \
+       iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
+#define iobuf_get_noeof(a)    (iobuf_get((a))&0xff)
+
+/* Fill BUF with up to BUFLEN bytes.  If a filter has no more data,
+   returns -1 to indicate the EOF.  Otherwise returns the number of
+   bytes read.  */
 int iobuf_read (iobuf_t a, void *buf, unsigned buflen);
+
+/* Read a line of input (including the '\n') from the pipeline.
+
+   The semantics are the same as for fgets(), but if the buffer is too
+   short a larger one will be allocated up to *MAX_LENGTH and the end
+   of the line except the trailing '\n' discarded.  (Thus,
+   *ADDR_OF_BUFFER must be allocated using malloc().)  If the buffer
+   is enlarged, then *LENGTH_OF_BUFFER will be updated to reflect the
+   new size.  If the line is truncated, then *MAX_LENGTH will be set
+   to 0.  If *ADDR_OF_BUFFER is NULL, a buffer is allocated using
+   malloc().
+
+   A line is considered a byte stream ending in a '\n'.  Returns the
+   number of characters written to the buffer (i.e., excluding any
+   discarded characters due to truncation).  Thus, use this instead of
+   strlen(buffer) to determine the length of the string as this is
+   unreliable if the input contains NUL characters.
+
+   EOF is indicated by a line of length zero.
+
+   The last LF may be missing due to an EOF.  */
 unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
                          unsigned *length_of_buffer, unsigned *max_length);
+
+/* Read up to BUFLEN bytes from pipeline A.  Note: this function can't
+   return more than the pipeline's internal buffer size.  The return
+   value is the number of bytes actually written to BUF.  If the
+   filter returns EOF, then this function returns -1.
+
+   This function does not clear any pending EOF.  That is, if the
+   pipeline consists of two filters and the first one returns EOF
+   during the peek, then the subsequent iobuf_read* will still return
+   EOF before returning the data from the second filter.  */
 int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen);
+
+/* Write a byte to the pipeline.  Returns 0 on success and an error
+   code otherwise.  */
 int iobuf_writebyte (iobuf_t a, unsigned c);
+
+/* Alias for iobuf_writebyte.  */
+#define iobuf_put(a,c) iobuf_writebyte(a,c)
+
+/* Write a sequence of bytes to the pipeline.  Returns 0 on success
+   and an error code otherwise.  */
 int iobuf_write (iobuf_t a, const void *buf, unsigned buflen);
+
+/* Write a string (not including the NUL terminator) to the pipeline.
+   Returns 0 on success and an error code otherwise.  */
 int iobuf_writestr (iobuf_t a, const char *buf);
 
+/* Flushes the pipeline removing all filters but the sink (the last
+   filter) in the process.  */
 void iobuf_flush_temp (iobuf_t temp);
-int iobuf_write_temp (iobuf_t a, iobuf_t temp);
+
+/* Flushes the pipeline SOURCE removing all filters but the sink (the
+   last filter) in the process (i.e., it calls
+   iobuf_flush_temp(source)) and then writes the data to the pipeline
+   DEST.  Note: this doesn't free (iobuf_close()) SOURCE.  Both SOURCE
+   and DEST must be output pipelines.  */
+int iobuf_write_temp (iobuf_t dest, iobuf_t source);
+
+/* Flushes each filter in the pipeline (i.e., sends any buffered data
+   to the filter by calling IOBUFCTRL_FLUSH).  Then, copies up to the
+   first BUFLEN bytes from the last filter's internal buffer (which
+   will only be non-empty if it is a temp filter) to the buffer
+   BUFFER.  Returns the number of bytes actually copied.  */
 size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen);
 
+/* Return the size of any underlying file.  This only works with
+   file_filter based pipelines.
+
+   On Win32, it is sometimes not possible to determine the size of
+   files larger than 4GB.  In this case, *OVERFLOW (if not NULL) is
+   set to 1.  Otherwise, *OVERFLOW is set to 0.  */
 off_t iobuf_get_filelength (iobuf_t a, int *overflow);
 #define IOBUF_FILELENGTH_LIMIT 0xffffffff
+
+/* Return the file descriptor designating the underlying file.  This
+   only works with file_filter based pipelines.  */
 int  iobuf_get_fd (iobuf_t a);
+
+/* Return the real filename, if available.  This only supports
+   pipelines that end in file filters.  Returns NULL if not
+   available.  */
 const char *iobuf_get_real_fname (iobuf_t a);
+
+/* Return the filename or a description thereof.  For instance, for
+   iobuf_open("-"), this will return "[stdin]".  This only supports
+   pipelines that end in file filters.  Returns NULL if not
+   available.  */
 const char *iobuf_get_fname (iobuf_t a);
+
+/* Like iobuf_getfname, but instead of returning NULL if no
+   description is available, return "[?]".  */
 const char *iobuf_get_fname_nonnull (iobuf_t a);
 
+/* Pushes a filter on the pipeline that interprets the datastream as
+   an OpenPGP data block whose length is encoded using partial body
+   length headers (see Section 4.2.2.4 of RFC 4880).  Concretely, it
+   just returns / writes the data and finishes the packet with an
+   EOF.  */
 void iobuf_set_partial_block_mode (iobuf_t a, size_t len);
 
-void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial);
-
+/* If PARTIAL is set, then read from the pipeline until the first EOF
+   is returned.
 
-/* Get a byte from the iobuf; must check for eof prior to this
- * function.  This function returns values in the range 0 .. 255 or -1
- * to indicate EOF.  iobuf_get_noeof() does not return -1 to indicate
- * EOF, but masks the returned value to be in the range 0 .. 255.
- */
-#define iobuf_get(a)  \
-     ( ((a)->nofast || (a)->d.start >= (a)->d.len )?  \
-       iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
-#define iobuf_get_noeof(a)    (iobuf_get((a))&0xff)
+   If PARTIAL is 0, then read up to N bytes or until the first EOF is
+   returned.
 
-/* write a byte to the iobuf and return true on write error
- * This macro does only write the low order byte
- */
-#define iobuf_put(a,c) iobuf_writebyte(a,c)
+   Recall: a filter can return EOF.  In this case, it and all
+   preceding filters are popped from the pipeline and the next read is
  from the following filter (which may or may not return EOF).  */
+void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial);
 
 #define iobuf_where(a) "[don't know]"
+
+/* Each time a filter is allocated (via iobuf_alloc()), a
+   monotonically increasing counter is incremented and this field is
+   set to the new value.  This macro returns that number.  */
 #define iobuf_id(a)    ((a)->no)
 
 #define iobuf_get_temp_buffer(a) ( (a)->d.buf )