1 | /* |
---|
2 | * stream.c: svn_stream operations |
---|
3 | * |
---|
4 | * ==================================================================== |
---|
5 | * Copyright (c) 2000-2004, 2006 CollabNet. All rights reserved. |
---|
6 | * |
---|
7 | * This software is licensed as described in the file COPYING, which |
---|
8 | * you should have received as part of this distribution. The terms |
---|
9 | * are also available at http://subversion.tigris.org/license-1.html. |
---|
10 | * If newer versions of this license are posted there, you may use a |
---|
11 | * newer version instead, at your option. |
---|
12 | * |
---|
13 | * This software consists of voluntary contributions made by many |
---|
14 | * individuals. For exact contribution history, see the revision |
---|
15 | * history and logs, available at http://subversion.tigris.org/. |
---|
16 | * ==================================================================== |
---|
17 | */ |
---|
18 | |
---|
19 | #include "svn_private_config.h" |
---|
20 | |
---|
21 | #include <assert.h> |
---|
22 | #include <stdio.h> |
---|
23 | |
---|
24 | #include <apr.h> |
---|
25 | #include <apr_pools.h> |
---|
26 | #include <apr_strings.h> |
---|
27 | #include <apr_file_io.h> |
---|
28 | #include <apr_errno.h> |
---|
29 | #include <apr_md5.h> |
---|
30 | |
---|
31 | #include <zlib.h> |
---|
32 | |
---|
33 | #include "svn_pools.h" |
---|
34 | #include "svn_io.h" |
---|
35 | #include "svn_error.h" |
---|
36 | #include "svn_string.h" |
---|
37 | #include "svn_utf.h" |
---|
38 | #include "svn_checksum.h" |
---|
39 | #include "svn_path.h" |
---|
40 | |
---|
41 | |
---|
42 | struct svn_stream_t { |
---|
43 | void *baton; |
---|
44 | svn_read_fn_t read_fn; |
---|
45 | svn_write_fn_t write_fn; |
---|
46 | svn_close_fn_t close_fn; |
---|
47 | }; |
---|
48 | |
---|
49 | |
---|
50 | |
---|
51 | /*** Generic streams. ***/ |
---|
52 | |
---|
53 | svn_stream_t * |
---|
54 | svn_stream_create(void *baton, apr_pool_t *pool) |
---|
55 | { |
---|
56 | svn_stream_t *stream; |
---|
57 | |
---|
58 | stream = apr_palloc(pool, sizeof(*stream)); |
---|
59 | stream->baton = baton; |
---|
60 | stream->read_fn = NULL; |
---|
61 | stream->write_fn = NULL; |
---|
62 | stream->close_fn = NULL; |
---|
63 | return stream; |
---|
64 | } |
---|
65 | |
---|
66 | |
---|
67 | void |
---|
68 | svn_stream_set_baton(svn_stream_t *stream, void *baton) |
---|
69 | { |
---|
70 | stream->baton = baton; |
---|
71 | } |
---|
72 | |
---|
73 | |
---|
74 | void |
---|
75 | svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn) |
---|
76 | { |
---|
77 | stream->read_fn = read_fn; |
---|
78 | } |
---|
79 | |
---|
80 | |
---|
81 | void |
---|
82 | svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn) |
---|
83 | { |
---|
84 | stream->write_fn = write_fn; |
---|
85 | } |
---|
86 | |
---|
87 | |
---|
88 | void |
---|
89 | svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn) |
---|
90 | { |
---|
91 | stream->close_fn = close_fn; |
---|
92 | } |
---|
93 | |
---|
94 | |
---|
95 | svn_error_t * |
---|
96 | svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len) |
---|
97 | { |
---|
98 | SVN_ERR_ASSERT(stream->read_fn != NULL); |
---|
99 | return stream->read_fn(stream->baton, buffer, len); |
---|
100 | } |
---|
101 | |
---|
102 | |
---|
103 | svn_error_t * |
---|
104 | svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len) |
---|
105 | { |
---|
106 | SVN_ERR_ASSERT(stream->write_fn != NULL); |
---|
107 | return stream->write_fn(stream->baton, data, len); |
---|
108 | } |
---|
109 | |
---|
110 | |
---|
111 | svn_error_t * |
---|
112 | svn_stream_close(svn_stream_t *stream) |
---|
113 | { |
---|
114 | if (stream->close_fn == NULL) |
---|
115 | return SVN_NO_ERROR; |
---|
116 | return stream->close_fn(stream->baton); |
---|
117 | } |
---|
118 | |
---|
119 | |
---|
120 | svn_error_t * |
---|
121 | svn_stream_printf(svn_stream_t *stream, |
---|
122 | apr_pool_t *pool, |
---|
123 | const char *fmt, |
---|
124 | ...) |
---|
125 | { |
---|
126 | const char *message; |
---|
127 | va_list ap; |
---|
128 | apr_size_t len; |
---|
129 | |
---|
130 | va_start(ap, fmt); |
---|
131 | message = apr_pvsprintf(pool, fmt, ap); |
---|
132 | va_end(ap); |
---|
133 | |
---|
134 | len = strlen(message); |
---|
135 | return svn_stream_write(stream, message, &len); |
---|
136 | } |
---|
137 | |
---|
138 | |
---|
139 | svn_error_t * |
---|
140 | svn_stream_printf_from_utf8(svn_stream_t *stream, |
---|
141 | const char *encoding, |
---|
142 | apr_pool_t *pool, |
---|
143 | const char *fmt, |
---|
144 | ...) |
---|
145 | { |
---|
146 | const char *message, *translated; |
---|
147 | va_list ap; |
---|
148 | apr_size_t len; |
---|
149 | |
---|
150 | va_start(ap, fmt); |
---|
151 | message = apr_pvsprintf(pool, fmt, ap); |
---|
152 | va_end(ap); |
---|
153 | |
---|
154 | SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding, |
---|
155 | pool)); |
---|
156 | |
---|
157 | len = strlen(translated); |
---|
158 | |
---|
159 | return svn_stream_write(stream, translated, &len); |
---|
160 | } |
---|
161 | |
---|
162 | |
---|
163 | svn_error_t * |
---|
164 | svn_stream_readline(svn_stream_t *stream, |
---|
165 | svn_stringbuf_t **stringbuf, |
---|
166 | const char *eol, |
---|
167 | svn_boolean_t *eof, |
---|
168 | apr_pool_t *pool) |
---|
169 | { |
---|
170 | apr_size_t numbytes; |
---|
171 | const char *match; |
---|
172 | char c; |
---|
173 | /* Since we're reading one character at a time, let's at least |
---|
174 | optimize for the 90% case. 90% of the time, we can avoid the |
---|
175 | stringbuf ever having to realloc() itself if we start it out at |
---|
176 | 80 chars. */ |
---|
177 | svn_stringbuf_t *str = svn_stringbuf_create_ensure(80, pool); |
---|
178 | |
---|
179 | match = eol; |
---|
180 | while (*match) |
---|
181 | { |
---|
182 | numbytes = 1; |
---|
183 | SVN_ERR(svn_stream_read(stream, &c, &numbytes)); |
---|
184 | if (numbytes != 1) |
---|
185 | { |
---|
186 | /* a 'short' read means the stream has run out. */ |
---|
187 | *eof = TRUE; |
---|
188 | *stringbuf = str; |
---|
189 | return SVN_NO_ERROR; |
---|
190 | } |
---|
191 | |
---|
192 | if (c == *match) |
---|
193 | match++; |
---|
194 | else |
---|
195 | match = eol; |
---|
196 | |
---|
197 | svn_stringbuf_appendbytes(str, &c, 1); |
---|
198 | } |
---|
199 | |
---|
200 | *eof = FALSE; |
---|
201 | svn_stringbuf_chop(str, match - eol); |
---|
202 | *stringbuf = str; |
---|
203 | return SVN_NO_ERROR; |
---|
204 | } |
---|
205 | |
---|
206 | |
---|
207 | svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to, |
---|
208 | svn_cancel_func_t cancel_func, |
---|
209 | void *cancel_baton, |
---|
210 | apr_pool_t *scratch_pool) |
---|
211 | { |
---|
212 | char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); |
---|
213 | svn_error_t *err; |
---|
214 | svn_error_t *err2; |
---|
215 | |
---|
216 | /* Read and write chunks until we get a short read, indicating the |
---|
217 | end of the stream. (We can't get a short write without an |
---|
218 | associated error.) */ |
---|
219 | while (1) |
---|
220 | { |
---|
221 | apr_size_t len = SVN__STREAM_CHUNK_SIZE; |
---|
222 | |
---|
223 | if (cancel_func) |
---|
224 | SVN_ERR(cancel_func(cancel_baton)); |
---|
225 | |
---|
226 | SVN_ERR(svn_stream_read(from, buf, &len)); |
---|
227 | if (len > 0) |
---|
228 | SVN_ERR(svn_stream_write(to, buf, &len)); |
---|
229 | if (len != SVN__STREAM_CHUNK_SIZE) |
---|
230 | break; |
---|
231 | } |
---|
232 | |
---|
233 | err = svn_stream_close(from); |
---|
234 | err2 = svn_stream_close(to); |
---|
235 | if (err) |
---|
236 | { |
---|
237 | /* ### it would be nice to compose the two errors in some way */ |
---|
238 | svn_error_clear(err2); /* note: might be NULL */ |
---|
239 | return err; |
---|
240 | } |
---|
241 | return err2; |
---|
242 | } |
---|
243 | |
---|
244 | svn_error_t *svn_stream_copy2(svn_stream_t *from, svn_stream_t *to, |
---|
245 | svn_cancel_func_t cancel_func, |
---|
246 | void *cancel_baton, |
---|
247 | apr_pool_t *scratch_pool) |
---|
248 | { |
---|
249 | return svn_stream_copy3(svn_stream_disown(from, scratch_pool), |
---|
250 | svn_stream_disown(to, scratch_pool), |
---|
251 | cancel_func, cancel_baton, scratch_pool); |
---|
252 | } |
---|
253 | |
---|
254 | svn_error_t *svn_stream_copy(svn_stream_t *from, svn_stream_t *to, |
---|
255 | apr_pool_t *scratch_pool) |
---|
256 | { |
---|
257 | return svn_stream_copy3(svn_stream_disown(from, scratch_pool), |
---|
258 | svn_stream_disown(to, scratch_pool), |
---|
259 | NULL, NULL, scratch_pool); |
---|
260 | } |
---|
261 | |
---|
262 | |
---|
263 | svn_error_t * |
---|
264 | svn_stream_contents_same(svn_boolean_t *same, |
---|
265 | svn_stream_t *stream1, |
---|
266 | svn_stream_t *stream2, |
---|
267 | apr_pool_t *pool) |
---|
268 | { |
---|
269 | char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); |
---|
270 | char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); |
---|
271 | apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE; |
---|
272 | apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE; |
---|
273 | |
---|
274 | *same = TRUE; /* assume TRUE, until disproved below */ |
---|
275 | while (bytes_read1 == SVN__STREAM_CHUNK_SIZE |
---|
276 | && bytes_read2 == SVN__STREAM_CHUNK_SIZE) |
---|
277 | { |
---|
278 | SVN_ERR(svn_stream_read(stream1, buf1, &bytes_read1)); |
---|
279 | SVN_ERR(svn_stream_read(stream2, buf2, &bytes_read2)); |
---|
280 | |
---|
281 | if ((bytes_read1 != bytes_read2) |
---|
282 | || (memcmp(buf1, buf2, bytes_read1))) |
---|
283 | { |
---|
284 | *same = FALSE; |
---|
285 | break; |
---|
286 | } |
---|
287 | } |
---|
288 | |
---|
289 | return SVN_NO_ERROR; |
---|
290 | } |
---|
291 | |
---|
292 | |
---|
293 | |
---|
294 | /*** Generic readable empty stream ***/ |
---|
295 | |
---|
296 | static svn_error_t * |
---|
297 | read_handler_empty(void *baton, char *buffer, apr_size_t *len) |
---|
298 | { |
---|
299 | *len = 0; |
---|
300 | return SVN_NO_ERROR; |
---|
301 | } |
---|
302 | |
---|
303 | |
---|
304 | static svn_error_t * |
---|
305 | write_handler_empty(void *baton, const char *data, apr_size_t *len) |
---|
306 | { |
---|
307 | return SVN_NO_ERROR; |
---|
308 | } |
---|
309 | |
---|
310 | |
---|
311 | svn_stream_t * |
---|
312 | svn_stream_empty(apr_pool_t *pool) |
---|
313 | { |
---|
314 | svn_stream_t *stream; |
---|
315 | |
---|
316 | stream = svn_stream_create(NULL, pool); |
---|
317 | svn_stream_set_read(stream, read_handler_empty); |
---|
318 | svn_stream_set_write(stream, write_handler_empty); |
---|
319 | return stream; |
---|
320 | } |
---|
321 | |
---|
322 | |
---|
323 | |
---|
324 | |
---|
325 | /*** Ownership detaching stream ***/ |
---|
326 | |
---|
327 | static svn_error_t * |
---|
328 | read_handler_disown(void *baton, char *buffer, apr_size_t *len) |
---|
329 | { |
---|
330 | return svn_stream_read((svn_stream_t *)baton, buffer, len); |
---|
331 | } |
---|
332 | |
---|
333 | static svn_error_t * |
---|
334 | write_handler_disown(void *baton, const char *buffer, apr_size_t *len) |
---|
335 | { |
---|
336 | return svn_stream_write((svn_stream_t *)baton, buffer, len); |
---|
337 | } |
---|
338 | |
---|
339 | |
---|
340 | svn_stream_t * |
---|
341 | svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool) |
---|
342 | { |
---|
343 | svn_stream_t *s = svn_stream_create(stream, pool); |
---|
344 | |
---|
345 | svn_stream_set_read(s, read_handler_disown); |
---|
346 | svn_stream_set_write(s, write_handler_disown); |
---|
347 | |
---|
348 | return s; |
---|
349 | } |
---|
350 | |
---|
351 | |
---|
352 | |
---|
353 | /*** Generic stream for APR files ***/ |
---|
354 | struct baton_apr { |
---|
355 | apr_file_t *file; |
---|
356 | apr_pool_t *pool; |
---|
357 | }; |
---|
358 | |
---|
359 | |
---|
360 | static svn_error_t * |
---|
361 | read_handler_apr(void *baton, char *buffer, apr_size_t *len) |
---|
362 | { |
---|
363 | struct baton_apr *btn = baton; |
---|
364 | svn_error_t *err; |
---|
365 | |
---|
366 | err = svn_io_file_read_full(btn->file, buffer, *len, len, btn->pool); |
---|
367 | if (err && APR_STATUS_IS_EOF(err->apr_err)) |
---|
368 | { |
---|
369 | svn_error_clear(err); |
---|
370 | err = SVN_NO_ERROR; |
---|
371 | } |
---|
372 | |
---|
373 | return err; |
---|
374 | } |
---|
375 | |
---|
376 | |
---|
377 | static svn_error_t * |
---|
378 | write_handler_apr(void *baton, const char *data, apr_size_t *len) |
---|
379 | { |
---|
380 | struct baton_apr *btn = baton; |
---|
381 | |
---|
382 | return svn_io_file_write_full(btn->file, data, *len, len, btn->pool); |
---|
383 | } |
---|
384 | |
---|
385 | static svn_error_t * |
---|
386 | close_handler_apr(void *baton) |
---|
387 | { |
---|
388 | struct baton_apr *btn = baton; |
---|
389 | |
---|
390 | return svn_io_file_close(btn->file, btn->pool); |
---|
391 | } |
---|
392 | |
---|
393 | |
---|
394 | svn_error_t * |
---|
395 | svn_stream_open_readonly(svn_stream_t **stream, |
---|
396 | const char *path, |
---|
397 | apr_pool_t *result_pool, |
---|
398 | apr_pool_t *scratch_pool) |
---|
399 | { |
---|
400 | apr_file_t *file; |
---|
401 | |
---|
402 | SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED | APR_BINARY, |
---|
403 | APR_OS_DEFAULT, result_pool)); |
---|
404 | *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); |
---|
405 | |
---|
406 | return SVN_NO_ERROR; |
---|
407 | } |
---|
408 | |
---|
409 | |
---|
410 | svn_error_t * |
---|
411 | svn_stream_open_writable(svn_stream_t **stream, |
---|
412 | const char *path, |
---|
413 | apr_pool_t *result_pool, |
---|
414 | apr_pool_t *scratch_pool) |
---|
415 | { |
---|
416 | apr_file_t *file; |
---|
417 | |
---|
418 | SVN_ERR(svn_io_file_open(&file, path, |
---|
419 | APR_WRITE |
---|
420 | | APR_BUFFERED |
---|
421 | | APR_BINARY |
---|
422 | | APR_CREATE |
---|
423 | | APR_EXCL, |
---|
424 | APR_OS_DEFAULT, result_pool)); |
---|
425 | *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); |
---|
426 | |
---|
427 | return SVN_NO_ERROR; |
---|
428 | } |
---|
429 | |
---|
430 | |
---|
431 | svn_error_t * |
---|
432 | svn_stream_open_unique(svn_stream_t **stream, |
---|
433 | const char **temp_path, |
---|
434 | const char *dirpath, |
---|
435 | svn_io_file_del_t delete_when, |
---|
436 | apr_pool_t *result_pool, |
---|
437 | apr_pool_t *scratch_pool) |
---|
438 | { |
---|
439 | apr_file_t *file; |
---|
440 | |
---|
441 | SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath, |
---|
442 | delete_when, result_pool, scratch_pool)); |
---|
443 | *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); |
---|
444 | |
---|
445 | return SVN_NO_ERROR; |
---|
446 | } |
---|
447 | |
---|
448 | |
---|
449 | svn_stream_t * |
---|
450 | svn_stream_from_aprfile2(apr_file_t *file, |
---|
451 | svn_boolean_t disown, |
---|
452 | apr_pool_t *pool) |
---|
453 | { |
---|
454 | struct baton_apr *baton; |
---|
455 | svn_stream_t *stream; |
---|
456 | |
---|
457 | if (file == NULL) |
---|
458 | return svn_stream_empty(pool); |
---|
459 | |
---|
460 | baton = apr_palloc(pool, sizeof(*baton)); |
---|
461 | baton->file = file; |
---|
462 | baton->pool = pool; |
---|
463 | stream = svn_stream_create(baton, pool); |
---|
464 | svn_stream_set_read(stream, read_handler_apr); |
---|
465 | svn_stream_set_write(stream, write_handler_apr); |
---|
466 | |
---|
467 | if (! disown) |
---|
468 | svn_stream_set_close(stream, close_handler_apr); |
---|
469 | |
---|
470 | return stream; |
---|
471 | } |
---|
472 | |
---|
473 | svn_stream_t * |
---|
474 | svn_stream_from_aprfile(apr_file_t *file, apr_pool_t *pool) |
---|
475 | { |
---|
476 | return svn_stream_from_aprfile2(file, TRUE, pool); |
---|
477 | } |
---|
478 | |
---|
479 | |
---|
480 | |
---|
481 | /* Compressed stream support */ |
---|
482 | |
---|
483 | #define ZBUFFER_SIZE 4096 /* The size of the buffer the |
---|
484 | compressed stream uses to read from |
---|
485 | the substream. Basically an |
---|
486 | arbitrary value, picked to be about |
---|
487 | page-sized. */ |
---|
488 | |
---|
489 | struct zbaton { |
---|
490 | z_stream *in; /* compressed stream for reading */ |
---|
491 | z_stream *out; /* compressed stream for writing */ |
---|
492 | svn_read_fn_t read; /* substream's read function */ |
---|
493 | svn_write_fn_t write; /* substream's write function */ |
---|
494 | svn_close_fn_t close; /* substream's close function */ |
---|
495 | void *read_buffer; /* buffer used for reading from |
---|
496 | substream */ |
---|
497 | int read_flush; /* what flush mode to use while |
---|
498 | reading */ |
---|
499 | apr_pool_t *pool; /* The pool this baton is allocated |
---|
500 | on */ |
---|
501 | void *subbaton; /* The substream's baton */ |
---|
502 | }; |
---|
503 | |
---|
504 | /* zlib alloc function. opaque is the pool we need. */ |
---|
505 | static voidpf |
---|
506 | zalloc(voidpf opaque, uInt items, uInt size) |
---|
507 | { |
---|
508 | apr_pool_t *pool = opaque; |
---|
509 | |
---|
510 | return apr_palloc(pool, items * size); |
---|
511 | } |
---|
512 | |
---|
513 | /* zlib free function */ |
---|
514 | static void |
---|
515 | zfree(voidpf opaque, voidpf address) |
---|
516 | { |
---|
517 | /* Empty, since we allocate on the pool */ |
---|
518 | } |
---|
519 | |
---|
520 | /* Converts a zlib error to an svn_error_t. zerr is the error code, |
---|
521 | function is the function name, and stream is the z_stream we are |
---|
522 | using. */ |
---|
523 | static svn_error_t * |
---|
524 | zerr_to_svn_error(int zerr, const char *function, z_stream *stream) |
---|
525 | { |
---|
526 | apr_status_t status; |
---|
527 | const char *message; |
---|
528 | |
---|
529 | if (zerr == Z_OK) |
---|
530 | return SVN_NO_ERROR; |
---|
531 | |
---|
532 | switch (zerr) |
---|
533 | { |
---|
534 | case Z_STREAM_ERROR: |
---|
535 | status = SVN_ERR_STREAM_MALFORMED_DATA; |
---|
536 | message = "stream error"; |
---|
537 | break; |
---|
538 | |
---|
539 | case Z_MEM_ERROR: |
---|
540 | status = APR_ENOMEM; |
---|
541 | message = "out of memory"; |
---|
542 | break; |
---|
543 | |
---|
544 | case Z_BUF_ERROR: |
---|
545 | status = APR_ENOMEM; |
---|
546 | message = "buffer error"; |
---|
547 | break; |
---|
548 | |
---|
549 | case Z_VERSION_ERROR: |
---|
550 | status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; |
---|
551 | message = "version error"; |
---|
552 | break; |
---|
553 | |
---|
554 | case Z_DATA_ERROR: |
---|
555 | status = SVN_ERR_STREAM_MALFORMED_DATA; |
---|
556 | message = "corrupted data"; |
---|
557 | break; |
---|
558 | |
---|
559 | default: |
---|
560 | status = SVN_ERR_STREAM_UNRECOGNIZED_DATA; |
---|
561 | message = "error"; |
---|
562 | break; |
---|
563 | } |
---|
564 | |
---|
565 | if (stream->msg != NULL) |
---|
566 | return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function, |
---|
567 | message, stream->msg); |
---|
568 | else |
---|
569 | return svn_error_createf(status, NULL, "zlib (%s): %s", function, |
---|
570 | message); |
---|
571 | } |
---|
572 | |
---|
573 | /* Helper function to figure out the sync mode */ |
---|
574 | static svn_error_t * |
---|
575 | read_helper_gz(svn_read_fn_t read_fn, |
---|
576 | void *baton, |
---|
577 | char *buffer, |
---|
578 | uInt *len, int *zflush) |
---|
579 | { |
---|
580 | uInt orig_len = *len; |
---|
581 | |
---|
582 | /* There's no reason this value should grow bigger than the range of |
---|
583 | uInt, but Subversion's API requires apr_size_t. */ |
---|
584 | apr_size_t apr_len = (apr_size_t) *len; |
---|
585 | |
---|
586 | SVN_ERR((*read_fn)(baton, buffer, &apr_len)); |
---|
587 | |
---|
588 | /* Type cast back to uInt type that zlib uses. On LP64 platforms |
---|
589 | apr_size_t will be bigger than uInt. */ |
---|
590 | *len = (uInt) apr_len; |
---|
591 | |
---|
592 | /* I wanted to use Z_FINISH here, but we need to know our buffer is |
---|
593 | big enough */ |
---|
594 | *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH; |
---|
595 | |
---|
596 | return SVN_NO_ERROR; |
---|
597 | } |
---|
598 | |
---|
599 | /* Handle reading from a compressed stream */ |
---|
600 | static svn_error_t * |
---|
601 | read_handler_gz(void *baton, char *buffer, apr_size_t *len) |
---|
602 | { |
---|
603 | struct zbaton *btn = baton; |
---|
604 | int zerr; |
---|
605 | |
---|
606 | if (btn->in == NULL) |
---|
607 | { |
---|
608 | btn->in = apr_palloc(btn->pool, sizeof(z_stream)); |
---|
609 | btn->in->zalloc = zalloc; |
---|
610 | btn->in->zfree = zfree; |
---|
611 | btn->in->opaque = btn->pool; |
---|
612 | btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE); |
---|
613 | btn->in->next_in = btn->read_buffer; |
---|
614 | btn->in->avail_in = ZBUFFER_SIZE; |
---|
615 | |
---|
616 | SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, |
---|
617 | &btn->in->avail_in, &btn->read_flush)); |
---|
618 | |
---|
619 | zerr = inflateInit(btn->in); |
---|
620 | SVN_ERR(zerr_to_svn_error(zerr, "inflateInit", btn->in)); |
---|
621 | } |
---|
622 | |
---|
623 | btn->in->next_out = (Bytef *) buffer; |
---|
624 | btn->in->avail_out = *len; |
---|
625 | |
---|
626 | while (btn->in->avail_out > 0) |
---|
627 | { |
---|
628 | if (btn->in->avail_in <= 0) |
---|
629 | { |
---|
630 | btn->in->avail_in = ZBUFFER_SIZE; |
---|
631 | btn->in->next_in = btn->read_buffer; |
---|
632 | SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, |
---|
633 | &btn->in->avail_in, &btn->read_flush)); |
---|
634 | } |
---|
635 | |
---|
636 | zerr = inflate(btn->in, btn->read_flush); |
---|
637 | if (zerr == Z_STREAM_END) |
---|
638 | break; |
---|
639 | else if (zerr != Z_OK) |
---|
640 | return zerr_to_svn_error(zerr, "inflate", btn->in); |
---|
641 | } |
---|
642 | |
---|
643 | *len -= btn->in->avail_out; |
---|
644 | return SVN_NO_ERROR; |
---|
645 | } |
---|
646 | |
---|
647 | /* Compress data and write it to the substream */ |
---|
648 | static svn_error_t * |
---|
649 | write_handler_gz(void *baton, const char *buffer, apr_size_t *len) |
---|
650 | { |
---|
651 | struct zbaton *btn = baton; |
---|
652 | apr_pool_t *subpool; |
---|
653 | void *write_buf; |
---|
654 | apr_size_t buf_size, write_len; |
---|
655 | int zerr; |
---|
656 | |
---|
657 | if (btn->out == NULL) |
---|
658 | { |
---|
659 | btn->out = apr_palloc(btn->pool, sizeof(z_stream)); |
---|
660 | btn->out->zalloc = zalloc; |
---|
661 | btn->out->zfree = zfree; |
---|
662 | btn->out->opaque = btn->pool; |
---|
663 | |
---|
664 | zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION); |
---|
665 | SVN_ERR(zerr_to_svn_error(zerr, "deflateInit", btn->out)); |
---|
666 | } |
---|
667 | |
---|
668 | /* The largest buffer we should need is 0.1% larger than the |
---|
669 | compressed data, + 12 bytes. This info comes from zlib.h. */ |
---|
670 | buf_size = *len + (*len / 1000) + 13; |
---|
671 | subpool = svn_pool_create(btn->pool); |
---|
672 | write_buf = apr_palloc(subpool, buf_size); |
---|
673 | |
---|
674 | btn->out->next_in = (Bytef *) buffer; /* Casting away const! */ |
---|
675 | btn->out->avail_in = *len; |
---|
676 | |
---|
677 | while (btn->out->avail_in > 0) |
---|
678 | { |
---|
679 | btn->out->next_out = write_buf; |
---|
680 | btn->out->avail_out = buf_size; |
---|
681 | |
---|
682 | zerr = deflate(btn->out, Z_NO_FLUSH); |
---|
683 | SVN_ERR(zerr_to_svn_error(zerr, "deflate", btn->out)); |
---|
684 | write_len = buf_size - btn->out->avail_out; |
---|
685 | if (write_len > 0) |
---|
686 | SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len)); |
---|
687 | } |
---|
688 | |
---|
689 | svn_pool_destroy(subpool); |
---|
690 | |
---|
691 | return SVN_NO_ERROR; |
---|
692 | } |
---|
693 | |
---|
694 | /* Handle flushing and closing the stream */ |
---|
695 | static svn_error_t * |
---|
696 | close_handler_gz(void *baton) |
---|
697 | { |
---|
698 | struct zbaton *btn = baton; |
---|
699 | int zerr; |
---|
700 | |
---|
701 | if (btn->in != NULL) |
---|
702 | { |
---|
703 | zerr = inflateEnd(btn->in); |
---|
704 | SVN_ERR(zerr_to_svn_error(zerr, "inflateEnd", btn->in)); |
---|
705 | } |
---|
706 | |
---|
707 | if (btn->out != NULL) |
---|
708 | { |
---|
709 | void *buf; |
---|
710 | apr_size_t write_len; |
---|
711 | |
---|
712 | buf = apr_palloc(btn->pool, ZBUFFER_SIZE); |
---|
713 | |
---|
714 | while (TRUE) |
---|
715 | { |
---|
716 | btn->out->next_out = buf; |
---|
717 | btn->out->avail_out = ZBUFFER_SIZE; |
---|
718 | |
---|
719 | zerr = deflate(btn->out, Z_FINISH); |
---|
720 | if (zerr != Z_STREAM_END && zerr != Z_OK) |
---|
721 | return zerr_to_svn_error(zerr, "deflate", btn->out); |
---|
722 | write_len = ZBUFFER_SIZE - btn->out->avail_out; |
---|
723 | if (write_len > 0) |
---|
724 | SVN_ERR(btn->write(btn->subbaton, buf, &write_len)); |
---|
725 | if (zerr == Z_STREAM_END) |
---|
726 | break; |
---|
727 | } |
---|
728 | |
---|
729 | zerr = deflateEnd(btn->out); |
---|
730 | SVN_ERR(zerr_to_svn_error(zerr, "deflateEnd", btn->out)); |
---|
731 | } |
---|
732 | |
---|
733 | if (btn->close != NULL) |
---|
734 | return btn->close(btn->subbaton); |
---|
735 | else |
---|
736 | return SVN_NO_ERROR; |
---|
737 | } |
---|
738 | |
---|
739 | |
---|
740 | svn_stream_t * |
---|
741 | svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool) |
---|
742 | { |
---|
743 | struct svn_stream_t *zstream; |
---|
744 | struct zbaton *baton; |
---|
745 | |
---|
746 | assert(stream != NULL); |
---|
747 | |
---|
748 | baton = apr_palloc(pool, sizeof(*baton)); |
---|
749 | baton->in = baton->out = NULL; |
---|
750 | baton->read = stream->read_fn; |
---|
751 | baton->write = stream->write_fn; |
---|
752 | baton->close = stream->close_fn; |
---|
753 | baton->subbaton = stream->baton; |
---|
754 | baton->pool = pool; |
---|
755 | baton->read_buffer = NULL; |
---|
756 | baton->read_flush = Z_SYNC_FLUSH; |
---|
757 | |
---|
758 | zstream = svn_stream_create(baton, pool); |
---|
759 | svn_stream_set_read(zstream, read_handler_gz); |
---|
760 | svn_stream_set_write(zstream, write_handler_gz); |
---|
761 | svn_stream_set_close(zstream, close_handler_gz); |
---|
762 | |
---|
763 | return zstream; |
---|
764 | } |
---|
765 | |
---|
766 | |
---|
767 | /* Checksummed stream support */ |
---|
768 | |
---|
769 | struct checksum_stream_baton |
---|
770 | { |
---|
771 | svn_checksum_ctx_t *read_ctx, *write_ctx; |
---|
772 | svn_checksum_t **read_checksum; /* Output value. */ |
---|
773 | svn_checksum_t **write_checksum; /* Output value. */ |
---|
774 | svn_stream_t *proxy; |
---|
775 | |
---|
776 | /* True if more data should be read when closing the stream. */ |
---|
777 | svn_boolean_t read_more; |
---|
778 | |
---|
779 | /* Pool to allocate read buffer and output values from. */ |
---|
780 | apr_pool_t *pool; |
---|
781 | }; |
---|
782 | |
---|
783 | static svn_error_t * |
---|
784 | read_handler_checksum(void *baton, char *buffer, apr_size_t *len) |
---|
785 | { |
---|
786 | struct checksum_stream_baton *btn = baton; |
---|
787 | apr_size_t saved_len = *len; |
---|
788 | |
---|
789 | SVN_ERR(svn_stream_read(btn->proxy, buffer, len)); |
---|
790 | |
---|
791 | if (btn->read_checksum) |
---|
792 | SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len)); |
---|
793 | |
---|
794 | if (saved_len != *len) |
---|
795 | btn->read_more = FALSE; |
---|
796 | |
---|
797 | return SVN_NO_ERROR; |
---|
798 | } |
---|
799 | |
---|
800 | |
---|
801 | static svn_error_t * |
---|
802 | write_handler_checksum(void *baton, const char *buffer, apr_size_t *len) |
---|
803 | { |
---|
804 | struct checksum_stream_baton *btn = baton; |
---|
805 | |
---|
806 | if (btn->write_checksum && *len > 0) |
---|
807 | SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len)); |
---|
808 | |
---|
809 | return svn_stream_write(btn->proxy, buffer, len); |
---|
810 | } |
---|
811 | |
---|
812 | |
---|
813 | static svn_error_t * |
---|
814 | close_handler_checksum(void *baton) |
---|
815 | { |
---|
816 | struct checksum_stream_baton *btn = baton; |
---|
817 | |
---|
818 | /* If we're supposed to drain the stream, do so before finalizing the |
---|
819 | checksum. */ |
---|
820 | if (btn->read_more) |
---|
821 | { |
---|
822 | char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE); |
---|
823 | apr_size_t len = SVN__STREAM_CHUNK_SIZE; |
---|
824 | |
---|
825 | do |
---|
826 | { |
---|
827 | SVN_ERR(read_handler_checksum(baton, buf, &len)); |
---|
828 | } |
---|
829 | while (btn->read_more); |
---|
830 | } |
---|
831 | |
---|
832 | if (btn->read_ctx) |
---|
833 | SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool)); |
---|
834 | |
---|
835 | if (btn->write_ctx) |
---|
836 | SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool)); |
---|
837 | |
---|
838 | return svn_stream_close(btn->proxy); |
---|
839 | } |
---|
840 | |
---|
841 | |
---|
842 | svn_stream_t * |
---|
843 | svn_stream_checksummed2(svn_stream_t *stream, |
---|
844 | svn_checksum_t **read_checksum, |
---|
845 | svn_checksum_t **write_checksum, |
---|
846 | svn_checksum_kind_t checksum_kind, |
---|
847 | svn_boolean_t read_all, |
---|
848 | apr_pool_t *pool) |
---|
849 | { |
---|
850 | svn_stream_t *s; |
---|
851 | struct checksum_stream_baton *baton; |
---|
852 | |
---|
853 | if (read_checksum == NULL && write_checksum == NULL) |
---|
854 | return stream; |
---|
855 | |
---|
856 | baton = apr_palloc(pool, sizeof(*baton)); |
---|
857 | if (read_checksum) |
---|
858 | baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool); |
---|
859 | else |
---|
860 | baton->read_ctx = NULL; |
---|
861 | |
---|
862 | if (write_checksum) |
---|
863 | baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool); |
---|
864 | else |
---|
865 | baton->write_ctx = NULL; |
---|
866 | |
---|
867 | baton->read_checksum = read_checksum; |
---|
868 | baton->write_checksum = write_checksum; |
---|
869 | baton->proxy = stream; |
---|
870 | baton->read_more = read_all; |
---|
871 | baton->pool = pool; |
---|
872 | |
---|
873 | s = svn_stream_create(baton, pool); |
---|
874 | svn_stream_set_read(s, read_handler_checksum); |
---|
875 | svn_stream_set_write(s, write_handler_checksum); |
---|
876 | svn_stream_set_close(s, close_handler_checksum); |
---|
877 | return s; |
---|
878 | } |
---|
879 | |
---|
880 | struct md5_stream_baton |
---|
881 | { |
---|
882 | const unsigned char **read_digest; |
---|
883 | const unsigned char **write_digest; |
---|
884 | svn_checksum_t *read_checksum; |
---|
885 | svn_checksum_t *write_checksum; |
---|
886 | svn_stream_t *proxy; |
---|
887 | apr_pool_t *pool; |
---|
888 | }; |
---|
889 | |
---|
890 | static svn_error_t * |
---|
891 | read_handler_md5(void *baton, char *buffer, apr_size_t *len) |
---|
892 | { |
---|
893 | struct md5_stream_baton *btn = baton; |
---|
894 | return svn_stream_read(btn->proxy, buffer, len); |
---|
895 | } |
---|
896 | |
---|
897 | static svn_error_t * |
---|
898 | write_handler_md5(void *baton, const char *buffer, apr_size_t *len) |
---|
899 | { |
---|
900 | struct md5_stream_baton *btn = baton; |
---|
901 | return svn_stream_write(btn->proxy, buffer, len); |
---|
902 | } |
---|
903 | |
---|
904 | static svn_error_t * |
---|
905 | close_handler_md5(void *baton) |
---|
906 | { |
---|
907 | struct md5_stream_baton *btn = baton; |
---|
908 | |
---|
909 | SVN_ERR(svn_stream_close(btn->proxy)); |
---|
910 | |
---|
911 | if (btn->read_digest) |
---|
912 | *btn->read_digest |
---|
913 | = apr_pmemdup(btn->pool, btn->read_checksum->digest, |
---|
914 | APR_MD5_DIGESTSIZE); |
---|
915 | |
---|
916 | if (btn->write_digest) |
---|
917 | *btn->write_digest |
---|
918 | = apr_pmemdup(btn->pool, btn->write_checksum->digest, |
---|
919 | APR_MD5_DIGESTSIZE); |
---|
920 | |
---|
921 | return SVN_NO_ERROR; |
---|
922 | } |
---|
923 | |
---|
924 | |
---|
925 | svn_stream_t * |
---|
926 | svn_stream_checksummed(svn_stream_t *stream, |
---|
927 | const unsigned char **read_digest, |
---|
928 | const unsigned char **write_digest, |
---|
929 | svn_boolean_t read_all, |
---|
930 | apr_pool_t *pool) |
---|
931 | { |
---|
932 | svn_stream_t *s; |
---|
933 | struct md5_stream_baton *baton; |
---|
934 | |
---|
935 | if (! read_digest && ! write_digest) |
---|
936 | return stream; |
---|
937 | |
---|
938 | baton = apr_palloc(pool, sizeof(*baton)); |
---|
939 | baton->read_digest = read_digest; |
---|
940 | baton->write_digest = write_digest; |
---|
941 | baton->pool = pool; |
---|
942 | |
---|
943 | /* Set BATON->proxy to a stream that will fill in BATON->read_checksum |
---|
944 | * and BATON->write_checksum (if we want them) when it is closed. */ |
---|
945 | baton->proxy |
---|
946 | = svn_stream_checksummed2(stream, |
---|
947 | read_digest ? &baton->read_checksum : NULL, |
---|
948 | write_digest ? &baton->write_checksum : NULL, |
---|
949 | svn_checksum_md5, |
---|
950 | read_all, pool); |
---|
951 | |
---|
952 | /* Create a stream that will forward its read/write/close operations to |
---|
953 | * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we |
---|
954 | * want them) after it closes BATON->proxy. */ |
---|
955 | s = svn_stream_create(baton, pool); |
---|
956 | svn_stream_set_read(s, read_handler_md5); |
---|
957 | svn_stream_set_write(s, write_handler_md5); |
---|
958 | svn_stream_set_close(s, close_handler_md5); |
---|
959 | return s; |
---|
960 | } |
---|
961 | |
---|
962 | |
---|
963 | |
---|
964 | |
---|
965 | /* Miscellaneous stream functions. */ |
---|
966 | struct stringbuf_stream_baton |
---|
967 | { |
---|
968 | svn_stringbuf_t *str; |
---|
969 | apr_size_t amt_read; |
---|
970 | }; |
---|
971 | |
---|
972 | static svn_error_t * |
---|
973 | read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len) |
---|
974 | { |
---|
975 | struct stringbuf_stream_baton *btn = baton; |
---|
976 | apr_size_t left_to_read = btn->str->len - btn->amt_read; |
---|
977 | |
---|
978 | *len = (*len > left_to_read) ? left_to_read : *len; |
---|
979 | memcpy(buffer, btn->str->data + btn->amt_read, *len); |
---|
980 | btn->amt_read += *len; |
---|
981 | return SVN_NO_ERROR; |
---|
982 | } |
---|
983 | |
---|
984 | static svn_error_t * |
---|
985 | write_handler_stringbuf(void *baton, const char *data, apr_size_t *len) |
---|
986 | { |
---|
987 | struct stringbuf_stream_baton *btn = baton; |
---|
988 | |
---|
989 | svn_stringbuf_appendbytes(btn->str, data, *len); |
---|
990 | return SVN_NO_ERROR; |
---|
991 | } |
---|
992 | |
---|
993 | svn_stream_t * |
---|
994 | svn_stream_from_stringbuf(svn_stringbuf_t *str, |
---|
995 | apr_pool_t *pool) |
---|
996 | { |
---|
997 | svn_stream_t *stream; |
---|
998 | struct stringbuf_stream_baton *baton; |
---|
999 | |
---|
1000 | if (! str) |
---|
1001 | return svn_stream_empty(pool); |
---|
1002 | |
---|
1003 | baton = apr_palloc(pool, sizeof(*baton)); |
---|
1004 | baton->str = str; |
---|
1005 | baton->amt_read = 0; |
---|
1006 | stream = svn_stream_create(baton, pool); |
---|
1007 | svn_stream_set_read(stream, read_handler_stringbuf); |
---|
1008 | svn_stream_set_write(stream, write_handler_stringbuf); |
---|
1009 | return stream; |
---|
1010 | } |
---|
1011 | |
---|
1012 | struct string_stream_baton |
---|
1013 | { |
---|
1014 | const svn_string_t *str; |
---|
1015 | apr_size_t amt_read; |
---|
1016 | }; |
---|
1017 | |
---|
1018 | static svn_error_t * |
---|
1019 | read_handler_string(void *baton, char *buffer, apr_size_t *len) |
---|
1020 | { |
---|
1021 | struct string_stream_baton *btn = baton; |
---|
1022 | apr_size_t left_to_read = btn->str->len - btn->amt_read; |
---|
1023 | |
---|
1024 | *len = (*len > left_to_read) ? left_to_read : *len; |
---|
1025 | memcpy(buffer, btn->str->data + btn->amt_read, *len); |
---|
1026 | btn->amt_read += *len; |
---|
1027 | return SVN_NO_ERROR; |
---|
1028 | } |
---|
1029 | |
---|
1030 | svn_stream_t * |
---|
1031 | svn_stream_from_string(const svn_string_t *str, |
---|
1032 | apr_pool_t *pool) |
---|
1033 | { |
---|
1034 | svn_stream_t *stream; |
---|
1035 | struct string_stream_baton *baton; |
---|
1036 | |
---|
1037 | if (! str) |
---|
1038 | return svn_stream_empty(pool); |
---|
1039 | |
---|
1040 | baton = apr_palloc(pool, sizeof(*baton)); |
---|
1041 | baton->str = str; |
---|
1042 | baton->amt_read = 0; |
---|
1043 | stream = svn_stream_create(baton, pool); |
---|
1044 | svn_stream_set_read(stream, read_handler_string); |
---|
1045 | return stream; |
---|
1046 | } |
---|
1047 | |
---|
1048 | |
---|
1049 | svn_error_t * |
---|
1050 | svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool) |
---|
1051 | { |
---|
1052 | apr_file_t *stdout_file; |
---|
1053 | apr_status_t apr_err; |
---|
1054 | |
---|
1055 | apr_err = apr_file_open_stdout(&stdout_file, pool); |
---|
1056 | if (apr_err) |
---|
1057 | return svn_error_wrap_apr(apr_err, "Can't open stdout"); |
---|
1058 | |
---|
1059 | *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool); |
---|
1060 | |
---|
1061 | return SVN_NO_ERROR; |
---|
1062 | } |
---|
1063 | |
---|
1064 | |
---|
1065 | svn_error_t * |
---|
1066 | svn_string_from_stream(svn_string_t **result, |
---|
1067 | svn_stream_t *stream, |
---|
1068 | apr_pool_t *result_pool, |
---|
1069 | apr_pool_t *scratch_pool) |
---|
1070 | { |
---|
1071 | svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE, |
---|
1072 | result_pool); |
---|
1073 | char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); |
---|
1074 | |
---|
1075 | while (1) |
---|
1076 | { |
---|
1077 | apr_size_t len = SVN__STREAM_CHUNK_SIZE; |
---|
1078 | |
---|
1079 | SVN_ERR(svn_stream_read(stream, buffer, &len)); |
---|
1080 | svn_stringbuf_appendbytes(work, buffer, len); |
---|
1081 | |
---|
1082 | if (len < SVN__STREAM_CHUNK_SIZE) |
---|
1083 | break; |
---|
1084 | } |
---|
1085 | |
---|
1086 | SVN_ERR(svn_stream_close(stream)); |
---|
1087 | |
---|
1088 | *result = apr_palloc(result_pool, sizeof(**result)); |
---|
1089 | (*result)->data = work->data; |
---|
1090 | (*result)->len = work->len; |
---|
1091 | |
---|
1092 | return SVN_NO_ERROR; |
---|
1093 | } |
---|