stream.c revision 299742
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
53struct 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
70static svn_error_t *
71skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn);
72
73
74/*** Generic streams. ***/
75
76svn_stream_t *
77svn_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
87void
88svn_stream_set_baton(svn_stream_t *stream, void *baton)
89{
90  stream->baton = baton;
91}
92
93
94void
95svn_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
103void
104svn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn)
105{
106  stream->skip_fn = skip_fn;
107}
108
109void
110svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
111{
112  stream->write_fn = write_fn;
113}
114
115void
116svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
117{
118  stream->close_fn = close_fn;
119}
120
121void
122svn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn)
123{
124  stream->mark_fn = mark_fn;
125}
126
127void
128svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn)
129{
130  stream->seek_fn = seek_fn;
131}
132
133void
134svn_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
140void
141svn_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) */
150static svn_error_t *
151full_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
172svn_boolean_t
173svn_stream_supports_partial_read(svn_stream_t *stream)
174{
175  return stream->read_fn != NULL;
176}
177
178svn_error_t *
179svn_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
187svn_error_t *
188svn_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
196svn_error_t *
197svn_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
207svn_error_t *
208svn_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
217svn_error_t *
218svn_stream_reset(svn_stream_t *stream)
219{
220  return svn_error_trace(
221            svn_stream_seek(stream, NULL));
222}
223
224svn_boolean_t
225svn_stream_supports_mark(svn_stream_t *stream)
226{
227  return stream->mark_fn != NULL;
228}
229
230svn_error_t *
231svn_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
240svn_error_t *
241svn_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
249svn_error_t *
250svn_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
260svn_boolean_t
261svn_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
269svn_error_t *
270svn_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
277svn_error_t *
278svn_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
286svn_error_t *
287svn_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
303svn_error_t *
304svn_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. */
329static svn_error_t *
330stream_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
376static svn_error_t *
377stream_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. */
482static svn_error_t *
483stream_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
520svn_error_t *
521svn_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
531svn_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
571svn_error_t *
572svn_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. */
612static svn_error_t *
613skip_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
633static svn_error_t *
634read_handler_empty(void *baton, char *buffer, apr_size_t *len)
635{
636  *len = 0;
637  return SVN_NO_ERROR;
638}
639
640static svn_error_t *
641write_handler_empty(void *baton, const char *data, apr_size_t *len)
642{
643  return SVN_NO_ERROR;
644}
645
646static svn_error_t *
647mark_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
653static svn_error_t *
654seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
655{
656  return SVN_NO_ERROR;
657}
658
659static svn_boolean_t
660is_buffered_handler_empty(void *baton)
661{
662  return FALSE;
663}
664
665
666svn_stream_t *
667svn_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 ***/
683struct baton_tee {
684  svn_stream_t *out1;
685  svn_stream_t *out2;
686};
687
688
689static svn_error_t *
690write_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
701static svn_error_t *
702close_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
713svn_stream_t *
714svn_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
741static svn_error_t *
742read_handler_disown(void *baton, char *buffer, apr_size_t *len)
743{
744  return svn_error_trace(svn_stream_read2(baton, buffer, len));
745}
746
747static svn_error_t *
748read_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
753static svn_error_t *
754skip_handler_disown(void *baton, apr_size_t len)
755{
756  return svn_error_trace(svn_stream_skip(baton, len));
757}
758
759static svn_error_t *
760write_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
765static svn_error_t *
766mark_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
771static svn_error_t *
772seek_handler_disown(void *baton, const svn_stream_mark_t *mark)
773{
774  return svn_error_trace(svn_stream_seek(baton, mark));
775}
776
777static svn_error_t *
778data_available_disown(void *baton, svn_boolean_t *data_available)
779{
780  return svn_error_trace(svn_stream_data_available(baton, data_available));
781}
782
783static svn_boolean_t
784is_buffered_handler_disown(void *baton)
785{
786  return svn_stream__is_buffered(baton);
787}
788
789svn_stream_t *
790svn_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 ***/
808struct 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. */
814struct mark_apr {
815  apr_off_t off;
816};
817
818static svn_error_t *
819read_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
850static svn_error_t *
851read_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
877static svn_error_t *
878skip_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
887static svn_error_t *
888write_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
905static svn_error_t *
906close_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
913static svn_error_t *
914mark_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
926static svn_error_t *
927seek_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
937static svn_error_t *
938data_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
993static svn_boolean_t
994is_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
1000svn_error_t *
1001svn_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
1016svn_error_t *
1017svn_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
1036svn_error_t *
1037svn_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. */
1055static svn_stream_t *
1056make_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
1091svn_stream_t *
1092svn_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
1099apr_file_t *
1100svn_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
1114struct 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. */
1127static voidpf
1128zalloc(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 */
1136static void
1137zfree(voidpf opaque, voidpf address)
1138{
1139  /* Empty, since we allocate on the pool */
1140}
1141
1142/* Helper function to figure out the sync mode */
1143static svn_error_t *
1144read_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 */
1168static svn_error_t *
1169read_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 */
1224static svn_error_t *
1225write_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 */
1271static svn_error_t *
1272close_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
1314svn_stream_t *
1315svn_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
1341struct 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
1355static svn_error_t *
1356read_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
1368static svn_error_t *
1369read_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
1386static svn_error_t *
1387write_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
1397static svn_error_t *
1398data_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
1406static svn_error_t *
1407close_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
1435svn_stream_t *
1436svn_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
1476svn_error_t *
1477svn_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
1506struct 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. */
1513struct stringbuf_stream_mark {
1514    apr_size_t pos;
1515};
1516
1517static svn_error_t *
1518read_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
1529static svn_error_t *
1530skip_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
1540static svn_error_t *
1541write_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
1549static svn_error_t *
1550mark_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
1563static svn_error_t *
1564seek_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
1581static svn_error_t *
1582data_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
1590static svn_boolean_t
1591is_buffered_handler_stringbuf(void *baton)
1592{
1593  return TRUE;
1594}
1595
1596svn_stream_t *
1597svn_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
1620struct 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. */
1627struct string_stream_mark {
1628    apr_size_t pos;
1629};
1630
1631static svn_error_t *
1632read_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
1643static svn_error_t *
1644mark_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
1657static svn_error_t *
1658seek_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
1675static svn_error_t *
1676skip_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
1686static svn_error_t *
1687data_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
1695static svn_boolean_t
1696is_buffered_handler_string(void *baton)
1697{
1698  return TRUE;
1699}
1700
1701svn_stream_t *
1702svn_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
1725svn_error_t *
1726svn_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
1745svn_error_t *
1746svn_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
1765svn_error_t *
1766svn_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
1785svn_error_t *
1786svn_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
1821svn_stream_t *
1822svn_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. */
1835typedef 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. */
1854static svn_error_t *
1855lazyopen_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 */
1874static svn_error_t *
1875read_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 */
1888static svn_error_t *
1889read_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 */
1902static svn_error_t *
1903skip_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 */
1915static svn_error_t *
1916write_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 */
1929static svn_error_t *
1930close_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 */
1943static svn_error_t *
1944mark_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 */
1957static svn_error_t *
1958seek_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
1969static svn_error_t *
1970data_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 */
1981static svn_boolean_t
1982is_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
1993svn_stream_t *
1994svn_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 */
2023struct 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 */
2032static svn_error_t *
2033create_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 */
2100static svn_error_t *
2101install_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
2113svn_error_t *
2114svn_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
2177svn_error_t *
2178svn_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
2254svn_error_t *
2255svn_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
2278svn_error_t *
2279svn_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