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
54struct 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
71static svn_error_t *
72skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_full_fn);
73
74
75/*** Generic streams. ***/
76
77svn_stream_t *
78svn_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
88void
89svn_stream_set_baton(svn_stream_t *stream, void *baton)
90{
91  stream->baton = baton;
92}
93
94
95void
96svn_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
104void
105svn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn)
106{
107  stream->skip_fn = skip_fn;
108}
109
110void
111svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn)
112{
113  stream->write_fn = write_fn;
114}
115
116void
117svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn)
118{
119  stream->close_fn = close_fn;
120}
121
122void
123svn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn)
124{
125  stream->mark_fn = mark_fn;
126}
127
128void
129svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn)
130{
131  stream->seek_fn = seek_fn;
132}
133
134void
135svn_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
141void
142svn_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) */
151static svn_error_t *
152full_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
173svn_boolean_t
174svn_stream_supports_partial_read(svn_stream_t *stream)
175{
176  return stream->read_fn != NULL;
177}
178
179svn_error_t *
180svn_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
188svn_error_t *
189svn_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
197svn_error_t *
198svn_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
215svn_error_t *
216svn_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
225svn_error_t *
226svn_stream_reset(svn_stream_t *stream)
227{
228  return svn_error_trace(
229            svn_stream_seek(stream, NULL));
230}
231
232svn_boolean_t
233svn_stream_supports_mark(svn_stream_t *stream)
234{
235  return stream->mark_fn != NULL;
236}
237
238svn_boolean_t
239svn_stream_supports_reset(svn_stream_t *stream)
240{
241  return stream->seek_fn != NULL;
242}
243
244svn_error_t *
245svn_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
254svn_error_t *
255svn_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
263svn_error_t *
264svn_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
274svn_error_t *
275svn_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
282svn_error_t *
283svn_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
291svn_error_t *
292svn_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
308svn_error_t *
309svn_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. */
332static svn_error_t *
333stream_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
379svn_error_t *
380svn_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
400svn_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
440svn_error_t *
441svn_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. */
481static svn_error_t *
482skip_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
502static svn_error_t *
503read_handler_empty(void *baton, char *buffer, apr_size_t *len)
504{
505  *len = 0;
506  return SVN_NO_ERROR;
507}
508
509static svn_error_t *
510write_handler_empty(void *baton, const char *data, apr_size_t *len)
511{
512  return SVN_NO_ERROR;
513}
514
515static svn_error_t *
516mark_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
522static svn_error_t *
523seek_handler_empty(void *baton, const svn_stream_mark_t *mark)
524{
525  return SVN_NO_ERROR;
526}
527
528
529
530svn_stream_t *
531svn_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 ***/
546struct baton_tee {
547  svn_stream_t *out1;
548  svn_stream_t *out2;
549};
550
551
552static svn_error_t *
553write_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
564static svn_error_t *
565close_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
576svn_stream_t *
577svn_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
604static svn_error_t *
605read_handler_disown(void *baton, char *buffer, apr_size_t *len)
606{
607  return svn_error_trace(svn_stream_read2(baton, buffer, len));
608}
609
610static svn_error_t *
611read_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
616static svn_error_t *
617skip_handler_disown(void *baton, apr_size_t len)
618{
619  return svn_error_trace(svn_stream_skip(baton, len));
620}
621
622static svn_error_t *
623write_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
628static svn_error_t *
629mark_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
634static svn_error_t *
635seek_handler_disown(void *baton, const svn_stream_mark_t *mark)
636{
637  return svn_error_trace(svn_stream_seek(baton, mark));
638}
639
640static svn_error_t *
641data_available_disown(void *baton, svn_boolean_t *data_available)
642{
643  return svn_error_trace(svn_stream_data_available(baton, data_available));
644}
645
646static svn_error_t *
647readline_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
657svn_stream_t *
658svn_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 ***/
676struct 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. */
683struct mark_apr {
684  apr_off_t off;
685};
686
687static svn_error_t *
688read_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
719static svn_error_t *
720read_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
746static svn_error_t *
747skip_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
756static svn_error_t *
757write_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
774static svn_error_t *
775close_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
782static svn_error_t *
783mark_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
794static svn_error_t *
795seek_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
814static svn_error_t *
815data_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
870static svn_error_t *
871readline_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
929static svn_error_t *
930readline_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
989static svn_error_t *
990readline_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
1012svn_error_t *
1013svn_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
1028svn_error_t *
1029svn_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
1048svn_error_t *
1049svn_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. */
1067static svn_stream_t *
1068make_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
1105svn_stream_t *
1106svn_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
1114svn_stream_t *
1115svn_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
1122apr_file_t *
1123svn_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
1137struct 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. */
1150static voidpf
1151zalloc(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 */
1159static void
1160zfree(voidpf opaque, voidpf address)
1161{
1162  /* Empty, since we allocate on the pool */
1163}
1164
1165/* Helper function to figure out the sync mode */
1166static svn_error_t *
1167read_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 */
1191static svn_error_t *
1192read_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 */
1247static svn_error_t *
1248write_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 */
1294static svn_error_t *
1295close_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
1337svn_stream_t *
1338svn_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
1364struct 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
1378static svn_error_t *
1379read_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
1391static svn_error_t *
1392read_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
1409static svn_error_t *
1410write_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
1420static svn_error_t *
1421data_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
1429static svn_error_t *
1430close_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
1457static svn_error_t *
1458seek_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
1483svn_stream_t *
1484svn_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. */
1526static svn_error_t *
1527compute_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
1553svn_error_t *
1554svn_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 */
1581svn_error_t *
1582svn_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
1613struct 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. */
1620struct stringbuf_stream_mark {
1621    apr_size_t pos;
1622};
1623
1624static svn_error_t *
1625read_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
1636static svn_error_t *
1637skip_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
1647static svn_error_t *
1648write_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
1656static svn_error_t *
1657mark_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
1670static svn_error_t *
1671seek_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
1688static svn_error_t *
1689data_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
1697static svn_error_t *
1698readline_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
1728svn_stream_t *
1729svn_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
1752struct 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. */
1759struct string_stream_mark {
1760    apr_size_t pos;
1761};
1762
1763static svn_error_t *
1764read_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
1775static svn_error_t *
1776mark_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
1789static svn_error_t *
1790seek_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
1807static svn_error_t *
1808skip_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
1818static svn_error_t *
1819data_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
1827static svn_error_t *
1828readline_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
1858svn_stream_t *
1859svn_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
1882svn_error_t *
1883svn_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
1905svn_error_t *
1906svn_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
1925svn_error_t *
1926svn_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
1945svn_error_t *
1946svn_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
1967svn_stream_t *
1968svn_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. */
1981typedef 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. */
2000static svn_error_t *
2001lazyopen_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 */
2020static svn_error_t *
2021read_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 */
2034static svn_error_t *
2035read_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 */
2048static svn_error_t *
2049skip_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 */
2061static svn_error_t *
2062write_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 */
2075static svn_error_t *
2076close_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 */
2089static svn_error_t *
2090mark_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 */
2103static svn_error_t *
2104seek_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
2115static svn_error_t *
2116data_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 */
2127static svn_error_t *
2128readline_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
2141svn_stream_t *
2142svn_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 */
2171struct 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 */
2180static svn_error_t *
2181create_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 */
2250static svn_error_t *
2251install_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
2261svn_error_t *
2262svn_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
2323svn_error_t *
2324svn_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
2401svn_error_t *
2402svn_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
2418svn_error_t *
2419svn_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