summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/stream.c')
-rw-r--r--subversion/libsvn_subr/stream.c800
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));
+}