/* * Copyright (C) 1996-2005 The Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* Code for the buffer data structure. */ #include "cvs.h" #include "buffer.h" #include "pagealign_alloc.h" #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) # include /* OS/2 doesn't have EIO. FIXME: this whole notion of turning a different error into EIO strikes me as pretty dubious. */ # if !defined( EIO ) # define EIO EBADPOS # endif /* Local functions. */ static void buf_default_memory_error (struct buffer *); static struct buffer_data *get_buffer_data (void); /* Initialize a buffer structure. */ struct buffer * buf_initialize (type_buf_input input, type_buf_output output, type_buf_flush flush, type_buf_block block, type_buf_get_fd get_fd, type_buf_shutdown shutdown, type_buf_memory_error memory_error, void *closure) { struct buffer *buf; buf = xmalloc (sizeof (struct buffer)); buf->data = NULL; buf->last = NULL; buf->nonblocking = false; buf->input = input; buf->output = output; buf->flush = flush; buf->block = block; buf->get_fd = get_fd; buf->shutdown = shutdown; buf->memory_error = memory_error ? memory_error : buf_default_memory_error; buf->closure = closure; return buf; } /* Free a buffer structure. */ void buf_free (struct buffer *buf) { if (buf->closure != NULL) { free (buf->closure); buf->closure = NULL; } buf_free_data (buf); free (buf); } /* Initialize a buffer structure which is not to be used for I/O. */ struct buffer * buf_nonio_initialize( void (*memory) (struct buffer *) ) { return buf_initialize (NULL, NULL, NULL, NULL, NULL, NULL, memory, NULL); } /* Default memory error handler. */ static void buf_default_memory_error (struct buffer *buf) { error (1, 0, "out of memory"); } /* Allocate more buffer_data structures. */ /* Get a new buffer_data structure. */ static struct buffer_data * get_buffer_data (void) { struct buffer_data *ret; ret = xmalloc (sizeof (struct buffer_data)); ret->text = pagealign_xalloc (BUFFER_DATA_SIZE); return ret; } /* See whether a buffer and its file descriptor is empty. */ int buf_empty (buf) struct buffer *buf; { /* Try and read any data on the file descriptor first. * We already know the descriptor is non-blocking. */ buf_input_data (buf, NULL); return buf_empty_p (buf); } /* See whether a buffer is empty. */ int buf_empty_p (struct buffer *buf) { struct buffer_data *data; for (data = buf->data; data != NULL; data = data->next) if (data->size > 0) return 0; return 1; } # if defined (SERVER_FLOWCONTROL) || defined (PROXY_SUPPORT) /* * Count how much data is stored in the buffer.. * Note that each buffer is a xmalloc'ed chunk BUFFER_DATA_SIZE. */ int buf_count_mem (struct buffer *buf) { struct buffer_data *data; int mem = 0; for (data = buf->data; data != NULL; data = data->next) mem += BUFFER_DATA_SIZE; return mem; } # endif /* SERVER_FLOWCONTROL || PROXY_SUPPORT */ /* Add data DATA of length LEN to BUF. */ void buf_output (struct buffer *buf, const char *data, size_t len) { if (buf->data != NULL && (((buf->last->text + BUFFER_DATA_SIZE) - (buf->last->bufp + buf->last->size)) >= len)) { memcpy (buf->last->bufp + buf->last->size, data, len); buf->last->size += len; return; } while (1) { struct buffer_data *newdata; newdata = get_buffer_data (); if (newdata == NULL) { (*buf->memory_error) (buf); return; } if (buf->data == NULL) buf->data = newdata; else buf->last->next = newdata; newdata->next = NULL; buf->last = newdata; newdata->bufp = newdata->text; if (len <= BUFFER_DATA_SIZE) { newdata->size = len; memcpy (newdata->text, data, len); return; } newdata->size = BUFFER_DATA_SIZE; memcpy (newdata->text, data, BUFFER_DATA_SIZE); data += BUFFER_DATA_SIZE; len -= BUFFER_DATA_SIZE; } /*NOTREACHED*/ } /* Add a '\0' terminated string to BUF. */ void buf_output0 (struct buffer *buf, const char *string) { buf_output (buf, string, strlen (string)); } /* Add a single character to BUF. */ void buf_append_char (struct buffer *buf, int ch) { if (buf->data != NULL && (buf->last->text + BUFFER_DATA_SIZE != buf->last->bufp + buf->last->size)) { *(buf->last->bufp + buf->last->size) = ch; ++buf->last->size; } else { char b; b = ch; buf_output (buf, &b, 1); } } /* Free struct buffer_data's from the list starting with FIRST and ending at * LAST, inclusive. */ static inline void buf_free_datas (struct buffer_data *first, struct buffer_data *last) { struct buffer_data *b, *n, *p; b = first; do { p = b; n = b->next; pagealign_free (b->text); free (b); b = n; } while (p != last); } /* * Send all the output we've been saving up. Returns 0 for success or * errno code. If the buffer has been set to be nonblocking, this * will just write until the write would block. */ int buf_send_output (struct buffer *buf) { assert (buf->output != NULL); while (buf->data != NULL) { struct buffer_data *data; data = buf->data; if (data->size > 0) { int status; size_t nbytes; status = (*buf->output) (buf->closure, data->bufp, data->size, &nbytes); if (status != 0) { /* Some sort of error. Discard the data, and return. */ buf_free_data (buf); return status; } if (nbytes != data->size) { /* Not all the data was written out. This is only permitted in nonblocking mode. Adjust the buffer, and return. */ assert (buf->nonblocking); data->size -= nbytes; data->bufp += nbytes; return 0; } } buf->data = data->next; buf_free_datas (data, data); } buf->last = NULL; return 0; } /* * Flush any data queued up in the buffer. If BLOCK is nonzero, then * if the buffer is in nonblocking mode, put it into blocking mode for * the duration of the flush. This returns 0 on success, or an error * code. */ int buf_flush (struct buffer *buf, bool block) { int nonblocking; int status; assert (buf->flush != NULL); nonblocking = buf->nonblocking; if (nonblocking && block) { status = set_block (buf); if (status != 0) return status; } status = buf_send_output (buf); if (status == 0) status = (*buf->flush) (buf->closure); if (nonblocking && block) { int blockstat; blockstat = set_nonblock (buf); if (status == 0) status = blockstat; } return status; } /* * Set buffer BUF to nonblocking I/O. Returns 0 for success or errno * code. */ int set_nonblock (struct buffer *buf) { int status; if (buf->nonblocking) return 0; assert (buf->block != NULL); status = (*buf->block) (buf->closure, 0); if (status != 0) return status; buf->nonblocking = true; return 0; } /* * Set buffer BUF to blocking I/O. Returns 0 for success or errno * code. */ int set_block (struct buffer *buf) { int status; if (! buf->nonblocking) return 0; assert (buf->block != NULL); status = (*buf->block) (buf->closure, 1); if (status != 0) return status; buf->nonblocking = false; return 0; } /* * Send a character count and some output. Returns errno code or 0 for * success. * * Sending the count in binary is OK since this is only used on a pipe * within the same system. */ int buf_send_counted (struct buffer *buf) { int size; struct buffer_data *data; size = 0; for (data = buf->data; data != NULL; data = data->next) size += data->size; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return ENOMEM; } data->next = buf->data; buf->data = data; if (buf->last == NULL) buf->last = data; data->bufp = data->text; data->size = sizeof (int); *((int *) data->text) = size; return buf_send_output (buf); } /* * Send a special count. COUNT should be negative. It will be * handled specially by buf_copy_counted. This function returns 0 or * an errno code. * * Sending the count in binary is OK since this is only used on a pipe * within the same system. */ int buf_send_special_count (struct buffer *buf, int count) { struct buffer_data *data; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return ENOMEM; } data->next = buf->data; buf->data = data; if (buf->last == NULL) buf->last = data; data->bufp = data->text; data->size = sizeof (int); *((int *) data->text) = count; return buf_send_output (buf); } /* Append a list of buffer_data structures to an buffer. */ void buf_append_data (struct buffer *buf, struct buffer_data *data, struct buffer_data *last) { if (data != NULL) { if (buf->data == NULL) buf->data = data; else buf->last->next = data; buf->last = last; } } # ifdef PROXY_SUPPORT /* Copy data structures and append them to a buffer. * * ERRORS * Failure to allocate memory here is fatal. */ void buf_copy_data (struct buffer *buf, struct buffer_data *data, struct buffer_data *last) { struct buffer_data *first, *new, *cur, *prev; assert (buf); assert (data); prev = first = NULL; cur = data; while (1) { new = get_buffer_data (); if (!new) error (1, errno, "Failed to allocate buffer data."); if (!first) first = new; memcpy (new->text, cur->bufp, cur->size); new->bufp = new->text; new->size = cur->size; new->next = NULL; if (prev) prev->next = new; if (cur == last) break; prev = new; cur = cur->next; } buf_append_data (buf, first, new); } # endif /* PROXY_SUPPORT */ /* Dispose of any remaining data in the buffer. */ void buf_free_data (struct buffer *buffer) { if (buf_empty_p (buffer)) return; buf_free_datas (buffer->data, buffer->last); buffer->data = buffer->last = NULL; } /* Append the data in one buffer to another. This removes the data * from the source buffer. */ void buf_append_buffer (struct buffer *to, struct buffer *from) { struct buffer_data *n; /* Copy the data pointer to the new buf. */ buf_append_data (to, from->data, from->last); n = from->data; while (n) { if (n == from->last) break; n = n->next; } /* Remove from the original location. */ from->data = NULL; from->last = NULL; } /* * Copy the contents of file F into buffer_data structures. We can't * copy directly into an buffer, because we want to handle failure and * success differently. Returns 0 on success, or -2 if out of * memory, or a status code on error. Since the caller happens to * know the size of the file, it is passed in as SIZE. On success, * this function sets *RETP and *LASTP, which may be passed to * buf_append_data. */ int buf_read_file (FILE *f, long int size, struct buffer_data **retp, struct buffer_data **lastp) { int status; *retp = NULL; *lastp = NULL; while (size > 0) { struct buffer_data *data; int get; data = get_buffer_data (); if (data == NULL) { status = -2; goto error_return; } if (*retp == NULL) *retp = data; else (*lastp)->next = data; data->next = NULL; *lastp = data; data->bufp = data->text; data->size = 0; if (size > BUFFER_DATA_SIZE) get = BUFFER_DATA_SIZE; else get = size; errno = EIO; if (fread (data->text, get, 1, f) != 1) { status = errno; goto error_return; } data->size += get; size -= get; } return 0; error_return: if (*retp != NULL) buf_free_datas (*retp, (*lastp)->next); return status; } /* * Copy the contents of file F into buffer_data structures. We can't * copy directly into an buffer, because we want to handle failure and * success differently. Returns 0 on success, or -2 if out of * memory, or a status code on error. On success, this function sets * *RETP and *LASTP, which may be passed to buf_append_data. */ int buf_read_file_to_eof (FILE *f, struct buffer_data **retp, struct buffer_data **lastp) { int status; *retp = NULL; *lastp = NULL; while (!feof (f)) { struct buffer_data *data; int get, nread; data = get_buffer_data (); if (data == NULL) { status = -2; goto error_return; } if (*retp == NULL) *retp = data; else (*lastp)->next = data; data->next = NULL; *lastp = data; data->bufp = data->text; data->size = 0; get = BUFFER_DATA_SIZE; errno = EIO; nread = fread (data->text, 1, get, f); if (nread == 0 && !feof (f)) { status = errno; goto error_return; } data->size = nread; } return 0; error_return: if (*retp != NULL) buf_free_datas (*retp, (*lastp)->next); return status; } /* Return the number of bytes in a chain of buffer_data structures. */ int buf_chain_length (struct buffer_data *buf) { int size = 0; while (buf) { size += buf->size; buf = buf->next; } return size; } /* Return the number of bytes in a buffer. */ int buf_length (struct buffer *buf) { return buf_chain_length (buf->data); } /* * Read an arbitrary amount of data into an input buffer. The buffer * will be in nonblocking mode, and we just grab what we can. Return * 0 on success, or -1 on end of file, or -2 if out of memory, or an * error code. If COUNTP is not NULL, *COUNTP is set to the number of * bytes read. */ int buf_input_data (struct buffer *buf, size_t *countp) { assert (buf->input != NULL); if (countp != NULL) *countp = 0; while (1) { int status; size_t get, nbytes; if (buf->data == NULL || (buf->last->bufp + buf->last->size == buf->last->text + BUFFER_DATA_SIZE)) { struct buffer_data *data; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return -2; } if (buf->data == NULL) buf->data = data; else buf->last->next = data; data->next = NULL; buf->last = data; data->bufp = data->text; data->size = 0; } get = ((buf->last->text + BUFFER_DATA_SIZE) - (buf->last->bufp + buf->last->size)); status = (*buf->input) (buf->closure, buf->last->bufp + buf->last->size, 0, get, &nbytes); if (status != 0) return status; buf->last->size += nbytes; if (countp != NULL) *countp += nbytes; if (nbytes < get) { /* If we did not fill the buffer, then presumably we read all the available data. */ return 0; } } /*NOTREACHED*/ } /* * Read a line (characters up to a \012) from an input buffer. (We * use \012 rather than \n for the benefit of non Unix clients for * which \n means something else). This returns 0 on success, or -1 * on end of file, or -2 if out of memory, or an error code. If it * succeeds, it sets *LINE to an allocated buffer holding the contents * of the line. The trailing \012 is not included in the buffer. If * LENP is not NULL, then *LENP is set to the number of bytes read; * strlen may not work, because there may be embedded null bytes. */ int buf_read_line (struct buffer *buf, char **line, size_t *lenp) { return buf_read_short_line (buf, line, lenp, SIZE_MAX); } /* Like buf_read_line, but return -2 if no newline is found in MAX characters. */ int buf_read_short_line (struct buffer *buf, char **line, size_t *lenp, size_t max) { assert (buf->input != NULL); *line = NULL; while (1) { size_t len, finallen, predicted_len; struct buffer_data *data; char *nl; /* See if there is a newline in BUF. */ len = 0; for (data = buf->data; data != NULL; data = data->next) { nl = memchr (data->bufp, '\012', data->size); if (nl != NULL) { finallen = nl - data->bufp; if (xsum (len, finallen) >= max) return -2; len += finallen; break; } else if (xsum (len, data->size) >= max) return -2; len += data->size; } /* If we found a newline, copy the line into a memory buffer, and remove it from BUF. */ if (data != NULL) { char *p; struct buffer_data *nldata; p = xmalloc (len + 1); if (p == NULL) return -2; *line = p; nldata = data; data = buf->data; while (data != nldata) { struct buffer_data *next; memcpy (p, data->bufp, data->size); p += data->size; next = data->next; buf_free_datas (data, data); data = next; } memcpy (p, data->bufp, finallen); p[finallen] = '\0'; data->size -= finallen + 1; data->bufp = nl + 1; buf->data = data; if (lenp != NULL) *lenp = len; return 0; } /* Read more data until we get a newline or MAX characters. */ predicted_len = 0; while (1) { int status; size_t size, nbytes; char *mem; if (buf->data == NULL || (buf->last->bufp + buf->last->size == buf->last->text + BUFFER_DATA_SIZE)) { data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return -2; } if (buf->data == NULL) buf->data = data; else buf->last->next = data; data->next = NULL; buf->last = data; data->bufp = data->text; data->size = 0; } mem = buf->last->bufp + buf->last->size; size = (buf->last->text + BUFFER_DATA_SIZE) - mem; /* We need to read at least 1 byte. We can handle up to SIZE bytes. This will only be efficient if the underlying communication stream does its own buffering, or is clever about getting more than 1 byte at a time. */ status = (*buf->input) (buf->closure, mem, 1, size, &nbytes); if (status != 0) return status; predicted_len += nbytes; buf->last->size += nbytes; /* Optimize slightly to avoid an unnecessary call to memchr. */ if (nbytes == 1) { if (*mem == '\012') break; } else { if (memchr (mem, '\012', nbytes) != NULL) break; } if (xsum (len, predicted_len) >= max) return -2; } } } /* * Extract data from the input buffer BUF. This will read up to WANT * bytes from the buffer. It will set *RETDATA to point at the bytes, * and set *GOT to the number of bytes to be found there. Any buffer * call which uses BUF may change the contents of the buffer at *DATA, * so the data should be fully processed before any further calls are * made. This returns 0 on success, or -1 on end of file, or -2 if * out of memory, or an error code. */ int buf_read_data (struct buffer *buf, size_t want, char **retdata, size_t *got) { assert (buf->input != NULL); while (buf->data != NULL && buf->data->size == 0) { struct buffer_data *next; next = buf->data->next; buf_free_datas (buf->data, buf->data); buf->data = next; if (next == NULL) buf->last = NULL; } if (buf->data == NULL) { struct buffer_data *data; int status; size_t get, nbytes; data = get_buffer_data (); if (data == NULL) { (*buf->memory_error) (buf); return -2; } buf->data = data; buf->last = data; data->next = NULL; data->bufp = data->text; data->size = 0; if (want < BUFFER_DATA_SIZE) get = want; else get = BUFFER_DATA_SIZE; status = (*buf->input) (buf->closure, data->bufp, get, BUFFER_DATA_SIZE, &nbytes); if (status != 0) return status; data->size = nbytes; } *retdata = buf->data->bufp; if (want < buf->data->size) { *got = want; buf->data->size -= want; buf->data->bufp += want; } else { *got = buf->data->size; buf->data->size = 0; } return 0; } /* * Copy lines from an input buffer to an output buffer. * This copies all complete lines (characters up to a * newline) from INBUF to OUTBUF. Each line in OUTBUF is preceded by the * character COMMAND and a space. */ void buf_copy_lines (struct buffer *outbuf, struct buffer *inbuf, int command) { while (1) { struct buffer_data *data; struct buffer_data *nldata; char *nl; int len; /* See if there is a newline in INBUF. */ nldata = NULL; nl = NULL; for (data = inbuf->data; data != NULL; data = data->next) { nl = memchr (data->bufp, '\n', data->size); if (nl != NULL) { nldata = data; break; } } if (nldata == NULL) { /* There are no more lines in INBUF. */ return; } /* Put in the command. */ buf_append_char (outbuf, command); buf_append_char (outbuf, ' '); if (inbuf->data != nldata) { /* * Simply move over all the buffers up to the one containing * the newline. */ for (data = inbuf->data; data->next != nldata; data = data->next); data->next = NULL; buf_append_data (outbuf, inbuf->data, data); inbuf->data = nldata; } /* * If the newline is at the very end of the buffer, just move * the buffer onto OUTBUF. Otherwise we must copy the data. */ len = nl + 1 - nldata->bufp; if (len == nldata->size) { inbuf->data = nldata->next; if (inbuf->data == NULL) inbuf->last = NULL; nldata->next = NULL; buf_append_data (outbuf, nldata, nldata); } else { buf_output (outbuf, nldata->bufp, len); nldata->bufp += len; nldata->size -= len; } } } /* * Copy counted data from one buffer to another. The count is an * integer, host size, host byte order (it is only used across a * pipe). If there is enough data, it should be moved over. If there * is not enough data, it should remain on the original buffer. A * negative count is a special case. if one is seen, *SPECIAL is set * to the (negative) count value and no additional data is gathered * from the buffer; normally *SPECIAL is set to 0. This function * returns the number of bytes it needs to see in order to actually * copy something over. */ int buf_copy_counted (struct buffer *outbuf, struct buffer *inbuf, int *special) { *special = 0; while (1) { struct buffer_data *data; int need; union { char intbuf[sizeof (int)]; int i; } u; char *intp; int count; struct buffer_data *start; int startoff; struct buffer_data *stop; int stopwant; /* See if we have enough bytes to figure out the count. */ need = sizeof (int); intp = u.intbuf; for (data = inbuf->data; data != NULL; data = data->next) { if (data->size >= need) { memcpy (intp, data->bufp, need); break; } memcpy (intp, data->bufp, data->size); intp += data->size; need -= data->size; } if (data == NULL) { /* We don't have enough bytes to form an integer. */ return need; } count = u.i; start = data; startoff = need; if (count < 0) { /* A negative COUNT is a special case meaning that we don't need any further information. */ stop = start; stopwant = 0; } else { /* * We have an integer in COUNT. We have gotten all the * data from INBUF in all buffers before START, and we * have gotten STARTOFF bytes from START. See if we have * enough bytes remaining in INBUF. */ need = count - (start->size - startoff); if (need <= 0) { stop = start; stopwant = count; } else { for (data = start->next; data != NULL; data = data->next) { if (need <= data->size) break; need -= data->size; } if (data == NULL) { /* We don't have enough bytes. */ return need; } stop = data; stopwant = need; } } /* * We have enough bytes. Free any buffers in INBUF before * START, and remove STARTOFF bytes from START, so that we can * forget about STARTOFF. */ start->bufp += startoff; start->size -= startoff; if (start->size == 0) start = start->next; if (stop->size == stopwant) { stop = stop->next; stopwant = 0; } while (inbuf->data != start) { data = inbuf->data; inbuf->data = data->next; buf_free_datas (data, data); } /* If COUNT is negative, set *SPECIAL and get out now. */ if (count < 0) { *special = count; return 0; } /* * We want to copy over the bytes from START through STOP. We * only want STOPWANT bytes from STOP. */ if (start != stop) { /* Attach the buffers from START through STOP to OUTBUF. */ for (data = start; data->next != stop; data = data->next); inbuf->data = stop; data->next = NULL; buf_append_data (outbuf, start, data); } if (stopwant > 0) { buf_output (outbuf, stop->bufp, stopwant); stop->bufp += stopwant; stop->size -= stopwant; } } /*NOTREACHED*/ } int buf_get_fd (struct buffer *buf) { if (buf->get_fd) return (*buf->get_fd) (buf->closure); return -1; } /* Shut down a buffer. This returns 0 on success, or an errno code. */ int buf_shutdown (struct buffer *buf) { if (buf->shutdown) return (*buf->shutdown) (buf); return 0; } /* Certain types of communication input and output data in packets, where each packet is translated in some fashion. The packetizing buffer type supports that, given a buffer which handles lower level I/O and a routine to translate the data in a packet. This code uses two bytes for the size of a packet, so packets are restricted to 65536 bytes in total. The translation functions should just translate; they may not significantly increase or decrease the amount of data. The actual size of the initial data is part of the translated data. The output translation routine may add up to PACKET_SLOP additional bytes, and the input translation routine should shrink the data correspondingly. */ # define PACKET_SLOP (100) /* This structure is the closure field of a packetizing buffer. */ struct packetizing_buffer { /* The underlying buffer. */ struct buffer *buf; /* The input translation function. Exactly one of inpfn and outfn will be NULL. The input translation function should untranslate the data in INPUT, storing the result in OUTPUT. SIZE is the amount of data in INPUT, and is also the size of OUTPUT. This should return 0 on success, or an errno code. */ int (*inpfn) (void *fnclosure, const char *input, char *output, size_t size); /* The output translation function. This should translate the data in INPUT, storing the result in OUTPUT. The first two bytes in INPUT will be the size of the data, and so will SIZE. This should set *TRANSLATED to the amount of translated data in OUTPUT. OUTPUT is large enough to hold SIZE + PACKET_SLOP bytes. This should return 0 on success, or an errno code. */ int (*outfn) (void *fnclosure, const char *input, char *output, size_t size, size_t *translated); /* A closure for the translation function. */ void *fnclosure; /* For an input buffer, we may have to buffer up data here. */ /* This is non-zero if the buffered data has been translated. Otherwise, the buffered data has not been translated, and starts with the two byte packet size. */ bool translated; /* The amount of buffered data. */ size_t holdsize; /* The buffer allocated to hold the data. */ char *holdbuf; /* The size of holdbuf. */ size_t holdbufsize; /* If translated is set, we need another data pointer to track where we are in holdbuf. If translated is clear, then this pointer is not used. */ char *holddata; }; static int packetizing_buffer_input (void *, char *, size_t, size_t, size_t *); static int packetizing_buffer_output (void *, const char *, size_t, size_t *); static int packetizing_buffer_flush (void *); static int packetizing_buffer_block (void *, bool); static int packetizing_buffer_get_fd (void *); static int packetizing_buffer_shutdown (struct buffer *); /* Create a packetizing buffer. */ struct buffer * packetizing_buffer_initialize (struct buffer *buf, int (*inpfn) (void *, const char *, char *, size_t), int (*outfn) (void *, const char *, char *, size_t, size_t *), void *fnclosure, void (*memory) (struct buffer *)) { struct packetizing_buffer *pb; pb = xmalloc (sizeof *pb); memset (pb, 0, sizeof *pb); pb->buf = buf; pb->inpfn = inpfn; pb->outfn = outfn; pb->fnclosure = fnclosure; if (inpfn != NULL) { /* Add PACKET_SLOP to handle larger translated packets, and add 2 for the count. This buffer is increased if necessary. */ pb->holdbufsize = BUFFER_DATA_SIZE + PACKET_SLOP + 2; pb->holdbuf = xmalloc (pb->holdbufsize); } return buf_initialize (inpfn != NULL ? packetizing_buffer_input : NULL, inpfn != NULL ? NULL : packetizing_buffer_output, inpfn != NULL ? NULL : packetizing_buffer_flush, packetizing_buffer_block, packetizing_buffer_get_fd, packetizing_buffer_shutdown, memory, pb); } /* Input data from a packetizing buffer. */ static int packetizing_buffer_input (void *closure, char *data, size_t need, size_t size, size_t *got) { struct packetizing_buffer *pb = closure; *got = 0; if (pb->holdsize > 0 && pb->translated) { size_t copy; copy = pb->holdsize; if (copy > size) { memcpy (data, pb->holddata, size); pb->holdsize -= size; pb->holddata += size; *got = size; return 0; } memcpy (data, pb->holddata, copy); pb->holdsize = 0; pb->translated = false; data += copy; need -= copy; size -= copy; *got = copy; } while (need > 0 || *got == 0) { int status; size_t get, nread, count, tcount; char *bytes; static char *stackoutbuf = NULL; char *inbuf, *outbuf; if (!stackoutbuf) stackoutbuf = xmalloc (BUFFER_DATA_SIZE + PACKET_SLOP); /* If we don't already have the two byte count, get it. */ if (pb->holdsize < 2) { get = 2 - pb->holdsize; status = buf_read_data (pb->buf, get, &bytes, &nread); if (status != 0) { /* buf_read_data can return -2, but a buffer input function is only supposed to return -1, 0, or an error code. */ if (status == -2) status = ENOMEM; return status; } if (nread == 0) { /* The buffer is in nonblocking mode, and we didn't manage to read anything. */ return 0; } if (get == 1) pb->holdbuf[1] = bytes[0]; else { pb->holdbuf[0] = bytes[0]; if (nread < 2) { /* We only got one byte, but we needed two. Stash the byte we got, and try again. */ pb->holdsize = 1; continue; } pb->holdbuf[1] = bytes[1]; } pb->holdsize = 2; } /* Read the packet. */ count = (((pb->holdbuf[0] & 0xff) << 8) + (pb->holdbuf[1] & 0xff)); if (count + 2 > pb->holdbufsize) { char *n; /* We didn't allocate enough space in the initialize function. */ n = xrealloc (pb->holdbuf, count + 2); if (n == NULL) { (*pb->buf->memory_error) (pb->buf); return ENOMEM; } pb->holdbuf = n; pb->holdbufsize = count + 2; } get = count - (pb->holdsize - 2); status = buf_read_data (pb->buf, get, &bytes, &nread); if (status != 0) { /* buf_read_data can return -2, but a buffer input function is only supposed to return -1, 0, or an error code. */ if (status == -2) status = ENOMEM; return status; } if (nread == 0) { /* We did not get any data. Presumably the buffer is in nonblocking mode. */ return 0; } if (nread < get) { /* We did not get all the data we need to fill the packet. buf_read_data does not promise to return all the bytes requested, so we must try again. */ memcpy (pb->holdbuf + pb->holdsize, bytes, nread); pb->holdsize += nread; continue; } /* We have a complete untranslated packet of COUNT bytes. */ if (pb->holdsize == 2) { /* We just read the entire packet (the 2 bytes in PB->HOLDBUF are the size). Save a memcpy by translating directly from BYTES. */ inbuf = bytes; } else { /* We already had a partial packet in PB->HOLDBUF. We need to copy the new data over to make the input contiguous. */ memcpy (pb->holdbuf + pb->holdsize, bytes, nread); inbuf = pb->holdbuf + 2; } if (count <= BUFFER_DATA_SIZE + PACKET_SLOP) outbuf = stackoutbuf; else { outbuf = xmalloc (count); if (outbuf == NULL) { (*pb->buf->memory_error) (pb->buf); return ENOMEM; } } status = (*pb->inpfn) (pb->fnclosure, inbuf, outbuf, count); if (status != 0) return status; /* The first two bytes in the translated buffer are the real length of the translated data. */ tcount = ((outbuf[0] & 0xff) << 8) + (outbuf[1] & 0xff); if (tcount > count) error (1, 0, "Input translation failure"); if (tcount > size) { /* We have more data than the caller has provided space for. We need to save some of it for the next call. */ memcpy (data, outbuf + 2, size); *got += size; pb->holdsize = tcount - size; memcpy (pb->holdbuf, outbuf + 2 + size, tcount - size); pb->holddata = pb->holdbuf; pb->translated = true; if (outbuf != stackoutbuf) free (outbuf); return 0; } memcpy (data, outbuf + 2, tcount); if (outbuf != stackoutbuf) free (outbuf); pb->holdsize = 0; data += tcount; need -= tcount; size -= tcount; *got += tcount; } return 0; } /* Output data to a packetizing buffer. */ static int packetizing_buffer_output (void *closure, const char *data, size_t have, size_t *wrote) { struct packetizing_buffer *pb = closure; static char *inbuf = NULL; /* These two buffers are static so that they * depend on the size of BUFFER_DATA_SIZE yet * still only be allocated once per run. */ static char *stack_outbuf = NULL; struct buffer_data *outdata = NULL; /* Initialize to silence -Wall. Dumb. */ char *outbuf; size_t size, translated; int status; /* It would be easy to xmalloc a buffer, but I don't think this case can ever arise. */ assert (have <= BUFFER_DATA_SIZE); if (!inbuf) { inbuf = xmalloc (BUFFER_DATA_SIZE + 2); stack_outbuf = xmalloc (BUFFER_DATA_SIZE + PACKET_SLOP + 4); } inbuf[0] = (have >> 8) & 0xff; inbuf[1] = have & 0xff; memcpy (inbuf + 2, data, have); size = have + 2; /* The output function is permitted to add up to PACKET_SLOP bytes, and we need 2 bytes for the size of the translated data. If we can guarantee that the result will fit in a buffer_data, we translate directly into one to avoid a memcpy in buf_output. */ if (size + PACKET_SLOP + 2 > BUFFER_DATA_SIZE) outbuf = stack_outbuf; else { outdata = get_buffer_data (); if (outdata == NULL) { (*pb->buf->memory_error) (pb->buf); return ENOMEM; } outdata->next = NULL; outdata->bufp = outdata->text; outbuf = outdata->text; } status = (*pb->outfn) (pb->fnclosure, inbuf, outbuf + 2, size, &translated); if (status != 0) return status; /* The output function is permitted to add up to PACKET_SLOP bytes. */ assert (translated <= size + PACKET_SLOP); outbuf[0] = (translated >> 8) & 0xff; outbuf[1] = translated & 0xff; if (outbuf == stack_outbuf) buf_output (pb->buf, outbuf, translated + 2); else { outdata->size = translated + 2; buf_append_data (pb->buf, outdata, outdata); } *wrote = have; /* We will only be here because buf_send_output was called on the packetizing buffer. That means that we should now call buf_send_output on the underlying buffer. */ return buf_send_output (pb->buf); } /* Flush data to a packetizing buffer. */ static int packetizing_buffer_flush (void *closure) { struct packetizing_buffer *pb = closure; /* Flush the underlying buffer. Note that if the original call to buf_flush passed 1 for the BLOCK argument, then the buffer will already have been set into blocking mode, so we should always pass 0 here. */ return buf_flush (pb->buf, 0); } /* The block routine for a packetizing buffer. */ static int packetizing_buffer_block (void *closure, bool block) { struct packetizing_buffer *pb = closure; if (block) return set_block (pb->buf); else return set_nonblock (pb->buf); } /* Return the file descriptor underlying any child buffers. */ static int packetizing_buffer_get_fd (void *closure) { struct packetizing_buffer *cb = closure; return buf_get_fd (cb->buf); } /* Shut down a packetizing buffer. */ static int packetizing_buffer_shutdown (struct buffer *buf) { struct packetizing_buffer *pb = buf->closure; return buf_shutdown (pb->buf); } /* All server communication goes through buffer structures. Most of the buffers are built on top of a file descriptor. This structure is used as the closure field in a buffer. */ struct fd_buffer { /* The file descriptor. */ int fd; /* Nonzero if the file descriptor is in blocking mode. */ int blocking; /* The child process id when fd is a pipe. */ pid_t child_pid; /* The connection info, when fd is a pipe to a server. */ cvsroot_t *root; }; static int fd_buffer_input (void *, char *, size_t, size_t, size_t *); static int fd_buffer_output (void *, const char *, size_t, size_t *); static int fd_buffer_flush (void *); static int fd_buffer_block (void *, bool); static int fd_buffer_get_fd (void *); static int fd_buffer_shutdown (struct buffer *); /* Initialize a buffer built on a file descriptor. FD is the file descriptor. INPUT is nonzero if this is for input, zero if this is for output. MEMORY is the function to call when a memory error occurs. */ struct buffer * fd_buffer_initialize (int fd, pid_t child_pid, cvsroot_t *root, bool input, void (*memory) (struct buffer *)) { struct fd_buffer *n; n = xmalloc (sizeof *n); n->fd = fd; n->child_pid = child_pid; n->root = root; fd_buffer_block (n, true); return buf_initialize (input ? fd_buffer_input : NULL, input ? NULL : fd_buffer_output, input ? NULL : fd_buffer_flush, fd_buffer_block, fd_buffer_get_fd, fd_buffer_shutdown, memory, n); } /* The buffer input function for a buffer built on a file descriptor. * * In non-blocking mode, this function will read as many bytes as it can in a * single try, up to SIZE bytes, and return. * * In blocking mode with NEED > 0, this function will read as many bytes as it * can but will not return until it has read at least NEED bytes. * * In blocking mode with NEED == 0, this function will block until it can read * either at least one byte or EOF, then read as many bytes as are available * and return. At the very least, compress_buffer_shutdown depends on this * behavior to read EOF and can loop indefinitely without it. * * ASSUMPTIONS * NEED <= SIZE. * * INPUTS * closure Our FD_BUFFER struct. * data The start of our input buffer. * need How many bytes our caller needs. * size How many bytes are available in DATA. * got Where to store the number of bytes read. * * OUTPUTS * data Filled with bytes read. * *got Number of bytes actually read into DATA. * * RETURNS * errno On error. * -1 On EOF. * 0 Otherwise. * * ERRORS * This function can return an error if fd_buffer_block(), or the system * read() or select() calls do. */ static int fd_buffer_input (void *closure, char *data, size_t need, size_t size, size_t *got) { struct fd_buffer *fb = closure; int nbytes; assert (need <= size); *got = 0; if (fb->blocking) { int status; fd_set readfds; /* Set non-block. */ status = fd_buffer_block (fb, false); if (status != 0) return status; FD_ZERO (&readfds); FD_SET (fb->fd, &readfds); do { int numfds; do { /* This used to select on exceptions too, but as far as I know there was never any reason to do that and SCO doesn't let you select on exceptions on pipes. */ numfds = fd_select (fb->fd + 1, &readfds, NULL, NULL, NULL); if (numfds < 0 && errno != EINTR) { status = errno; goto block_done; } } while (numfds < 0); nbytes = read (fb->fd, data + *got, size - *got); if (nbytes == 0) { /* End of file. This assumes that we are using POSIX or BSD style nonblocking I/O. On System V we will get a zero return if there is no data, even when not at EOF. */ if (*got) { /* We already read some data, so return no error, counting * on the fact that we will read EOF again next time. */ status = 0; break; } else { /* Return EOF. */ status = -1; break; } } if (nbytes < 0) { /* Some error occurred. */ if (!blocking_error (errno)) { status = errno; break; } /* else Everything's fine, we just didn't get any data. */ } *got += nbytes; } while (*got < need); block_done: if (status == 0 || status == -1) { int newstatus; /* OK or EOF - Reset block. */ newstatus = fd_buffer_block (fb, true); if (newstatus) status = newstatus; } return status; } /* The above will always return. Handle non-blocking read. */ nbytes = read (fb->fd, data, size); if (nbytes > 0) { *got = nbytes; return 0; } if (nbytes == 0) /* End of file. This assumes that we are using POSIX or BSD style nonblocking I/O. On System V we will get a zero return if there is no data, even when not at EOF. */ return -1; /* Some error occurred. */ if (blocking_error (errno)) /* Everything's fine, we just didn't get any data. */ return 0; return errno; } /* The buffer output function for a buffer built on a file descriptor. */ static int fd_buffer_output (void *closure, const char *data, size_t have, size_t *wrote) { struct fd_buffer *fd = closure; *wrote = 0; while (have > 0) { int nbytes; nbytes = write (fd->fd, data, have); if (nbytes <= 0) { if (! fd->blocking && (nbytes == 0 || blocking_error (errno))) { /* A nonblocking write failed to write any data. Just return. */ return 0; } /* Some sort of error occurred. */ if (nbytes == 0) return EIO; return errno; } *wrote += nbytes; data += nbytes; have -= nbytes; } return 0; } /* The buffer flush function for a buffer built on a file descriptor. */ static int fd_buffer_flush (void *closure) { /* We don't need to do anything here. Our fd doesn't have its own buffer * and syncing won't do anything but slow us down. * * struct fd_buffer *fb = closure; * * if (fsync (fb->fd) < 0 && errno != EROFS && errno != EINVAL) * return errno; */ return 0; } static struct stat devnull; static int devnull_set = -1; /* The buffer block function for a buffer built on a file descriptor. */ static int fd_buffer_block (void *closure, bool block) { struct fd_buffer *fb = closure; # if defined (F_GETFL) && defined (O_NONBLOCK) && defined (F_SETFL) int flags; flags = fcntl (fb->fd, F_GETFL, 0); if (flags < 0) return errno; if (block) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if (fcntl (fb->fd, F_SETFL, flags) < 0) { /* * BSD returns ENODEV when we try to set block/nonblock on /dev/null. * BSDI returns ENOTTY when we try to set block/nonblock on /dev/null. */ struct stat sb; int save_errno = errno; bool isdevnull = false; if (devnull_set == -1) devnull_set = stat ("/dev/null", &devnull); if (devnull_set >= 0) /* Equivalent to /dev/null ? */ isdevnull = (fstat (fb->fd, &sb) >= 0 && sb.st_dev == devnull.st_dev && sb.st_ino == devnull.st_ino && sb.st_mode == devnull.st_mode && sb.st_uid == devnull.st_uid && sb.st_gid == devnull.st_gid && sb.st_size == devnull.st_size && sb.st_blocks == devnull.st_blocks && sb.st_blksize == devnull.st_blksize); if (isdevnull) errno = 0; else { errno = save_errno; return errno; } } # endif /* F_GETFL && O_NONBLOCK && F_SETFL */ fb->blocking = block; return 0; } static int fd_buffer_get_fd (void *closure) { struct fd_buffer *fb = closure; return fb->fd; } /* The buffer shutdown function for a buffer built on a file descriptor. * * This function disposes of memory allocated for this buffer. */ static int fd_buffer_shutdown (struct buffer *buf) { struct fd_buffer *fb = buf->closure; struct stat s; bool closefd, statted; /* Must be an open pipe, socket, or file. What could go wrong? */ if (fstat (fb->fd, &s) == -1) statted = false; else statted = true; /* Don't bother to try closing the FD if we couldn't stat it. This * probably won't work. * * (buf_shutdown() on some of the server/child communication pipes is * getting EBADF on both the fstat and the close. I'm not sure why - * perhaps they were alredy closed somehow? */ closefd = statted; /* Flush the buffer if possible. */ if (buf->flush) { buf_flush (buf, 1); buf->flush = NULL; } if (buf->input) { /* There used to be a check here for unread data in the buffer of * the pipe, but it was deemed unnecessary and possibly dangerous. In * some sense it could be second-guessing the caller who requested it * closed, as well. */ /* FIXME: * * This mess of #ifdefs is hard to read. There must be some relation between * the macros being checked which at least deserves comments - if * SHUTDOWN_SERVER, NO_SOCKET_TO_FD, & START_RSH_WITH_POPEN_RW were completely * independant, then the next few lines could easily refuse to compile. * * The note below about START_RSH_WITH_POPEN_RW never being set when * SHUTDOWN_SERVER is defined means that this code would always break on * systems with SHUTDOWN_SERVER defined and thus the comment must now be * incorrect or the code was broken since the comment was written. */ # ifdef SHUTDOWN_SERVER if (fb->root && fb->root->method != server_method) # endif # ifndef NO_SOCKET_TO_FD { /* shutdown() sockets */ if (statted && S_ISSOCK (s.st_mode)) shutdown (fb->fd, 0); } # endif /* NO_SOCKET_TO_FD */ # ifdef START_RSH_WITH_POPEN_RW /* Can't be set with SHUTDOWN_SERVER defined */ /* FIXME: This is now certainly broken since pclose is defined by ANSI * C to accept a FILE * argument. The switch will need to happen at a * higher abstraction level to switch between initializing stdio & fd * buffers on systems that need this (or maybe an fd buffer that keeps * track of the FILE * could be used - I think flushing the stream * before beginning exclusive access via the FD is OK. */ else if (fb->root && pclose (fb->fd) == EOF) { error (1, errno, "closing connection to %s", fb->root->hostname); closefd = false; } # endif /* START_RSH_WITH_POPEN_RW */ buf->input = NULL; } else if (buf->output) { # ifdef SHUTDOWN_SERVER /* FIXME: Should have a SHUTDOWN_SERVER_INPUT & * SHUTDOWN_SERVER_OUTPUT */ if (fb->root && fb->root->method == server_method) SHUTDOWN_SERVER (fb->fd); else # endif # ifndef NO_SOCKET_TO_FD /* shutdown() sockets */ if (statted && S_ISSOCK (s.st_mode)) shutdown (fb->fd, 1); # else { /* I'm not sure I like this empty block, but the alternative * is another nested NO_SOCKET_TO_FD switch as above. */ } # endif /* NO_SOCKET_TO_FD */ buf->output = NULL; } if (statted && closefd && close (fb->fd) == -1) { if (server_active) { /* Syslog this? */ } # ifdef CLIENT_SUPPORT else if (fb->root) error (1, errno, "closing down connection to %s", fb->root->hostname); /* EXITS */ # endif /* CLIENT_SUPPORT */ error (0, errno, "closing down buffer"); } /* If we were talking to a process, make sure it exited */ if (fb->child_pid) { int w; do w = waitpid (fb->child_pid, NULL, 0); while (w == -1 && errno == EINTR); if (w == -1) error (1, errno, "waiting for process %d", fb->child_pid); } free (buf->closure); buf->closure = NULL; return 0; } #endif /* defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT) */