1 /*
2 * marshal.c : Marshalling routines for Subversion protocol
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
25
26 #include <assert.h>
27 #include <stdlib.h>
28
29 #define APR_WANT_STRFUNC
30 #include <apr_want.h>
31 #include <apr_general.h>
32 #include <apr_lib.h>
33 #include <apr_strings.h>
34
35 #include "svn_hash.h"
36 #include "svn_types.h"
37 #include "svn_string.h"
38 #include "svn_error.h"
39 #include "svn_pools.h"
40 #include "svn_ra_svn.h"
41 #include "svn_private_config.h"
42 #include "svn_ctype.h"
43 #include "svn_sorts.h"
44 #include "svn_time.h"
45
46 #include "ra_svn.h"
47
48 #include "private/svn_string_private.h"
49 #include "private/svn_dep_compat.h"
50 #include "private/svn_error_private.h"
51 #include "private/svn_subr_private.h"
52
53 #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
54
55 /* If we receive data that *claims* to be followed by a very long string,
56 * we should not trust that claim right away. But everything up to 1 MB
57 * should be too small to be instrumental for a DOS attack. */
58
59 #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
60
61 /* We don't use "words" longer than this in our protocol. The longest word
62 * we are currently using is only about 16 chars long but we leave room for
63 * longer future capability and command names. See read_item() to understand
64 * why MAX_WORD_LENGTH - 1 should be a multiple of 8.
65 */
66 #define MAX_WORD_LENGTH 25
67
68 /* The generic parsers will use the following value to limit the recursion
69 * depth to some reasonable value. The current protocol implementation
70 * actually uses only maximum item nesting level of around 5. So, there is
71 * plenty of headroom here.
72 */
73 #define ITEM_NESTING_LIMIT 64
74
75 /* The protocol words for booleans. */
76 static const svn_string_t str_true = SVN__STATIC_STRING("true");
77 static const svn_string_t str_false = SVN__STATIC_STRING("false");
78
79 /* Return the APR socket timeout to be used for the connection depending
80 * on whether there is a blockage handler or zero copy has been activated. */
81 static apr_interval_time_t
get_timeout(svn_ra_svn_conn_t * conn)82 get_timeout(svn_ra_svn_conn_t *conn)
83 {
84 return conn->block_handler ? 0 : -1;
85 }
86
87 /* --- Public / private API data conversion --- */
88
89 void
svn_ra_svn__to_public_item(svn_ra_svn_item_t * target,const svn_ra_svn__item_t * source,apr_pool_t * result_pool)90 svn_ra_svn__to_public_item(svn_ra_svn_item_t *target,
91 const svn_ra_svn__item_t *source,
92 apr_pool_t *result_pool)
93 {
94 target->kind = source->kind;
95 switch (source->kind)
96 {
97 case SVN_RA_SVN_STRING:
98 target->u.string = svn_string_dup(&source->u.string, result_pool);
99 break;
100 case SVN_RA_SVN_NUMBER:
101 target->u.number = source->u.number;
102 break;
103 case SVN_RA_SVN_WORD:
104 target->u.word = source->u.word.data;
105 break;
106 case SVN_RA_SVN_LIST:
107 target->u.list = svn_ra_svn__to_public_array(&source->u.list,
108 result_pool);
109 break;
110 }
111 }
112
113 apr_array_header_t *
svn_ra_svn__to_public_array(const svn_ra_svn__list_t * source,apr_pool_t * result_pool)114 svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
115 apr_pool_t *result_pool)
116 {
117 apr_array_header_t *result = apr_array_make(result_pool, source->nelts,
118 sizeof(svn_ra_svn_item_t));
119
120 int i;
121 for (i = 0; i < source->nelts; ++i)
122 {
123 svn_ra_svn_item_t *sub_target = apr_array_push(result);
124 svn_ra_svn__item_t *sub_source = &SVN_RA_SVN__LIST_ITEM(source, i);
125
126 svn_ra_svn__to_public_item(sub_target, sub_source, result_pool);
127 }
128
129 return result;
130 }
131
132 void
svn_ra_svn__to_private_item(svn_ra_svn__item_t * target,const svn_ra_svn_item_t * source,apr_pool_t * result_pool)133 svn_ra_svn__to_private_item(svn_ra_svn__item_t *target,
134 const svn_ra_svn_item_t *source,
135 apr_pool_t *result_pool)
136 {
137 target->kind = source->kind;
138 switch (source->kind)
139 {
140 case SVN_RA_SVN_STRING:
141 target->u.string = *source->u.string;
142 break;
143 case SVN_RA_SVN_NUMBER:
144 target->u.number = source->u.number;
145 break;
146 case SVN_RA_SVN_WORD:
147 target->u.word.data = source->u.word;
148 target->u.word.len = strlen(source->u.word);
149 break;
150 case SVN_RA_SVN_LIST:
151 target->u.list = *svn_ra_svn__to_private_array(source->u.list,
152 result_pool);
153 break;
154 }
155 }
156
157 svn_ra_svn__list_t *
svn_ra_svn__to_private_array(const apr_array_header_t * source,apr_pool_t * result_pool)158 svn_ra_svn__to_private_array(const apr_array_header_t *source,
159 apr_pool_t *result_pool)
160 {
161 int i;
162
163 svn_ra_svn__list_t *result = apr_pcalloc(result_pool, sizeof(*result));
164 result->nelts = source->nelts;
165 result->items = apr_palloc(result_pool,
166 source->nelts * sizeof(*result->items));
167
168 for (i = 0; i < source->nelts; ++i)
169 {
170 svn_ra_svn__item_t *sub_target = &result->items[i];
171 svn_ra_svn_item_t *sub_source = &APR_ARRAY_IDX(source, i,
172 svn_ra_svn_item_t);
173
174 svn_ra_svn__to_private_item(sub_target, sub_source, result_pool);
175 }
176
177 return result;
178 }
179
180 /* --- CONNECTION INITIALIZATION --- */
181
svn_ra_svn_create_conn5(apr_socket_t * sock,svn_stream_t * in_stream,svn_stream_t * out_stream,int compression_level,apr_size_t zero_copy_limit,apr_size_t error_check_interval,apr_uint64_t max_in,apr_uint64_t max_out,apr_pool_t * result_pool)182 svn_ra_svn_conn_t *svn_ra_svn_create_conn5(apr_socket_t *sock,
183 svn_stream_t *in_stream,
184 svn_stream_t *out_stream,
185 int compression_level,
186 apr_size_t zero_copy_limit,
187 apr_size_t error_check_interval,
188 apr_uint64_t max_in,
189 apr_uint64_t max_out,
190 apr_pool_t *result_pool)
191 {
192 svn_ra_svn_conn_t *conn;
193 void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
194 conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
195
196 assert((sock && !in_stream && !out_stream)
197 || (!sock && in_stream && out_stream));
198 #ifdef SVN_HAVE_SASL
199 conn->sock = sock;
200 conn->encrypted = FALSE;
201 #endif
202 conn->session = NULL;
203 conn->read_ptr = conn->read_buf;
204 conn->read_end = conn->read_buf;
205 conn->write_pos = 0;
206 conn->written_since_error_check = 0;
207 conn->error_check_interval = error_check_interval;
208 conn->may_check_for_error = error_check_interval == 0;
209 conn->max_in = max_in;
210 conn->current_in = 0;
211 conn->max_out = max_out;
212 conn->current_out = 0;
213 conn->block_handler = NULL;
214 conn->block_baton = NULL;
215 conn->capabilities = apr_hash_make(result_pool);
216 conn->compression_level = compression_level;
217 conn->zero_copy_limit = zero_copy_limit;
218 conn->pool = result_pool;
219
220 if (sock != NULL)
221 {
222 apr_sockaddr_t *sa;
223 conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
224 if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
225 && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
226 conn->remote_ip = NULL;
227 svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
228 }
229 else
230 {
231 conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
232 result_pool);
233 conn->remote_ip = NULL;
234 }
235
236 return conn;
237 }
238
239 svn_error_t *
svn_ra_svn_set_capabilities(svn_ra_svn_conn_t * conn,const apr_array_header_t * list)240 svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
241 const apr_array_header_t *list)
242 {
243 svn_ra_svn__list_t *internal
244 = svn_ra_svn__to_private_array(list, list->pool);
245 return svn_error_trace(svn_ra_svn__set_capabilities(conn, internal));
246 }
247
248 svn_error_t *
svn_ra_svn__set_capabilities(svn_ra_svn_conn_t * conn,const svn_ra_svn__list_t * list)249 svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
250 const svn_ra_svn__list_t *list)
251 {
252 int i;
253 svn_ra_svn__item_t *item;
254 const char *word;
255
256 for (i = 0; i < list->nelts; i++)
257 {
258 item = &SVN_RA_SVN__LIST_ITEM(list, i);
259 if (item->kind != SVN_RA_SVN_WORD)
260 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
261 _("Capability entry is not a word"));
262 word = apr_pstrmemdup(conn->pool, item->u.word.data, item->u.word.len);
263 apr_hash_set(conn->capabilities, word, item->u.word.len, word);
264 }
265 return SVN_NO_ERROR;
266 }
267
268 int
svn_ra_svn__svndiff_version(svn_ra_svn_conn_t * conn)269 svn_ra_svn__svndiff_version(svn_ra_svn_conn_t *conn)
270 {
271 /* If we don't want to use compression, use the non-compressing
272 * "version 0" implementation. */
273 if (svn_ra_svn_compression_level(conn) <= 0)
274 return 0;
275
276 /* Prefer SVNDIFF2 over SVNDIFF1. */
277 if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED))
278 return 2;
279 if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF1))
280 return 1;
281
282 /* The connection does not support SVNDIFF1/2; default to "version 0". */
283 return 0;
284 }
285
286 apr_pool_t *
svn_ra_svn__get_pool(svn_ra_svn_conn_t * conn)287 svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
288 {
289 return conn->pool;
290 }
291
292 svn_error_t *
svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t * conn,svn_delta_shim_callbacks_t * shim_callbacks)293 svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
294 svn_delta_shim_callbacks_t *shim_callbacks)
295 {
296 conn->shim_callbacks = shim_callbacks;
297 return SVN_NO_ERROR;
298 }
299
svn_ra_svn_has_capability(svn_ra_svn_conn_t * conn,const char * capability)300 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
301 const char *capability)
302 {
303 return (svn_hash_gets(conn->capabilities, capability) != NULL);
304 }
305
306 int
svn_ra_svn_compression_level(svn_ra_svn_conn_t * conn)307 svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
308 {
309 return conn->compression_level;
310 }
311
312 apr_size_t
svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t * conn)313 svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
314 {
315 return conn->zero_copy_limit;
316 }
317
svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t * conn)318 const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
319 {
320 return conn->remote_ip;
321 }
322
323 void
svn_ra_svn__set_block_handler(svn_ra_svn_conn_t * conn,ra_svn_block_handler_t handler,void * baton)324 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
325 ra_svn_block_handler_t handler,
326 void *baton)
327 {
328 conn->block_handler = handler;
329 conn->block_baton = baton;
330 svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
331 }
332
svn_ra_svn__data_available(svn_ra_svn_conn_t * conn,svn_boolean_t * data_available)333 svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
334 svn_boolean_t *data_available)
335 {
336 return svn_ra_svn__stream_data_available(conn->stream, data_available);
337 }
338
339 void
svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t * conn)340 svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t *conn)
341 {
342 conn->current_in = 0;
343 conn->current_out = 0;
344 }
345
346
347 /* --- WRITE BUFFER MANAGEMENT --- */
348
349 /* Return an error object if CONN exceeded its send or receive limits. */
350 static svn_error_t *
check_io_limits(svn_ra_svn_conn_t * conn)351 check_io_limits(svn_ra_svn_conn_t *conn)
352 {
353 if (conn->max_in && (conn->current_in > conn->max_in))
354 return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
355 "The client request size exceeds the "
356 "configured limit");
357
358 if (conn->max_out && (conn->current_out > conn->max_out))
359 return svn_error_create(SVN_ERR_RA_SVN_RESPONSE_SIZE, NULL,
360 "The server response size exceeds the "
361 "configured limit");
362
363 return SVN_NO_ERROR;
364 }
365
366 /* Write data to socket or output file as appropriate. */
writebuf_output(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * data,apr_size_t len)367 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
368 const char *data, apr_size_t len)
369 {
370 const char *end = data + len;
371 apr_size_t count;
372 apr_pool_t *subpool = NULL;
373 svn_ra_svn__session_baton_t *session = conn->session;
374
375 /* Limit the size of the response, if a limit has been configured.
376 * This is to limit the server load in case users e.g. accidentally ran
377 * an export on the root folder. */
378 conn->current_out += len;
379 SVN_ERR(check_io_limits(conn));
380
381 while (data < end)
382 {
383 count = end - data;
384
385 if (session && session->callbacks && session->callbacks->cancel_func)
386 SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
387
388 SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
389 if (count == 0)
390 {
391 if (!subpool)
392 subpool = svn_pool_create(pool);
393 else
394 svn_pool_clear(subpool);
395 SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
396 }
397 data += count;
398
399 if (session)
400 {
401 const svn_ra_callbacks2_t *cb = session->callbacks;
402 session->bytes_written += count;
403
404 if (cb && cb->progress_func)
405 (cb->progress_func)(session->bytes_written + session->bytes_read,
406 -1, cb->progress_baton, subpool);
407 }
408 }
409
410 conn->written_since_error_check += len;
411 conn->may_check_for_error
412 = conn->written_since_error_check >= conn->error_check_interval;
413
414 if (subpool)
415 svn_pool_destroy(subpool);
416 return SVN_NO_ERROR;
417 }
418
419 /* Write data from the write buffer out to the socket. */
writebuf_flush(svn_ra_svn_conn_t * conn,apr_pool_t * pool)420 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
421 {
422 apr_size_t write_pos = conn->write_pos;
423
424 /* Clear conn->write_pos first in case the block handler does a read. */
425 conn->write_pos = 0;
426 SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
427 return SVN_NO_ERROR;
428 }
429
writebuf_write(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * data,apr_size_t len)430 static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
431 const char *data, apr_size_t len)
432 {
433 /* data >= 8k is sent immediately */
434 if (len >= sizeof(conn->write_buf) / 2)
435 {
436 if (conn->write_pos > 0)
437 SVN_ERR(writebuf_flush(conn, pool));
438
439 return writebuf_output(conn, pool, data, len);
440 }
441
442 /* ensure room for the data to add */
443 if (conn->write_pos + len > sizeof(conn->write_buf))
444 SVN_ERR(writebuf_flush(conn, pool));
445
446 /* buffer the new data block as well */
447 memcpy(conn->write_buf + conn->write_pos, data, len);
448 conn->write_pos += len;
449
450 return SVN_NO_ERROR;
451 }
452
453 /* Write STRING_LITERAL, which is a string literal argument.
454
455 Note: The purpose of the empty string "" in the macro definition is to
456 assert that STRING_LITERAL is in fact a string literal. Otherwise, the
457 string concatenation attempt should produce a compile-time error. */
458 #define writebuf_write_literal(conn, pool, string_literal) \
459 writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
460
461 static APR_INLINE svn_error_t *
writebuf_writechar(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char data)462 writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
463 {
464 if (conn->write_pos < sizeof(conn->write_buf))
465 {
466 conn->write_buf[conn->write_pos] = data;
467 conn->write_pos++;
468
469 return SVN_NO_ERROR;
470 }
471 else
472 {
473 char temp = data;
474 return writebuf_write(conn, pool, &temp, 1);
475 }
476 }
477
478 /* --- READ BUFFER MANAGEMENT --- */
479
480 /* Read bytes into DATA until either the read buffer is empty or
481 * we reach END. */
readbuf_drain(svn_ra_svn_conn_t * conn,char * data,char * end)482 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
483 {
484 apr_ssize_t buflen, copylen;
485
486 buflen = conn->read_end - conn->read_ptr;
487 copylen = (buflen < end - data) ? buflen : end - data;
488 memcpy(data, conn->read_ptr, copylen);
489 conn->read_ptr += copylen;
490 return data + copylen;
491 }
492
493 /* Read data from socket or input file as appropriate. */
readbuf_input(svn_ra_svn_conn_t * conn,char * data,apr_size_t * len,apr_pool_t * pool)494 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
495 apr_size_t *len, apr_pool_t *pool)
496 {
497 svn_ra_svn__session_baton_t *session = conn->session;
498
499 /* First, give the user a chance to cancel the request before we do. */
500 if (session && session->callbacks && session->callbacks->cancel_func)
501 SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
502
503 /* Limit our memory usage, if a limit has been configured. Note that
504 * we first read the whole request into memory before process it. */
505 SVN_ERR(check_io_limits(conn));
506
507 /* Actually fill the buffer. */
508 SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
509 if (*len == 0)
510 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
511 conn->current_in += *len;
512
513 if (session)
514 {
515 const svn_ra_callbacks2_t *cb = session->callbacks;
516 session->bytes_read += *len;
517
518 if (cb && cb->progress_func)
519 (cb->progress_func)(session->bytes_read + session->bytes_written,
520 -1, cb->progress_baton, pool);
521 }
522
523 return SVN_NO_ERROR;
524 }
525
526 /* Treat the next LEN input bytes from CONN as "read" */
readbuf_skip(svn_ra_svn_conn_t * conn,apr_uint64_t len)527 static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
528 {
529 do
530 {
531 apr_size_t buflen = conn->read_end - conn->read_ptr;
532 apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
533 conn->read_ptr += copylen;
534 len -= copylen;
535 if (len == 0)
536 break;
537
538 buflen = sizeof(conn->read_buf);
539 SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
540 if (buflen == 0)
541 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
542
543 conn->read_end = conn->read_buf + buflen;
544 conn->read_ptr = conn->read_buf;
545 }
546 while (len > 0);
547
548 return SVN_NO_ERROR;
549 }
550
551 /* Read data from the socket into the read buffer, which must be empty. */
readbuf_fill(svn_ra_svn_conn_t * conn,apr_pool_t * pool)552 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
553 {
554 apr_size_t len;
555
556 SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
557
558 /* Make sure we tell the other side everything we have to say before
559 * reading / waiting for an answer. */
560 if (conn->write_pos)
561 SVN_ERR(writebuf_flush(conn, pool));
562
563 /* Fill (some of the) buffer. */
564 len = sizeof(conn->read_buf);
565 SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
566 conn->read_ptr = conn->read_buf;
567 conn->read_end = conn->read_buf + len;
568 return SVN_NO_ERROR;
569 }
570
571 /* This is a hot function calling a cold function. GCC and others tend to
572 * inline the cold sub-function instead of this hot one. Therefore, be
573 * very insistent on lining this one. It is not a correctness issue, though.
574 */
575 static SVN__FORCE_INLINE svn_error_t *
readbuf_getchar(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char * result)576 readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
577 {
578 if (conn->read_ptr == conn->read_end)
579 SVN_ERR(readbuf_fill(conn, pool));
580 *result = *conn->read_ptr++;
581 return SVN_NO_ERROR;
582 }
583
readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char * result)584 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
585 apr_pool_t *pool,
586 char *result)
587 {
588 do
589 SVN_ERR(readbuf_getchar(conn, pool, result));
590 while (svn_iswhitespace(*result));
591 return SVN_NO_ERROR;
592 }
593
594 /* Read the next LEN bytes from CONN and copy them to *DATA. */
readbuf_read(svn_ra_svn_conn_t * conn,apr_pool_t * pool,char * data,apr_size_t len)595 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
596 char *data, apr_size_t len)
597 {
598 char *end = data + len;
599 apr_size_t count;
600
601 /* Copy in an appropriate amount of data from the buffer. */
602 data = readbuf_drain(conn, data, end);
603
604 /* Read large chunks directly into buffer. */
605 while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
606 {
607 SVN_ERR(writebuf_flush(conn, pool));
608 count = end - data;
609 SVN_ERR(readbuf_input(conn, data, &count, pool));
610 data += count;
611 }
612
613 while (end > data)
614 {
615 /* The remaining amount to read is small; fill the buffer and
616 * copy from that. */
617 SVN_ERR(readbuf_fill(conn, pool));
618 data = readbuf_drain(conn, data, end);
619 }
620
621 return SVN_NO_ERROR;
622 }
623
readbuf_skip_leading_garbage(svn_ra_svn_conn_t * conn,apr_pool_t * pool)624 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
625 apr_pool_t *pool)
626 {
627 char buf[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */
628 const char *p, *end;
629 apr_size_t len;
630 svn_boolean_t lparen = FALSE;
631
632 SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
633 while (1)
634 {
635 /* Read some data directly from the connection input source. */
636 len = sizeof(buf);
637 SVN_ERR(readbuf_input(conn, buf, &len, pool));
638 end = buf + len;
639
640 /* Scan the data for '(' WS with a very simple state machine. */
641 for (p = buf; p < end; p++)
642 {
643 if (lparen && svn_iswhitespace(*p))
644 break;
645 else
646 lparen = (*p == '(');
647 }
648 if (p < end)
649 break;
650 }
651
652 /* p now points to the whitespace just after the left paren. Fake
653 * up the left paren and then copy what we have into the read
654 * buffer. */
655 conn->read_buf[0] = '(';
656 memcpy(conn->read_buf + 1, p, end - p);
657 conn->read_ptr = conn->read_buf;
658 conn->read_end = conn->read_buf + 1 + (end - p);
659 return SVN_NO_ERROR;
660 }
661
662 /* --- WRITING DATA ITEMS --- */
663
write_number(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_uint64_t number,char follow)664 static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
665 apr_uint64_t number, char follow)
666 {
667 apr_size_t written;
668
669 /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
670 * svn__ui64toa will always append. */
671 if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
672 SVN_ERR(writebuf_flush(conn, pool));
673
674 written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
675 conn->write_buf[conn->write_pos + written] = follow;
676 conn->write_pos += written + 1;
677
678 return SVN_NO_ERROR;
679 }
680
681 svn_error_t *
svn_ra_svn__write_number(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_uint64_t number)682 svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
683 apr_pool_t *pool,
684 apr_uint64_t number)
685 {
686 return write_number(conn, pool, number, ' ');
687 }
688
689 /* Write string S of length LEN to TARGET and return the first position
690 after the written data.
691
692 NOTE: This function assumes that TARGET has enough room for S, the LEN
693 prefix and the required separators. The available buffer size
694 should be SVN_INT64_BUFFER_SIZE + LEN + 1 to avoid any chance of
695 overflow.
696 */
697 static char *
write_ncstring_quick(char * target,const char * s,apr_size_t len)698 write_ncstring_quick(char *target,
699 const char *s,
700 apr_size_t len)
701 {
702 /* Write string length. */
703 if (len < 10)
704 {
705 *target = (char)(len + '0');
706 target++;
707 }
708 else
709 {
710 target += svn__ui64toa(target, len);
711 }
712
713 /* Separator & contents. */
714 target[0] = ':';
715 memcpy(target + 1, s, len);
716 target[len + 1] = ' ';
717
718 /* First location after the string. */
719 return target + len + 2;
720 }
721
722
723 static svn_error_t *
svn_ra_svn__write_ncstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * s,apr_size_t len)724 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
725 apr_pool_t *pool,
726 const char *s,
727 apr_size_t len)
728 {
729 /* Apart from LEN bytes of string contents, we need room for a number,
730 a colon and a space. */
731 apr_size_t max_fill = sizeof(conn->write_buf) - SVN_INT64_BUFFER_SIZE - 2;
732
733 /* In most cases, there is enough left room in the WRITE_BUF
734 the we can serialize directly into it. On platforms with
735 segmented memory, LEN might actually be close to APR_SIZE_MAX.
736 Blindly doing arithmetic on it might cause an overflow. */
737 if ((len <= max_fill) && (conn->write_pos <= max_fill - len))
738 {
739 /* Quick path. */
740 conn->write_pos = write_ncstring_quick(conn->write_buf
741 + conn->write_pos, s, len)
742 - conn->write_buf;
743 }
744 else
745 {
746 /* Slower fallback code. */
747 SVN_ERR(write_number(conn, pool, len, ':'));
748
749 SVN_ERR(writebuf_write(conn, pool, s, len));
750 SVN_ERR(writebuf_writechar(conn, pool, ' '));
751 }
752
753 return SVN_NO_ERROR;
754 }
755
756 svn_error_t *
svn_ra_svn__write_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)757 svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
758 apr_pool_t *pool,
759 const svn_string_t *str)
760 {
761 SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
762 return SVN_NO_ERROR;
763 }
764
765 svn_error_t *
svn_ra_svn__write_cstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * s)766 svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
767 apr_pool_t *pool,
768 const char *s)
769 {
770 SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
771 return SVN_NO_ERROR;
772 }
773
774 svn_error_t *
svn_ra_svn__write_word(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * word)775 svn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
776 apr_pool_t *pool,
777 const char *word)
778 {
779 SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
780 SVN_ERR(writebuf_writechar(conn, pool, ' '));
781
782 return SVN_NO_ERROR;
783 }
784
785 svn_error_t *
svn_ra_svn__write_boolean(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_boolean_t value)786 svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
787 apr_pool_t *pool,
788 svn_boolean_t value)
789 {
790 if (value)
791 SVN_ERR(writebuf_write_literal(conn, pool, "true "));
792 else
793 SVN_ERR(writebuf_write_literal(conn, pool, "false "));
794
795 return SVN_NO_ERROR;
796 }
797
798 svn_error_t *
svn_ra_svn__write_proplist(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_hash_t * props)799 svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
800 apr_pool_t *pool,
801 apr_hash_t *props)
802 {
803 apr_hash_index_t *hi;
804 const char *propname;
805 svn_string_t *propval;
806 apr_size_t len;
807
808 /* One might use an iterpool here but that would only be used when the
809 send buffer gets flushed and only by the CONN's progress callback.
810 That should happen at most once for typical prop lists and even then
811 use only a few bytes at best.
812 */
813 if (props)
814 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
815 {
816 apr_hash_this(hi, (const void **)&propname,
817 (apr_ssize_t *)&len,
818 (void **)&propval);
819
820 SVN_ERR(svn_ra_svn__start_list(conn, pool));
821 SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len));
822 SVN_ERR(svn_ra_svn__write_string(conn, pool, propval));
823 SVN_ERR(svn_ra_svn__end_list(conn, pool));
824 }
825
826 return SVN_NO_ERROR;
827 }
828
829 svn_error_t *
svn_ra_svn__start_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)830 svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
831 apr_pool_t *pool)
832 {
833 if (conn->write_pos + 2 <= sizeof(conn->write_buf))
834 {
835 conn->write_buf[conn->write_pos] = '(';
836 conn->write_buf[conn->write_pos+1] = ' ';
837 conn->write_pos += 2;
838 return SVN_NO_ERROR;
839 }
840
841 return writebuf_write(conn, pool, "( ", 2);
842 }
843
844 svn_error_t *
svn_ra_svn__end_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)845 svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
846 apr_pool_t *pool)
847 {
848 if (conn->write_pos + 2 <= sizeof(conn->write_buf))
849 {
850 conn->write_buf[conn->write_pos] = ')';
851 conn->write_buf[conn->write_pos+1] = ' ';
852 conn->write_pos += 2;
853 return SVN_NO_ERROR;
854 }
855
856 return writebuf_write(conn, pool, ") ", 2);
857 }
858
859 svn_error_t *
svn_ra_svn__flush(svn_ra_svn_conn_t * conn,apr_pool_t * pool)860 svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
861 apr_pool_t *pool)
862 {
863 SVN_ERR(writebuf_flush(conn, pool));
864 conn->may_check_for_error = TRUE;
865
866 return SVN_NO_ERROR;
867 }
868
869 /* --- WRITING TUPLES --- */
870
871 static svn_error_t *
vwrite_tuple_cstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)872 vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
873 {
874 const char *cstr = va_arg(*ap, const char *);
875 SVN_ERR_ASSERT(cstr);
876 return svn_ra_svn__write_cstring(conn, pool, cstr);
877 }
878
879 static svn_error_t *
vwrite_tuple_cstring_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)880 vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
881 {
882 const char *cstr = va_arg(*ap, const char *);
883 return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
884 }
885
886 static svn_error_t *
vwrite_tuple_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)887 vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
888 {
889 const svn_string_t *str = va_arg(*ap, const svn_string_t *);
890 SVN_ERR_ASSERT(str);
891 return svn_ra_svn__write_string(conn, pool, str);
892 }
893
894 static svn_error_t *
vwrite_tuple_string_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)895 vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
896 {
897 const svn_string_t *str = va_arg(*ap, const svn_string_t *);
898 return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
899 }
900
901 static svn_error_t *
vwrite_tuple_word(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)902 vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
903 {
904 const char *cstr = va_arg(*ap, const char *);
905 SVN_ERR_ASSERT(cstr);
906 return svn_ra_svn__write_word(conn, pool, cstr);
907 }
908
909 static svn_error_t *
vwrite_tuple_word_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)910 vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
911 {
912 const char *cstr = va_arg(*ap, const char *);
913 return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
914 }
915
916 static svn_error_t *
vwrite_tuple_revision(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)917 vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
918 {
919 svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
920 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
921 return svn_ra_svn__write_number(conn, pool, rev);
922 }
923
924 static svn_error_t *
vwrite_tuple_revision_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)925 vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
926 {
927 svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
928 return SVN_IS_VALID_REVNUM(rev)
929 ? svn_ra_svn__write_number(conn, pool, rev)
930 : SVN_NO_ERROR;
931 }
932
933 static svn_error_t *
vwrite_tuple_number(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)934 vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
935 {
936 return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
937 }
938
939 static svn_error_t *
vwrite_tuple_boolean(svn_ra_svn_conn_t * conn,apr_pool_t * pool,va_list * ap)940 vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
941 {
942 return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
943 }
944
945 static svn_error_t *
write_tuple_cstring(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * cstr)946 write_tuple_cstring(svn_ra_svn_conn_t *conn,
947 apr_pool_t *pool,
948 const char *cstr)
949 {
950 SVN_ERR_ASSERT(cstr);
951 return svn_ra_svn__write_cstring(conn, pool, cstr);
952 }
953
954 static svn_error_t *
write_tuple_cstring_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * cstr)955 write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
956 apr_pool_t *pool,
957 const char *cstr)
958 {
959 return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
960 }
961
962 static svn_error_t *
write_tuple_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)963 write_tuple_string(svn_ra_svn_conn_t *conn,
964 apr_pool_t *pool,
965 const svn_string_t *str)
966 {
967 SVN_ERR_ASSERT(str);
968 return svn_ra_svn__write_string(conn, pool, str);
969 }
970
971 static svn_error_t *
write_tuple_string_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)972 write_tuple_string_opt(svn_ra_svn_conn_t *conn,
973 apr_pool_t *pool,
974 const svn_string_t *str)
975 {
976 return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
977 }
978
979 /* Optimized sending code for the "(s?)" pattern. */
980 static svn_error_t *
write_tuple_string_opt_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * str)981 write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
982 apr_pool_t *pool,
983 const svn_string_t *str)
984 {
985 apr_size_t max_fill;
986
987 /* Special case. */
988 if (!str)
989 return writebuf_write(conn, pool, "( ) ", 4);
990
991 /* If this how far we can fill the WRITE_BUF with string data and still
992 guarantee that the length info will fit in as well. */
993 max_fill = sizeof(conn->write_buf)
994 - 2 /* open list */
995 - SVN_INT64_BUFFER_SIZE /* string length + separator */
996 - 2; /* close list */
997
998 /* On platforms with segmented memory, STR->LEN might actually be
999 close to APR_SIZE_MAX. Blindly doing arithmetic on it might
1000 cause an overflow. */
1001 if ((str->len <= max_fill) && (conn->write_pos <= max_fill - str->len))
1002 {
1003 /* Quick path. */
1004 /* Open list. */
1005 char *p = conn->write_buf + conn->write_pos;
1006 p[0] = '(';
1007 p[1] = ' ';
1008
1009 /* Write string. */
1010 p = write_ncstring_quick(p + 2, str->data, str->len);
1011
1012 /* Close list. */
1013 p[0] = ')';
1014 p[1] = ' ';
1015 conn->write_pos = p + 2 - conn->write_buf;
1016 }
1017 else
1018 {
1019 /* Standard code path (fallback). */
1020 SVN_ERR(svn_ra_svn__start_list(conn, pool));
1021 SVN_ERR(svn_ra_svn__write_string(conn, pool, str));
1022 SVN_ERR(svn_ra_svn__end_list(conn, pool));
1023 }
1024
1025 return SVN_NO_ERROR;
1026 }
1027
1028 static svn_error_t *
write_tuple_start_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)1029 write_tuple_start_list(svn_ra_svn_conn_t *conn,
1030 apr_pool_t *pool)
1031 {
1032 return svn_ra_svn__start_list(conn, pool);
1033 }
1034
1035 static svn_error_t *
write_tuple_end_list(svn_ra_svn_conn_t * conn,apr_pool_t * pool)1036 write_tuple_end_list(svn_ra_svn_conn_t *conn,
1037 apr_pool_t *pool)
1038 {
1039 return svn_ra_svn__end_list(conn, pool);
1040 }
1041
1042 static svn_error_t *
write_tuple_revision(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)1043 write_tuple_revision(svn_ra_svn_conn_t *conn,
1044 apr_pool_t *pool,
1045 svn_revnum_t rev)
1046 {
1047 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
1048 return svn_ra_svn__write_number(conn, pool, rev);
1049 }
1050
1051 static svn_error_t *
write_tuple_revision_opt(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)1052 write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
1053 apr_pool_t *pool,
1054 svn_revnum_t rev)
1055 {
1056 return SVN_IS_VALID_REVNUM(rev)
1057 ? svn_ra_svn__write_number(conn, pool, rev)
1058 : SVN_NO_ERROR;
1059 }
1060
1061 static svn_error_t *
write_tuple_boolean(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_boolean_t value)1062 write_tuple_boolean(svn_ra_svn_conn_t *conn,
1063 apr_pool_t *pool,
1064 svn_boolean_t value)
1065 {
1066 return svn_ra_svn__write_boolean(conn, pool, value);
1067 }
1068
1069 static svn_error_t *
write_tuple_depth(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_depth_t depth)1070 write_tuple_depth(svn_ra_svn_conn_t *conn,
1071 apr_pool_t *pool,
1072 svn_depth_t depth)
1073 {
1074 return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
1075 }
1076
1077
1078 static svn_error_t *
write_cmd_add_node(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,const char * copy_path,svn_revnum_t copy_rev)1079 write_cmd_add_node(svn_ra_svn_conn_t *conn,
1080 apr_pool_t *pool,
1081 const char *path,
1082 const svn_string_t *parent_token,
1083 const svn_string_t *token,
1084 const char *copy_path,
1085 svn_revnum_t copy_rev)
1086 {
1087 SVN_ERR(write_tuple_cstring(conn, pool, path));
1088 SVN_ERR(write_tuple_string(conn, pool, parent_token));
1089 SVN_ERR(write_tuple_string(conn, pool, token));
1090 SVN_ERR(write_tuple_start_list(conn, pool));
1091 SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
1092 SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
1093 SVN_ERR(write_tuple_end_list(conn, pool));
1094
1095 return SVN_NO_ERROR;
1096 }
1097
1098 static svn_error_t *
write_cmd_open_node(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,svn_revnum_t rev)1099 write_cmd_open_node(svn_ra_svn_conn_t *conn,
1100 apr_pool_t *pool,
1101 const char *path,
1102 const svn_string_t *parent_token,
1103 const svn_string_t *token,
1104 svn_revnum_t rev)
1105 {
1106 SVN_ERR(write_tuple_cstring(conn, pool, path));
1107 SVN_ERR(write_tuple_string(conn, pool, parent_token));
1108 SVN_ERR(write_tuple_string(conn, pool, token));
1109 SVN_ERR(write_tuple_start_list(conn, pool));
1110 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1111 SVN_ERR(write_tuple_end_list(conn, pool));
1112
1113 return SVN_NO_ERROR;
1114 }
1115
1116 static svn_error_t *
write_cmd_change_node_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * name,const svn_string_t * value)1117 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
1118 apr_pool_t *pool,
1119 const svn_string_t *token,
1120 const char *name,
1121 const svn_string_t *value)
1122 {
1123 SVN_ERR(write_tuple_string(conn, pool, token));
1124 SVN_ERR(write_tuple_cstring(conn, pool, name));
1125 SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
1126
1127 return SVN_NO_ERROR;
1128 }
1129
1130 static svn_error_t *
write_cmd_absent_node(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * token)1131 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
1132 apr_pool_t *pool,
1133 const char *path,
1134 const svn_string_t *token)
1135 {
1136 SVN_ERR(write_tuple_cstring(conn, pool, path));
1137 SVN_ERR(write_tuple_string(conn, pool, token));
1138
1139 return SVN_NO_ERROR;
1140 }
1141
1142
1143
1144
vwrite_tuple(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,va_list * ap)1145 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1146 const char *fmt, va_list *ap)
1147 {
1148 svn_boolean_t opt = FALSE;
1149
1150 if (*fmt == '!')
1151 fmt++;
1152 else
1153 SVN_ERR(svn_ra_svn__start_list(conn, pool));
1154 for (; *fmt; fmt++)
1155 {
1156 if (*fmt == 'c')
1157 SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
1158 : vwrite_tuple_cstring(conn, pool, ap));
1159 else if (*fmt == 's')
1160 SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
1161 : vwrite_tuple_string(conn, pool, ap));
1162 else if (*fmt == '(' && !opt)
1163 {
1164 /* Optional sub-tuples are not supported.
1165 * If OPT was set, we would fall through to the malfunction call. */
1166 SVN_ERR(write_tuple_start_list(conn, pool));
1167 }
1168 else if (*fmt == ')')
1169 {
1170 SVN_ERR(write_tuple_end_list(conn, pool));
1171
1172 /* OPT could not have been set when opening the list (see above),
1173 * hence this is correct and handles nested tuples just fine. */
1174 opt = FALSE;
1175 }
1176 else if (*fmt == '?')
1177 opt = TRUE;
1178 else if (*fmt == 'w')
1179 SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
1180 : vwrite_tuple_word(conn, pool, ap));
1181 else if (*fmt == 'r')
1182 SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
1183 : vwrite_tuple_revision(conn, pool, ap));
1184 else if (*fmt == 'n' && !opt)
1185 SVN_ERR(vwrite_tuple_number(conn, pool, ap));
1186 else if (*fmt == 'b' && !opt)
1187 SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
1188 else if (*fmt == '!' && !*(fmt + 1))
1189 return SVN_NO_ERROR;
1190 else
1191 SVN_ERR_MALFUNCTION();
1192 }
1193 SVN_ERR(svn_ra_svn__end_list(conn, pool));
1194 return SVN_NO_ERROR;
1195 }
1196
1197 svn_error_t *
svn_ra_svn__write_tuple(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)1198 svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
1199 apr_pool_t *pool,
1200 const char *fmt, ...)
1201 {
1202 svn_error_t *err;
1203 va_list ap;
1204
1205 va_start(ap, fmt);
1206 err = vwrite_tuple(conn, pool, fmt, &ap);
1207 va_end(ap);
1208 return err;
1209 }
1210
1211 /* --- READING DATA ITEMS --- */
1212
1213 /* Read LEN bytes from CONN into already-allocated structure ITEM.
1214 * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
1215 * data is allocated in POOL. */
read_string(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_ra_svn__item_t * item,apr_uint64_t len64)1216 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1217 svn_ra_svn__item_t *item, apr_uint64_t len64)
1218 {
1219 apr_size_t len = (apr_size_t)len64;
1220 apr_size_t readbuf_len;
1221 char *dest;
1222 apr_size_t buflen;
1223
1224 /* We can't store strings longer than the maximum size of apr_size_t,
1225 * so check before using the truncated value. */
1226 if (len64 > APR_SIZE_MAX)
1227 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1228 _("String length larger than maximum"));
1229
1230 buflen = conn->read_end - conn->read_ptr;
1231 /* Shorter strings can be copied directly from the read buffer. */
1232 if (len <= buflen)
1233 {
1234 item->kind = SVN_RA_SVN_STRING;
1235 item->u.string.data = apr_pstrmemdup(pool, conn->read_ptr, len);
1236 item->u.string.len = len;
1237 conn->read_ptr += len;
1238 }
1239 else
1240 {
1241 svn_stringbuf_t *stringbuf;
1242
1243 /* Don't even attempt to read anything that exceeds the I/O limit.
1244 * So, we can terminate the transfer at an early point, saving
1245 * everybody's time and resources. */
1246 if (conn->max_in && (conn->max_in < len64))
1247 return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
1248 "The client request size exceeds the "
1249 "configured limit");
1250
1251 /* Read the string in chunks. The chunk size is large enough to avoid
1252 * re-allocation in typical cases, and small enough to ensure we do
1253 * not pre-allocate an unreasonable amount of memory if (perhaps due
1254 * to network data corruption or a DOS attack), we receive a bogus
1255 * claim that a very long string is going to follow. In that case, we
1256 * start small and wait for all that data to actually show up. This
1257 * does not fully prevent DOS attacks but makes them harder (you have
1258 * to actually send gigabytes of data). */
1259 stringbuf = svn_stringbuf_create_empty(pool);
1260
1261 /* Read string data directly into the string structure.
1262 * Do it iteratively. */
1263 do
1264 {
1265 /* Determine length of chunk to read and re-alloc the buffer. */
1266 readbuf_len
1267 = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
1268 ? len
1269 : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
1270
1271 svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
1272 dest = stringbuf->data + stringbuf->len;
1273
1274 /* read data & update length info */
1275 SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
1276
1277 stringbuf->len += readbuf_len;
1278 len -= readbuf_len;
1279 }
1280 while (len);
1281
1282 /* zero-terminate the string */
1283 stringbuf->data[stringbuf->len] = '\0';
1284
1285 /* Return the string properly wrapped into an RA_SVN item. */
1286 item->kind = SVN_RA_SVN_STRING;
1287 item->u.string.data = stringbuf->data;
1288 item->u.string.len = stringbuf->len;
1289 }
1290
1291 return SVN_NO_ERROR;
1292 }
1293
1294 /* Given the first non-whitespace character FIRST_CHAR, read an item
1295 * into the already allocated structure ITEM. LEVEL should be set
1296 * to 0 for the first call and is used to enforce a recursion limit
1297 * on the parser. */
read_item(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_ra_svn__item_t * item,char first_char,int level)1298 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1299 svn_ra_svn__item_t *item, char first_char,
1300 int level)
1301 {
1302 char c = first_char;
1303 apr_uint64_t val;
1304 svn_ra_svn__item_t *listitem;
1305
1306 if (++level >= ITEM_NESTING_LIMIT)
1307 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1308 _("Items are nested too deeply"));
1309
1310
1311 /* Determine the item type and read it in. Make sure that c is the
1312 * first character at the end of the item so we can test to make
1313 * sure it's whitespace. */
1314 if (svn_ctype_isdigit(c))
1315 {
1316 /* It's a number or a string. Read the number part, either way. */
1317 val = c - '0';
1318 while (1)
1319 {
1320 apr_uint64_t prev_val = val;
1321 SVN_ERR(readbuf_getchar(conn, pool, &c));
1322 if (!svn_ctype_isdigit(c))
1323 break;
1324 val = val * 10 + (c - '0');
1325 /* val wrapped past maximum value? */
1326 if ((prev_val >= (APR_UINT64_MAX / 10))
1327 && (val < APR_UINT64_MAX - 10))
1328 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1329 _("Number is larger than maximum"));
1330 }
1331 if (c == ':')
1332 {
1333 /* It's a string. */
1334 SVN_ERR(read_string(conn, pool, item, val));
1335 SVN_ERR(readbuf_getchar(conn, pool, &c));
1336 }
1337 else
1338 {
1339 /* It's a number. */
1340 item->kind = SVN_RA_SVN_NUMBER;
1341 item->u.number = val;
1342 }
1343 }
1344 else if (svn_ctype_isalpha(c))
1345 {
1346 /* It's a word. Read it into a buffer of limited size. */
1347 char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1348 char *end = buffer + MAX_WORD_LENGTH;
1349 char *p = buffer + 1;
1350
1351 buffer[0] = c;
1352 if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
1353 {
1354 /* Fast path: we can simply take a chunk from the read
1355 * buffer and inspect it with no overflow checks etc.
1356 *
1357 * Copying these 24 bytes unconditionally is also faster
1358 * than a variable-sized memcpy. Note that P is at BUFFER[1].
1359 */
1360 memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
1361 *end = 0;
1362
1363 /* This will terminate at P == END because of *END == NUL. */
1364 while (svn_ctype_isalnum(*p) || *p == '-')
1365 ++p;
1366
1367 /* Only now do we mark data as actually read. */
1368 conn->read_ptr += p - buffer;
1369 }
1370 else
1371 {
1372 /* Slow path. Byte-by-byte copying and checking for
1373 * input and output buffer boundaries. */
1374 for (p = buffer + 1; p != end; ++p)
1375 {
1376 SVN_ERR(readbuf_getchar(conn, pool, p));
1377 if (!svn_ctype_isalnum(*p) && *p != '-')
1378 break;
1379 }
1380 }
1381
1382 if (p == end)
1383 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1384 _("Word is too long"));
1385
1386 c = *p;
1387 *p = '\0';
1388
1389 /* Store the word in ITEM. */
1390 item->kind = SVN_RA_SVN_WORD;
1391 item->u.word.data = buffer;
1392 item->u.word.len = p - buffer;
1393 }
1394 else if (c == '(')
1395 {
1396 /* The largest struct that the protocol currently defines has 10
1397 * elements (log-entry) and add some headroom for future extensions.
1398 * At a maximum nesting level of 64 this use <= 18kB of stack.
1399 *
1400 * All system-defined data structures will fit into this and will be
1401 * copied into ITEM after a single apr_palloc with no over-provision.
1402 * Unbounded lists with more than 12 but less than 25 entries will
1403 * also see only a single allocation from POOL. However, there will
1404 * be some over-provision. Longer lists will see log N resizes and
1405 * O(N) total cost.
1406 */
1407 svn_ra_svn__item_t stack_items[12];
1408 svn_ra_svn__item_t *items = stack_items;
1409 int capacity = sizeof(stack_items) / sizeof(stack_items[0]);
1410 int count = 0;
1411
1412 /* Read in the list items. */
1413 item->kind = SVN_RA_SVN_LIST;
1414 while (1)
1415 {
1416 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1417 if (c == ')')
1418 break;
1419
1420 /* Auto-expand the list. */
1421 if (count == capacity)
1422 {
1423 svn_ra_svn__item_t *new_items
1424 = apr_palloc(pool, 2 * capacity * sizeof(*new_items));
1425 memcpy(new_items, items, capacity * sizeof(*new_items));
1426 items = new_items;
1427 capacity = 2 * capacity;
1428 }
1429
1430 listitem = &items[count];
1431 ++count;
1432
1433 SVN_ERR(read_item(conn, pool, listitem, c, level));
1434 }
1435
1436 /* Store the list in ITEM - if not empty (= default). */
1437 if (count)
1438 {
1439 item->u.list.nelts = count;
1440
1441 /* If we haven't allocated from POOL, yet, do it now. */
1442 if (items == stack_items)
1443 item->u.list.items = apr_pmemdup(pool, items,
1444 count * sizeof(*items));
1445 else
1446 item->u.list.items = items;
1447 }
1448 else
1449 {
1450 item->u.list.items = NULL;
1451 item->u.list.nelts = 0;
1452 }
1453
1454 SVN_ERR(readbuf_getchar(conn, pool, &c));
1455 }
1456
1457 if (!svn_iswhitespace(c))
1458 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1459 _("Malformed network data"));
1460 return SVN_NO_ERROR;
1461 }
1462
1463 /* Given the first non-whitespace character FIRST_CHAR, read the first
1464 * command (word) encountered in CONN into *ITEM. If ITEM is NULL, skip
1465 * to the end of the current list. Use POOL for allocations. */
1466 static svn_error_t *
read_command_only(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char ** item,char first_char)1467 read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1468 const char **item, char first_char)
1469 {
1470 char c = first_char;
1471
1472 /* Determine the item type and read it in. Make sure that c is the
1473 * first character at the end of the item so we can test to make
1474 * sure it's whitespace. */
1475 if (svn_ctype_isdigit(c))
1476 {
1477 /* It's a number or a string. Read the number part, either way. */
1478 apr_uint64_t val, prev_val=0;
1479 val = c - '0';
1480 while (1)
1481 {
1482 prev_val = val;
1483 SVN_ERR(readbuf_getchar(conn, pool, &c));
1484 if (!svn_ctype_isdigit(c))
1485 break;
1486 val = val * 10 + (c - '0');
1487 if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1488 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1489 _("Number is larger than maximum"));
1490 }
1491 if (c == ':')
1492 {
1493 /* It's a string. */
1494 SVN_ERR(readbuf_skip(conn, val));
1495 SVN_ERR(readbuf_getchar(conn, pool, &c));
1496 }
1497 }
1498 else if (svn_ctype_isalpha(c))
1499 {
1500 /* It's a word. */
1501 if (item)
1502 {
1503 /* This is the word we want to read */
1504
1505 char *buf = apr_palloc(pool, 32);
1506 apr_size_t len = 1;
1507 buf[0] = c;
1508
1509 while (1)
1510 {
1511 SVN_ERR(readbuf_getchar(conn, pool, &c));
1512 if (!svn_ctype_isalnum(c) && c != '-')
1513 break;
1514 buf[len] = c;
1515 if (++len == 32)
1516 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1517 _("Word too long"));
1518 }
1519 buf[len] = 0;
1520 *item = buf;
1521 }
1522 else
1523 {
1524 /* we don't need the actual word, just skip it */
1525 do
1526 {
1527 SVN_ERR(readbuf_getchar(conn, pool, &c));
1528 }
1529 while (svn_ctype_isalnum(c) || c == '-');
1530 }
1531 }
1532 else if (c == '(')
1533 {
1534 /* Read in the list items. */
1535 while (1)
1536 {
1537 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1538 if (c == ')')
1539 break;
1540
1541 if (item && *item == NULL)
1542 SVN_ERR(read_command_only(conn, pool, item, c));
1543 else
1544 SVN_ERR(read_command_only(conn, pool, NULL, c));
1545 }
1546 SVN_ERR(readbuf_getchar(conn, pool, &c));
1547 }
1548
1549 return SVN_NO_ERROR;
1550 }
1551
1552 svn_error_t *
svn_ra_svn__read_item(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_ra_svn__item_t ** item)1553 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1554 apr_pool_t *pool,
1555 svn_ra_svn__item_t **item)
1556 {
1557 char c;
1558
1559 /* Allocate space, read the first character, and then do the rest of
1560 * the work. This makes sense because of the way lists are read. */
1561 *item = apr_palloc(pool, sizeof(**item));
1562 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1563 return read_item(conn, pool, *item, c, 0);
1564 }
1565
1566 /* Drain existing whitespace from the receive buffer of CONN until either
1567 there is no data in the underlying receive socket anymore or we found
1568 a non-whitespace char. Set *HAS_ITEM to TRUE in the latter case.
1569 */
1570 static svn_error_t *
svn_ra_svn__has_item(svn_boolean_t * has_item,svn_ra_svn_conn_t * conn,apr_pool_t * pool)1571 svn_ra_svn__has_item(svn_boolean_t *has_item,
1572 svn_ra_svn_conn_t *conn,
1573 apr_pool_t *pool)
1574 {
1575 do
1576 {
1577 if (conn->read_ptr == conn->read_end)
1578 {
1579 svn_boolean_t available;
1580 if (conn->write_pos)
1581 SVN_ERR(writebuf_flush(conn, pool));
1582
1583 SVN_ERR(svn_ra_svn__data_available(conn, &available));
1584 if (!available)
1585 break;
1586
1587 SVN_ERR(readbuf_fill(conn, pool));
1588 }
1589 }
1590 while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1591
1592 *has_item = conn->read_ptr != conn->read_end;
1593 return SVN_NO_ERROR;
1594 }
1595
1596 svn_error_t *
svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t * conn,apr_pool_t * pool)1597 svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1598 apr_pool_t *pool)
1599 {
1600 return readbuf_skip_leading_garbage(conn, pool);
1601 }
1602
1603 /* --- READING AND PARSING TUPLES --- */
1604
1605 /* Parse a tuple of svn_ra_svn__item_t *'s. Advance *FMT to the end of the
1606 * tuple specification and advance AP by the corresponding arguments. */
1607 static svn_error_t *
vparse_tuple(const svn_ra_svn__list_t * items,const char ** fmt,va_list * ap)1608 vparse_tuple(const svn_ra_svn__list_t *items,
1609 const char **fmt,
1610 va_list *ap)
1611 {
1612 int count, nesting_level;
1613 svn_ra_svn__item_t *elt;
1614
1615 for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1616 {
1617 /* '?' just means the tuple may stop; skip past it. */
1618 if (**fmt == '?')
1619 (*fmt)++;
1620 elt = &SVN_RA_SVN__LIST_ITEM(items, count);
1621 if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1622 {
1623 (*fmt)++;
1624 SVN_ERR(vparse_tuple(&elt->u.list, fmt, ap));
1625 }
1626 else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1627 *va_arg(*ap, const char **) = elt->u.string.data;
1628 else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1629 *va_arg(*ap, svn_string_t **) = &elt->u.string;
1630 else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1631 *va_arg(*ap, const char **) = elt->u.word.data;
1632 else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1633 {
1634 if (svn_string_compare(&elt->u.word, &str_true))
1635 *va_arg(*ap, svn_boolean_t *) = TRUE;
1636 else if (svn_string_compare(&elt->u.word, &str_false))
1637 *va_arg(*ap, svn_boolean_t *) = FALSE;
1638 else
1639 break;
1640 }
1641 else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1642 *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1643 else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1644 *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1645 else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1646 {
1647 if (svn_string_compare(&elt->u.word, &str_true))
1648 *va_arg(*ap, apr_uint64_t *) = TRUE;
1649 else if (svn_string_compare(&elt->u.word, &str_false))
1650 *va_arg(*ap, apr_uint64_t *) = FALSE;
1651 else
1652 break;
1653 }
1654 else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1655 {
1656 if (svn_string_compare(&elt->u.word, &str_true))
1657 *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1658 else if (svn_string_compare(&elt->u.word, &str_false))
1659 *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1660 else
1661 break;
1662 }
1663 else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1664 *va_arg(*ap, svn_ra_svn__list_t **) = &elt->u.list;
1665 else if (**fmt == ')')
1666 return SVN_NO_ERROR;
1667 else
1668 break;
1669 }
1670 if (**fmt == '?')
1671 {
1672 nesting_level = 0;
1673 for (; **fmt; (*fmt)++)
1674 {
1675 switch (**fmt)
1676 {
1677 case '?':
1678 break;
1679 case 'r':
1680 *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1681 break;
1682 case 's':
1683 *va_arg(*ap, svn_string_t **) = NULL;
1684 break;
1685 case 'c':
1686 case 'w':
1687 *va_arg(*ap, const char **) = NULL;
1688 break;
1689 case 'l':
1690 *va_arg(*ap, svn_ra_svn__list_t **) = NULL;
1691 break;
1692 case 'B':
1693 case 'n':
1694 *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1695 break;
1696 case '3':
1697 *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1698 break;
1699 case 'b':
1700 *va_arg(*ap, svn_boolean_t *) = FALSE;
1701 break;
1702 case '(':
1703 nesting_level++;
1704 break;
1705 case ')':
1706 if (--nesting_level < 0)
1707 return SVN_NO_ERROR;
1708 break;
1709 default:
1710 SVN_ERR_MALFUNCTION();
1711 }
1712 }
1713 }
1714 if (**fmt && **fmt != ')')
1715 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1716 _("Malformed network data"));
1717 return SVN_NO_ERROR;
1718 }
1719
1720 svn_error_t *
svn_ra_svn__parse_tuple(const svn_ra_svn__list_t * list,const char * fmt,...)1721 svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
1722 const char *fmt, ...)
1723 {
1724 svn_error_t *err;
1725 va_list ap;
1726
1727 va_start(ap, fmt);
1728 err = vparse_tuple(list, &fmt, &ap);
1729 va_end(ap);
1730 return err;
1731 }
1732
1733 svn_error_t *
svn_ra_svn__read_tuple(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)1734 svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1735 apr_pool_t *pool,
1736 const char *fmt, ...)
1737 {
1738 va_list ap;
1739 svn_ra_svn__item_t *item;
1740 svn_error_t *err;
1741
1742 SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1743 if (item->kind != SVN_RA_SVN_LIST)
1744 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1745 _("Malformed network data"));
1746 va_start(ap, fmt);
1747 err = vparse_tuple(&item->u.list, &fmt, &ap);
1748 va_end(ap);
1749 return err;
1750 }
1751
1752 svn_error_t *
svn_ra_svn__read_command_only(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char ** command)1753 svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1754 apr_pool_t *pool,
1755 const char **command)
1756 {
1757 char c;
1758 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1759
1760 *command = NULL;
1761 return read_command_only(conn, pool, command, c);
1762 }
1763
1764
1765 svn_error_t *
svn_ra_svn__parse_proplist(const svn_ra_svn__list_t * list,apr_pool_t * pool,apr_hash_t ** props)1766 svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
1767 apr_pool_t *pool,
1768 apr_hash_t **props)
1769 {
1770 svn_string_t *name;
1771 svn_string_t *value;
1772 svn_ra_svn__item_t *elt;
1773 int i;
1774
1775 *props = svn_hash__make(pool);
1776 for (i = 0; i < list->nelts; i++)
1777 {
1778 elt = &SVN_RA_SVN__LIST_ITEM(list, i);
1779 if (elt->kind != SVN_RA_SVN_LIST)
1780 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1781 _("Proplist element not a list"));
1782 SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "ss", &name, &value));
1783 apr_hash_set(*props, name->data, name->len, value);
1784 }
1785
1786 return SVN_NO_ERROR;
1787 }
1788
1789
1790 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1791
svn_ra_svn__locate_real_error_child(svn_error_t * err)1792 svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1793 {
1794 svn_error_t *this_link;
1795
1796 SVN_ERR_ASSERT(err);
1797
1798 for (this_link = err;
1799 this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1800 this_link = this_link->child)
1801 ;
1802
1803 SVN_ERR_ASSERT(this_link);
1804 return this_link;
1805 }
1806
1807 svn_error_t *
svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t * params)1808 svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params)
1809 {
1810 const char *message, *file;
1811 svn_error_t *err = NULL;
1812 svn_ra_svn__item_t *elt;
1813 int i;
1814 apr_uint64_t apr_err, line;
1815
1816 if (params->nelts == 0)
1817 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1818 _("Empty error list"));
1819
1820 /* Rebuild the error list from the end, to avoid reversing the order. */
1821 for (i = params->nelts - 1; i >= 0; i--)
1822 {
1823 elt = &SVN_RA_SVN__LIST_ITEM(params, i);
1824 if (elt->kind != SVN_RA_SVN_LIST)
1825 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1826 _("Malformed error list"));
1827 SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "nccn",
1828 &apr_err, &message, &file, &line));
1829 /* The message field should have been optional, but we can't
1830 easily change that, so "" means a nonexistent message. */
1831 if (!*message)
1832 message = NULL;
1833
1834 /* Skip over links in the error chain that were intended only to
1835 exist on the server (to wrap real errors intended for the
1836 client) but accidentally got included in the server's actual
1837 response. */
1838 if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1839 {
1840 err = svn_error_create((apr_status_t)apr_err, err, message);
1841 err->file = apr_pstrdup(err->pool, file);
1842 err->line = (long)line;
1843 }
1844 }
1845
1846 /* If we get here, then we failed to find a real error in the error
1847 chain that the server proported to be sending us. That's bad. */
1848 if (! err)
1849 err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1850 _("Malformed error list"));
1851
1852 return err;
1853 }
1854
1855 svn_error_t *
svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)1856 svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1857 apr_pool_t *pool,
1858 const char *fmt, ...)
1859 {
1860 va_list ap;
1861 const char *status;
1862 svn_ra_svn__list_t *params;
1863 svn_error_t *err;
1864
1865 SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, ¶ms));
1866 if (strcmp(status, "success") == 0)
1867 {
1868 va_start(ap, fmt);
1869 err = vparse_tuple(params, &fmt, &ap);
1870 va_end(ap);
1871 return err;
1872 }
1873 else if (strcmp(status, "failure") == 0)
1874 {
1875 return svn_error_trace(svn_ra_svn__handle_failure_status(params));
1876 }
1877
1878 return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1879 _("Unknown status '%s' in command response"),
1880 status);
1881 }
1882
1883 svn_error_t *
svn_ra_svn__has_command(svn_boolean_t * has_command,svn_boolean_t * terminated,svn_ra_svn_conn_t * conn,apr_pool_t * pool)1884 svn_ra_svn__has_command(svn_boolean_t *has_command,
1885 svn_boolean_t *terminated,
1886 svn_ra_svn_conn_t *conn,
1887 apr_pool_t *pool)
1888 {
1889 svn_error_t *err;
1890
1891 /* Don't make whitespace between commands trigger I/O limitiations. */
1892 svn_ra_svn__reset_command_io_counters(conn);
1893
1894 err = svn_ra_svn__has_item(has_command, conn, pool);
1895 if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1896 {
1897 *terminated = TRUE;
1898 svn_error_clear(err);
1899 return SVN_NO_ERROR;
1900 }
1901
1902 *terminated = FALSE;
1903 return svn_error_trace(err);
1904 }
1905
1906 svn_error_t *
svn_ra_svn__handle_command(svn_boolean_t * terminate,apr_hash_t * cmd_hash,void * baton,svn_ra_svn_conn_t * conn,svn_boolean_t error_on_disconnect,apr_pool_t * pool)1907 svn_ra_svn__handle_command(svn_boolean_t *terminate,
1908 apr_hash_t *cmd_hash,
1909 void *baton,
1910 svn_ra_svn_conn_t *conn,
1911 svn_boolean_t error_on_disconnect,
1912 apr_pool_t *pool)
1913 {
1914 const char *cmdname;
1915 svn_error_t *err, *write_err;
1916 svn_ra_svn__list_t *params;
1917 const svn_ra_svn__cmd_entry_t *command;
1918
1919 *terminate = FALSE;
1920
1921 /* Limit I/O for every command separately. */
1922 svn_ra_svn__reset_command_io_counters(conn);
1923
1924 err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms);
1925 if (err)
1926 {
1927 if (!error_on_disconnect
1928 && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1929 {
1930 svn_error_clear(err);
1931 *terminate = TRUE;
1932 return SVN_NO_ERROR;
1933 }
1934 return err;
1935 }
1936
1937 command = svn_hash_gets(cmd_hash, cmdname);
1938 if (command)
1939 {
1940 /* Call the standard command handler.
1941 * If that is not set, then this is a lecagy API call and we invoke
1942 * the legacy command handler. */
1943 if (command->handler)
1944 {
1945 err = (*command->handler)(conn, pool, params, baton);
1946 }
1947 else
1948 {
1949 apr_array_header_t *deprecated_params
1950 = svn_ra_svn__to_public_array(params, pool);
1951 err = (*command->deprecated_handler)(conn, pool, deprecated_params,
1952 baton);
1953 }
1954
1955 /* The command implementation may have swallowed or wrapped the I/O
1956 * error not knowing that we may no longer be able to send data.
1957 *
1958 * So, check again for the limit violations and exit the command
1959 * processing quickly if we may have truncated data. */
1960 err = svn_error_compose_create(check_io_limits(conn), err);
1961
1962 *terminate = command->terminate;
1963 }
1964 else
1965 {
1966 err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1967 _("Unknown editor command '%s'"), cmdname);
1968 err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1969 }
1970
1971 if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1972 {
1973 write_err = svn_ra_svn__write_cmd_failure(
1974 conn, pool,
1975 svn_ra_svn__locate_real_error_child(err));
1976 svn_error_clear(err);
1977 return write_err ? write_err : SVN_NO_ERROR;
1978 }
1979
1980 return err;
1981 }
1982
1983 svn_error_t *
svn_ra_svn__handle_commands2(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_ra_svn__cmd_entry_t * commands,void * baton,svn_boolean_t error_on_disconnect)1984 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1985 apr_pool_t *pool,
1986 const svn_ra_svn__cmd_entry_t *commands,
1987 void *baton,
1988 svn_boolean_t error_on_disconnect)
1989 {
1990 apr_pool_t *subpool = svn_pool_create(pool);
1991 apr_pool_t *iterpool = svn_pool_create(subpool);
1992 const svn_ra_svn__cmd_entry_t *command;
1993 apr_hash_t *cmd_hash = apr_hash_make(subpool);
1994
1995 for (command = commands; command->cmdname; command++)
1996 svn_hash_sets(cmd_hash, command->cmdname, command);
1997
1998 while (1)
1999 {
2000 svn_boolean_t terminate;
2001 svn_error_t *err;
2002 svn_pool_clear(iterpool);
2003
2004 err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
2005 error_on_disconnect, iterpool);
2006 if (err)
2007 {
2008 svn_pool_destroy(subpool);
2009 return svn_error_trace(err);
2010 }
2011 if (terminate)
2012 break;
2013 }
2014 svn_pool_destroy(iterpool);
2015 svn_pool_destroy(subpool);
2016 return SVN_NO_ERROR;
2017 }
2018
2019 svn_error_t *
svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)2020 svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
2021 apr_pool_t *pool,
2022 svn_revnum_t rev)
2023 {
2024 SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
2025 SVN_ERR(write_tuple_revision(conn, pool, rev));
2026 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2027
2028 return SVN_NO_ERROR;
2029 }
2030
2031 svn_error_t *
svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const svn_string_t * token)2032 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
2033 apr_pool_t *pool,
2034 svn_revnum_t rev,
2035 const svn_string_t *token)
2036 {
2037 SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
2038 SVN_ERR(write_tuple_start_list(conn, pool));
2039 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2040 SVN_ERR(write_tuple_end_list(conn, pool));
2041 SVN_ERR(write_tuple_string(conn, pool, token));
2042 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2043
2044 return SVN_NO_ERROR;
2045 }
2046
2047 svn_error_t *
svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev,const svn_string_t * token)2048 svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
2049 apr_pool_t *pool,
2050 const char *path,
2051 svn_revnum_t rev,
2052 const svn_string_t *token)
2053 {
2054 SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
2055 SVN_ERR(write_tuple_cstring(conn, pool, path));
2056 SVN_ERR(write_tuple_start_list(conn, pool));
2057 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2058 SVN_ERR(write_tuple_end_list(conn, pool));
2059 SVN_ERR(write_tuple_string(conn, pool, token));
2060 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2061
2062 return SVN_NO_ERROR;
2063 }
2064
2065 svn_error_t *
svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,const char * copy_path,svn_revnum_t copy_rev)2066 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
2067 apr_pool_t *pool,
2068 const char *path,
2069 const svn_string_t *parent_token,
2070 const svn_string_t *token,
2071 const char *copy_path,
2072 svn_revnum_t copy_rev)
2073 {
2074 SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
2075 SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
2076 copy_path, copy_rev));
2077 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2078
2079 return SVN_NO_ERROR;
2080 }
2081
2082 svn_error_t *
svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,svn_revnum_t rev)2083 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
2084 apr_pool_t *pool,
2085 const char *path,
2086 const svn_string_t *parent_token,
2087 const svn_string_t *token,
2088 svn_revnum_t rev)
2089 {
2090 SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
2091 SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
2092 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2093
2094 return SVN_NO_ERROR;
2095 }
2096
2097 svn_error_t *
svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * name,const svn_string_t * value)2098 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
2099 apr_pool_t *pool,
2100 const svn_string_t *token,
2101 const char *name,
2102 const svn_string_t *value)
2103 {
2104 SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
2105 SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
2106 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2107
2108 return SVN_NO_ERROR;
2109 }
2110
2111 svn_error_t *
svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token)2112 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
2113 apr_pool_t *pool,
2114 const svn_string_t *token)
2115 {
2116 SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
2117 SVN_ERR(write_tuple_string(conn, pool, token));
2118 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2119
2120 return SVN_NO_ERROR;
2121 }
2122
2123 svn_error_t *
svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token)2124 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
2125 apr_pool_t *pool,
2126 const char *path,
2127 const svn_string_t *parent_token)
2128 {
2129 SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
2130 SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
2131 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2132
2133 return SVN_NO_ERROR;
2134 }
2135
2136 svn_error_t *
svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,const char * copy_path,svn_revnum_t copy_rev)2137 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
2138 apr_pool_t *pool,
2139 const char *path,
2140 const svn_string_t *parent_token,
2141 const svn_string_t *token,
2142 const char *copy_path,
2143 svn_revnum_t copy_rev)
2144 {
2145 SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
2146 SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
2147 copy_path, copy_rev));
2148 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2149
2150 return SVN_NO_ERROR;
2151 }
2152
2153 svn_error_t *
svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token,const svn_string_t * token,svn_revnum_t rev)2154 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
2155 apr_pool_t *pool,
2156 const char *path,
2157 const svn_string_t *parent_token,
2158 const svn_string_t *token,
2159 svn_revnum_t rev)
2160 {
2161 SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
2162 SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
2163 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2164
2165 return SVN_NO_ERROR;
2166 }
2167
2168 svn_error_t *
svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * name,const svn_string_t * value)2169 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
2170 apr_pool_t *pool,
2171 const svn_string_t *token,
2172 const char *name,
2173 const svn_string_t *value)
2174 {
2175 SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
2176 SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
2177 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2178
2179 return SVN_NO_ERROR;
2180 }
2181
2182 svn_error_t *
svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * text_checksum)2183 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
2184 apr_pool_t *pool,
2185 const svn_string_t *token,
2186 const char *text_checksum)
2187 {
2188 SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
2189 SVN_ERR(write_tuple_string(conn, pool, token));
2190 SVN_ERR(write_tuple_start_list(conn, pool));
2191 SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
2192 SVN_ERR(write_tuple_end_list(conn, pool));
2193 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2194
2195 return SVN_NO_ERROR;
2196 }
2197
2198 svn_error_t *
svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * parent_token)2199 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
2200 apr_pool_t *pool,
2201 const char *path,
2202 const svn_string_t *parent_token)
2203 {
2204 SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
2205 SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
2206 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2207
2208 return SVN_NO_ERROR;
2209 }
2210
2211 svn_error_t *
svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const svn_string_t * chunk)2212 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
2213 apr_pool_t *pool,
2214 const svn_string_t *token,
2215 const svn_string_t *chunk)
2216 {
2217 SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
2218 SVN_ERR(write_tuple_string(conn, pool, token));
2219 SVN_ERR(write_tuple_string(conn, pool, chunk));
2220 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2221
2222 return SVN_NO_ERROR;
2223 }
2224
2225 svn_error_t *
svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token)2226 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
2227 apr_pool_t *pool,
2228 const svn_string_t *token)
2229 {
2230 SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
2231 SVN_ERR(write_tuple_string(conn, pool, token));
2232 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2233
2234 return SVN_NO_ERROR;
2235 }
2236
2237 svn_error_t *
svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * token,const char * base_checksum)2238 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
2239 apr_pool_t *pool,
2240 const svn_string_t *token,
2241 const char *base_checksum)
2242 {
2243 SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
2244 SVN_ERR(write_tuple_string(conn, pool, token));
2245 SVN_ERR(write_tuple_start_list(conn, pool));
2246 SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
2247 SVN_ERR(write_tuple_end_list(conn, pool));
2248 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2249
2250 return SVN_NO_ERROR;
2251 }
2252
2253 svn_error_t *
svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2254 svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
2255 apr_pool_t *pool)
2256 {
2257 return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
2258 }
2259
2260 svn_error_t *
svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2261 svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
2262 apr_pool_t *pool)
2263 {
2264 return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
2265 }
2266
2267 svn_error_t *
svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev,svn_boolean_t start_empty,const char * lock_token,svn_depth_t depth)2268 svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
2269 apr_pool_t *pool,
2270 const char *path,
2271 svn_revnum_t rev,
2272 svn_boolean_t start_empty,
2273 const char *lock_token,
2274 svn_depth_t depth)
2275 {
2276 SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
2277 SVN_ERR(write_tuple_cstring(conn, pool, path));
2278 SVN_ERR(write_tuple_revision(conn, pool, rev));
2279 SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
2280 SVN_ERR(write_tuple_start_list(conn, pool));
2281 SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
2282 SVN_ERR(write_tuple_end_list(conn, pool));
2283 SVN_ERR(write_tuple_depth(conn, pool, depth));
2284 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2285
2286 return SVN_NO_ERROR;
2287 }
2288
2289 svn_error_t *
svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path)2290 svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
2291 apr_pool_t *pool,
2292 const char *path)
2293 {
2294 SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
2295 SVN_ERR(write_tuple_cstring(conn, pool, path));
2296 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2297
2298 return SVN_NO_ERROR;
2299 }
2300
2301 svn_error_t *
svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const char * url,svn_revnum_t rev,svn_boolean_t start_empty,const char * lock_token,svn_depth_t depth)2302 svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
2303 apr_pool_t *pool,
2304 const char *path,
2305 const char *url,
2306 svn_revnum_t rev,
2307 svn_boolean_t start_empty,
2308 const char *lock_token,
2309 svn_depth_t depth)
2310 {
2311 SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
2312 SVN_ERR(write_tuple_cstring(conn, pool, path));
2313 SVN_ERR(write_tuple_cstring(conn, pool, url));
2314 SVN_ERR(write_tuple_revision(conn, pool, rev));
2315 SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
2316 SVN_ERR(write_tuple_start_list(conn, pool));
2317 SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
2318 SVN_ERR(write_tuple_end_list(conn, pool));
2319 SVN_ERR(write_tuple_depth(conn, pool, depth));
2320 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2321
2322 return SVN_NO_ERROR;
2323 }
2324
2325 svn_error_t *
svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2326 svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
2327 apr_pool_t *pool)
2328 {
2329 return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
2330 }
2331
2332 svn_error_t *
svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2333 svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
2334 apr_pool_t *pool)
2335 {
2336 return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
2337 }
2338
2339 svn_error_t *
svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * url)2340 svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
2341 apr_pool_t *pool,
2342 const char *url)
2343 {
2344 SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
2345 SVN_ERR(write_tuple_cstring(conn, pool, url));
2346 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2347
2348 return SVN_NO_ERROR;
2349 }
2350
2351 svn_error_t *
svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2352 svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
2353 apr_pool_t *pool)
2354 {
2355 return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
2356 }
2357
2358 svn_error_t *
svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool,apr_time_t tm)2359 svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
2360 apr_pool_t *pool,
2361 apr_time_t tm)
2362 {
2363 SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
2364 SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
2365 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2366
2367 return SVN_NO_ERROR;
2368 }
2369
2370 svn_error_t *
svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * name,const svn_string_t * value,svn_boolean_t dont_care,const svn_string_t * old_value)2371 svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
2372 apr_pool_t *pool,
2373 svn_revnum_t rev,
2374 const char *name,
2375 const svn_string_t *value,
2376 svn_boolean_t dont_care,
2377 const svn_string_t *old_value)
2378 {
2379 SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
2380 SVN_ERR(write_tuple_revision(conn, pool, rev));
2381 SVN_ERR(write_tuple_cstring(conn, pool, name));
2382 SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
2383 SVN_ERR(write_tuple_start_list(conn, pool));
2384 SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
2385 SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
2386 SVN_ERR(write_tuple_end_list(conn, pool));
2387 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2388
2389 return SVN_NO_ERROR;
2390 }
2391
2392 svn_error_t *
svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * name,const svn_string_t * value)2393 svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2394 apr_pool_t *pool,
2395 svn_revnum_t rev,
2396 const char *name,
2397 const svn_string_t *value)
2398 {
2399 SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2400 SVN_ERR(write_tuple_revision(conn, pool, rev));
2401 SVN_ERR(write_tuple_cstring(conn, pool, name));
2402 SVN_ERR(write_tuple_string_opt(conn, pool, value));
2403 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2404
2405 return SVN_NO_ERROR;
2406 }
2407
2408 svn_error_t *
svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev)2409 svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2410 apr_pool_t *pool,
2411 svn_revnum_t rev)
2412 {
2413 SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2414 SVN_ERR(write_tuple_revision(conn, pool, rev));
2415 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2416
2417 return SVN_NO_ERROR;
2418 }
2419
2420 svn_error_t *
svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * name)2421 svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2422 apr_pool_t *pool,
2423 svn_revnum_t rev,
2424 const char *name)
2425 {
2426 SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2427 SVN_ERR(write_tuple_revision(conn, pool, rev));
2428 SVN_ERR(write_tuple_cstring(conn, pool, name));
2429 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2430
2431 return SVN_NO_ERROR;
2432 }
2433
2434 svn_error_t *
svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev,svn_boolean_t props,svn_boolean_t stream)2435 svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2436 apr_pool_t *pool,
2437 const char *path,
2438 svn_revnum_t rev,
2439 svn_boolean_t props,
2440 svn_boolean_t stream)
2441 {
2442 SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2443 SVN_ERR(write_tuple_cstring(conn, pool, path));
2444 SVN_ERR(write_tuple_start_list(conn, pool));
2445 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2446 SVN_ERR(write_tuple_end_list(conn, pool));
2447 SVN_ERR(write_tuple_boolean(conn, pool, props));
2448 SVN_ERR(write_tuple_boolean(conn, pool, stream));
2449
2450 /* Always send the, nominally optional, want-iprops as "false" to
2451 workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2452 to see "true" if it is omitted. */
2453 SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2454
2455 return SVN_NO_ERROR;
2456 }
2457
2458 svn_error_t *
svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * target,svn_boolean_t recurse,svn_depth_t depth,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry)2459 svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2460 apr_pool_t *pool,
2461 svn_revnum_t rev,
2462 const char *target,
2463 svn_boolean_t recurse,
2464 svn_depth_t depth,
2465 svn_boolean_t send_copyfrom_args,
2466 svn_boolean_t ignore_ancestry)
2467 {
2468 SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2469 SVN_ERR(write_tuple_start_list(conn, pool));
2470 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2471 SVN_ERR(write_tuple_end_list(conn, pool));
2472 SVN_ERR(write_tuple_cstring(conn, pool, target));
2473 SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2474 SVN_ERR(write_tuple_depth(conn, pool, depth));
2475 SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2476 SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2477 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2478
2479 return SVN_NO_ERROR;
2480 }
2481
2482 svn_error_t *
svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * target,svn_boolean_t recurse,const char * switch_url,svn_depth_t depth,svn_boolean_t send_copyfrom_args,svn_boolean_t ignore_ancestry)2483 svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2484 apr_pool_t *pool,
2485 svn_revnum_t rev,
2486 const char *target,
2487 svn_boolean_t recurse,
2488 const char *switch_url,
2489 svn_depth_t depth,
2490 svn_boolean_t send_copyfrom_args,
2491 svn_boolean_t ignore_ancestry)
2492 {
2493 SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2494 SVN_ERR(write_tuple_start_list(conn, pool));
2495 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2496 SVN_ERR(write_tuple_end_list(conn, pool));
2497 SVN_ERR(write_tuple_cstring(conn, pool, target));
2498 SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2499 SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2500 SVN_ERR(write_tuple_depth(conn, pool, depth));
2501 SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2502 SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2503 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2504
2505 return SVN_NO_ERROR;
2506 }
2507
2508 svn_error_t *
svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * target,svn_boolean_t recurse,svn_revnum_t rev,svn_depth_t depth)2509 svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2510 apr_pool_t *pool,
2511 const char *target,
2512 svn_boolean_t recurse,
2513 svn_revnum_t rev,
2514 svn_depth_t depth)
2515 {
2516 SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2517 SVN_ERR(write_tuple_cstring(conn, pool, target));
2518 SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2519 SVN_ERR(write_tuple_start_list(conn, pool));
2520 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2521 SVN_ERR(write_tuple_end_list(conn, pool));
2522 SVN_ERR(write_tuple_depth(conn, pool, depth));
2523 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2524
2525 return SVN_NO_ERROR;
2526 }
2527
2528 svn_error_t *
svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,const char * target,svn_boolean_t recurse,svn_boolean_t ignore_ancestry,const char * versus_url,svn_boolean_t text_deltas,svn_depth_t depth)2529 svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2530 apr_pool_t *pool,
2531 svn_revnum_t rev,
2532 const char *target,
2533 svn_boolean_t recurse,
2534 svn_boolean_t ignore_ancestry,
2535 const char *versus_url,
2536 svn_boolean_t text_deltas,
2537 svn_depth_t depth)
2538 {
2539 SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2540 SVN_ERR(write_tuple_start_list(conn, pool));
2541 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2542 SVN_ERR(write_tuple_end_list(conn, pool));
2543 SVN_ERR(write_tuple_cstring(conn, pool, target));
2544 SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2545 SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2546 SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2547 SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2548 SVN_ERR(write_tuple_depth(conn, pool, depth));
2549 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2550
2551 return SVN_NO_ERROR;
2552 }
2553
2554 svn_error_t *
svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev)2555 svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2556 apr_pool_t *pool,
2557 const char *path,
2558 svn_revnum_t rev)
2559 {
2560 SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2561 SVN_ERR(write_tuple_cstring(conn, pool, path));
2562 SVN_ERR(write_tuple_start_list(conn, pool));
2563 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2564 SVN_ERR(write_tuple_end_list(conn, pool));
2565 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2566
2567 return SVN_NO_ERROR;
2568 }
2569
2570 svn_error_t *
svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t rev)2571 svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2572 apr_pool_t *pool,
2573 const char *path,
2574 svn_revnum_t rev)
2575 {
2576 SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2577 SVN_ERR(write_tuple_cstring(conn, pool, path));
2578 SVN_ERR(write_tuple_start_list(conn, pool));
2579 SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2580 SVN_ERR(write_tuple_end_list(conn, pool));
2581 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2582
2583 return SVN_NO_ERROR;
2584 }
2585
2586 svn_error_t *
svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t start,svn_revnum_t end,svn_boolean_t include_merged_revisions)2587 svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2588 apr_pool_t *pool,
2589 const char *path,
2590 svn_revnum_t start,
2591 svn_revnum_t end,
2592 svn_boolean_t include_merged_revisions)
2593 {
2594 SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2595 SVN_ERR(write_tuple_cstring(conn, pool, path));
2596 SVN_ERR(write_tuple_start_list(conn, pool));
2597 SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2598 SVN_ERR(write_tuple_end_list(conn, pool));
2599 SVN_ERR(write_tuple_start_list(conn, pool));
2600 SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2601 SVN_ERR(write_tuple_end_list(conn, pool));
2602 SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2603 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2604
2605 return SVN_NO_ERROR;
2606 }
2607
2608 svn_error_t *
svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const char * comment,svn_boolean_t steal_lock,svn_revnum_t revnum)2609 svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2610 apr_pool_t *pool,
2611 const char *path,
2612 const char *comment,
2613 svn_boolean_t steal_lock,
2614 svn_revnum_t revnum)
2615 {
2616 SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2617 SVN_ERR(write_tuple_cstring(conn, pool, path));
2618 SVN_ERR(write_tuple_start_list(conn, pool));
2619 SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2620 SVN_ERR(write_tuple_end_list(conn, pool));
2621 SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2622 SVN_ERR(write_tuple_start_list(conn, pool));
2623 SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2624 SVN_ERR(write_tuple_end_list(conn, pool));
2625 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2626
2627 return SVN_NO_ERROR;
2628 }
2629
2630 svn_error_t *
svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,const svn_string_t * token,svn_boolean_t break_lock)2631 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2632 apr_pool_t *pool,
2633 const char *path,
2634 const svn_string_t *token,
2635 svn_boolean_t break_lock)
2636 {
2637 SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2638 SVN_ERR(write_tuple_cstring(conn, pool, path));
2639 SVN_ERR(write_tuple_string_opt_list(conn, pool, token));
2640 SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2641 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2642
2643 return SVN_NO_ERROR;
2644 }
2645
2646 svn_error_t *
svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path)2647 svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2648 apr_pool_t *pool,
2649 const char *path)
2650 {
2651 SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2652 SVN_ERR(write_tuple_cstring(conn, pool, path));
2653 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2654
2655 return SVN_NO_ERROR;
2656 }
2657
2658 svn_error_t *
svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_depth_t depth)2659 svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2660 apr_pool_t *pool,
2661 const char *path,
2662 svn_depth_t depth)
2663 {
2664 SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2665 SVN_ERR(write_tuple_cstring(conn, pool, path));
2666 SVN_ERR(write_tuple_start_list(conn, pool));
2667 SVN_ERR(write_tuple_depth(conn, pool, depth));
2668 SVN_ERR(write_tuple_end_list(conn, pool));
2669 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2670
2671 return SVN_NO_ERROR;
2672 }
2673
2674 svn_error_t *
svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t rev,svn_revnum_t low_water_mark,svn_boolean_t send_deltas)2675 svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2676 apr_pool_t *pool,
2677 svn_revnum_t rev,
2678 svn_revnum_t low_water_mark,
2679 svn_boolean_t send_deltas)
2680 {
2681 SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2682 SVN_ERR(write_tuple_revision(conn, pool, rev));
2683 SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2684 SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2685 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2686
2687 return SVN_NO_ERROR;
2688 }
2689
2690 svn_error_t *
svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t start_revision,svn_revnum_t end_revision,svn_revnum_t low_water_mark,svn_boolean_t send_deltas)2691 svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2692 apr_pool_t *pool,
2693 svn_revnum_t start_revision,
2694 svn_revnum_t end_revision,
2695 svn_revnum_t low_water_mark,
2696 svn_boolean_t send_deltas)
2697 {
2698 SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2699 SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2700 SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2701 SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2702 SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2703 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2704
2705 return SVN_NO_ERROR;
2706 }
2707
2708 svn_error_t *
svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t peg_revision,svn_revnum_t end_revision)2709 svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2710 apr_pool_t *pool,
2711 const char *path,
2712 svn_revnum_t peg_revision,
2713 svn_revnum_t end_revision)
2714 {
2715 SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2716 SVN_ERR(write_tuple_cstring(conn, pool, path));
2717 SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2718 SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2719 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2720
2721 return SVN_NO_ERROR;
2722 }
2723
2724 svn_error_t *
svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_revnum_t revision)2725 svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2726 apr_pool_t *pool,
2727 const char *path,
2728 svn_revnum_t revision)
2729 {
2730 SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2731 SVN_ERR(write_tuple_cstring(conn, pool, path));
2732 SVN_ERR(write_tuple_start_list(conn, pool));
2733 SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2734 SVN_ERR(write_tuple_end_list(conn, pool));
2735 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2736
2737 return SVN_NO_ERROR;
2738 }
2739
2740 svn_error_t *
svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t * conn,apr_pool_t * pool)2741 svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2742 apr_pool_t *pool)
2743 {
2744 return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2745 }
2746
svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * fmt,...)2747 svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2748 apr_pool_t *pool,
2749 const char *fmt, ...)
2750 {
2751 va_list ap;
2752 svn_error_t *err;
2753
2754 SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2755 va_start(ap, fmt);
2756 err = vwrite_tuple(conn, pool, fmt, &ap);
2757 va_end(ap);
2758 return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2759 }
2760
svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_error_t * err)2761 svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2762 apr_pool_t *pool,
2763 const svn_error_t *err)
2764 {
2765 char buffer[128];
2766 SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2767 for (; err; err = err->child)
2768 {
2769 const char *msg;
2770
2771 #ifdef SVN_ERR__TRACING
2772 if (svn_error__is_tracing_link(err))
2773 msg = err->message;
2774 else
2775 #endif
2776 msg = svn_err_best_message(err, buffer, sizeof(buffer));
2777
2778 /* The message string should have been optional, but we can't
2779 easily change that, so marshal nonexistent messages as "". */
2780 SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2781 (apr_uint64_t) err->apr_err,
2782 msg ? msg : "",
2783 err->file ? err->file : "",
2784 (apr_uint64_t) err->line));
2785 }
2786 return writebuf_write_literal(conn, pool, ") ) ");
2787 }
2788
2789 /* Initializer for static svn_string_t . */
2790 #define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
2791
2792 /* Return a pre-cooked serialized representation for the changed path
2793 flags NODE_KIND, TEXT_MODIFIED and PROPS_MODIFIED. If we don't
2794 have a suitable pre-cooked string, return an empty string. */
2795 static const svn_string_t *
changed_path_flags(svn_node_kind_t node_kind,svn_boolean_t text_modified,svn_boolean_t props_modified)2796 changed_path_flags(svn_node_kind_t node_kind,
2797 svn_boolean_t text_modified,
2798 svn_boolean_t props_modified)
2799 {
2800 static const svn_string_t file_flags[4]
2801 = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
2802 STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
2803 STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
2804 STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
2805
2806 static const svn_string_t dir_flags[4]
2807 = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
2808 STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
2809 STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
2810 STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
2811
2812 static const svn_string_t no_flags = STATIC_SVN_STRING("");
2813
2814 /* Select the array based on the NODE_KIND. */
2815 const svn_string_t *flags;
2816 if (node_kind == svn_node_file)
2817 flags = file_flags;
2818 else if (node_kind == svn_node_dir)
2819 flags = dir_flags;
2820 else
2821 return &no_flags;
2822
2823 /* Select the correct array entry. */
2824 if (text_modified)
2825 flags += 2;
2826 if (props_modified)
2827 flags++;
2828
2829 return flags;
2830 }
2831
2832 svn_error_t *
svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const svn_string_t * path,char action,const char * copyfrom_path,svn_revnum_t copyfrom_rev,svn_node_kind_t node_kind,svn_boolean_t text_modified,svn_boolean_t props_modified)2833 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2834 apr_pool_t *pool,
2835 const svn_string_t *path,
2836 char action,
2837 const char *copyfrom_path,
2838 svn_revnum_t copyfrom_rev,
2839 svn_node_kind_t node_kind,
2840 svn_boolean_t text_modified,
2841 svn_boolean_t props_modified)
2842 {
2843 apr_size_t path_len = path->len;
2844 apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
2845 const svn_string_t *flags_str = changed_path_flags(node_kind,
2846 text_modified,
2847 props_modified);
2848 apr_size_t flags_len = flags_str->len;
2849
2850 /* How much buffer space can we use for non-string data (worst case)? */
2851 apr_size_t max_fill = sizeof(conn->write_buf)
2852 - 2 /* list start */
2853 - 2 - SVN_INT64_BUFFER_SIZE /* path */
2854 - 2 /* action */
2855 - 2 /* list start */
2856 - 2 - SVN_INT64_BUFFER_SIZE /* copy-from path */
2857 - 1 - SVN_INT64_BUFFER_SIZE; /* copy-from rev */
2858
2859 /* If the remaining buffer is big enough and we've got all parts,
2860 directly copy into the buffer. On platforms with segmented memory,
2861 PATH_LEN + COPYFROM_LEN might actually be close to APR_SIZE_MAX.
2862 Blindly doing arithmetic on them might cause an overflow.
2863 The sum in here cannot overflow because WRITE_BUF is small, i.e.
2864 MAX_FILL and WRITE_POS are much smaller than APR_SIZE_MAX. */
2865 if ( (path_len <= max_fill) && (copyfrom_len <= max_fill)
2866 && (conn->write_pos + path_len + copyfrom_len + flags_len <= max_fill)
2867 && (flags_len > 0))
2868 {
2869 /* Quick path. */
2870 /* Open list. */
2871 char *p = conn->write_buf + conn->write_pos;
2872 p[0] = '(';
2873 p[1] = ' ';
2874
2875 /* Write path. */
2876 p = write_ncstring_quick(p + 2, path->data, path_len);
2877
2878 /* Action */
2879 p[0] = action;
2880 p[1] = ' ';
2881 p[2] = '(';
2882
2883 /* Copy-from info (if given) */
2884 if (copyfrom_path)
2885 {
2886 p[3] = ' ';
2887 p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
2888 p += svn__ui64toa(p, copyfrom_rev);
2889 }
2890 else
2891 {
2892 p += 3;
2893 }
2894
2895 /* Close with flags. */
2896 memcpy(p, flags_str->data, flags_str->len);
2897 conn->write_pos = p + flags_str->len - conn->write_buf;
2898 }
2899 else
2900 {
2901 /* Standard code path (fallback). */
2902 SVN_ERR(write_tuple_start_list(conn, pool));
2903
2904 SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path->data, path_len));
2905 SVN_ERR(writebuf_writechar(conn, pool, action));
2906 SVN_ERR(writebuf_writechar(conn, pool, ' '));
2907 SVN_ERR(write_tuple_start_list(conn, pool));
2908 SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2909 SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2910 SVN_ERR(write_tuple_end_list(conn, pool));
2911 SVN_ERR(write_tuple_start_list(conn, pool));
2912 SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2913 SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2914 SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2915
2916 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2917 }
2918
2919 return SVN_NO_ERROR;
2920 }
2921
2922 svn_error_t *
svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t * conn,apr_pool_t * pool,svn_revnum_t revision,const svn_string_t * author,const svn_string_t * date,const svn_string_t * message,svn_boolean_t has_children,svn_boolean_t invalid_revnum,unsigned revprop_count)2923 svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2924 apr_pool_t *pool,
2925 svn_revnum_t revision,
2926 const svn_string_t *author,
2927 const svn_string_t *date,
2928 const svn_string_t *message,
2929 svn_boolean_t has_children,
2930 svn_boolean_t invalid_revnum,
2931 unsigned revprop_count)
2932 {
2933 SVN_ERR(write_tuple_revision(conn, pool, revision));
2934 SVN_ERR(write_tuple_string_opt_list(conn, pool, author));
2935 SVN_ERR(write_tuple_string_opt_list(conn, pool, date));
2936 SVN_ERR(write_tuple_string_opt_list(conn, pool, message));
2937 SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2938 SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2939 SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2940
2941 return SVN_NO_ERROR;
2942 }
2943
2944 svn_error_t *
svn_ra_svn__write_dirent(svn_ra_svn_conn_t * conn,apr_pool_t * pool,const char * path,svn_dirent_t * dirent,apr_uint32_t dirent_fields)2945 svn_ra_svn__write_dirent(svn_ra_svn_conn_t *conn,
2946 apr_pool_t *pool,
2947 const char *path,
2948 svn_dirent_t *dirent,
2949 apr_uint32_t dirent_fields)
2950 {
2951 const char *kind = (dirent_fields & SVN_DIRENT_KIND)
2952 ? svn_node_kind_to_word(dirent->kind)
2953 : "unknown";
2954
2955 if (dirent_fields & ~SVN_DIRENT_KIND)
2956 {
2957 SVN_ERR(write_tuple_start_list(conn, pool));
2958 SVN_ERR(write_tuple_cstring(conn, pool, path));
2959 SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
2960
2961 SVN_ERR(writebuf_write_literal(conn, pool, " ( "));
2962 if (dirent_fields & SVN_DIRENT_SIZE)
2963 SVN_ERR(svn_ra_svn__write_number(conn, pool, dirent->size));
2964
2965 SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2966 if (dirent_fields & SVN_DIRENT_HAS_PROPS)
2967 SVN_ERR(write_tuple_boolean(conn, pool, dirent->has_props));
2968
2969 SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2970 if (dirent_fields & SVN_DIRENT_CREATED_REV)
2971 SVN_ERR(write_tuple_revision(conn, pool, dirent->created_rev));
2972
2973 SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2974 if (dirent_fields & SVN_DIRENT_TIME)
2975 SVN_ERR(write_tuple_cstring_opt(conn, pool,
2976 svn_time_to_cstring(dirent->time, pool)));
2977
2978 SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2979 if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
2980 SVN_ERR(write_tuple_cstring_opt(conn, pool, dirent->last_author));
2981
2982 SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2983 }
2984 else
2985 {
2986 SVN_ERR(write_tuple_start_list(conn, pool));
2987 SVN_ERR(write_tuple_cstring(conn, pool, path));
2988 SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
2989 SVN_ERR(writebuf_write_literal(conn, pool, " ) "));
2990 }
2991
2992 return SVN_NO_ERROR;
2993 }
2994
2995 /* If condition COND is not met, return a "malformed network data" error.
2996 */
2997 #define CHECK_PROTOCOL_COND(cond)\
2998 if (!(cond)) \
2999 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
3000 _("Malformed network data"));
3001
3002 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
3003 */
3004 static svn_error_t *
svn_ra_svn__read_string(const svn_ra_svn__list_t * items,int idx,svn_string_t ** result)3005 svn_ra_svn__read_string(const svn_ra_svn__list_t *items,
3006 int idx,
3007 svn_string_t **result)
3008 {
3009 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3010 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
3011 *result = &elt->u.string;
3012
3013 return SVN_NO_ERROR;
3014 }
3015
3016 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
3017 */
3018 static svn_error_t *
svn_ra_svn__read_cstring(const svn_ra_svn__list_t * items,int idx,const char ** result)3019 svn_ra_svn__read_cstring(const svn_ra_svn__list_t *items,
3020 int idx,
3021 const char **result)
3022 {
3023 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3024 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
3025 *result = elt->u.string.data;
3026
3027 return SVN_NO_ERROR;
3028 }
3029
3030 /* In *RESULT, return the word at index IDX in tuple ITEMS.
3031 */
3032 static svn_error_t *
svn_ra_svn__read_word(const svn_ra_svn__list_t * items,int idx,const char ** result)3033 svn_ra_svn__read_word(const svn_ra_svn__list_t *items,
3034 int idx,
3035 const char **result)
3036 {
3037 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3038 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
3039 *result = elt->u.word.data;
3040
3041 return SVN_NO_ERROR;
3042 }
3043
3044 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
3045 */
3046 static svn_error_t *
svn_ra_svn__read_revision(const svn_ra_svn__list_t * items,int idx,svn_revnum_t * result)3047 svn_ra_svn__read_revision(const svn_ra_svn__list_t *items,
3048 int idx,
3049 svn_revnum_t *result)
3050 {
3051 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3052 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
3053 *result = (svn_revnum_t)elt->u.number;
3054
3055 return SVN_NO_ERROR;
3056 }
3057
3058 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
3059 */
3060 static svn_error_t *
svn_ra_svn__read_boolean(const svn_ra_svn__list_t * items,int idx,apr_uint64_t * result)3061 svn_ra_svn__read_boolean(const svn_ra_svn__list_t *items,
3062 int idx,
3063 apr_uint64_t *result)
3064 {
3065 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3066 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
3067 if (svn_string_compare(&elt->u.word, &str_true))
3068 *result = TRUE;
3069 else if (svn_string_compare(&elt->u.word, &str_false))
3070 *result = FALSE;
3071 else
3072 CHECK_PROTOCOL_COND(FALSE);
3073
3074 return SVN_NO_ERROR;
3075 }
3076
3077 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
3078 */
3079 static svn_error_t *
svn_ra_svn__read_list(const svn_ra_svn__list_t * items,int idx,const svn_ra_svn__list_t ** result)3080 svn_ra_svn__read_list(const svn_ra_svn__list_t *items,
3081 int idx,
3082 const svn_ra_svn__list_t **result)
3083 {
3084 svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3085 CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
3086
3087 *result = &elt->u.list;
3088 return SVN_NO_ERROR;
3089 }
3090
3091 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
3092 */
3093 static svn_error_t *
svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t * items,int min,int max)3094 svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t *items,
3095 int min,
3096 int max)
3097 {
3098 CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
3099 return SVN_NO_ERROR;
3100 }
3101
3102 svn_error_t *
svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t * items,svn_string_t ** cpath,const char ** action,const char ** copy_path,svn_revnum_t * copy_rev,const char ** kind_str,apr_uint64_t * text_mods,apr_uint64_t * prop_mods)3103 svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t *items,
3104 svn_string_t **cpath,
3105 const char **action,
3106 const char **copy_path,
3107 svn_revnum_t *copy_rev,
3108 const char **kind_str,
3109 apr_uint64_t *text_mods,
3110 apr_uint64_t *prop_mods)
3111 {
3112 const svn_ra_svn__list_t *sub_items;
3113
3114 /* initialize optional values */
3115 *copy_path = NULL;
3116 *copy_rev = SVN_INVALID_REVNUM;
3117 *kind_str = NULL;
3118 *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3119 *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3120
3121 /* top-level elements (mandatory) */
3122 SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
3123 SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
3124 SVN_ERR(svn_ra_svn__read_word(items, 1, action));
3125
3126 /* first sub-structure (mandatory) */
3127 SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
3128 if (sub_items->nelts)
3129 {
3130 SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
3131 SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
3132 SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
3133 }
3134
3135 /* second sub-structure (optional) */
3136 if (items->nelts >= 4)
3137 {
3138 SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
3139 switch (MIN(3, sub_items->nelts))
3140 {
3141 case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
3142 case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
3143 case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
3144 default: break;
3145 }
3146 }
3147
3148 return SVN_NO_ERROR;
3149 }
3150