1 /*
2 * stream.c: svn_stream operations
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 #include <assert.h>
25 #include <stdio.h>
26
27 #include <apr.h>
28 #include <apr_pools.h>
29 #include <apr_strings.h>
30 #include <apr_file_io.h>
31 #include <apr_errno.h>
32 #include <apr_poll.h>
33 #include <apr_portable.h>
34
35 #include <zlib.h>
36
37 #include "svn_pools.h"
38 #include "svn_io.h"
39 #include "svn_error.h"
40 #include "svn_string.h"
41 #include "svn_utf.h"
42 #include "svn_checksum.h"
43 #include "svn_path.h"
44 #include "svn_private_config.h"
45 #include "private/svn_atomic.h"
46 #include "private/svn_error_private.h"
47 #include "private/svn_eol_private.h"
48 #include "private/svn_io_private.h"
49 #include "private/svn_subr_private.h"
50 #include "private/svn_utf_private.h"
51
52
53 struct svn_stream_t {
54 void *baton;
55 svn_read_fn_t read_fn;
56 svn_read_fn_t read_full_fn;
57 svn_stream_skip_fn_t skip_fn;
58 svn_write_fn_t write_fn;
59 svn_close_fn_t close_fn;
60 svn_stream_mark_fn_t mark_fn;
61 svn_stream_seek_fn_t seek_fn;
62 svn_stream_data_available_fn_t data_available_fn;
63 svn_stream__is_buffered_fn_t is_buffered_fn;
64 apr_file_t *file; /* Maybe NULL */
65 };
66
67
68 /*** Forward declarations. ***/
69
70 static svn_error_t *
71 skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn);
72
73
74 /*** Generic streams. ***/
75
76 svn_stream_t *
svn_stream_create(void * baton,apr_pool_t * pool)77 svn_stream_create(void *baton, apr_pool_t *pool)
78 {
79 svn_stream_t *stream;
80
81 stream = apr_pcalloc(pool, sizeof(*stream));
82 stream->baton = baton;
83 return stream;
84 }
85
86
87 void
svn_stream_set_baton(svn_stream_t * stream,void * baton)88 svn_stream_set_baton(svn_stream_t *stream, void *baton)
89 {
90 stream->baton = baton;
91 }
92
93
94 void
svn_stream_set_read2(svn_stream_t * stream,svn_read_fn_t read_fn,svn_read_fn_t read_full_fn)95 svn_stream_set_read2(svn_stream_t *stream,
96 svn_read_fn_t read_fn,
97 svn_read_fn_t read_full_fn)
98 {
99 stream->read_fn = read_fn;
100 stream->read_full_fn = read_full_fn;
101 }
102
103 void
svn_stream_set_skip(svn_stream_t * stream,svn_stream_skip_fn_t skip_fn)104 svn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn)
105 {
106 stream->skip_fn = skip_fn;
107 }
108
109 void
svn_stream_set_write(svn_stream_t * stream,svn_write_fn_t write_fn)110 svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
111 {
112 stream->write_fn = write_fn;
113 }
114
115 void
svn_stream_set_close(svn_stream_t * stream,svn_close_fn_t close_fn)116 svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
117 {
118 stream->close_fn = close_fn;
119 }
120
121 void
svn_stream_set_mark(svn_stream_t * stream,svn_stream_mark_fn_t mark_fn)122 svn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn)
123 {
124 stream->mark_fn = mark_fn;
125 }
126
127 void
svn_stream_set_seek(svn_stream_t * stream,svn_stream_seek_fn_t seek_fn)128 svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn)
129 {
130 stream->seek_fn = seek_fn;
131 }
132
133 void
svn_stream_set_data_available(svn_stream_t * stream,svn_stream_data_available_fn_t data_available_fn)134 svn_stream_set_data_available(svn_stream_t *stream,
135 svn_stream_data_available_fn_t data_available_fn)
136 {
137 stream->data_available_fn = data_available_fn;
138 }
139
140 void
svn_stream__set_is_buffered(svn_stream_t * stream,svn_stream__is_buffered_fn_t is_buffered_fn)141 svn_stream__set_is_buffered(svn_stream_t *stream,
142 svn_stream__is_buffered_fn_t is_buffered_fn)
143 {
144 stream->is_buffered_fn = is_buffered_fn;
145 }
146
147 /* Standard implementation for svn_stream_read_full() based on
148 multiple svn_stream_read2() calls (in separate function to make
149 it more likely for svn_stream_read_full to be inlined) */
150 static svn_error_t *
full_read_fallback(svn_stream_t * stream,char * buffer,apr_size_t * len)151 full_read_fallback(svn_stream_t *stream, char *buffer, apr_size_t *len)
152 {
153 apr_size_t remaining = *len;
154 while (remaining > 0)
155 {
156 apr_size_t length = remaining;
157 SVN_ERR(svn_stream_read2(stream, buffer, &length));
158
159 if (length == 0)
160 {
161 *len -= remaining;
162 return SVN_NO_ERROR;
163 }
164
165 remaining -= length;
166 buffer += length;
167 }
168
169 return SVN_NO_ERROR;
170 }
171
172 svn_boolean_t
svn_stream_supports_partial_read(svn_stream_t * stream)173 svn_stream_supports_partial_read(svn_stream_t *stream)
174 {
175 return stream->read_fn != NULL;
176 }
177
178 svn_error_t *
svn_stream_read2(svn_stream_t * stream,char * buffer,apr_size_t * len)179 svn_stream_read2(svn_stream_t *stream, char *buffer, apr_size_t *len)
180 {
181 if (stream->read_fn == NULL)
182 return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
183
184 return svn_error_trace(stream->read_fn(stream->baton, buffer, len));
185 }
186
187 svn_error_t *
svn_stream_read_full(svn_stream_t * stream,char * buffer,apr_size_t * len)188 svn_stream_read_full(svn_stream_t *stream, char *buffer, apr_size_t *len)
189 {
190 if (stream->read_full_fn == NULL)
191 return svn_error_trace(full_read_fallback(stream, buffer, len));
192
193 return svn_error_trace(stream->read_full_fn(stream->baton, buffer, len));
194 }
195
196 svn_error_t *
svn_stream_skip(svn_stream_t * stream,apr_size_t len)197 svn_stream_skip(svn_stream_t *stream, apr_size_t len)
198 {
199 if (stream->skip_fn == NULL)
200 return svn_error_trace(
201 skip_default_handler(stream->baton, len, stream->read_full_fn));
202
203 return svn_error_trace(stream->skip_fn(stream->baton, len));
204 }
205
206
207 svn_error_t *
svn_stream_write(svn_stream_t * stream,const char * data,apr_size_t * len)208 svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len)
209 {
210 if (stream->write_fn == NULL)
211 return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
212
213 return svn_error_trace(stream->write_fn(stream->baton, data, len));
214 }
215
216
217 svn_error_t *
svn_stream_reset(svn_stream_t * stream)218 svn_stream_reset(svn_stream_t *stream)
219 {
220 return svn_error_trace(
221 svn_stream_seek(stream, NULL));
222 }
223
224 svn_boolean_t
svn_stream_supports_mark(svn_stream_t * stream)225 svn_stream_supports_mark(svn_stream_t *stream)
226 {
227 return stream->mark_fn != NULL;
228 }
229
230 svn_error_t *
svn_stream_mark(svn_stream_t * stream,svn_stream_mark_t ** mark,apr_pool_t * pool)231 svn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark,
232 apr_pool_t *pool)
233 {
234 if (stream->mark_fn == NULL)
235 return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
236
237 return svn_error_trace(stream->mark_fn(stream->baton, mark, pool));
238 }
239
240 svn_error_t *
svn_stream_seek(svn_stream_t * stream,const svn_stream_mark_t * mark)241 svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark)
242 {
243 if (stream->seek_fn == NULL)
244 return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
245
246 return svn_error_trace(stream->seek_fn(stream->baton, mark));
247 }
248
249 svn_error_t *
svn_stream_data_available(svn_stream_t * stream,svn_boolean_t * data_available)250 svn_stream_data_available(svn_stream_t *stream,
251 svn_boolean_t *data_available)
252 {
253 if (stream->data_available_fn == NULL)
254 return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED, NULL, NULL);
255
256 return svn_error_trace(stream->data_available_fn(stream->baton,
257 data_available));
258 }
259
260 svn_boolean_t
svn_stream__is_buffered(svn_stream_t * stream)261 svn_stream__is_buffered(svn_stream_t *stream)
262 {
263 if (stream->is_buffered_fn == NULL)
264 return FALSE;
265
266 return stream->is_buffered_fn(stream->baton);
267 }
268
269 svn_error_t *
svn_stream_close(svn_stream_t * stream)270 svn_stream_close(svn_stream_t *stream)
271 {
272 if (stream->close_fn == NULL)
273 return SVN_NO_ERROR;
274 return svn_error_trace(stream->close_fn(stream->baton));
275 }
276
277 svn_error_t *
svn_stream_puts(svn_stream_t * stream,const char * str)278 svn_stream_puts(svn_stream_t *stream,
279 const char *str)
280 {
281 apr_size_t len;
282 len = strlen(str);
283 return svn_error_trace(svn_stream_write(stream, str, &len));
284 }
285
286 svn_error_t *
svn_stream_printf(svn_stream_t * stream,apr_pool_t * pool,const char * fmt,...)287 svn_stream_printf(svn_stream_t *stream,
288 apr_pool_t *pool,
289 const char *fmt,
290 ...)
291 {
292 const char *message;
293 va_list ap;
294
295 va_start(ap, fmt);
296 message = apr_pvsprintf(pool, fmt, ap);
297 va_end(ap);
298
299 return svn_error_trace(svn_stream_puts(stream, message));
300 }
301
302
303 svn_error_t *
svn_stream_printf_from_utf8(svn_stream_t * stream,const char * encoding,apr_pool_t * pool,const char * fmt,...)304 svn_stream_printf_from_utf8(svn_stream_t *stream,
305 const char *encoding,
306 apr_pool_t *pool,
307 const char *fmt,
308 ...)
309 {
310 const char *message, *translated;
311 va_list ap;
312
313 va_start(ap, fmt);
314 message = apr_pvsprintf(pool, fmt, ap);
315 va_end(ap);
316
317 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding,
318 pool));
319
320 return svn_error_trace(svn_stream_puts(stream, translated));
321 }
322
323 /* Guts of svn_stream_readline().
324 * Returns the line read from STREAM in *STRINGBUF, and indicates
325 * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator
326 * is detected automatically and returned in *EOL.
327 * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line
328 * indicator. STRINGBUF is allocated in POOL. */
329 static svn_error_t *
stream_readline_bytewise(svn_stringbuf_t ** stringbuf,svn_boolean_t * eof,const char * eol,svn_stream_t * stream,apr_pool_t * pool)330 stream_readline_bytewise(svn_stringbuf_t **stringbuf,
331 svn_boolean_t *eof,
332 const char *eol,
333 svn_stream_t *stream,
334 apr_pool_t *pool)
335 {
336 svn_stringbuf_t *str;
337 apr_size_t numbytes;
338 const char *match;
339 char c;
340
341 /* Since we're reading one character at a time, let's at least
342 optimize for the 90% case. 90% of the time, we can avoid the
343 stringbuf ever having to realloc() itself if we start it out at
344 80 chars. */
345 str = svn_stringbuf_create_ensure(SVN__LINE_CHUNK_SIZE, pool);
346
347 /* Read into STR up to and including the next EOL sequence. */
348 match = eol;
349 while (*match)
350 {
351 numbytes = 1;
352 SVN_ERR(svn_stream_read_full(stream, &c, &numbytes));
353 if (numbytes != 1)
354 {
355 /* a 'short' read means the stream has run out. */
356 *eof = TRUE;
357 *stringbuf = str;
358 return SVN_NO_ERROR;
359 }
360
361 if (c == *match)
362 match++;
363 else
364 match = eol;
365
366 svn_stringbuf_appendbyte(str, c);
367 }
368
369 *eof = FALSE;
370 svn_stringbuf_chop(str, match - eol);
371 *stringbuf = str;
372
373 return SVN_NO_ERROR;
374 }
375
376 static svn_error_t *
stream_readline_chunky(svn_stringbuf_t ** stringbuf,svn_boolean_t * eof,const char * eol,svn_stream_t * stream,apr_pool_t * pool)377 stream_readline_chunky(svn_stringbuf_t **stringbuf,
378 svn_boolean_t *eof,
379 const char *eol,
380 svn_stream_t *stream,
381 apr_pool_t *pool)
382 {
383 /* Read larger chunks of data at once into this buffer and scan
384 * that for EOL. A good chunk size should be about 80 chars since
385 * most text lines will be shorter. However, don't use a much
386 * larger value because filling the buffer from the stream takes
387 * time as well.
388 */
389 char buffer[SVN__LINE_CHUNK_SIZE+1];
390
391 /* variables */
392 svn_stream_mark_t *mark;
393 apr_size_t numbytes;
394 const char *eol_pos;
395 apr_size_t total_parsed = 0;
396
397 /* invariant for this call */
398 const size_t eol_len = strlen(eol);
399
400 /* Remember the line start so this plus the line length will be
401 * the position to move to at the end of this function.
402 */
403 SVN_ERR(svn_stream_mark(stream, &mark, pool));
404
405 /* Read the first chunk. */
406 numbytes = SVN__LINE_CHUNK_SIZE;
407 SVN_ERR(svn_stream_read_full(stream, buffer, &numbytes));
408 buffer[numbytes] = '\0';
409
410 /* Look for the EOL in this first chunk. If we find it, we are done here.
411 */
412 eol_pos = strstr(buffer, eol);
413 if (eol_pos != NULL)
414 {
415 *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool);
416 total_parsed = eol_pos - buffer + eol_len;
417 }
418 else if (numbytes < SVN__LINE_CHUNK_SIZE)
419 {
420 /* We hit EOF but not EOL.
421 */
422 *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool);
423 *eof = TRUE;
424 return SVN_NO_ERROR;
425 }
426 else
427 {
428 /* A larger buffer for the string is needed. */
429 svn_stringbuf_t *str;
430 str = svn_stringbuf_create_ensure(2*SVN__LINE_CHUNK_SIZE, pool);
431 svn_stringbuf_appendbytes(str, buffer, numbytes);
432 *stringbuf = str;
433
434 /* Loop reading chunks until an EOL was found. If we hit EOF, fall
435 * back to the standard implementation. */
436 do
437 {
438 /* Append the next chunk to the string read so far.
439 */
440 svn_stringbuf_ensure(str, str->len + SVN__LINE_CHUNK_SIZE);
441 numbytes = SVN__LINE_CHUNK_SIZE;
442 SVN_ERR(svn_stream_read_full(stream, str->data + str->len, &numbytes));
443 str->len += numbytes;
444 str->data[str->len] = '\0';
445
446 /* Look for the EOL in the new data plus the last part of the
447 * previous chunk because the EOL may span over the boundary
448 * between both chunks.
449 */
450 eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol);
451
452 if ((numbytes < SVN__LINE_CHUNK_SIZE) && (eol_pos == NULL))
453 {
454 /* We hit EOF instead of EOL. */
455 *eof = TRUE;
456 return SVN_NO_ERROR;
457 }
458 }
459 while (eol_pos == NULL);
460
461 /* Number of bytes we actually consumed (i.e. line + EOF).
462 * We need to "return" the rest to the stream by moving its
463 * read pointer.
464 */
465 total_parsed = eol_pos - str->data + eol_len;
466
467 /* Terminate the string at the EOL postion and return it. */
468 str->len = eol_pos - str->data;
469 str->data[str->len] = 0;
470 }
471
472 /* Move the stream read pointer to the first position behind the EOL.
473 */
474 SVN_ERR(svn_stream_seek(stream, mark));
475 return svn_error_trace(svn_stream_skip(stream, total_parsed));
476 }
477
478 /* Guts of svn_stream_readline().
479 * Returns the line read from STREAM in *STRINGBUF, and indicates
480 * end-of-file in *EOF. EOL must point to the desired end-of-line
481 * indicator. STRINGBUF is allocated in POOL. */
482 static svn_error_t *
stream_readline(svn_stringbuf_t ** stringbuf,svn_boolean_t * eof,const char * eol,svn_stream_t * stream,apr_pool_t * pool)483 stream_readline(svn_stringbuf_t **stringbuf,
484 svn_boolean_t *eof,
485 const char *eol,
486 svn_stream_t *stream,
487 apr_pool_t *pool)
488 {
489 *eof = FALSE;
490
491 /* Often, we operate on APR file or string-based streams and know what
492 * EOL we are looking for. Optimize that common case.
493 */
494 if (svn_stream_supports_mark(stream) &&
495 svn_stream__is_buffered(stream))
496 {
497 /* We can efficiently read chunks speculatively and reposition the
498 * stream pointer to the end of the line once we found that.
499 */
500 SVN_ERR(stream_readline_chunky(stringbuf,
501 eof,
502 eol,
503 stream,
504 pool));
505 }
506 else
507 {
508 /* Use the standard byte-byte implementation.
509 */
510 SVN_ERR(stream_readline_bytewise(stringbuf,
511 eof,
512 eol,
513 stream,
514 pool));
515 }
516
517 return SVN_NO_ERROR;
518 }
519
520 svn_error_t *
svn_stream_readline(svn_stream_t * stream,svn_stringbuf_t ** stringbuf,const char * eol,svn_boolean_t * eof,apr_pool_t * pool)521 svn_stream_readline(svn_stream_t *stream,
522 svn_stringbuf_t **stringbuf,
523 const char *eol,
524 svn_boolean_t *eof,
525 apr_pool_t *pool)
526 {
527 return svn_error_trace(stream_readline(stringbuf, eof, eol, stream,
528 pool));
529 }
530
svn_stream_copy3(svn_stream_t * from,svn_stream_t * to,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)531 svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to,
532 svn_cancel_func_t cancel_func,
533 void *cancel_baton,
534 apr_pool_t *scratch_pool)
535 {
536 char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
537 svn_error_t *err;
538 svn_error_t *err2;
539
540 /* Read and write chunks until we get a short read, indicating the
541 end of the stream. (We can't get a short write without an
542 associated error.) */
543 while (1)
544 {
545 apr_size_t len = SVN__STREAM_CHUNK_SIZE;
546
547 if (cancel_func)
548 {
549 err = cancel_func(cancel_baton);
550 if (err)
551 break;
552 }
553
554 err = svn_stream_read_full(from, buf, &len);
555 if (err)
556 break;
557
558 if (len > 0)
559 err = svn_stream_write(to, buf, &len);
560
561 if (err || (len != SVN__STREAM_CHUNK_SIZE))
562 break;
563 }
564
565 err2 = svn_error_compose_create(svn_stream_close(from),
566 svn_stream_close(to));
567
568 return svn_error_compose_create(err, err2);
569 }
570
571 svn_error_t *
svn_stream_contents_same2(svn_boolean_t * same,svn_stream_t * stream1,svn_stream_t * stream2,apr_pool_t * pool)572 svn_stream_contents_same2(svn_boolean_t *same,
573 svn_stream_t *stream1,
574 svn_stream_t *stream2,
575 apr_pool_t *pool)
576 {
577 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
578 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
579 apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE;
580 apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE;
581 svn_error_t *err = NULL;
582
583 *same = TRUE; /* assume TRUE, until disproved below */
584 while (bytes_read1 == SVN__STREAM_CHUNK_SIZE
585 && bytes_read2 == SVN__STREAM_CHUNK_SIZE)
586 {
587 err = svn_stream_read_full(stream1, buf1, &bytes_read1);
588 if (err)
589 break;
590 err = svn_stream_read_full(stream2, buf2, &bytes_read2);
591 if (err)
592 break;
593
594 if ((bytes_read1 != bytes_read2)
595 || (memcmp(buf1, buf2, bytes_read1)))
596 {
597 *same = FALSE;
598 break;
599 }
600 }
601
602 return svn_error_compose_create(err,
603 svn_error_compose_create(
604 svn_stream_close(stream1),
605 svn_stream_close(stream2)));
606 }
607
608
609 /*** Stream implementation utilities ***/
610
611 /* Skip data from any stream by reading and simply discarding it. */
612 static svn_error_t *
skip_default_handler(void * baton,apr_size_t len,svn_read_fn_t read_full_fn)613 skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn)
614 {
615 apr_size_t bytes_read = 1;
616 char buffer[4096];
617 apr_size_t to_read = len;
618
619 while ((to_read > 0) && (bytes_read > 0))
620 {
621 bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read;
622 SVN_ERR(read_full_fn(baton, buffer, &bytes_read));
623 to_read -= bytes_read;
624 }
625
626 return SVN_NO_ERROR;
627 }
628
629
630
631 /*** Generic readable empty stream ***/
632
633 static svn_error_t *
read_handler_empty(void * baton,char * buffer,apr_size_t * len)634 read_handler_empty(void *baton, char *buffer, apr_size_t *len)
635 {
636 *len = 0;
637 return SVN_NO_ERROR;
638 }
639
640 static svn_error_t *
write_handler_empty(void * baton,const char * data,apr_size_t * len)641 write_handler_empty(void *baton, const char *data, apr_size_t *len)
642 {
643 return SVN_NO_ERROR;
644 }
645
646 static svn_error_t *
mark_handler_empty(void * baton,svn_stream_mark_t ** mark,apr_pool_t * pool)647 mark_handler_empty(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
648 {
649 *mark = NULL; /* Seek to start of stream marker */
650 return SVN_NO_ERROR;
651 }
652
653 static svn_error_t *
seek_handler_empty(void * baton,const svn_stream_mark_t * mark)654 seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
655 {
656 return SVN_NO_ERROR;
657 }
658
659 static svn_boolean_t
is_buffered_handler_empty(void * baton)660 is_buffered_handler_empty(void *baton)
661 {
662 return FALSE;
663 }
664
665
666 svn_stream_t *
svn_stream_empty(apr_pool_t * pool)667 svn_stream_empty(apr_pool_t *pool)
668 {
669 svn_stream_t *stream;
670
671 stream = svn_stream_create(NULL, pool);
672 svn_stream_set_read2(stream, read_handler_empty, read_handler_empty);
673 svn_stream_set_write(stream, write_handler_empty);
674 svn_stream_set_mark(stream, mark_handler_empty);
675 svn_stream_set_seek(stream, seek_handler_empty);
676 svn_stream__set_is_buffered(stream, is_buffered_handler_empty);
677 return stream;
678 }
679
680
681
682 /*** Stream duplication support ***/
683 struct baton_tee {
684 svn_stream_t *out1;
685 svn_stream_t *out2;
686 };
687
688
689 static svn_error_t *
write_handler_tee(void * baton,const char * data,apr_size_t * len)690 write_handler_tee(void *baton, const char *data, apr_size_t *len)
691 {
692 struct baton_tee *bt = baton;
693
694 SVN_ERR(svn_stream_write(bt->out1, data, len));
695 SVN_ERR(svn_stream_write(bt->out2, data, len));
696
697 return SVN_NO_ERROR;
698 }
699
700
701 static svn_error_t *
close_handler_tee(void * baton)702 close_handler_tee(void *baton)
703 {
704 struct baton_tee *bt = baton;
705
706 SVN_ERR(svn_stream_close(bt->out1));
707 SVN_ERR(svn_stream_close(bt->out2));
708
709 return SVN_NO_ERROR;
710 }
711
712
713 svn_stream_t *
svn_stream_tee(svn_stream_t * out1,svn_stream_t * out2,apr_pool_t * pool)714 svn_stream_tee(svn_stream_t *out1,
715 svn_stream_t *out2,
716 apr_pool_t *pool)
717 {
718 struct baton_tee *baton;
719 svn_stream_t *stream;
720
721 if (out1 == NULL)
722 return out2;
723
724 if (out2 == NULL)
725 return out1;
726
727 baton = apr_palloc(pool, sizeof(*baton));
728 baton->out1 = out1;
729 baton->out2 = out2;
730 stream = svn_stream_create(baton, pool);
731 svn_stream_set_write(stream, write_handler_tee);
732 svn_stream_set_close(stream, close_handler_tee);
733
734 return stream;
735 }
736
737
738
739 /*** Ownership detaching stream ***/
740
741 static svn_error_t *
read_handler_disown(void * baton,char * buffer,apr_size_t * len)742 read_handler_disown(void *baton, char *buffer, apr_size_t *len)
743 {
744 return svn_error_trace(svn_stream_read2(baton, buffer, len));
745 }
746
747 static svn_error_t *
read_full_handler_disown(void * baton,char * buffer,apr_size_t * len)748 read_full_handler_disown(void *baton, char *buffer, apr_size_t *len)
749 {
750 return svn_error_trace(svn_stream_read_full(baton, buffer, len));
751 }
752
753 static svn_error_t *
skip_handler_disown(void * baton,apr_size_t len)754 skip_handler_disown(void *baton, apr_size_t len)
755 {
756 return svn_error_trace(svn_stream_skip(baton, len));
757 }
758
759 static svn_error_t *
write_handler_disown(void * baton,const char * buffer,apr_size_t * len)760 write_handler_disown(void *baton, const char *buffer, apr_size_t *len)
761 {
762 return svn_error_trace(svn_stream_write(baton, buffer, len));
763 }
764
765 static svn_error_t *
mark_handler_disown(void * baton,svn_stream_mark_t ** mark,apr_pool_t * pool)766 mark_handler_disown(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
767 {
768 return svn_error_trace(svn_stream_mark(baton, mark, pool));
769 }
770
771 static svn_error_t *
seek_handler_disown(void * baton,const svn_stream_mark_t * mark)772 seek_handler_disown(void *baton, const svn_stream_mark_t *mark)
773 {
774 return svn_error_trace(svn_stream_seek(baton, mark));
775 }
776
777 static svn_error_t *
data_available_disown(void * baton,svn_boolean_t * data_available)778 data_available_disown(void *baton, svn_boolean_t *data_available)
779 {
780 return svn_error_trace(svn_stream_data_available(baton, data_available));
781 }
782
783 static svn_boolean_t
is_buffered_handler_disown(void * baton)784 is_buffered_handler_disown(void *baton)
785 {
786 return svn_stream__is_buffered(baton);
787 }
788
789 svn_stream_t *
svn_stream_disown(svn_stream_t * stream,apr_pool_t * pool)790 svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool)
791 {
792 svn_stream_t *s = svn_stream_create(stream, pool);
793
794 svn_stream_set_read2(s, read_handler_disown, read_full_handler_disown);
795 svn_stream_set_skip(s, skip_handler_disown);
796 svn_stream_set_write(s, write_handler_disown);
797 svn_stream_set_mark(s, mark_handler_disown);
798 svn_stream_set_seek(s, seek_handler_disown);
799 svn_stream_set_data_available(s, data_available_disown);
800 svn_stream__set_is_buffered(s, is_buffered_handler_disown);
801
802 return s;
803 }
804
805
806
807 /*** Generic stream for APR files ***/
808 struct baton_apr {
809 apr_file_t *file;
810 apr_pool_t *pool;
811 };
812
813 /* svn_stream_mark_t for streams backed by APR files. */
814 struct mark_apr {
815 apr_off_t off;
816 };
817
818 static svn_error_t *
read_handler_apr(void * baton,char * buffer,apr_size_t * len)819 read_handler_apr(void *baton, char *buffer, apr_size_t *len)
820 {
821 struct baton_apr *btn = baton;
822 svn_error_t *err;
823
824 if (*len == 1)
825 {
826 err = svn_io_file_getc(buffer, btn->file, btn->pool);
827 if (err)
828 {
829 *len = 0;
830 if (APR_STATUS_IS_EOF(err->apr_err))
831 {
832 svn_error_clear(err);
833 err = SVN_NO_ERROR;
834 }
835 }
836 }
837 else
838 {
839 err = svn_io_file_read(btn->file, buffer, len, btn->pool);
840 if (err && APR_STATUS_IS_EOF(err->apr_err))
841 {
842 svn_error_clear(err);
843 err = NULL;
844 }
845 }
846
847 return svn_error_trace(err);
848 }
849
850 static svn_error_t *
read_full_handler_apr(void * baton,char * buffer,apr_size_t * len)851 read_full_handler_apr(void *baton, char *buffer, apr_size_t *len)
852 {
853 struct baton_apr *btn = baton;
854 svn_error_t *err;
855 svn_boolean_t eof;
856
857 if (*len == 1)
858 {
859 err = svn_io_file_getc(buffer, btn->file, btn->pool);
860 if (err)
861 {
862 *len = 0;
863 if (APR_STATUS_IS_EOF(err->apr_err))
864 {
865 svn_error_clear(err);
866 err = SVN_NO_ERROR;
867 }
868 }
869 }
870 else
871 err = svn_io_file_read_full2(btn->file, buffer, *len, len,
872 &eof, btn->pool);
873
874 return svn_error_trace(err);
875 }
876
877 static svn_error_t *
skip_handler_apr(void * baton,apr_size_t len)878 skip_handler_apr(void *baton, apr_size_t len)
879 {
880 struct baton_apr *btn = baton;
881 apr_off_t offset = len;
882
883 return svn_error_trace(
884 svn_io_file_seek(btn->file, APR_CUR, &offset, btn->pool));
885 }
886
887 static svn_error_t *
write_handler_apr(void * baton,const char * data,apr_size_t * len)888 write_handler_apr(void *baton, const char *data, apr_size_t *len)
889 {
890 struct baton_apr *btn = baton;
891 svn_error_t *err;
892
893 if (*len == 1)
894 {
895 err = svn_io_file_putc(*data, btn->file, btn->pool);
896 if (err)
897 *len = 0;
898 }
899 else
900 err = svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
901
902 return svn_error_trace(err);
903 }
904
905 static svn_error_t *
close_handler_apr(void * baton)906 close_handler_apr(void *baton)
907 {
908 struct baton_apr *btn = baton;
909
910 return svn_error_trace(svn_io_file_close(btn->file, btn->pool));
911 }
912
913 static svn_error_t *
mark_handler_apr(void * baton,svn_stream_mark_t ** mark,apr_pool_t * pool)914 mark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
915 {
916 struct baton_apr *btn = baton;
917 struct mark_apr *mark_apr;
918
919 mark_apr = apr_palloc(pool, sizeof(*mark_apr));
920 mark_apr->off = 0;
921 SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool));
922 *mark = (svn_stream_mark_t *)mark_apr;
923 return SVN_NO_ERROR;
924 }
925
926 static svn_error_t *
seek_handler_apr(void * baton,const svn_stream_mark_t * mark)927 seek_handler_apr(void *baton, const svn_stream_mark_t *mark)
928 {
929 struct baton_apr *btn = baton;
930 apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0;
931
932 SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool));
933
934 return SVN_NO_ERROR;
935 }
936
937 static svn_error_t *
data_available_handler_apr(void * baton,svn_boolean_t * data_available)938 data_available_handler_apr(void *baton, svn_boolean_t *data_available)
939 {
940 struct baton_apr *btn = baton;
941 apr_status_t status;
942 #if !defined(WIN32) || APR_FILES_AS_SOCKETS
943 apr_pollfd_t pfd;
944 int n;
945
946 pfd.desc_type = APR_POLL_FILE;
947 pfd.desc.f = btn->file;
948 pfd.p = btn->pool; /* If we had a scratch pool... Luckily apr doesn't
949 store anything in this pool at this time */
950 pfd.reqevents = APR_POLLIN;
951
952 status = apr_poll(&pfd, 1, &n, 0);
953
954 if (status == APR_SUCCESS)
955 {
956 *data_available = (n > 0);
957 return SVN_NO_ERROR;
958 }
959 else if (APR_STATUS_IS_EOF(status) || APR_STATUS_IS_TIMEUP(status))
960 {
961 *data_available = FALSE;
962 return SVN_NO_ERROR;
963 }
964 else
965 {
966 return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED,
967 svn_error_wrap_apr(
968 status,
969 _("Polling for available data on filestream "
970 "failed")),
971 NULL);
972 }
973 #else
974 HANDLE h;
975 DWORD dwAvail;
976 status = apr_os_file_get(&h, btn->file);
977
978 if (status)
979 return svn_error_wrap_apr(status, NULL);
980
981 if (PeekNamedPipe(h, NULL, 0, NULL, &dwAvail, NULL))
982 {
983 *data_available = (dwAvail > 0);
984 return SVN_NO_ERROR;
985 }
986
987 return svn_error_create(SVN_ERR_STREAM_NOT_SUPPORTED,
988 svn_error_wrap_apr(apr_get_os_error(), NULL),
989 _("Windows doesn't support polling on files"));
990 #endif
991 }
992
993 static svn_boolean_t
is_buffered_handler_apr(void * baton)994 is_buffered_handler_apr(void *baton)
995 {
996 struct baton_apr *btn = baton;
997 return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0;
998 }
999
1000 svn_error_t *
svn_stream_open_readonly(svn_stream_t ** stream,const char * path,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1001 svn_stream_open_readonly(svn_stream_t **stream,
1002 const char *path,
1003 apr_pool_t *result_pool,
1004 apr_pool_t *scratch_pool)
1005 {
1006 apr_file_t *file;
1007
1008 SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
1009 APR_OS_DEFAULT, result_pool));
1010 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1011
1012 return SVN_NO_ERROR;
1013 }
1014
1015
1016 svn_error_t *
svn_stream_open_writable(svn_stream_t ** stream,const char * path,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1017 svn_stream_open_writable(svn_stream_t **stream,
1018 const char *path,
1019 apr_pool_t *result_pool,
1020 apr_pool_t *scratch_pool)
1021 {
1022 apr_file_t *file;
1023
1024 SVN_ERR(svn_io_file_open(&file, path,
1025 APR_WRITE
1026 | APR_BUFFERED
1027 | APR_CREATE
1028 | APR_EXCL,
1029 APR_OS_DEFAULT, result_pool));
1030 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1031
1032 return SVN_NO_ERROR;
1033 }
1034
1035
1036 svn_error_t *
svn_stream_open_unique(svn_stream_t ** stream,const char ** temp_path,const char * dirpath,svn_io_file_del_t delete_when,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1037 svn_stream_open_unique(svn_stream_t **stream,
1038 const char **temp_path,
1039 const char *dirpath,
1040 svn_io_file_del_t delete_when,
1041 apr_pool_t *result_pool,
1042 apr_pool_t *scratch_pool)
1043 {
1044 apr_file_t *file;
1045
1046 SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath,
1047 delete_when, result_pool, scratch_pool));
1048 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
1049
1050 return SVN_NO_ERROR;
1051 }
1052
1053
1054 /* Helper function that creates a stream from an APR file. */
1055 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)1056 make_stream_from_apr_file(apr_file_t *file,
1057 svn_boolean_t disown,
1058 svn_boolean_t supports_seek,
1059 apr_pool_t *pool)
1060 {
1061 struct baton_apr *baton;
1062 svn_stream_t *stream;
1063
1064 if (file == NULL)
1065 return svn_stream_empty(pool);
1066
1067 baton = apr_palloc(pool, sizeof(*baton));
1068 baton->file = file;
1069 baton->pool = pool;
1070 stream = svn_stream_create(baton, pool);
1071 svn_stream_set_read2(stream, read_handler_apr, read_full_handler_apr);
1072 svn_stream_set_write(stream, write_handler_apr);
1073
1074 if (supports_seek)
1075 {
1076 svn_stream_set_skip(stream, skip_handler_apr);
1077 svn_stream_set_mark(stream, mark_handler_apr);
1078 svn_stream_set_seek(stream, seek_handler_apr);
1079 }
1080
1081 svn_stream_set_data_available(stream, data_available_handler_apr);
1082 svn_stream__set_is_buffered(stream, is_buffered_handler_apr);
1083 stream->file = file;
1084
1085 if (! disown)
1086 svn_stream_set_close(stream, close_handler_apr);
1087
1088 return stream;
1089 }
1090
1091 svn_stream_t *
svn_stream_from_aprfile2(apr_file_t * file,svn_boolean_t disown,apr_pool_t * pool)1092 svn_stream_from_aprfile2(apr_file_t *file,
1093 svn_boolean_t disown,
1094 apr_pool_t *pool)
1095 {
1096 return make_stream_from_apr_file(file, disown, TRUE, pool);
1097 }
1098
1099 apr_file_t *
svn_stream__aprfile(svn_stream_t * stream)1100 svn_stream__aprfile(svn_stream_t *stream)
1101 {
1102 return stream->file;
1103 }
1104
1105
1106 /* Compressed stream support */
1107
1108 #define ZBUFFER_SIZE 4096 /* The size of the buffer the
1109 compressed stream uses to read from
1110 the substream. Basically an
1111 arbitrary value, picked to be about
1112 page-sized. */
1113
1114 struct zbaton {
1115 z_stream *in; /* compressed stream for reading */
1116 z_stream *out; /* compressed stream for writing */
1117 void *substream; /* The substream */
1118 void *read_buffer; /* buffer used for reading from
1119 substream */
1120 int read_flush; /* what flush mode to use while
1121 reading */
1122 apr_pool_t *pool; /* The pool this baton is allocated
1123 on */
1124 };
1125
1126 /* zlib alloc function. opaque is the pool we need. */
1127 static voidpf
zalloc(voidpf opaque,uInt items,uInt size)1128 zalloc(voidpf opaque, uInt items, uInt size)
1129 {
1130 apr_pool_t *pool = opaque;
1131
1132 return apr_palloc(pool, items * size);
1133 }
1134
1135 /* zlib free function */
1136 static void
zfree(voidpf opaque,voidpf address)1137 zfree(voidpf opaque, voidpf address)
1138 {
1139 /* Empty, since we allocate on the pool */
1140 }
1141
1142 /* Helper function to figure out the sync mode */
1143 static svn_error_t *
read_helper_gz(svn_stream_t * substream,char * buffer,uInt * len,int * zflush)1144 read_helper_gz(svn_stream_t *substream,
1145 char *buffer,
1146 uInt *len, int *zflush)
1147 {
1148 uInt orig_len = *len;
1149
1150 /* There's no reason this value should grow bigger than the range of
1151 uInt, but Subversion's API requires apr_size_t. */
1152 apr_size_t apr_len = (apr_size_t) *len;
1153
1154 SVN_ERR(svn_stream_read_full(substream, buffer, &apr_len));
1155
1156 /* Type cast back to uInt type that zlib uses. On LP64 platforms
1157 apr_size_t will be bigger than uInt. */
1158 *len = (uInt) apr_len;
1159
1160 /* I wanted to use Z_FINISH here, but we need to know our buffer is
1161 big enough */
1162 *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH;
1163
1164 return SVN_NO_ERROR;
1165 }
1166
1167 /* Handle reading from a compressed stream */
1168 static svn_error_t *
read_handler_gz(void * baton,char * buffer,apr_size_t * len)1169 read_handler_gz(void *baton, char *buffer, apr_size_t *len)
1170 {
1171 struct zbaton *btn = baton;
1172 int zerr;
1173
1174 if (btn->in == NULL)
1175 {
1176 btn->in = apr_palloc(btn->pool, sizeof(z_stream));
1177 btn->in->zalloc = zalloc;
1178 btn->in->zfree = zfree;
1179 btn->in->opaque = btn->pool;
1180 btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE);
1181 btn->in->next_in = btn->read_buffer;
1182 btn->in->avail_in = ZBUFFER_SIZE;
1183
1184 SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer,
1185 &btn->in->avail_in, &btn->read_flush));
1186
1187 zerr = inflateInit(btn->in);
1188 SVN_ERR(svn_error__wrap_zlib(zerr, "inflateInit", btn->in->msg));
1189 }
1190
1191 btn->in->next_out = (Bytef *) buffer;
1192 btn->in->avail_out = (uInt) *len;
1193
1194 while (btn->in->avail_out > 0)
1195 {
1196 if (btn->in->avail_in <= 0)
1197 {
1198 btn->in->avail_in = ZBUFFER_SIZE;
1199 btn->in->next_in = btn->read_buffer;
1200 SVN_ERR(read_helper_gz(btn->substream, btn->read_buffer,
1201 &btn->in->avail_in, &btn->read_flush));
1202 }
1203
1204 /* Short read means underlying stream has run out. */
1205 if (btn->in->avail_in == 0)
1206 {
1207 *len = 0;
1208 return SVN_NO_ERROR;
1209 }
1210
1211 zerr = inflate(btn->in, btn->read_flush);
1212 if (zerr == Z_STREAM_END)
1213 break;
1214 else if (zerr != Z_OK)
1215 return svn_error_trace(svn_error__wrap_zlib(zerr, "inflate",
1216 btn->in->msg));
1217 }
1218
1219 *len -= btn->in->avail_out;
1220 return SVN_NO_ERROR;
1221 }
1222
1223 /* Compress data and write it to the substream */
1224 static svn_error_t *
write_handler_gz(void * baton,const char * buffer,apr_size_t * len)1225 write_handler_gz(void *baton, const char *buffer, apr_size_t *len)
1226 {
1227 struct zbaton *btn = baton;
1228 apr_pool_t *subpool;
1229 void *write_buf;
1230 apr_size_t buf_size, write_len;
1231 int zerr;
1232
1233 if (btn->out == NULL)
1234 {
1235 btn->out = apr_palloc(btn->pool, sizeof(z_stream));
1236 btn->out->zalloc = zalloc;
1237 btn->out->zfree = zfree;
1238 btn->out->opaque = btn->pool;
1239
1240 zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION);
1241 SVN_ERR(svn_error__wrap_zlib(zerr, "deflateInit", btn->out->msg));
1242 }
1243
1244 /* The largest buffer we should need is 0.1% larger than the
1245 compressed data, + 12 bytes. This info comes from zlib.h. */
1246 buf_size = *len + (*len / 1000) + 13;
1247 subpool = svn_pool_create(btn->pool);
1248 write_buf = apr_palloc(subpool, buf_size);
1249
1250 btn->out->next_in = (Bytef *) buffer; /* Casting away const! */
1251 btn->out->avail_in = (uInt) *len;
1252
1253 while (btn->out->avail_in > 0)
1254 {
1255 btn->out->next_out = write_buf;
1256 btn->out->avail_out = (uInt) buf_size;
1257
1258 zerr = deflate(btn->out, Z_NO_FLUSH);
1259 SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg));
1260 write_len = buf_size - btn->out->avail_out;
1261 if (write_len > 0)
1262 SVN_ERR(svn_stream_write(btn->substream, write_buf, &write_len));
1263 }
1264
1265 svn_pool_destroy(subpool);
1266
1267 return SVN_NO_ERROR;
1268 }
1269
1270 /* Handle flushing and closing the stream */
1271 static svn_error_t *
close_handler_gz(void * baton)1272 close_handler_gz(void *baton)
1273 {
1274 struct zbaton *btn = baton;
1275 int zerr;
1276
1277 if (btn->in != NULL)
1278 {
1279 zerr = inflateEnd(btn->in);
1280 SVN_ERR(svn_error__wrap_zlib(zerr, "inflateEnd", btn->in->msg));
1281 }
1282
1283 if (btn->out != NULL)
1284 {
1285 void *buf;
1286 apr_size_t write_len;
1287
1288 buf = apr_palloc(btn->pool, ZBUFFER_SIZE);
1289
1290 while (TRUE)
1291 {
1292 btn->out->next_out = buf;
1293 btn->out->avail_out = ZBUFFER_SIZE;
1294
1295 zerr = deflate(btn->out, Z_FINISH);
1296 if (zerr != Z_STREAM_END && zerr != Z_OK)
1297 return svn_error_trace(svn_error__wrap_zlib(zerr, "deflate",
1298 btn->out->msg));
1299 write_len = ZBUFFER_SIZE - btn->out->avail_out;
1300 if (write_len > 0)
1301 SVN_ERR(svn_stream_write(btn->substream, buf, &write_len));
1302 if (zerr == Z_STREAM_END)
1303 break;
1304 }
1305
1306 zerr = deflateEnd(btn->out);
1307 SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg));
1308 }
1309
1310 return svn_error_trace(svn_stream_close(btn->substream));
1311 }
1312
1313
1314 svn_stream_t *
svn_stream_compressed(svn_stream_t * stream,apr_pool_t * pool)1315 svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool)
1316 {
1317 struct svn_stream_t *zstream;
1318 struct zbaton *baton;
1319
1320 assert(stream != NULL);
1321
1322 baton = apr_palloc(pool, sizeof(*baton));
1323 baton->in = baton->out = NULL;
1324 baton->substream = stream;
1325 baton->pool = pool;
1326 baton->read_buffer = NULL;
1327 baton->read_flush = Z_SYNC_FLUSH;
1328
1329 zstream = svn_stream_create(baton, pool);
1330 svn_stream_set_read2(zstream, NULL /* only full read support */,
1331 read_handler_gz);
1332 svn_stream_set_write(zstream, write_handler_gz);
1333 svn_stream_set_close(zstream, close_handler_gz);
1334
1335 return zstream;
1336 }
1337
1338
1339 /* Checksummed stream support */
1340
1341 struct checksum_stream_baton
1342 {
1343 svn_checksum_ctx_t *read_ctx, *write_ctx;
1344 svn_checksum_t **read_checksum; /* Output value. */
1345 svn_checksum_t **write_checksum; /* Output value. */
1346 svn_stream_t *proxy;
1347
1348 /* True if more data should be read when closing the stream. */
1349 svn_boolean_t read_more;
1350
1351 /* Pool to allocate read buffer and output values from. */
1352 apr_pool_t *pool;
1353 };
1354
1355 static svn_error_t *
read_handler_checksum(void * baton,char * buffer,apr_size_t * len)1356 read_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1357 {
1358 struct checksum_stream_baton *btn = baton;
1359
1360 SVN_ERR(svn_stream_read2(btn->proxy, buffer, len));
1361
1362 if (btn->read_checksum)
1363 SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1364
1365 return SVN_NO_ERROR;
1366 }
1367
1368 static svn_error_t *
read_full_handler_checksum(void * baton,char * buffer,apr_size_t * len)1369 read_full_handler_checksum(void *baton, char *buffer, apr_size_t *len)
1370 {
1371 struct checksum_stream_baton *btn = baton;
1372 apr_size_t saved_len = *len;
1373
1374 SVN_ERR(svn_stream_read_full(btn->proxy, buffer, len));
1375
1376 if (btn->read_checksum)
1377 SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len));
1378
1379 if (saved_len != *len)
1380 btn->read_more = FALSE;
1381
1382 return SVN_NO_ERROR;
1383 }
1384
1385
1386 static svn_error_t *
write_handler_checksum(void * baton,const char * buffer,apr_size_t * len)1387 write_handler_checksum(void *baton, const char *buffer, apr_size_t *len)
1388 {
1389 struct checksum_stream_baton *btn = baton;
1390
1391 if (btn->write_checksum && *len > 0)
1392 SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len));
1393
1394 return svn_error_trace(svn_stream_write(btn->proxy, buffer, len));
1395 }
1396
1397 static svn_error_t *
data_available_handler_checksum(void * baton,svn_boolean_t * data_available)1398 data_available_handler_checksum(void *baton, svn_boolean_t *data_available)
1399 {
1400 struct checksum_stream_baton *btn = baton;
1401
1402 return svn_error_trace(svn_stream_data_available(btn->proxy,
1403 data_available));
1404 }
1405
1406 static svn_error_t *
close_handler_checksum(void * baton)1407 close_handler_checksum(void *baton)
1408 {
1409 struct checksum_stream_baton *btn = baton;
1410
1411 /* If we're supposed to drain the stream, do so before finalizing the
1412 checksum. */
1413 if (btn->read_more)
1414 {
1415 char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE);
1416 apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1417
1418 do
1419 {
1420 SVN_ERR(read_full_handler_checksum(baton, buf, &len));
1421 }
1422 while (btn->read_more);
1423 }
1424
1425 if (btn->read_ctx)
1426 SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool));
1427
1428 if (btn->write_ctx)
1429 SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool));
1430
1431 return svn_error_trace(svn_stream_close(btn->proxy));
1432 }
1433
1434
1435 svn_stream_t *
svn_stream_checksummed2(svn_stream_t * stream,svn_checksum_t ** read_checksum,svn_checksum_t ** write_checksum,svn_checksum_kind_t checksum_kind,svn_boolean_t read_all,apr_pool_t * pool)1436 svn_stream_checksummed2(svn_stream_t *stream,
1437 svn_checksum_t **read_checksum,
1438 svn_checksum_t **write_checksum,
1439 svn_checksum_kind_t checksum_kind,
1440 svn_boolean_t read_all,
1441 apr_pool_t *pool)
1442 {
1443 svn_stream_t *s;
1444 struct checksum_stream_baton *baton;
1445
1446 if (read_checksum == NULL && write_checksum == NULL)
1447 return stream;
1448
1449 baton = apr_palloc(pool, sizeof(*baton));
1450 if (read_checksum)
1451 baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1452 else
1453 baton->read_ctx = NULL;
1454
1455 if (write_checksum)
1456 baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool);
1457 else
1458 baton->write_ctx = NULL;
1459
1460 baton->read_checksum = read_checksum;
1461 baton->write_checksum = write_checksum;
1462 baton->proxy = stream;
1463 baton->read_more = read_all;
1464 baton->pool = pool;
1465
1466 s = svn_stream_create(baton, pool);
1467 svn_stream_set_read2(s, read_handler_checksum, read_full_handler_checksum);
1468 svn_stream_set_write(s, write_handler_checksum);
1469 svn_stream_set_data_available(s, data_available_handler_checksum);
1470 svn_stream_set_close(s, close_handler_checksum);
1471 return s;
1472 }
1473
1474 /* Miscellaneous stream functions. */
1475
1476 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)1477 svn_stringbuf_from_stream(svn_stringbuf_t **str,
1478 svn_stream_t *stream,
1479 apr_size_t len_hint,
1480 apr_pool_t *result_pool)
1481 {
1482 #define MIN_READ_SIZE 64
1483
1484 apr_size_t to_read = 0;
1485 svn_stringbuf_t *text
1486 = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE,
1487 result_pool);
1488
1489 do
1490 {
1491 to_read = text->blocksize - 1 - text->len;
1492 SVN_ERR(svn_stream_read_full(stream, text->data + text->len, &to_read));
1493 text->len += to_read;
1494
1495 if (to_read && text->blocksize < text->len + MIN_READ_SIZE)
1496 svn_stringbuf_ensure(text, text->blocksize * 2);
1497 }
1498 while (to_read);
1499
1500 text->data[text->len] = '\0';
1501 *str = text;
1502
1503 return SVN_NO_ERROR;
1504 }
1505
1506 struct stringbuf_stream_baton
1507 {
1508 svn_stringbuf_t *str;
1509 apr_size_t amt_read;
1510 };
1511
1512 /* svn_stream_mark_t for streams backed by stringbufs. */
1513 struct stringbuf_stream_mark {
1514 apr_size_t pos;
1515 };
1516
1517 static svn_error_t *
read_handler_stringbuf(void * baton,char * buffer,apr_size_t * len)1518 read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len)
1519 {
1520 struct stringbuf_stream_baton *btn = baton;
1521 apr_size_t left_to_read = btn->str->len - btn->amt_read;
1522
1523 *len = (*len > left_to_read) ? left_to_read : *len;
1524 memcpy(buffer, btn->str->data + btn->amt_read, *len);
1525 btn->amt_read += *len;
1526 return SVN_NO_ERROR;
1527 }
1528
1529 static svn_error_t *
skip_handler_stringbuf(void * baton,apr_size_t len)1530 skip_handler_stringbuf(void *baton, apr_size_t len)
1531 {
1532 struct stringbuf_stream_baton *btn = baton;
1533 apr_size_t left_to_read = btn->str->len - btn->amt_read;
1534
1535 len = (len > left_to_read) ? left_to_read : len;
1536 btn->amt_read += len;
1537 return SVN_NO_ERROR;
1538 }
1539
1540 static svn_error_t *
write_handler_stringbuf(void * baton,const char * data,apr_size_t * len)1541 write_handler_stringbuf(void *baton, const char *data, apr_size_t *len)
1542 {
1543 struct stringbuf_stream_baton *btn = baton;
1544
1545 svn_stringbuf_appendbytes(btn->str, data, *len);
1546 return SVN_NO_ERROR;
1547 }
1548
1549 static svn_error_t *
mark_handler_stringbuf(void * baton,svn_stream_mark_t ** mark,apr_pool_t * pool)1550 mark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1551 {
1552 struct stringbuf_stream_baton *btn;
1553 struct stringbuf_stream_mark *stringbuf_stream_mark;
1554
1555 btn = baton;
1556
1557 stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark));
1558 stringbuf_stream_mark->pos = btn->amt_read;
1559 *mark = (svn_stream_mark_t *)stringbuf_stream_mark;
1560 return SVN_NO_ERROR;
1561 }
1562
1563 static svn_error_t *
seek_handler_stringbuf(void * baton,const svn_stream_mark_t * mark)1564 seek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark)
1565 {
1566 struct stringbuf_stream_baton *btn = baton;
1567
1568 if (mark != NULL)
1569 {
1570 const struct stringbuf_stream_mark *stringbuf_stream_mark;
1571
1572 stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark;
1573 btn->amt_read = stringbuf_stream_mark->pos;
1574 }
1575 else
1576 btn->amt_read = 0;
1577
1578 return SVN_NO_ERROR;
1579 }
1580
1581 static svn_error_t *
data_available_handler_stringbuf(void * baton,svn_boolean_t * data_available)1582 data_available_handler_stringbuf(void *baton, svn_boolean_t *data_available)
1583 {
1584 struct stringbuf_stream_baton *btn = baton;
1585
1586 *data_available = ((btn->str->len - btn->amt_read) > 0);
1587 return SVN_NO_ERROR;
1588 }
1589
1590 static svn_boolean_t
is_buffered_handler_stringbuf(void * baton)1591 is_buffered_handler_stringbuf(void *baton)
1592 {
1593 return TRUE;
1594 }
1595
1596 svn_stream_t *
svn_stream_from_stringbuf(svn_stringbuf_t * str,apr_pool_t * pool)1597 svn_stream_from_stringbuf(svn_stringbuf_t *str,
1598 apr_pool_t *pool)
1599 {
1600 svn_stream_t *stream;
1601 struct stringbuf_stream_baton *baton;
1602
1603 if (! str)
1604 return svn_stream_empty(pool);
1605
1606 baton = apr_palloc(pool, sizeof(*baton));
1607 baton->str = str;
1608 baton->amt_read = 0;
1609 stream = svn_stream_create(baton, pool);
1610 svn_stream_set_read2(stream, read_handler_stringbuf, read_handler_stringbuf);
1611 svn_stream_set_skip(stream, skip_handler_stringbuf);
1612 svn_stream_set_write(stream, write_handler_stringbuf);
1613 svn_stream_set_mark(stream, mark_handler_stringbuf);
1614 svn_stream_set_seek(stream, seek_handler_stringbuf);
1615 svn_stream_set_data_available(stream, data_available_handler_stringbuf);
1616 svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf);
1617 return stream;
1618 }
1619
1620 struct string_stream_baton
1621 {
1622 const svn_string_t *str;
1623 apr_size_t amt_read;
1624 };
1625
1626 /* svn_stream_mark_t for streams backed by stringbufs. */
1627 struct string_stream_mark {
1628 apr_size_t pos;
1629 };
1630
1631 static svn_error_t *
read_handler_string(void * baton,char * buffer,apr_size_t * len)1632 read_handler_string(void *baton, char *buffer, apr_size_t *len)
1633 {
1634 struct string_stream_baton *btn = baton;
1635 apr_size_t left_to_read = btn->str->len - btn->amt_read;
1636
1637 *len = (*len > left_to_read) ? left_to_read : *len;
1638 memcpy(buffer, btn->str->data + btn->amt_read, *len);
1639 btn->amt_read += *len;
1640 return SVN_NO_ERROR;
1641 }
1642
1643 static svn_error_t *
mark_handler_string(void * baton,svn_stream_mark_t ** mark,apr_pool_t * pool)1644 mark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
1645 {
1646 struct string_stream_baton *btn;
1647 struct string_stream_mark *marker;
1648
1649 btn = baton;
1650
1651 marker = apr_palloc(pool, sizeof(*marker));
1652 marker->pos = btn->amt_read;
1653 *mark = (svn_stream_mark_t *)marker;
1654 return SVN_NO_ERROR;
1655 }
1656
1657 static svn_error_t *
seek_handler_string(void * baton,const svn_stream_mark_t * mark)1658 seek_handler_string(void *baton, const svn_stream_mark_t *mark)
1659 {
1660 struct string_stream_baton *btn = baton;
1661
1662 if (mark != NULL)
1663 {
1664 const struct string_stream_mark *marker;
1665
1666 marker = (const struct string_stream_mark *)mark;
1667 btn->amt_read = marker->pos;
1668 }
1669 else
1670 btn->amt_read = 0;
1671
1672 return SVN_NO_ERROR;
1673 }
1674
1675 static svn_error_t *
skip_handler_string(void * baton,apr_size_t len)1676 skip_handler_string(void *baton, apr_size_t len)
1677 {
1678 struct string_stream_baton *btn = baton;
1679 apr_size_t left_to_read = btn->str->len - btn->amt_read;
1680
1681 len = (len > left_to_read) ? left_to_read : len;
1682 btn->amt_read += len;
1683 return SVN_NO_ERROR;
1684 }
1685
1686 static svn_error_t *
data_available_handler_string(void * baton,svn_boolean_t * data_available)1687 data_available_handler_string(void *baton, svn_boolean_t *data_available)
1688 {
1689 struct string_stream_baton *btn = baton;
1690
1691 *data_available = ((btn->str->len - btn->amt_read) > 0);
1692 return SVN_NO_ERROR;
1693 }
1694
1695 static svn_boolean_t
is_buffered_handler_string(void * baton)1696 is_buffered_handler_string(void *baton)
1697 {
1698 return TRUE;
1699 }
1700
1701 svn_stream_t *
svn_stream_from_string(const svn_string_t * str,apr_pool_t * pool)1702 svn_stream_from_string(const svn_string_t *str,
1703 apr_pool_t *pool)
1704 {
1705 svn_stream_t *stream;
1706 struct string_stream_baton *baton;
1707
1708 if (! str)
1709 return svn_stream_empty(pool);
1710
1711 baton = apr_palloc(pool, sizeof(*baton));
1712 baton->str = str;
1713 baton->amt_read = 0;
1714 stream = svn_stream_create(baton, pool);
1715 svn_stream_set_read2(stream, read_handler_string, read_handler_string);
1716 svn_stream_set_mark(stream, mark_handler_string);
1717 svn_stream_set_seek(stream, seek_handler_string);
1718 svn_stream_set_skip(stream, skip_handler_string);
1719 svn_stream_set_data_available(stream, data_available_handler_string);
1720 svn_stream__set_is_buffered(stream, is_buffered_handler_string);
1721 return stream;
1722 }
1723
1724
1725 svn_error_t *
svn_stream_for_stdin(svn_stream_t ** in,apr_pool_t * pool)1726 svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool)
1727 {
1728 apr_file_t *stdin_file;
1729 apr_status_t apr_err;
1730
1731 apr_err = apr_file_open_stdin(&stdin_file, pool);
1732 if (apr_err)
1733 return svn_error_wrap_apr(apr_err, "Can't open stdin");
1734
1735 /* STDIN may or may not support positioning requests, but generally
1736 it does not, or the behavior is implementation-specific. Hence,
1737 we cannot safely advertise mark(), seek() and non-default skip()
1738 support. */
1739 *in = make_stream_from_apr_file(stdin_file, TRUE, FALSE, pool);
1740
1741 return SVN_NO_ERROR;
1742 }
1743
1744
1745 svn_error_t *
svn_stream_for_stdout(svn_stream_t ** out,apr_pool_t * pool)1746 svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool)
1747 {
1748 apr_file_t *stdout_file;
1749 apr_status_t apr_err;
1750
1751 apr_err = apr_file_open_stdout(&stdout_file, pool);
1752 if (apr_err)
1753 return svn_error_wrap_apr(apr_err, "Can't open stdout");
1754
1755 /* STDOUT may or may not support positioning requests, but generally
1756 it does not, or the behavior is implementation-specific. Hence,
1757 we cannot safely advertise mark(), seek() and non-default skip()
1758 support. */
1759 *out = make_stream_from_apr_file(stdout_file, TRUE, FALSE, pool);
1760
1761 return SVN_NO_ERROR;
1762 }
1763
1764
1765 svn_error_t *
svn_stream_for_stderr(svn_stream_t ** err,apr_pool_t * pool)1766 svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool)
1767 {
1768 apr_file_t *stderr_file;
1769 apr_status_t apr_err;
1770
1771 apr_err = apr_file_open_stderr(&stderr_file, pool);
1772 if (apr_err)
1773 return svn_error_wrap_apr(apr_err, "Can't open stderr");
1774
1775 /* STDERR may or may not support positioning requests, but generally
1776 it does not, or the behavior is implementation-specific. Hence,
1777 we cannot safely advertise mark(), seek() and non-default skip()
1778 support. */
1779 *err = make_stream_from_apr_file(stderr_file, TRUE, FALSE, pool);
1780
1781 return SVN_NO_ERROR;
1782 }
1783
1784
1785 svn_error_t *
svn_string_from_stream(svn_string_t ** result,svn_stream_t * stream,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1786 svn_string_from_stream(svn_string_t **result,
1787 svn_stream_t *stream,
1788 apr_pool_t *result_pool,
1789 apr_pool_t *scratch_pool)
1790 {
1791 svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE,
1792 result_pool);
1793 char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE);
1794
1795 while (1)
1796 {
1797 apr_size_t len = SVN__STREAM_CHUNK_SIZE;
1798
1799 SVN_ERR(svn_stream_read_full(stream, buffer, &len));
1800 svn_stringbuf_appendbytes(work, buffer, len);
1801
1802 if (len < SVN__STREAM_CHUNK_SIZE)
1803 break;
1804 }
1805
1806 SVN_ERR(svn_stream_close(stream));
1807
1808 *result = apr_palloc(result_pool, sizeof(**result));
1809 (*result)->data = work->data;
1810 (*result)->len = work->len;
1811
1812 return SVN_NO_ERROR;
1813 }
1814
1815
1816 /* These are somewhat arbitrary, if we ever get good empirical data as to
1817 actually valid values, feel free to update them. */
1818 #define BUFFER_BLOCK_SIZE 1024
1819 #define BUFFER_MAX_SIZE 100000
1820
1821 svn_stream_t *
svn_stream_buffered(apr_pool_t * result_pool)1822 svn_stream_buffered(apr_pool_t *result_pool)
1823 {
1824 return svn_stream__from_spillbuf(svn_spillbuf__create(BUFFER_BLOCK_SIZE,
1825 BUFFER_MAX_SIZE,
1826 result_pool),
1827 result_pool);
1828 }
1829
1830
1831
1832 /*** Lazyopen Streams ***/
1833
1834 /* Custom baton for lazyopen-style wrapper streams. */
1835 typedef struct lazyopen_baton_t {
1836
1837 /* Callback function and baton for opening the wrapped stream. */
1838 svn_stream_lazyopen_func_t open_func;
1839 void *open_baton;
1840
1841 /* The wrapped stream, or NULL if the stream hasn't yet been
1842 opened. */
1843 svn_stream_t *real_stream;
1844 apr_pool_t *pool;
1845
1846 /* Whether to open the wrapped stream on a close call. */
1847 svn_boolean_t open_on_close;
1848
1849 } lazyopen_baton_t;
1850
1851
1852 /* Use B->open_func/baton to create and set B->real_stream iff it
1853 isn't already set. */
1854 static svn_error_t *
lazyopen_if_unopened(lazyopen_baton_t * b)1855 lazyopen_if_unopened(lazyopen_baton_t *b)
1856 {
1857 if (b->real_stream == NULL)
1858 {
1859 svn_stream_t *stream;
1860 apr_pool_t *scratch_pool = svn_pool_create(b->pool);
1861
1862 SVN_ERR(b->open_func(&stream, b->open_baton,
1863 b->pool, scratch_pool));
1864
1865 svn_pool_destroy(scratch_pool);
1866
1867 b->real_stream = stream;
1868 }
1869
1870 return SVN_NO_ERROR;
1871 }
1872
1873 /* Implements svn_read_fn_t */
1874 static svn_error_t *
read_handler_lazyopen(void * baton,char * buffer,apr_size_t * len)1875 read_handler_lazyopen(void *baton,
1876 char *buffer,
1877 apr_size_t *len)
1878 {
1879 lazyopen_baton_t *b = baton;
1880
1881 SVN_ERR(lazyopen_if_unopened(b));
1882 SVN_ERR(svn_stream_read2(b->real_stream, buffer, len));
1883
1884 return SVN_NO_ERROR;
1885 }
1886
1887 /* Implements svn_read_fn_t */
1888 static svn_error_t *
read_full_handler_lazyopen(void * baton,char * buffer,apr_size_t * len)1889 read_full_handler_lazyopen(void *baton,
1890 char *buffer,
1891 apr_size_t *len)
1892 {
1893 lazyopen_baton_t *b = baton;
1894
1895 SVN_ERR(lazyopen_if_unopened(b));
1896 SVN_ERR(svn_stream_read_full(b->real_stream, buffer, len));
1897
1898 return SVN_NO_ERROR;
1899 }
1900
1901 /* Implements svn_stream_skip_fn_t */
1902 static svn_error_t *
skip_handler_lazyopen(void * baton,apr_size_t len)1903 skip_handler_lazyopen(void *baton,
1904 apr_size_t len)
1905 {
1906 lazyopen_baton_t *b = baton;
1907
1908 SVN_ERR(lazyopen_if_unopened(b));
1909 SVN_ERR(svn_stream_skip(b->real_stream, len));
1910
1911 return SVN_NO_ERROR;
1912 }
1913
1914 /* Implements svn_write_fn_t */
1915 static svn_error_t *
write_handler_lazyopen(void * baton,const char * data,apr_size_t * len)1916 write_handler_lazyopen(void *baton,
1917 const char *data,
1918 apr_size_t *len)
1919 {
1920 lazyopen_baton_t *b = baton;
1921
1922 SVN_ERR(lazyopen_if_unopened(b));
1923 SVN_ERR(svn_stream_write(b->real_stream, data, len));
1924
1925 return SVN_NO_ERROR;
1926 }
1927
1928 /* Implements svn_close_fn_t */
1929 static svn_error_t *
close_handler_lazyopen(void * baton)1930 close_handler_lazyopen(void *baton)
1931 {
1932 lazyopen_baton_t *b = baton;
1933
1934 if (b->open_on_close)
1935 SVN_ERR(lazyopen_if_unopened(b));
1936 if (b->real_stream)
1937 SVN_ERR(svn_stream_close(b->real_stream));
1938
1939 return SVN_NO_ERROR;
1940 }
1941
1942 /* Implements svn_stream_mark_fn_t */
1943 static svn_error_t *
mark_handler_lazyopen(void * baton,svn_stream_mark_t ** mark,apr_pool_t * pool)1944 mark_handler_lazyopen(void *baton,
1945 svn_stream_mark_t **mark,
1946 apr_pool_t *pool)
1947 {
1948 lazyopen_baton_t *b = baton;
1949
1950 SVN_ERR(lazyopen_if_unopened(b));
1951 SVN_ERR(svn_stream_mark(b->real_stream, mark, pool));
1952
1953 return SVN_NO_ERROR;
1954 }
1955
1956 /* Implements svn_stream_seek_fn_t */
1957 static svn_error_t *
seek_handler_lazyopen(void * baton,const svn_stream_mark_t * mark)1958 seek_handler_lazyopen(void *baton,
1959 const svn_stream_mark_t *mark)
1960 {
1961 lazyopen_baton_t *b = baton;
1962
1963 SVN_ERR(lazyopen_if_unopened(b));
1964 SVN_ERR(svn_stream_seek(b->real_stream, mark));
1965
1966 return SVN_NO_ERROR;
1967 }
1968
1969 static svn_error_t *
data_available_handler_lazyopen(void * baton,svn_boolean_t * data_available)1970 data_available_handler_lazyopen(void *baton,
1971 svn_boolean_t *data_available)
1972 {
1973 lazyopen_baton_t *b = baton;
1974
1975 SVN_ERR(lazyopen_if_unopened(b));
1976 return svn_error_trace(svn_stream_data_available(b->real_stream,
1977 data_available));
1978 }
1979
1980 /* Implements svn_stream__is_buffered_fn_t */
1981 static svn_boolean_t
is_buffered_lazyopen(void * baton)1982 is_buffered_lazyopen(void *baton)
1983 {
1984 lazyopen_baton_t *b = baton;
1985
1986 /* No lazy open as we cannot handle an open error. */
1987 if (!b->real_stream)
1988 return FALSE;
1989
1990 return svn_stream__is_buffered(b->real_stream);
1991 }
1992
1993 svn_stream_t *
svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,void * open_baton,svn_boolean_t open_on_close,apr_pool_t * result_pool)1994 svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func,
1995 void *open_baton,
1996 svn_boolean_t open_on_close,
1997 apr_pool_t *result_pool)
1998 {
1999 lazyopen_baton_t *lob = apr_pcalloc(result_pool, sizeof(*lob));
2000 svn_stream_t *stream;
2001
2002 lob->open_func = open_func;
2003 lob->open_baton = open_baton;
2004 lob->real_stream = NULL;
2005 lob->pool = result_pool;
2006 lob->open_on_close = open_on_close;
2007
2008 stream = svn_stream_create(lob, result_pool);
2009 svn_stream_set_read2(stream, read_handler_lazyopen,
2010 read_full_handler_lazyopen);
2011 svn_stream_set_skip(stream, skip_handler_lazyopen);
2012 svn_stream_set_write(stream, write_handler_lazyopen);
2013 svn_stream_set_close(stream, close_handler_lazyopen);
2014 svn_stream_set_mark(stream, mark_handler_lazyopen);
2015 svn_stream_set_seek(stream, seek_handler_lazyopen);
2016 svn_stream_set_data_available(stream, data_available_handler_lazyopen);
2017 svn_stream__set_is_buffered(stream, is_buffered_lazyopen);
2018
2019 return stream;
2020 }
2021
2022 /* Baton for install streams */
2023 struct install_baton_t
2024 {
2025 struct baton_apr baton_apr;
2026 const char *tmp_path;
2027 };
2028
2029 #ifdef WIN32
2030
2031 /* Create and open a tempfile in DIRECTORY. Return its handle and path */
2032 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)2033 create_tempfile(HANDLE *hFile,
2034 const char **file_path,
2035 const char *directory,
2036 apr_pool_t *result_pool,
2037 apr_pool_t *scratch_pool)
2038 {
2039 const char *unique_name;
2040 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2041 static svn_atomic_t tempname_counter;
2042 int baseNr = (GetTickCount() << 11) + 13 * svn_atomic_inc(&tempname_counter)
2043 + GetCurrentProcessId();
2044 int i = 0;
2045 HANDLE h;
2046
2047 /* Shares common idea with io.c's temp_file_create */
2048
2049 do
2050 {
2051 apr_uint32_t unique_nr;
2052 WCHAR *w_name;
2053
2054 /* Generate a number that should be unique for this application and
2055 usually for the entire computer to reduce the number of cycles
2056 through this loop. (A bit of calculation is much cheaper than
2057 disk io) */
2058 unique_nr = baseNr + 7 * i++;
2059
2060
2061 svn_pool_clear(iterpool);
2062 unique_name = svn_dirent_join(directory,
2063 apr_psprintf(iterpool, "svn-%X",
2064 unique_nr),
2065 iterpool);
2066
2067 SVN_ERR(svn_io__utf8_to_unicode_longpath(&w_name, unique_name,
2068 iterpool));
2069
2070 /* Create a completely not-sharable file to avoid indexers, and other
2071 filesystem watchers locking the file while we are still writing.
2072
2073 We need DELETE privileges to move the file. */
2074 h = CreateFileW(w_name, GENERIC_WRITE | DELETE, 0 /* share */,
2075 NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
2076
2077 if (h == INVALID_HANDLE_VALUE)
2078 {
2079 apr_status_t status = apr_get_os_error();
2080 if (i > 1000)
2081 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
2082 svn_error_wrap_apr(status, NULL),
2083 _("Unable to make name in '%s'"),
2084 svn_dirent_local_style(directory, scratch_pool));
2085
2086 if (!APR_STATUS_IS_EEXIST(status) && !APR_STATUS_IS_EACCES(status))
2087 return svn_error_wrap_apr(status, NULL);
2088 }
2089 }
2090 while (h == INVALID_HANDLE_VALUE);
2091
2092 *hFile = h;
2093 *file_path = apr_pstrdup(result_pool, unique_name);
2094 svn_pool_destroy(iterpool);
2095
2096 return SVN_NO_ERROR;
2097 }
2098
2099 /* Implements svn_close_fn_t */
2100 static svn_error_t *
install_close(void * baton)2101 install_close(void *baton)
2102 {
2103 struct install_baton_t *ib = baton;
2104
2105 /* Flush the data cached in APR, but don't close the file yet */
2106 SVN_ERR(svn_io_file_flush(ib->baton_apr.file, ib->baton_apr.pool));
2107
2108 return SVN_NO_ERROR;
2109 }
2110
2111 #endif /* WIN32 */
2112
2113 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)2114 svn_stream__create_for_install(svn_stream_t **install_stream,
2115 const char *tmp_abspath,
2116 apr_pool_t *result_pool,
2117 apr_pool_t *scratch_pool)
2118 {
2119 apr_file_t *file;
2120 struct install_baton_t *ib;
2121 const char *tmp_path;
2122
2123 #ifdef WIN32
2124 HANDLE hInstall;
2125 apr_status_t status;
2126
2127 SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2128
2129 SVN_ERR(create_tempfile(&hInstall, &tmp_path, tmp_abspath,
2130 scratch_pool, scratch_pool));
2131
2132 /* Wrap as a standard APR file to allow sharing implementation.
2133
2134 But do note that some file functions (such as retrieving the name)
2135 don't work on this wrapper. */
2136 /* ### Buffered, or not? */
2137 status = apr_os_file_put(&file, &hInstall,
2138 APR_WRITE | APR_BINARY | APR_BUFFERED,
2139 result_pool);
2140
2141 if (status)
2142 {
2143 CloseHandle(hInstall);
2144 return svn_error_wrap_apr(status, NULL);
2145 }
2146
2147 tmp_path = svn_dirent_internal_style(tmp_path, result_pool);
2148 #else
2149
2150 SVN_ERR_ASSERT(svn_dirent_is_absolute(tmp_abspath));
2151
2152 SVN_ERR(svn_io_open_unique_file3(&file, &tmp_path, tmp_abspath,
2153 svn_io_file_del_none,
2154 result_pool, scratch_pool));
2155 #endif
2156 *install_stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
2157
2158 ib = apr_pcalloc(result_pool, sizeof(*ib));
2159 ib->baton_apr = *(struct baton_apr*)(*install_stream)->baton;
2160
2161 assert((void*)&ib->baton_apr == (void*)ib); /* baton pointer is the same */
2162
2163 (*install_stream)->baton = ib;
2164
2165 ib->tmp_path = tmp_path;
2166
2167 #ifdef WIN32
2168 /* Don't close the file on stream close; flush instead */
2169 svn_stream_set_close(*install_stream, install_close);
2170 #else
2171 /* ### Install pool cleanup handler for tempfile? */
2172 #endif
2173
2174 return SVN_NO_ERROR;
2175 }
2176
2177 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)2178 svn_stream__install_stream(svn_stream_t *install_stream,
2179 const char *final_abspath,
2180 svn_boolean_t make_parents,
2181 apr_pool_t *scratch_pool)
2182 {
2183 struct install_baton_t *ib = install_stream->baton;
2184 svn_error_t *err;
2185
2186 SVN_ERR_ASSERT(svn_dirent_is_absolute(final_abspath));
2187 #ifdef WIN32
2188 err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path,
2189 final_abspath, scratch_pool);
2190 if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2191 {
2192 svn_error_t *err2;
2193
2194 err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2195 scratch_pool),
2196 scratch_pool);
2197
2198 if (err2)
2199 return svn_error_trace(svn_error_compose_create(err, err2));
2200 else
2201 svn_error_clear(err);
2202
2203 err = svn_io__win_rename_open_file(ib->baton_apr.file, ib->tmp_path,
2204 final_abspath, scratch_pool);
2205 }
2206
2207 /* ### rhuijben: I wouldn't be surprised if we later find out that we
2208 have to fall back to close+rename on some specific
2209 error values here, to support some non standard NAS
2210 and filesystem scenarios. */
2211 if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
2212 {
2213 /* Rename open files is not supported on this platform: fallback to
2214 svn_io_file_rename2(). */
2215 svn_error_clear(err);
2216 err = SVN_NO_ERROR;
2217
2218 SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2219 }
2220 else
2221 {
2222 return svn_error_compose_create(err,
2223 svn_io_file_close(ib->baton_apr.file,
2224 scratch_pool));
2225 }
2226 #endif
2227
2228 err = svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool);
2229
2230 /* A missing directory is too common to not cover here. */
2231 if (make_parents && err && APR_STATUS_IS_ENOENT(err->apr_err))
2232 {
2233 svn_error_t *err2;
2234
2235 err2 = svn_io_make_dir_recursively(svn_dirent_dirname(final_abspath,
2236 scratch_pool),
2237 scratch_pool);
2238
2239 if (err2)
2240 /* Creating directory didn't work: Return all errors */
2241 return svn_error_trace(svn_error_compose_create(err, err2));
2242 else
2243 /* We could create a directory: retry install */
2244 svn_error_clear(err);
2245
2246 SVN_ERR(svn_io_file_rename(ib->tmp_path, final_abspath, scratch_pool));
2247 }
2248 else
2249 SVN_ERR(err);
2250
2251 return SVN_NO_ERROR;
2252 }
2253
2254 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)2255 svn_stream__install_get_info(apr_finfo_t *finfo,
2256 svn_stream_t *install_stream,
2257 apr_int32_t wanted,
2258 apr_pool_t *scratch_pool)
2259 {
2260 struct install_baton_t *ib = install_stream->baton;
2261
2262 #ifdef WIN32
2263 /* On WIN32 the file is still open, so we can obtain the information
2264 from the handle without race conditions */
2265 apr_status_t status;
2266
2267 status = apr_file_info_get(finfo, wanted, ib->baton_apr.file);
2268
2269 if (status)
2270 return svn_error_wrap_apr(status, NULL);
2271 #else
2272 SVN_ERR(svn_io_stat(finfo, ib->tmp_path, wanted, scratch_pool));
2273 #endif
2274
2275 return SVN_NO_ERROR;
2276 }
2277
2278 svn_error_t *
svn_stream__install_delete(svn_stream_t * install_stream,apr_pool_t * scratch_pool)2279 svn_stream__install_delete(svn_stream_t *install_stream,
2280 apr_pool_t *scratch_pool)
2281 {
2282 struct install_baton_t *ib = install_stream->baton;
2283
2284 #ifdef WIN32
2285 svn_error_t *err;
2286
2287 /* Mark the file as delete on close to avoid having to reopen
2288 the file as part of the delete handling. */
2289 err = svn_io__win_delete_file_on_close(ib->baton_apr.file, ib->tmp_path,
2290 scratch_pool);
2291 if (err == SVN_NO_ERROR)
2292 {
2293 SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2294 return SVN_NO_ERROR; /* File is already gone */
2295 }
2296
2297 /* Deleting file on close may be unsupported, so ignore errors and
2298 fallback to svn_io_remove_file2(). */
2299 svn_error_clear(err);
2300 SVN_ERR(svn_io_file_close(ib->baton_apr.file, scratch_pool));
2301 #endif
2302
2303 return svn_error_trace(svn_io_remove_file2(ib->tmp_path, FALSE,
2304 scratch_pool));
2305 }
2306