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. */ 76static const svn_string_t str_true = SVN__STATIC_STRING("true"); 77static 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. */ 81static apr_interval_time_t 82get_timeout(svn_ra_svn_conn_t *conn) 83{ 84 return conn->block_handler ? 0 : -1; 85} 86 87/* --- Public / private API data conversion --- */ 88 89void 90svn_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 113apr_array_header_t * 114svn_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 132void 133svn_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 157svn_ra_svn__list_t * 158svn_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 182svn_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 239svn_error_t * 240svn_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 248svn_error_t * 249svn_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 268int 269svn_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 286apr_pool_t * 287svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn) 288{ 289 return conn->pool; 290} 291 292svn_error_t * 293svn_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 300svn_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 306int 307svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn) 308{ 309 return conn->compression_level; 310} 311 312apr_size_t 313svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn) 314{ 315 return conn->zero_copy_limit; 316} 317 318const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn) 319{ 320 return conn->remote_ip; 321} 322 323void 324svn_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 333svn_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 339void 340svn_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. */ 350static svn_error_t * 351check_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. */ 367static 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. */ 420static 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 430static 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 461static APR_INLINE svn_error_t * 462writebuf_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. */ 482static 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. */ 494static 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" */ 527static 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. */ 552static 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 */ 575static SVN__FORCE_INLINE svn_error_t * 576readbuf_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 584static 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. */ 595static 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 624static 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 664static 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 681svn_error_t * 682svn_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 */ 697static char * 698write_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 723static svn_error_t * 724svn_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 756svn_error_t * 757svn_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 765svn_error_t * 766svn_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 774svn_error_t * 775svn_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 785svn_error_t * 786svn_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 798svn_error_t * 799svn_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 829svn_error_t * 830svn_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 844svn_error_t * 845svn_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 859svn_error_t * 860svn_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 871static svn_error_t * 872vwrite_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 879static svn_error_t * 880vwrite_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 886static svn_error_t * 887vwrite_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 894static svn_error_t * 895vwrite_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 901static svn_error_t * 902vwrite_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 909static svn_error_t * 910vwrite_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 916static svn_error_t * 917vwrite_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 924static svn_error_t * 925vwrite_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 933static svn_error_t * 934vwrite_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 939static svn_error_t * 940vwrite_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 945static svn_error_t * 946write_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 954static svn_error_t * 955write_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 962static svn_error_t * 963write_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 971static svn_error_t * 972write_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. */ 980static svn_error_t * 981write_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 1028static svn_error_t * 1029write_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 1035static svn_error_t * 1036write_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 1042static svn_error_t * 1043write_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 1051static svn_error_t * 1052write_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 1061static svn_error_t * 1062write_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 1069static svn_error_t * 1070write_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 1078static svn_error_t * 1079write_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 1098static svn_error_t * 1099write_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 1116static svn_error_t * 1117write_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 1130static svn_error_t * 1131write_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 1145static 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 1197svn_error_t * 1198svn_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. */ 1216static 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. */ 1298static 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. */ 1466static svn_error_t * 1467read_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 1552svn_error_t * 1553svn_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 */ 1570static svn_error_t * 1571svn_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 1596svn_error_t * 1597svn_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. */ 1607static svn_error_t * 1608vparse_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 1720svn_error_t * 1721svn_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 1733svn_error_t * 1734svn_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 1752svn_error_t * 1753svn_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 1765svn_error_t * 1766svn_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 1792svn_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 1807svn_error_t * 1808svn_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 1855svn_error_t * 1856svn_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 1883svn_error_t * 1884svn_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 1906svn_error_t * 1907svn_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 1983svn_error_t * 1984svn_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 2019svn_error_t * 2020svn_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 2031svn_error_t * 2032svn_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 2047svn_error_t * 2048svn_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 2065svn_error_t * 2066svn_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 2082svn_error_t * 2083svn_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 2097svn_error_t * 2098svn_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 2111svn_error_t * 2112svn_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 2123svn_error_t * 2124svn_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 2136svn_error_t * 2137svn_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 2153svn_error_t * 2154svn_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 2168svn_error_t * 2169svn_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 2182svn_error_t * 2183svn_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 2198svn_error_t * 2199svn_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 2211svn_error_t * 2212svn_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 2225svn_error_t * 2226svn_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 2237svn_error_t * 2238svn_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 2253svn_error_t * 2254svn_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 2260svn_error_t * 2261svn_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 2267svn_error_t * 2268svn_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 2289svn_error_t * 2290svn_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 2301svn_error_t * 2302svn_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 2325svn_error_t * 2326svn_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 2332svn_error_t * 2333svn_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 2339svn_error_t * 2340svn_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 2351svn_error_t * 2352svn_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 2358svn_error_t * 2359svn_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 2370svn_error_t * 2371svn_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 2392svn_error_t * 2393svn_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 2408svn_error_t * 2409svn_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 2420svn_error_t * 2421svn_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 2434svn_error_t * 2435svn_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 2458svn_error_t * 2459svn_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 2482svn_error_t * 2483svn_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 2508svn_error_t * 2509svn_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 2528svn_error_t * 2529svn_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 2554svn_error_t * 2555svn_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 2570svn_error_t * 2571svn_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 2586svn_error_t * 2587svn_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 2608svn_error_t * 2609svn_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 2630svn_error_t * 2631svn_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 2646svn_error_t * 2647svn_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 2658svn_error_t * 2659svn_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 2674svn_error_t * 2675svn_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 2690svn_error_t * 2691svn_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 2708svn_error_t * 2709svn_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 2724svn_error_t * 2725svn_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 2740svn_error_t * 2741svn_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 2747svn_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 2761svn_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. */ 2795static const svn_string_t * 2796changed_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 2832svn_error_t * 2833svn_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 2922svn_error_t * 2923svn_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 2944svn_error_t * 2945svn_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 */ 3004static svn_error_t * 3005svn_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 */ 3018static svn_error_t * 3019svn_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 */ 3032static svn_error_t * 3033svn_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 */ 3046static svn_error_t * 3047svn_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 */ 3060static svn_error_t * 3061svn_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 */ 3079static svn_error_t * 3080svn_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 */ 3093static svn_error_t * 3094svn_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 3102svn_error_t * 3103svn_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