diff options
Diffstat (limited to 'subversion/libsvn_subr/stream.c')
-rw-r--r-- | subversion/libsvn_subr/stream.c | 800 |
1 files changed, 635 insertions, 165 deletions
diff --git a/subversion/libsvn_subr/stream.c b/subversion/libsvn_subr/stream.c index 93a4c42..2f803b8 100644 --- a/subversion/libsvn_subr/stream.c +++ b/subversion/libsvn_subr/stream.c @@ -29,7 +29,8 @@ #include <apr_strings.h> #include <apr_file_io.h> #include <apr_errno.h> -#include <apr_md5.h> +#include <apr_poll.h> +#include <apr_portable.h> #include <zlib.h> @@ -41,20 +42,24 @@ #include "svn_checksum.h" #include "svn_path.h" #include "svn_private_config.h" +#include "private/svn_atomic.h" #include "private/svn_error_private.h" #include "private/svn_eol_private.h" #include "private/svn_io_private.h" #include "private/svn_subr_private.h" +#include "private/svn_utf_private.h" struct svn_stream_t { void *baton; svn_read_fn_t read_fn; + svn_read_fn_t read_full_fn; svn_stream_skip_fn_t skip_fn; svn_write_fn_t write_fn; svn_close_fn_t close_fn; svn_stream_mark_fn_t mark_fn; svn_stream_seek_fn_t seek_fn; + svn_stream_data_available_fn_t data_available_fn; svn_stream__is_buffered_fn_t is_buffered_fn; apr_file_t *file; /* Maybe NULL */ }; @@ -63,7 +68,7 @@ struct svn_stream_t { /*** Forward declarations. ***/ static svn_error_t * -skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn); +skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn); /*** Generic streams. ***/ @@ -73,16 +78,8 @@ svn_stream_create(void *baton, apr_pool_t *pool) { svn_stream_t *stream; - stream = apr_palloc(pool, sizeof(*stream)); + stream = apr_pcalloc(pool, sizeof(*stream)); stream->baton = baton; - stream->read_fn = NULL; - stream->skip_fn = NULL; - stream->write_fn = NULL; - stream->close_fn = NULL; - stream->mark_fn = NULL; - stream->seek_fn = NULL; - stream->is_buffered_fn = NULL; - stream->file = NULL; return stream; } @@ -95,9 +92,12 @@ svn_stream_set_baton(svn_stream_t *stream, void *baton) void -svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn) +svn_stream_set_read2(svn_stream_t *stream, + svn_read_fn_t read_fn, + svn_read_fn_t read_full_fn) { stream->read_fn = read_fn; + stream->read_full_fn = read_full_fn; } void @@ -131,26 +131,74 @@ svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn) } void +svn_stream_set_data_available(svn_stream_t *stream, + svn_stream_data_available_fn_t data_available_fn) +{ + stream->data_available_fn = data_available_fn; +} + +void svn_stream__set_is_buffered(svn_stream_t *stream, svn_stream__is_buffered_fn_t is_buffered_fn) { stream->is_buffered_fn = is_buffered_fn; } +/* Standard implementation for svn_stream_read_full() based on + multiple svn_stream_read2() calls (in separate function to make + it more likely for svn_stream_read_full to be inlined) */ +static svn_error_t * +full_read_fallback(svn_stream_t *stream, char *buffer, apr_size_t *len) +{ + apr_size_t remaining = *len; + while (remaining > 0) + { + apr_size_t length = remaining; + SVN_ERR(svn_stream_read2(stream, buffer, &length)); + + if (length == 0) + { + *len -= remaining; + return SVN_NO_ERROR; + } + + remaining -= length; + buffer += length; + } + + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_stream_supports_partial_read(svn_stream_t *stream) +{ + return stream->read_fn != NULL; +} + svn_error_t * -svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len) +svn_stream_read2(svn_stream_t *stream, char *buffer, apr_size_t *len) { - SVN_ERR_ASSERT(stream->read_fn != NULL); + if (stream->read_fn == NULL) + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL); + return svn_error_trace(stream->read_fn(stream->baton, buffer, len)); } +svn_error_t * +svn_stream_read_full(svn_stream_t *stream, char *buffer, apr_size_t *len) +{ + if (stream->read_full_fn == NULL) + return svn_error_trace(full_read_fallback(stream, buffer, len)); + + return svn_error_trace(stream->read_full_fn(stream->baton, buffer, len)); +} svn_error_t * svn_stream_skip(svn_stream_t *stream, apr_size_t len) { if (stream->skip_fn == NULL) return svn_error_trace( - skip_default_handler(stream->baton, len, stream->read_fn)); + skip_default_handler(stream->baton, len, stream->read_full_fn)); return svn_error_trace(stream->skip_fn(stream->baton, len)); } @@ -159,7 +207,9 @@ svn_stream_skip(svn_stream_t *stream, apr_size_t len) svn_error_t * svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len) { - SVN_ERR_ASSERT(stream->write_fn != NULL); + if (stream->write_fn == NULL) + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL); + return svn_error_trace(stream->write_fn(stream->baton, data, len)); } @@ -196,6 +246,17 @@ svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark) return svn_error_trace(stream->seek_fn(stream->baton, mark)); } +svn_error_t * +svn_stream_data_available(svn_stream_t *stream, + svn_boolean_t *data_available) +{ + if (stream->data_available_fn == NULL) + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL); + + return svn_error_trace(stream->data_available_fn(stream->baton, + data_available)); +} + svn_boolean_t svn_stream__is_buffered(svn_stream_t *stream) { @@ -259,11 +320,6 @@ svn_stream_printf_from_utf8(svn_stream_t *stream, return svn_error_trace(svn_stream_puts(stream, translated)); } -/* Size that 90% of the lines we encounter will be not longer than. - used by stream_readline_bytewise() and stream_readline_chunky(). - */ -#define LINE_CHUNK_SIZE 80 - /* Guts of svn_stream_readline(). * Returns the line read from STREAM in *STRINGBUF, and indicates * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator @@ -286,14 +342,14 @@ stream_readline_bytewise(svn_stringbuf_t **stringbuf, optimize for the 90% case. 90% of the time, we can avoid the stringbuf ever having to realloc() itself if we start it out at 80 chars. */ - str = svn_stringbuf_create_ensure(LINE_CHUNK_SIZE, pool); + str = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool); /* Read into STR up to and including the next EOL sequence. */ match = eol; while (*match) { numbytes = 1; - SVN_ERR(svn_stream_read(stream, &c, &numbytes)); + SVN_ERR(svn_stream_read_full(stream, &c, &numbytes)); if (numbytes != 1) { /* a 'short' read means the stream has run out. */ @@ -330,7 +386,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, * larger value because filling the buffer from the stream takes * time as well. */ - char buffer[LINE_CHUNK_SIZE+1]; + char buffer[SVN__LINE_CHUNK_SIZE+1]; /* variables */ svn_stream_mark_t *mark; @@ -347,8 +403,8 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, SVN_ERR(svn_stream_mark(stream, &mark, pool)); /* Read the first chunk. */ - numbytes = LINE_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, buffer, &numbytes)); + numbytes = SVN__LINE_CHUNK_SIZE; + SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes)); buffer[numbytes] = '\0'; /* Look for the EOL in this first chunk. If we find it, we are done here. @@ -359,7 +415,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool); total_parsed = eol_pos - buffer + eol_len; } - else if (numbytes < LINE_CHUNK_SIZE) + else if (numbytes < SVN__LINE_CHUNK_SIZE) { /* We hit EOF but not EOL. */ @@ -371,7 +427,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, { /* A larger buffer for the string is needed. */ svn_stringbuf_t *str; - str = svn_stringbuf_create_ensure(2*LINE_CHUNK_SIZE, pool); + str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool); svn_stringbuf_appendbytes(str, buffer, numbytes); *stringbuf = str; @@ -381,9 +437,9 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, { /* Append the next chunk to the string read so far. */ - svn_stringbuf_ensure(str, str->len + LINE_CHUNK_SIZE); - numbytes = LINE_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, str->data + str->len, &numbytes)); + svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE); + numbytes = SVN__LINE_CHUNK_SIZE; + SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes)); str->len += numbytes; str->data[str->len] = '\0'; @@ -393,7 +449,7 @@ stream_readline_chunky(svn_stringbuf_t **stringbuf, */ eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol); - if ((numbytes < LINE_CHUNK_SIZE) && (eol_pos == NULL)) + if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL)) { /* We hit EOF instead of EOL. */ *eof = TRUE; @@ -495,7 +551,7 @@ svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to, break; } - err = svn_stream_read(from, buf, &len); + err = svn_stream_read_full(from, buf, &len); if (err) break; @@ -528,10 +584,10 @@ svn_stream_contents_same2(svn_boolean_t *same, while (bytes_read1 == SVN__STREAM_CHUNK_SIZE && bytes_read2 == SVN__STREAM_CHUNK_SIZE) { - err = svn_stream_read(stream1, buf1, &bytes_read1); + err = svn_stream_read_full(stream1, buf1, &bytes_read1); if (err) break; - err = svn_stream_read(stream2, buf2, &bytes_read2); + err = svn_stream_read_full(stream2, buf2, &bytes_read2); if (err) break; @@ -554,7 +610,7 @@ svn_stream_contents_same2(svn_boolean_t *same, /* Skip data from any stream by reading and simply discarding it. */ static svn_error_t * -skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn) +skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn) { apr_size_t bytes_read = 1; char buffer[4096]; @@ -563,7 +619,7 @@ skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn) while ((to_read > 0) && (bytes_read > 0)) { bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read; - SVN_ERR(read_fn(baton, buffer, &bytes_read)); + SVN_ERR(read_full_fn(baton, buffer, &bytes_read)); to_read -= bytes_read; } @@ -613,7 +669,7 @@ svn_stream_empty(apr_pool_t *pool) svn_stream_t *stream; stream = svn_stream_create(NULL, pool); - svn_stream_set_read(stream, read_handler_empty); + svn_stream_set_read2(stream, read_handler_empty, read_handler_empty); svn_stream_set_write(stream, write_handler_empty); svn_stream_set_mark(stream, mark_handler_empty); svn_stream_set_seek(stream, seek_handler_empty); @@ -685,7 +741,13 @@ svn_stream_tee(svn_stream_t *out1, static svn_error_t * read_handler_disown(void *baton, char *buffer, apr_size_t *len) { - return svn_error_trace(svn_stream_read(baton, buffer, len)); + return svn_error_trace(svn_stream_read2(baton, buffer, len)); +} + +static svn_error_t * +read_full_handler_disown(void *baton, char *buffer, apr_size_t *len) +{ + return svn_error_trace(svn_stream_read_full(baton, buffer, len)); } static svn_error_t * @@ -712,6 +774,12 @@ seek_handler_disown(void *baton, const svn_stream_mark_t *mark) return svn_error_trace(svn_stream_seek(baton, mark)); } +static svn_error_t * +data_available_disown(void *baton, svn_boolean_t *data_available) +{ + return svn_error_trace(svn_stream_data_available(baton, data_available)); +} + static svn_boolean_t is_buffered_handler_disown(void *baton) { @@ -723,11 +791,12 @@ svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool) { svn_stream_t *s = svn_stream_create(stream, pool); - svn_stream_set_read(s, read_handler_disown); + svn_stream_set_read2(s, read_handler_disown, read_full_handler_disown); svn_stream_set_skip(s, skip_handler_disown); svn_stream_set_write(s, write_handler_disown); svn_stream_set_mark(s, mark_handler_disown); svn_stream_set_seek(s, seek_handler_disown); + svn_stream_set_data_available(s, data_available_disown); svn_stream__set_is_buffered(s, is_buffered_handler_disown); return s; @@ -751,6 +820,38 @@ read_handler_apr(void *baton, char *buffer, apr_size_t *len) { struct baton_apr *btn = baton; svn_error_t *err; + + if (*len == 1) + { + err = svn_io_file_getc(buffer, btn->file, btn->pool); + if (err) + { + *len = 0; + if (APR_STATUS_IS_EOF(err->apr_err)) + { + svn_error_clear(err); + err = SVN_NO_ERROR; + } + } + } + else + { + err = svn_io_file_read(btn->file, buffer, len, btn->pool); + if (err && APR_STATUS_IS_EOF(err->apr_err)) + { + svn_error_clear(err); + err = NULL; + } + } + + return svn_error_trace(err); +} + +static svn_error_t * +read_full_handler_apr(void *baton, char *buffer, apr_size_t *len) +{ + struct baton_apr *btn = baton; + svn_error_t *err; svn_boolean_t eof; if (*len == 1) @@ -833,6 +934,62 @@ seek_handler_apr(void *baton, const svn_stream_mark_t *mark) return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_apr(void *baton, svn_boolean_t *data_available) +{ + struct baton_apr *btn = baton; + apr_status_t status; +#if !defined(WIN32) || APR_FILES_AS_SOCKETS + apr_pollfd_t pfd; + int n; + + pfd.desc_type = APR_POLL_FILE; + pfd.desc.f = btn->file; + pfd.p = btn->pool; /* If we had a scratch pool... Luckily apr doesn't + store anything in this pool at this time */ + pfd.reqevents = APR_POLLIN; + + status = apr_poll(&pfd, 1, &n, 0); + + if (status == APR_SUCCESS) + { + *data_available = (n > 0); + return SVN_NO_ERROR; + } + else if (APR_STATUS_IS_EOF(status) || APR_STATUS_IS_TIMEUP(status)) + { + *data_available = FALSE; + return SVN_NO_ERROR; + } + else + { + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, + svn_error_wrap_apr( + status, + _("Polling for available data on filestream " + "failed")), + NULL); + } +#else + HANDLE h; + DWORD dwAvail; + status = apr_os_file_get(&h, btn->file); + + if (status) + return svn_error_wrap_apr(status, NULL); + + if (PeekNamedPipe(h, NULL, 0, NULL, &dwAvail, NULL)) + { + *data_available = (dwAvail > 0); + return SVN_NO_ERROR; + } + + return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, + svn_error_wrap_apr(apr_get_os_error(), NULL), + _("Windows doesn't support polling on files")); +#endif +} + static svn_boolean_t is_buffered_handler_apr(void *baton) { @@ -894,10 +1051,12 @@ svn_stream_open_unique(svn_stream_t **stream, } -svn_stream_t * -svn_stream_from_aprfile2(apr_file_t *file, - svn_boolean_t disown, - apr_pool_t *pool) +/* Helper function that creates a stream from an APR file. */ +static svn_stream_t * +make_stream_from_apr_file(apr_file_t *file, + svn_boolean_t disown, + svn_boolean_t supports_seek, + apr_pool_t *pool) { struct baton_apr *baton; svn_stream_t *stream; @@ -909,11 +1068,17 @@ svn_stream_from_aprfile2(apr_file_t *file, baton->file = file; baton->pool = pool; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_apr); + svn_stream_set_read2(stream, read_handler_apr, read_full_handler_apr); svn_stream_set_write(stream, write_handler_apr); - svn_stream_set_skip(stream, skip_handler_apr); - svn_stream_set_mark(stream, mark_handler_apr); - svn_stream_set_seek(stream, seek_handler_apr); + + if (supports_seek) + { + svn_stream_set_skip(stream, skip_handler_apr); + svn_stream_set_mark(stream, mark_handler_apr); + svn_stream_set_seek(stream, seek_handler_apr); + } + + svn_stream_set_data_available(stream, data_available_handler_apr); svn_stream__set_is_buffered(stream, is_buffered_handler_apr); stream->file = file; @@ -923,6 +1088,14 @@ svn_stream_from_aprfile2(apr_file_t *file, return stream; } +svn_stream_t * +svn_stream_from_aprfile2(apr_file_t *file, + svn_boolean_t disown, + apr_pool_t *pool) +{ + return make_stream_from_apr_file(file, disown, TRUE, pool); +} + apr_file_t * svn_stream__aprfile(svn_stream_t *stream) { @@ -941,16 +1114,13 @@ svn_stream__aprfile(svn_stream_t *stream) struct zbaton { z_stream *in; /* compressed stream for reading */ z_stream *out; /* compressed stream for writing */ - svn_read_fn_t read; /* substream's read function */ - svn_write_fn_t write; /* substream's write function */ - svn_close_fn_t close; /* substream's close function */ + void *substream; /* The substream */ void *read_buffer; /* buffer used for reading from substream */ int read_flush; /* what flush mode to use while reading */ apr_pool_t *pool; /* The pool this baton is allocated on */ - void *subbaton; /* The substream's baton */ }; /* zlib alloc function. opaque is the pool we need. */ @@ -971,8 +1141,7 @@ zfree(voidpf opaque, voidpf address) /* Helper function to figure out the sync mode */ static svn_error_t * -read_helper_gz(svn_read_fn_t read_fn, - void *baton, +read_helper_gz(svn_stream_t *substream, char *buffer, uInt *len, int *zflush) { @@ -982,7 +1151,7 @@ read_helper_gz(svn_read_fn_t read_fn, uInt, but Subversion's API requires apr_size_t. */ apr_size_t apr_len = (apr_size_t) *len; - SVN_ERR((*read_fn)(baton, buffer, &apr_len)); + SVN_ERR(svn_stream_read_full(substream, buffer, &apr_len)); /* Type cast back to uInt type that zlib uses. On LP64 platforms apr_size_t will be bigger than uInt. */ @@ -1012,7 +1181,7 @@ read_handler_gz(void *baton, char *buffer, apr_size_t *len) btn->in->next_in = btn->read_buffer; btn->in->avail_in = ZBUFFER_SIZE; - SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, + SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer, &btn->in->avail_in, &btn->read_flush)); zerr = inflateInit(btn->in); @@ -1028,7 +1197,7 @@ read_handler_gz(void *baton, char *buffer, apr_size_t *len) { btn->in->avail_in = ZBUFFER_SIZE; btn->in->next_in = btn->read_buffer; - SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, + SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer, &btn->in->avail_in, &btn->read_flush)); } @@ -1090,7 +1259,7 @@ write_handler_gz(void *baton, const char *buffer, apr_size_t *len) SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg)); write_len = buf_size - btn->out->avail_out; if (write_len > 0) - SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len)); + SVN_ERR(svn_stream_write(btn->substream, write_buf, &write_len)); } svn_pool_destroy(subpool); @@ -1129,7 +1298,7 @@ close_handler_gz(void *baton) btn->out->msg)); write_len = ZBUFFER_SIZE - btn->out->avail_out; if (write_len > 0) - SVN_ERR(btn->write(btn->subbaton, buf, &write_len)); + SVN_ERR(svn_stream_write(btn->substream, buf, &write_len)); if (zerr == Z_STREAM_END) break; } @@ -1138,10 +1307,7 @@ close_handler_gz(void *baton) SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg)); } - if (btn->close != NULL) - return svn_error_trace(btn->close(btn->subbaton)); - else - return SVN_NO_ERROR; + return svn_error_trace(svn_stream_close(btn->substream)); } @@ -1155,16 +1321,14 @@ svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool) baton = apr_palloc(pool, sizeof(*baton)); baton->in = baton->out = NULL; - baton->read = stream->read_fn; - baton->write = stream->write_fn; - baton->close = stream->close_fn; - baton->subbaton = stream->baton; + baton->substream = stream; baton->pool = pool; baton->read_buffer = NULL; baton->read_flush = Z_SYNC_FLUSH; zstream = svn_stream_create(baton, pool); - svn_stream_set_read(zstream, read_handler_gz); + svn_stream_set_read2(zstream, NULL /* only full read support */, + read_handler_gz); svn_stream_set_write(zstream, write_handler_gz); svn_stream_set_close(zstream, close_handler_gz); @@ -1192,9 +1356,22 @@ static svn_error_t * read_handler_checksum(void *baton, char *buffer, apr_size_t *len) { struct checksum_stream_baton *btn = baton; + + SVN_ERR(svn_stream_read2(btn->proxy, buffer, len)); + + if (btn->read_checksum) + SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +read_full_handler_checksum(void *baton, char *buffer, apr_size_t *len) +{ + struct checksum_stream_baton *btn = baton; apr_size_t saved_len = *len; - SVN_ERR(svn_stream_read(btn->proxy, buffer, len)); + SVN_ERR(svn_stream_read_full(btn->proxy, buffer, len)); if (btn->read_checksum) SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len)); @@ -1217,6 +1394,14 @@ write_handler_checksum(void *baton, const char *buffer, apr_size_t *len) return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); } +static svn_error_t * +data_available_handler_checksum(void *baton, svn_boolean_t *data_available) +{ + struct checksum_stream_baton *btn = baton; + + return svn_error_trace(svn_stream_data_available(btn->proxy, + data_available)); +} static svn_error_t * close_handler_checksum(void *baton) @@ -1232,7 +1417,7 @@ close_handler_checksum(void *baton) do { - SVN_ERR(read_handler_checksum(baton, buf, &len)); + SVN_ERR(read_full_handler_checksum(baton, buf, &len)); } while (btn->read_more); } @@ -1279,106 +1464,45 @@ svn_stream_checksummed2(svn_stream_t *stream, baton->pool = pool; s = svn_stream_create(baton, pool); - svn_stream_set_read(s, read_handler_checksum); + svn_stream_set_read2(s, read_handler_checksum, read_full_handler_checksum); svn_stream_set_write(s, write_handler_checksum); + svn_stream_set_data_available(s, data_available_handler_checksum); svn_stream_set_close(s, close_handler_checksum); return s; } -struct md5_stream_baton -{ - const unsigned char **read_digest; - const unsigned char **write_digest; - svn_checksum_t *read_checksum; - svn_checksum_t *write_checksum; - svn_stream_t *proxy; - apr_pool_t *pool; -}; - -static svn_error_t * -read_handler_md5(void *baton, char *buffer, apr_size_t *len) -{ - struct md5_stream_baton *btn = baton; - return svn_error_trace(svn_stream_read(btn->proxy, buffer, len)); -} - -static svn_error_t * -skip_handler_md5(void *baton, apr_size_t len) -{ - struct md5_stream_baton *btn = baton; - return svn_error_trace(svn_stream_skip(btn->proxy, len)); -} +/* Miscellaneous stream functions. */ -static svn_error_t * -write_handler_md5(void *baton, const char *buffer, apr_size_t *len) +svn_error_t * +svn_stringbuf_from_stream(svn_stringbuf_t **str, + svn_stream_t *stream, + apr_size_t len_hint, + apr_pool_t *result_pool) { - struct md5_stream_baton *btn = baton; - return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); -} +#define MIN_READ_SIZE 64 -static svn_error_t * -close_handler_md5(void *baton) -{ - struct md5_stream_baton *btn = baton; + apr_size_t to_read = 0; + svn_stringbuf_t *text + = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE, + result_pool); - SVN_ERR(svn_stream_close(btn->proxy)); + do + { + to_read = text->blocksize - 1 - text->len; + SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read)); + text->len += to_read; - if (btn->read_digest) - *btn->read_digest - = apr_pmemdup(btn->pool, btn->read_checksum->digest, - APR_MD5_DIGESTSIZE); + if (to_read && text->blocksize < text->len + MIN_READ_SIZE) + svn_stringbuf_ensure(text, text->blocksize * 2); + } + while (to_read); - if (btn->write_digest) - *btn->write_digest - = apr_pmemdup(btn->pool, btn->write_checksum->digest, - APR_MD5_DIGESTSIZE); + text->data[text->len] = '\0'; + *str = text; return SVN_NO_ERROR; } - -svn_stream_t * -svn_stream_checksummed(svn_stream_t *stream, - const unsigned char **read_digest, - const unsigned char **write_digest, - svn_boolean_t read_all, - apr_pool_t *pool) -{ - svn_stream_t *s; - struct md5_stream_baton *baton; - - if (! read_digest && ! write_digest) - return stream; - - baton = apr_palloc(pool, sizeof(*baton)); - baton->read_digest = read_digest; - baton->write_digest = write_digest; - baton->pool = pool; - - /* Set BATON->proxy to a stream that will fill in BATON->read_checksum - * and BATON->write_checksum (if we want them) when it is closed. */ - baton->proxy - = svn_stream_checksummed2(stream, - read_digest ? &baton->read_checksum : NULL, - write_digest ? &baton->write_checksum : NULL, - svn_checksum_md5, - read_all, pool); - - /* Create a stream that will forward its read/write/close operations to - * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we - * want them) after it closes BATON->proxy. */ - s = svn_stream_create(baton, pool); - svn_stream_set_read(s, read_handler_md5); - svn_stream_set_skip(s, skip_handler_md5); - svn_stream_set_write(s, write_handler_md5); - svn_stream_set_close(s, close_handler_md5); - return s; -} - - - - -/* Miscellaneous stream functions. */ struct stringbuf_stream_baton { svn_stringbuf_t *str; @@ -1454,6 +1578,15 @@ seek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark) return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_stringbuf(void *baton, svn_boolean_t *data_available) +{ + struct stringbuf_stream_baton *btn = baton; + + *data_available = ((btn->str->len - btn->amt_read) > 0); + return SVN_NO_ERROR; +} + static svn_boolean_t is_buffered_handler_stringbuf(void *baton) { @@ -1474,11 +1607,12 @@ svn_stream_from_stringbuf(svn_stringbuf_t *str, baton->str = str; baton->amt_read = 0; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_stringbuf); + svn_stream_set_read2(stream, read_handler_stringbuf, read_handler_stringbuf); svn_stream_set_skip(stream, skip_handler_stringbuf); svn_stream_set_write(stream, write_handler_stringbuf); svn_stream_set_mark(stream, mark_handler_stringbuf); svn_stream_set_seek(stream, seek_handler_stringbuf); + svn_stream_set_data_available(stream, data_available_handler_stringbuf); svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf); return stream; } @@ -1549,6 +1683,15 @@ skip_handler_string(void *baton, apr_size_t len) return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_string(void *baton, svn_boolean_t *data_available) +{ + struct string_stream_baton *btn = baton; + + *data_available = ((btn->str->len - btn->amt_read) > 0); + return SVN_NO_ERROR; +} + static svn_boolean_t is_buffered_handler_string(void *baton) { @@ -1569,10 +1712,11 @@ svn_stream_from_string(const svn_string_t *str, baton->str = str; baton->amt_read = 0; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_string); + svn_stream_set_read2(stream, read_handler_string, read_handler_string); svn_stream_set_mark(stream, mark_handler_string); svn_stream_set_seek(stream, seek_handler_string); svn_stream_set_skip(stream, skip_handler_string); + svn_stream_set_data_available(stream, data_available_handler_string); svn_stream__set_is_buffered(stream, is_buffered_handler_string); return stream; } @@ -1588,7 +1732,11 @@ svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool) if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stdin"); - *in = svn_stream_from_aprfile2(stdin_file, TRUE, pool); + /* STDIN may or may not support positioning requests, but generally + it does not, or the behavior is implementation-specific. Hence, + we cannot safely advertise mark(), seek() and non-default skip() + support. */ + *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, pool); return SVN_NO_ERROR; } @@ -1604,7 +1752,11 @@ svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool) if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stdout"); - *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool); + /* STDOUT may or may not support positioning requests, but generally + it does not, or the behavior is implementation-specific. Hence, + we cannot safely advertise mark(), seek() and non-default skip() + support. */ + *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, pool); return SVN_NO_ERROR; } @@ -1620,7 +1772,11 @@ svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool) if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stderr"); - *err = svn_stream_from_aprfile2(stderr_file, TRUE, pool); + /* STDERR may or may not support positioning requests, but generally + it does not, or the behavior is implementation-specific. Hence, + we cannot safely advertise mark(), seek() and non-default skip() + support. */ + *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, pool); return SVN_NO_ERROR; } @@ -1640,7 +1796,7 @@ svn_string_from_stream(svn_string_t **result, { apr_size_t len = SVN__STREAM_CHUNK_SIZE; - SVN_ERR(svn_stream_read(stream, buffer, &len)); + SVN_ERR(svn_stream_read_full(stream, buffer, &len)); svn_stringbuf_appendbytes(work, buffer, len); if (len < SVN__STREAM_CHUNK_SIZE) @@ -1657,7 +1813,7 @@ svn_string_from_stream(svn_string_t **result, } -/* These are somewhat arbirary, if we ever get good empirical data as to +/* These are somewhat arbitrary, if we ever get good empirical data as to actually valid values, feel free to update them. */ #define BUFFER_BLOCK_SIZE 1024 #define BUFFER_MAX_SIZE 100000 @@ -1665,7 +1821,9 @@ svn_string_from_stream(svn_string_t **result, svn_stream_t * svn_stream_buffered(apr_pool_t *result_pool) { - return svn_stream__from_spillbuf(BUFFER_BLOCK_SIZE, BUFFER_MAX_SIZE, + return svn_stream__from_spillbuf(svn_spillbuf__create(BUFFER_BLOCK_SIZE, + BUFFER_MAX_SIZE, + result_pool), result_pool); } @@ -1721,7 +1879,21 @@ read_handler_lazyopen(void *baton, lazyopen_baton_t *b = baton; SVN_ERR(lazyopen_if_unopened(b)); - SVN_ERR(svn_stream_read(b->real_stream, buffer, len)); + SVN_ERR(svn_stream_read2(b->real_stream, buffer, len)); + + return SVN_NO_ERROR; +} + +/* Implements svn_read_fn_t */ +static svn_error_t * +read_full_handler_lazyopen(void *baton, + char *buffer, + apr_size_t *len) +{ + lazyopen_baton_t *b = baton; + + SVN_ERR(lazyopen_if_unopened(b)); + SVN_ERR(svn_stream_read_full(b->real_stream, buffer, len)); return SVN_NO_ERROR; } @@ -1794,6 +1966,17 @@ seek_handler_lazyopen(void *baton, return SVN_NO_ERROR; } +static svn_error_t * +data_available_handler_lazyopen(void *baton, + svn_boolean_t *data_available) +{ + lazyopen_baton_t *b = baton; + + SVN_ERR(lazyopen_if_unopened(b)); + return svn_error_trace(svn_stream_data_available(b->real_stream, + data_available)); +} + /* Implements svn_stream__is_buffered_fn_t */ static svn_boolean_t is_buffered_lazyopen(void *baton) @@ -1823,13 +2006,300 @@ svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func, lob->open_on_close = open_on_close; stream = svn_stream_create(lob, result_pool); - svn_stream_set_read(stream, read_handler_lazyopen); + svn_stream_set_read2(stream, read_handler_lazyopen, + read_full_handler_lazyopen); svn_stream_set_skip(stream, skip_handler_lazyopen); svn_stream_set_write(stream, write_handler_lazyopen); svn_stream_set_close(stream, close_handler_lazyopen); svn_stream_set_mark(stream, mark_handler_lazyopen); svn_stream_set_seek(stream, seek_handler_lazyopen); + svn_stream_set_data_available(stream, data_available_handler_lazyopen); svn_stream__set_is_buffered(stream, is_buffered_lazyopen); return stream; } + +/* Baton for install streams */ +struct install_baton_t +{ + struct baton_apr baton_apr; + const char *tmp_path; +}; + +#ifdef WIN32 + +/* Create and open a tempfile in DIRECTORY. Return its handle and path */ +static svn_error_t * +create_tempfile(HANDLE *hFile, + const char **file_path, + const char *directory, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *unique_name; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + static svn_atomic_t tempname_counter; + int baseNr = (GetTickCount() << 11) + 13 * svn_atomic_inc(&tempname_counter) + + GetCurrentProcessId(); + int i = 0; + HANDLE h; + + /* Shares common idea with io.c's temp_file_create */ + + do + { + apr_uint32_t unique_nr; + WCHAR *w_name; + + /* Generate a number that should be unique for this application and + usually for the entire computer to reduce the number of cycles + through this loop. (A bit of calculation is much cheaper than + disk io) */ + unique_nr = baseNr + 7 * i++; + + + svn_pool_clear(iterpool); + unique_name = svn_dirent_join(directory, + apr_psprintf(iterpool, "svn-%X", + unique_nr), + iterpool); + + SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_name, unique_name, + iterpool)); + + /* Create a completely not-sharable file to avoid indexers, and other + filesystem watchers locking the file while we are still writing. + + We need DELETE privileges to move the file. */ + h = CreateFileW(w_name, GENERIC_WRITE | DELETE, 0 /* share */, + NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + apr_status_t status = apr_get_os_error(); + if (i > 1000) + return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED, + svn_error_wrap_apr(status, NULL), + _("Unable to make name in '%s'"), + svn_dirent_local_style(directory, scratch_pool)); + + if (!APR_STATUS_IS_EEXIST(status) && !APR_STATUS_IS_EACCES(status)) + return svn_error_wrap_apr(status, NULL); + } + } + while (h == INVALID_HANDLE_VALUE); + + *hFile = h; + *file_path = apr_pstrdup(result_pool, unique_name); + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Implements svn_close_fn_t */ +static svn_error_t * +install_close(void *baton) +{ + struct install_baton_t *ib = baton; + + /* Flush the data cached in APR, but don't close the file yet */ + SVN_ERR(svn_io_file_flush(ib->baton_apr.file, ib->baton_apr.pool)); + + return SVN_NO_ERROR; +} + +#endif /* WIN32 */ + +svn_error_t * +svn_stream__create_for_install(svn_stream_t **install_stream, + const char *tmp_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_file_t *file; + struct install_baton_t *ib; + const char *tmp_path; + +#ifdef WIN32 + HANDLE hInstall; + apr_status_t status; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath)); + + SVN_ERR(create_tempfile(&hInstall, &tmp_path, tmp_abspath, + scratch_pool, scratch_pool)); + + /* Wrap as a standard APR file to allow sharing implementation. + + But do note that some file functions (such as retrieving the name) + don't work on this wrapper. */ + /* ### Buffered, or not? */ + status = apr_os_file_put(&file, &hInstall, + APR_WRITE | APR_BINARY | APR_BUFFERED, + result_pool); + + if (status) + { + CloseHandle(hInstall); + return svn_error_wrap_apr(status, NULL); + } + + tmp_path = svn_dirent_internal_style(tmp_path, result_pool); +#else + + SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath)); + + SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, tmp_abspath, + svn_io_file_del_none, + result_pool, scratch_pool)); +#endif + *install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool); + + ib = apr_pcalloc(result_pool, sizeof(*ib)); + ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton; + + assert((void*)&ib->baton_apr == (void*)ib); /* baton pointer is the same */ + + (*install_stream)->baton = ib; + + ib->tmp_path = tmp_path; + +#ifdef WIN32 + /* Don't close the file on stream close; flush instead */ + svn_stream_set_close(*install_stream, install_close); +#else + /* ### Install pool cleanup handler for tempfile? */ +#endif + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream__install_stream(svn_stream_t *install_stream, + const char *final_abspath, + svn_boolean_t make_parents, + apr_pool_t *scratch_pool) +{ + struct install_baton_t *ib = install_stream->baton; + svn_error_t *err; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(final_abspath)); +#ifdef WIN32 + err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path, + final_abspath, scratch_pool); + if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_t *err2; + + err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath, + scratch_pool), + scratch_pool); + + if (err2) + return svn_error_trace(svn_error_compose_create(err, err2)); + else + svn_error_clear(err); + + err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path, + final_abspath, scratch_pool); + } + + /* ### rhuijben: I wouldn't be surprised if we later find out that we + have to fall back to close+rename on some specific + error values here, to support some non standard NAS + and filesystem scenarios. */ + if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) + { + /* Rename open files is not supported on this platform: fallback to + svn_io_file_rename2(). */ + svn_error_clear(err); + err = SVN_NO_ERROR; + + SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool)); + } + else + { + return svn_error_compose_create(err, + svn_io_file_close(ib->baton_apr.file, + scratch_pool)); + } +#endif + + err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool); + + /* A missing directory is too common to not cover here. */ + if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_t *err2; + + err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath, + scratch_pool), + scratch_pool); + + if (err2) + /* Creating directory didn't work: Return all errors */ + return svn_error_trace(svn_error_compose_create(err, err2)); + else + /* We could create a directory: retry install */ + svn_error_clear(err); + + SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool)); + } + else + SVN_ERR(err); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream__install_get_info(apr_finfo_t *finfo, + svn_stream_t *install_stream, + apr_int32_t wanted, + apr_pool_t *scratch_pool) +{ + struct install_baton_t *ib = install_stream->baton; + +#ifdef WIN32 + /* On WIN32 the file is still open, so we can obtain the information + from the handle without race conditions */ + apr_status_t status; + + status = apr_file_info_get(finfo, wanted, ib->baton_apr.file); + + if (status) + return svn_error_wrap_apr(status, NULL); +#else + SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool)); +#endif + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_stream__install_delete(svn_stream_t *install_stream, + apr_pool_t *scratch_pool) +{ + struct install_baton_t *ib = install_stream->baton; + +#ifdef WIN32 + svn_error_t *err; + + /* Mark the file as delete on close to avoid having to reopen + the file as part of the delete handling. */ + err = svn_io__win_delete_file_on_close(ib->baton_apr.file, ib->tmp_path, + scratch_pool); + if (err == SVN_NO_ERROR) + { + SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool)); + return SVN_NO_ERROR; /* File is already gone */ + } + + /* Deleting file on close may be unsupported, so ignore errors and + fallback to svn_io_remove_file2(). */ + svn_error_clear(err); + SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool)); +#endif + + return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE, + scratch_pool)); +} |