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