diff options
author | Neal H. Walfield <[email protected]> | 2015-08-17 10:30:04 +0000 |
---|---|---|
committer | Neal H. Walfield <[email protected]> | 2015-08-20 12:16:25 +0000 |
commit | 1bfd1e43246c16e20f819bf5381ca21abde54458 (patch) | |
tree | 6899765629f6e4ec368ac8adb982bbee7baad4a6 /common/iobuf.h | |
parent | common/iobuf.c: Adjust buffer size of filters in front of temp filters. (diff) | |
download | gnupg-1bfd1e43246c16e20f819bf5381ca21abde54458.tar.gz gnupg-1bfd1e43246c16e20f819bf5381ca21abde54458.zip |
common/iobuf: Improve documentation and code comments.
common/iobuf.h: Improve documentation and code comments.
common/iobuf.c: Likewise.
--
Signed-off-by: Neal H. Walfield <[email protected]>.
Diffstat (limited to 'common/iobuf.h')
-rw-r--r-- | common/iobuf.h | 487 |
1 files changed, 450 insertions, 37 deletions
diff --git a/common/iobuf.h b/common/iobuf.h index 5cfccb8f0..7157e0f44 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -31,6 +31,63 @@ #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> @@ -40,13 +97,16 @@ #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 ) |