low_level.c revision 289177
1289177Speter/* low_level.c --- low level r/w access to fs_x file structures 2289177Speter * 3289177Speter * ==================================================================== 4289177Speter * Licensed to the Apache Software Foundation (ASF) under one 5289177Speter * or more contributor license agreements. See the NOTICE file 6289177Speter * distributed with this work for additional information 7289177Speter * regarding copyright ownership. The ASF licenses this file 8289177Speter * to you under the Apache License, Version 2.0 (the 9289177Speter * "License"); you may not use this file except in compliance 10289177Speter * with the License. You may obtain a copy of the License at 11289177Speter * 12289177Speter * http://www.apache.org/licenses/LICENSE-2.0 13289177Speter * 14289177Speter * Unless required by applicable law or agreed to in writing, 15289177Speter * software distributed under the License is distributed on an 16289177Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17289177Speter * KIND, either express or implied. See the License for the 18289177Speter * specific language governing permissions and limitations 19289177Speter * under the License. 20289177Speter * ==================================================================== 21289177Speter */ 22289177Speter 23289177Speter#include "svn_private_config.h" 24289177Speter#include "svn_hash.h" 25289177Speter#include "svn_pools.h" 26289177Speter#include "svn_sorts.h" 27289177Speter#include "private/svn_sorts_private.h" 28289177Speter#include "private/svn_string_private.h" 29289177Speter#include "private/svn_subr_private.h" 30289177Speter#include "private/svn_fspath.h" 31289177Speter 32289177Speter#include "../libsvn_fs/fs-loader.h" 33289177Speter 34289177Speter#include "low_level.h" 35289177Speter#include "util.h" 36289177Speter#include "pack.h" 37289177Speter#include "cached_data.h" 38289177Speter 39289177Speter/* Headers used to describe node-revision in the revision file. */ 40289177Speter#define HEADER_ID "id" 41289177Speter#define HEADER_NODE "node" 42289177Speter#define HEADER_COPY "copy" 43289177Speter#define HEADER_TYPE "type" 44289177Speter#define HEADER_COUNT "count" 45289177Speter#define HEADER_PROPS "props" 46289177Speter#define HEADER_TEXT "text" 47289177Speter#define HEADER_CPATH "cpath" 48289177Speter#define HEADER_PRED "pred" 49289177Speter#define HEADER_COPYFROM "copyfrom" 50289177Speter#define HEADER_COPYROOT "copyroot" 51289177Speter#define HEADER_MINFO_HERE "minfo-here" 52289177Speter#define HEADER_MINFO_CNT "minfo-cnt" 53289177Speter 54289177Speter/* Kinds that a change can be. */ 55289177Speter#define ACTION_MODIFY "modify" 56289177Speter#define ACTION_ADD "add" 57289177Speter#define ACTION_DELETE "delete" 58289177Speter#define ACTION_REPLACE "replace" 59289177Speter#define ACTION_RESET "reset" 60289177Speter 61289177Speter/* True and False flags. */ 62289177Speter#define FLAG_TRUE "true" 63289177Speter#define FLAG_FALSE "false" 64289177Speter 65289177Speter/* Kinds of representation. */ 66289177Speter#define REP_DELTA "DELTA" 67289177Speter 68289177Speter/* An arbitrary maximum path length, so clients can't run us out of memory 69289177Speter * by giving us arbitrarily large paths. */ 70289177Speter#define FSX_MAX_PATH_LEN 4096 71289177Speter 72289177Speter/* The 256 is an arbitrary size large enough to hold the node id and the 73289177Speter * various flags. */ 74289177Speter#define MAX_CHANGE_LINE_LEN FSX_MAX_PATH_LEN + 256 75289177Speter 76289177Speter/* Convert the C string in *TEXT to a revision number and return it in *REV. 77289177Speter * Overflows, negative values other than -1 and terminating characters other 78289177Speter * than 0x20 or 0x0 will cause an error. Set *TEXT to the first char after 79289177Speter * the initial separator or to EOS. 80289177Speter */ 81289177Speterstatic svn_error_t * 82289177Speterparse_revnum(svn_revnum_t *rev, 83289177Speter const char **text) 84289177Speter{ 85289177Speter const char *string = *text; 86289177Speter if ((string[0] == '-') && (string[1] == '1')) 87289177Speter { 88289177Speter *rev = SVN_INVALID_REVNUM; 89289177Speter string += 2; 90289177Speter } 91289177Speter else 92289177Speter { 93289177Speter SVN_ERR(svn_revnum_parse(rev, string, &string)); 94289177Speter } 95289177Speter 96289177Speter if (*string == ' ') 97289177Speter ++string; 98289177Speter else if (*string != '\0') 99289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 100289177Speter _("Invalid character in revision number")); 101289177Speter 102289177Speter *text = string; 103289177Speter return SVN_NO_ERROR; 104289177Speter} 105289177Speter 106289177Spetersvn_error_t * 107289177Spetersvn_fs_x__parse_footer(apr_off_t *l2p_offset, 108289177Speter svn_checksum_t **l2p_checksum, 109289177Speter apr_off_t *p2l_offset, 110289177Speter svn_checksum_t **p2l_checksum, 111289177Speter svn_stringbuf_t *footer, 112289177Speter svn_revnum_t rev, 113289177Speter apr_pool_t *result_pool) 114289177Speter{ 115289177Speter apr_int64_t val; 116289177Speter char *last_str = footer->data; 117289177Speter 118289177Speter /* Get the L2P offset. */ 119289177Speter const char *str = svn_cstring_tokenize(" ", &last_str); 120289177Speter if (str == NULL) 121289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 122289177Speter _("Invalid revision footer")); 123289177Speter 124289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 125289177Speter *l2p_offset = (apr_off_t)val; 126289177Speter 127289177Speter /* Get the L2P checksum. */ 128289177Speter str = svn_cstring_tokenize(" ", &last_str); 129289177Speter if (str == NULL) 130289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 131289177Speter _("Invalid revision footer")); 132289177Speter 133289177Speter SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str, 134289177Speter result_pool)); 135289177Speter 136289177Speter /* Get the P2L offset. */ 137289177Speter str = svn_cstring_tokenize(" ", &last_str); 138289177Speter if (str == NULL) 139289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 140289177Speter _("Invalid revision footer")); 141289177Speter 142289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 143289177Speter *p2l_offset = (apr_off_t)val; 144289177Speter 145289177Speter /* Get the P2L checksum. */ 146289177Speter str = svn_cstring_tokenize(" ", &last_str); 147289177Speter if (str == NULL) 148289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 149289177Speter _("Invalid revision footer")); 150289177Speter 151289177Speter SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str, 152289177Speter result_pool)); 153289177Speter 154289177Speter return SVN_NO_ERROR; 155289177Speter} 156289177Speter 157289177Spetersvn_stringbuf_t * 158289177Spetersvn_fs_x__unparse_footer(apr_off_t l2p_offset, 159289177Speter svn_checksum_t *l2p_checksum, 160289177Speter apr_off_t p2l_offset, 161289177Speter svn_checksum_t *p2l_checksum, 162289177Speter apr_pool_t *result_pool, 163289177Speter apr_pool_t *scratch_pool) 164289177Speter{ 165289177Speter return svn_stringbuf_createf(result_pool, 166289177Speter "%" APR_OFF_T_FMT " %s %" APR_OFF_T_FMT " %s", 167289177Speter l2p_offset, 168289177Speter svn_checksum_to_cstring(l2p_checksum, 169289177Speter scratch_pool), 170289177Speter p2l_offset, 171289177Speter svn_checksum_to_cstring(p2l_checksum, 172289177Speter scratch_pool)); 173289177Speter} 174289177Speter 175289177Speter/* Given a revision file FILE that has been pre-positioned at the 176289177Speter beginning of a Node-Rev header block, read in that header block and 177289177Speter store it in the apr_hash_t HEADERS. All allocations will be from 178289177Speter RESULT_POOL. */ 179289177Speterstatic svn_error_t * 180289177Speterread_header_block(apr_hash_t **headers, 181289177Speter svn_stream_t *stream, 182289177Speter apr_pool_t *result_pool) 183289177Speter{ 184289177Speter *headers = svn_hash__make(result_pool); 185289177Speter 186289177Speter while (1) 187289177Speter { 188289177Speter svn_stringbuf_t *header_str; 189289177Speter const char *name, *value; 190289177Speter apr_size_t i = 0; 191289177Speter apr_size_t name_len; 192289177Speter svn_boolean_t eof; 193289177Speter 194289177Speter SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, 195289177Speter result_pool)); 196289177Speter 197289177Speter if (eof || header_str->len == 0) 198289177Speter break; /* end of header block */ 199289177Speter 200289177Speter while (header_str->data[i] != ':') 201289177Speter { 202289177Speter if (header_str->data[i] == '\0') 203289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 204289177Speter _("Found malformed header '%s' in " 205289177Speter "revision file"), 206289177Speter header_str->data); 207289177Speter i++; 208289177Speter } 209289177Speter 210289177Speter /* Create a 'name' string and point to it. */ 211289177Speter header_str->data[i] = '\0'; 212289177Speter name = header_str->data; 213289177Speter name_len = i; 214289177Speter 215289177Speter /* Check if we have enough data to parse. */ 216289177Speter if (i + 2 > header_str->len) 217289177Speter { 218289177Speter /* Restore the original line for the error. */ 219289177Speter header_str->data[i] = ':'; 220289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 221289177Speter _("Found malformed header '%s' in " 222289177Speter "revision file"), 223289177Speter header_str->data); 224289177Speter } 225289177Speter 226289177Speter /* Skip over the NULL byte and the space following it. */ 227289177Speter i += 2; 228289177Speter 229289177Speter value = header_str->data + i; 230289177Speter 231289177Speter /* header_str is safely in our pool, so we can use bits of it as 232289177Speter key and value. */ 233289177Speter apr_hash_set(*headers, name, name_len, value); 234289177Speter } 235289177Speter 236289177Speter return SVN_NO_ERROR; 237289177Speter} 238289177Speter 239289177Spetersvn_error_t * 240289177Spetersvn_fs_x__parse_representation(svn_fs_x__representation_t **rep_p, 241289177Speter svn_stringbuf_t *text, 242289177Speter apr_pool_t *result_pool, 243289177Speter apr_pool_t *scratch_pool) 244289177Speter{ 245289177Speter svn_fs_x__representation_t *rep; 246289177Speter char *str; 247289177Speter apr_int64_t val; 248289177Speter char *string = text->data; 249289177Speter svn_checksum_t *checksum; 250289177Speter 251289177Speter rep = apr_pcalloc(result_pool, sizeof(*rep)); 252289177Speter *rep_p = rep; 253289177Speter 254289177Speter str = svn_cstring_tokenize(" ", &string); 255289177Speter if (str == NULL) 256289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 257289177Speter _("Malformed text representation offset line in node-rev")); 258289177Speter 259289177Speter SVN_ERR(svn_cstring_atoi64(&rep->id.change_set, str)); 260289177Speter 261289177Speter /* while in transactions, it is legal to simply write "-1" */ 262289177Speter if (rep->id.change_set == -1) 263289177Speter return SVN_NO_ERROR; 264289177Speter 265289177Speter str = svn_cstring_tokenize(" ", &string); 266289177Speter if (str == NULL) 267289177Speter { 268289177Speter if (rep->id.change_set == SVN_FS_X__INVALID_CHANGE_SET) 269289177Speter return SVN_NO_ERROR; 270289177Speter 271289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 272289177Speter _("Malformed text representation offset line in node-rev")); 273289177Speter } 274289177Speter 275289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 276289177Speter rep->id.number = (apr_off_t)val; 277289177Speter 278289177Speter str = svn_cstring_tokenize(" ", &string); 279289177Speter if (str == NULL) 280289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 281289177Speter _("Malformed text representation offset line in node-rev")); 282289177Speter 283289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 284289177Speter rep->size = (svn_filesize_t)val; 285289177Speter 286289177Speter str = svn_cstring_tokenize(" ", &string); 287289177Speter if (str == NULL) 288289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 289289177Speter _("Malformed text representation offset line in node-rev")); 290289177Speter 291289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 292289177Speter rep->expanded_size = (svn_filesize_t)val; 293289177Speter 294289177Speter /* Read in the MD5 hash. */ 295289177Speter str = svn_cstring_tokenize(" ", &string); 296289177Speter if ((str == NULL) || (strlen(str) != (APR_MD5_DIGESTSIZE * 2))) 297289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 298289177Speter _("Malformed text representation offset line in node-rev")); 299289177Speter 300289177Speter SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, str, 301289177Speter scratch_pool)); 302289177Speter if (checksum) 303289177Speter memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest)); 304289177Speter 305289177Speter /* The remaining fields are only used for formats >= 4, so check that. */ 306289177Speter str = svn_cstring_tokenize(" ", &string); 307289177Speter if (str == NULL) 308289177Speter return SVN_NO_ERROR; 309289177Speter 310289177Speter /* Read the SHA1 hash. */ 311289177Speter if (strlen(str) != (APR_SHA1_DIGESTSIZE * 2)) 312289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 313289177Speter _("Malformed text representation offset line in node-rev")); 314289177Speter 315289177Speter SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str, 316289177Speter scratch_pool)); 317289177Speter rep->has_sha1 = checksum != NULL; 318289177Speter if (checksum) 319289177Speter memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest)); 320289177Speter 321289177Speter return SVN_NO_ERROR; 322289177Speter} 323289177Speter 324289177Speter/* Wrap read_rep_offsets_body(), extracting its TXN_ID from our NODEREV_ID, 325289177Speter and adding an error message. */ 326289177Speterstatic svn_error_t * 327289177Speterread_rep_offsets(svn_fs_x__representation_t **rep_p, 328289177Speter char *string, 329289177Speter const svn_fs_x__id_t *noderev_id, 330289177Speter apr_pool_t *result_pool, 331289177Speter apr_pool_t *scratch_pool) 332289177Speter{ 333289177Speter svn_error_t *err 334289177Speter = svn_fs_x__parse_representation(rep_p, 335289177Speter svn_stringbuf_create_wrap(string, 336289177Speter scratch_pool), 337289177Speter result_pool, 338289177Speter scratch_pool); 339289177Speter if (err) 340289177Speter { 341289177Speter const svn_string_t *id_unparsed; 342289177Speter const char *where; 343289177Speter 344289177Speter id_unparsed = svn_fs_x__id_unparse(noderev_id, scratch_pool); 345289177Speter where = apr_psprintf(scratch_pool, 346289177Speter _("While reading representation offsets " 347289177Speter "for node-revision '%s':"), 348289177Speter id_unparsed->data); 349289177Speter 350289177Speter return svn_error_quick_wrap(err, where); 351289177Speter } 352289177Speter 353289177Speter return SVN_NO_ERROR; 354289177Speter} 355289177Speter 356289177Speter/* If PATH needs to be escaped, return an escaped version of it, allocated 357289177Speter * from RESULT_POOL. Otherwise, return PATH directly. */ 358289177Speterstatic const char * 359289177Speterauto_escape_path(const char *path, 360289177Speter apr_pool_t *result_pool) 361289177Speter{ 362289177Speter apr_size_t len = strlen(path); 363289177Speter apr_size_t i; 364289177Speter const char esc = '\x1b'; 365289177Speter 366289177Speter for (i = 0; i < len; ++i) 367289177Speter if (path[i] < ' ') 368289177Speter { 369289177Speter svn_stringbuf_t *escaped = svn_stringbuf_create_ensure(2 * len, 370289177Speter result_pool); 371289177Speter for (i = 0; i < len; ++i) 372289177Speter if (path[i] < ' ') 373289177Speter { 374289177Speter svn_stringbuf_appendbyte(escaped, esc); 375289177Speter svn_stringbuf_appendbyte(escaped, path[i] + 'A' - 1); 376289177Speter } 377289177Speter else 378289177Speter { 379289177Speter svn_stringbuf_appendbyte(escaped, path[i]); 380289177Speter } 381289177Speter 382289177Speter return escaped->data; 383289177Speter } 384289177Speter 385289177Speter return path; 386289177Speter} 387289177Speter 388289177Speter/* If PATH has been escaped, return the un-escaped version of it, allocated 389289177Speter * from RESULT_POOL. Otherwise, return PATH directly. */ 390289177Speterstatic const char * 391289177Speterauto_unescape_path(const char *path, 392289177Speter apr_pool_t *result_pool) 393289177Speter{ 394289177Speter const char esc = '\x1b'; 395289177Speter if (strchr(path, esc)) 396289177Speter { 397289177Speter apr_size_t len = strlen(path); 398289177Speter apr_size_t i; 399289177Speter 400289177Speter svn_stringbuf_t *unescaped = svn_stringbuf_create_ensure(len, 401289177Speter result_pool); 402289177Speter for (i = 0; i < len; ++i) 403289177Speter if (path[i] == esc) 404289177Speter svn_stringbuf_appendbyte(unescaped, path[++i] + 1 - 'A'); 405289177Speter else 406289177Speter svn_stringbuf_appendbyte(unescaped, path[i]); 407289177Speter 408289177Speter return unescaped->data; 409289177Speter } 410289177Speter 411289177Speter return path; 412289177Speter} 413289177Speter 414289177Speter/* Find entry HEADER_NAME in HEADERS and parse its value into *ID. */ 415289177Speterstatic svn_error_t * 416289177Speterread_id_part(svn_fs_x__id_t *id, 417289177Speter apr_hash_t *headers, 418289177Speter const char *header_name) 419289177Speter{ 420289177Speter const char *value = svn_hash_gets(headers, header_name); 421289177Speter if (value == NULL) 422289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 423289177Speter _("Missing %s field in node-rev"), 424289177Speter header_name); 425289177Speter 426289177Speter SVN_ERR(svn_fs_x__id_parse(id, value)); 427289177Speter return SVN_NO_ERROR; 428289177Speter} 429289177Speter 430289177Spetersvn_error_t * 431289177Spetersvn_fs_x__read_noderev(svn_fs_x__noderev_t **noderev_p, 432289177Speter svn_stream_t *stream, 433289177Speter apr_pool_t *result_pool, 434289177Speter apr_pool_t *scratch_pool) 435289177Speter{ 436289177Speter apr_hash_t *headers; 437289177Speter svn_fs_x__noderev_t *noderev; 438289177Speter char *value; 439289177Speter const char *noderev_id; 440289177Speter 441289177Speter SVN_ERR(read_header_block(&headers, stream, scratch_pool)); 442289177Speter SVN_ERR(svn_stream_close(stream)); 443289177Speter 444289177Speter noderev = apr_pcalloc(result_pool, sizeof(*noderev)); 445289177Speter 446289177Speter /* for error messages later */ 447289177Speter noderev_id = svn_hash_gets(headers, HEADER_ID); 448289177Speter 449289177Speter /* Read the node-rev id. */ 450289177Speter SVN_ERR(read_id_part(&noderev->noderev_id, headers, HEADER_ID)); 451289177Speter SVN_ERR(read_id_part(&noderev->node_id, headers, HEADER_NODE)); 452289177Speter SVN_ERR(read_id_part(&noderev->copy_id, headers, HEADER_COPY)); 453289177Speter 454289177Speter /* Read the type. */ 455289177Speter value = svn_hash_gets(headers, HEADER_TYPE); 456289177Speter 457289177Speter if ((value == NULL) || 458289177Speter ( strcmp(value, SVN_FS_X__KIND_FILE) 459289177Speter && strcmp(value, SVN_FS_X__KIND_DIR))) 460289177Speter /* ### s/kind/type/ */ 461289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 462289177Speter _("Missing kind field in node-rev '%s'"), 463289177Speter noderev_id); 464289177Speter 465289177Speter noderev->kind = (strcmp(value, SVN_FS_X__KIND_FILE) == 0) 466289177Speter ? svn_node_file 467289177Speter : svn_node_dir; 468289177Speter 469289177Speter /* Read the 'count' field. */ 470289177Speter value = svn_hash_gets(headers, HEADER_COUNT); 471289177Speter if (value) 472289177Speter SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, value)); 473289177Speter else 474289177Speter noderev->predecessor_count = 0; 475289177Speter 476289177Speter /* Get the properties location. */ 477289177Speter value = svn_hash_gets(headers, HEADER_PROPS); 478289177Speter if (value) 479289177Speter { 480289177Speter SVN_ERR(read_rep_offsets(&noderev->prop_rep, value, 481289177Speter &noderev->noderev_id, result_pool, 482289177Speter scratch_pool)); 483289177Speter } 484289177Speter 485289177Speter /* Get the data location. */ 486289177Speter value = svn_hash_gets(headers, HEADER_TEXT); 487289177Speter if (value) 488289177Speter { 489289177Speter SVN_ERR(read_rep_offsets(&noderev->data_rep, value, 490289177Speter &noderev->noderev_id, result_pool, 491289177Speter scratch_pool)); 492289177Speter } 493289177Speter 494289177Speter /* Get the created path. */ 495289177Speter value = svn_hash_gets(headers, HEADER_CPATH); 496289177Speter if (value == NULL) 497289177Speter { 498289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 499289177Speter _("Missing cpath field in node-rev '%s'"), 500289177Speter noderev_id); 501289177Speter } 502289177Speter else 503289177Speter { 504289177Speter if (!svn_fspath__is_canonical(value)) 505289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 506289177Speter _("Non-canonical cpath field in node-rev '%s'"), 507289177Speter noderev_id); 508289177Speter 509289177Speter noderev->created_path = auto_unescape_path(apr_pstrdup(result_pool, 510289177Speter value), 511289177Speter result_pool); 512289177Speter } 513289177Speter 514289177Speter /* Get the predecessor ID. */ 515289177Speter value = svn_hash_gets(headers, HEADER_PRED); 516289177Speter if (value) 517289177Speter SVN_ERR(svn_fs_x__id_parse(&noderev->predecessor_id, value)); 518289177Speter else 519289177Speter svn_fs_x__id_reset(&noderev->predecessor_id); 520289177Speter 521289177Speter /* Get the copyroot. */ 522289177Speter value = svn_hash_gets(headers, HEADER_COPYROOT); 523289177Speter if (value == NULL) 524289177Speter { 525289177Speter noderev->copyroot_path = noderev->created_path; 526289177Speter noderev->copyroot_rev 527289177Speter = svn_fs_x__get_revnum(noderev->noderev_id.change_set); 528289177Speter } 529289177Speter else 530289177Speter { 531289177Speter SVN_ERR(parse_revnum(&noderev->copyroot_rev, (const char **)&value)); 532289177Speter 533289177Speter if (!svn_fspath__is_canonical(value)) 534289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 535289177Speter _("Malformed copyroot line in node-rev '%s'"), 536289177Speter noderev_id); 537289177Speter noderev->copyroot_path = auto_unescape_path(apr_pstrdup(result_pool, 538289177Speter value), 539289177Speter result_pool); 540289177Speter } 541289177Speter 542289177Speter /* Get the copyfrom. */ 543289177Speter value = svn_hash_gets(headers, HEADER_COPYFROM); 544289177Speter if (value == NULL) 545289177Speter { 546289177Speter noderev->copyfrom_path = NULL; 547289177Speter noderev->copyfrom_rev = SVN_INVALID_REVNUM; 548289177Speter } 549289177Speter else 550289177Speter { 551289177Speter SVN_ERR(parse_revnum(&noderev->copyfrom_rev, (const char **)&value)); 552289177Speter 553289177Speter if (*value == 0) 554289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 555289177Speter _("Malformed copyfrom line in node-rev '%s'"), 556289177Speter noderev_id); 557289177Speter noderev->copyfrom_path = auto_unescape_path(apr_pstrdup(result_pool, 558289177Speter value), 559289177Speter result_pool); 560289177Speter } 561289177Speter 562289177Speter /* Get the mergeinfo count. */ 563289177Speter value = svn_hash_gets(headers, HEADER_MINFO_CNT); 564289177Speter if (value) 565289177Speter SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, value)); 566289177Speter else 567289177Speter noderev->mergeinfo_count = 0; 568289177Speter 569289177Speter /* Get whether *this* node has mergeinfo. */ 570289177Speter value = svn_hash_gets(headers, HEADER_MINFO_HERE); 571289177Speter noderev->has_mergeinfo = (value != NULL); 572289177Speter 573289177Speter *noderev_p = noderev; 574289177Speter 575289177Speter return SVN_NO_ERROR; 576289177Speter} 577289177Speter 578289177Speter/* Return a textual representation of the DIGEST of given KIND. 579289177Speter * If IS_NULL is TRUE, no digest is available. 580289177Speter * Allocate the result in RESULT_POOL. 581289177Speter */ 582289177Speterstatic const char * 583289177Speterformat_digest(const unsigned char *digest, 584289177Speter svn_checksum_kind_t kind, 585289177Speter svn_boolean_t is_null, 586289177Speter apr_pool_t *result_pool) 587289177Speter{ 588289177Speter svn_checksum_t checksum; 589289177Speter checksum.digest = digest; 590289177Speter checksum.kind = kind; 591289177Speter 592289177Speter if (is_null) 593289177Speter return "(null)"; 594289177Speter 595289177Speter return svn_checksum_to_cstring_display(&checksum, result_pool); 596289177Speter} 597289177Speter 598289177Spetersvn_stringbuf_t * 599289177Spetersvn_fs_x__unparse_representation(svn_fs_x__representation_t *rep, 600289177Speter svn_boolean_t mutable_rep_truncated, 601289177Speter apr_pool_t *result_pool, 602289177Speter apr_pool_t *scratch_pool) 603289177Speter{ 604289177Speter if (!rep->has_sha1) 605289177Speter return svn_stringbuf_createf 606289177Speter (result_pool, 607289177Speter "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT 608289177Speter " %" SVN_FILESIZE_T_FMT " %s", 609289177Speter rep->id.change_set, rep->id.number, rep->size, 610289177Speter rep->expanded_size, 611289177Speter format_digest(rep->md5_digest, svn_checksum_md5, FALSE, 612289177Speter scratch_pool)); 613289177Speter 614289177Speter return svn_stringbuf_createf 615289177Speter (result_pool, 616289177Speter "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT 617289177Speter " %" SVN_FILESIZE_T_FMT " %s %s", 618289177Speter rep->id.change_set, rep->id.number, rep->size, 619289177Speter rep->expanded_size, 620289177Speter format_digest(rep->md5_digest, svn_checksum_md5, 621289177Speter FALSE, scratch_pool), 622289177Speter format_digest(rep->sha1_digest, svn_checksum_sha1, 623289177Speter !rep->has_sha1, scratch_pool)); 624289177Speter} 625289177Speter 626289177Speter 627289177Spetersvn_error_t * 628289177Spetersvn_fs_x__write_noderev(svn_stream_t *outfile, 629289177Speter svn_fs_x__noderev_t *noderev, 630289177Speter apr_pool_t *scratch_pool) 631289177Speter{ 632289177Speter svn_string_t *str_id; 633289177Speter 634289177Speter str_id = svn_fs_x__id_unparse(&noderev->noderev_id, scratch_pool); 635289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n", 636289177Speter str_id->data)); 637289177Speter str_id = svn_fs_x__id_unparse(&noderev->node_id, scratch_pool); 638289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_NODE ": %s\n", 639289177Speter str_id->data)); 640289177Speter str_id = svn_fs_x__id_unparse(&noderev->copy_id, scratch_pool); 641289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPY ": %s\n", 642289177Speter str_id->data)); 643289177Speter 644289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n", 645289177Speter (noderev->kind == svn_node_file) ? 646289177Speter SVN_FS_X__KIND_FILE : SVN_FS_X__KIND_DIR)); 647289177Speter 648289177Speter if (svn_fs_x__id_used(&noderev->predecessor_id)) 649289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n", 650289177Speter svn_fs_x__id_unparse(&noderev->predecessor_id, 651289177Speter scratch_pool)->data)); 652289177Speter 653289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n", 654289177Speter noderev->predecessor_count)); 655289177Speter 656289177Speter if (noderev->data_rep) 657289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n", 658289177Speter svn_fs_x__unparse_representation 659289177Speter (noderev->data_rep, 660289177Speter noderev->kind == svn_node_dir, 661289177Speter scratch_pool, scratch_pool)->data)); 662289177Speter 663289177Speter if (noderev->prop_rep) 664289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n", 665289177Speter svn_fs_x__unparse_representation 666289177Speter (noderev->prop_rep, 667289177Speter TRUE, scratch_pool, scratch_pool)->data)); 668289177Speter 669289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n", 670289177Speter auto_escape_path(noderev->created_path, 671289177Speter scratch_pool))); 672289177Speter 673289177Speter if (noderev->copyfrom_path) 674289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYFROM ": %ld" 675289177Speter " %s\n", 676289177Speter noderev->copyfrom_rev, 677289177Speter auto_escape_path(noderev->copyfrom_path, 678289177Speter scratch_pool))); 679289177Speter 680289177Speter if ( ( noderev->copyroot_rev 681289177Speter != svn_fs_x__get_revnum(noderev->noderev_id.change_set)) 682289177Speter || (strcmp(noderev->copyroot_path, noderev->created_path) != 0)) 683289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYROOT ": %ld" 684289177Speter " %s\n", 685289177Speter noderev->copyroot_rev, 686289177Speter auto_escape_path(noderev->copyroot_path, 687289177Speter scratch_pool))); 688289177Speter 689289177Speter if (noderev->mergeinfo_count > 0) 690289177Speter SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_MINFO_CNT ": %" 691289177Speter APR_INT64_T_FMT "\n", 692289177Speter noderev->mergeinfo_count)); 693289177Speter 694289177Speter if (noderev->has_mergeinfo) 695289177Speter SVN_ERR(svn_stream_puts(outfile, HEADER_MINFO_HERE ": y\n")); 696289177Speter 697289177Speter return svn_stream_puts(outfile, "\n"); 698289177Speter} 699289177Speter 700289177Spetersvn_error_t * 701289177Spetersvn_fs_x__read_rep_header(svn_fs_x__rep_header_t **header, 702289177Speter svn_stream_t *stream, 703289177Speter apr_pool_t *result_pool, 704289177Speter apr_pool_t *scratch_pool) 705289177Speter{ 706289177Speter svn_stringbuf_t *buffer; 707289177Speter char *str, *last_str; 708289177Speter apr_int64_t val; 709289177Speter svn_boolean_t eol = FALSE; 710289177Speter 711289177Speter SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, scratch_pool)); 712289177Speter 713289177Speter *header = apr_pcalloc(result_pool, sizeof(**header)); 714289177Speter (*header)->header_size = buffer->len + 1; 715289177Speter if (strcmp(buffer->data, REP_DELTA) == 0) 716289177Speter { 717289177Speter /* This is a delta against the empty stream. */ 718289177Speter (*header)->type = svn_fs_x__rep_self_delta; 719289177Speter return SVN_NO_ERROR; 720289177Speter } 721289177Speter 722289177Speter (*header)->type = svn_fs_x__rep_delta; 723289177Speter 724289177Speter /* We have hopefully a DELTA vs. a non-empty base revision. */ 725289177Speter last_str = buffer->data; 726289177Speter str = svn_cstring_tokenize(" ", &last_str); 727289177Speter if (! str || (strcmp(str, REP_DELTA) != 0)) 728289177Speter goto error; 729289177Speter 730289177Speter SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str)); 731289177Speter 732289177Speter str = svn_cstring_tokenize(" ", &last_str); 733289177Speter if (! str) 734289177Speter goto error; 735289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 736289177Speter (*header)->base_item_index = (apr_off_t)val; 737289177Speter 738289177Speter str = svn_cstring_tokenize(" ", &last_str); 739289177Speter if (! str) 740289177Speter goto error; 741289177Speter SVN_ERR(svn_cstring_atoi64(&val, str)); 742289177Speter (*header)->base_length = (svn_filesize_t)val; 743289177Speter 744289177Speter return SVN_NO_ERROR; 745289177Speter 746289177Speter error: 747289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 748289177Speter _("Malformed representation header")); 749289177Speter} 750289177Speter 751289177Spetersvn_error_t * 752289177Spetersvn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header, 753289177Speter svn_stream_t *stream, 754289177Speter apr_pool_t *scratch_pool) 755289177Speter{ 756289177Speter const char *text; 757289177Speter 758289177Speter switch (header->type) 759289177Speter { 760289177Speter case svn_fs_x__rep_self_delta: 761289177Speter text = REP_DELTA "\n"; 762289177Speter break; 763289177Speter 764289177Speter default: 765289177Speter text = apr_psprintf(scratch_pool, REP_DELTA " %ld %" APR_OFF_T_FMT 766289177Speter " %" SVN_FILESIZE_T_FMT "\n", 767289177Speter header->base_revision, header->base_item_index, 768289177Speter header->base_length); 769289177Speter } 770289177Speter 771289177Speter return svn_error_trace(svn_stream_puts(stream, text)); 772289177Speter} 773289177Speter 774289177Speter/* Read the next entry in the changes record from file FILE and store 775289177Speter the resulting change in *CHANGE_P. If there is no next record, 776289177Speter store NULL there. Perform all allocations from POOL. */ 777289177Speterstatic svn_error_t * 778289177Speterread_change(svn_fs_x__change_t **change_p, 779289177Speter svn_stream_t *stream, 780289177Speter apr_pool_t *result_pool, 781289177Speter apr_pool_t *scratch_pool) 782289177Speter{ 783289177Speter svn_stringbuf_t *line; 784289177Speter svn_boolean_t eof = TRUE; 785289177Speter svn_fs_x__change_t *change; 786289177Speter char *str, *last_str, *kind_str; 787289177Speter 788289177Speter /* Default return value. */ 789289177Speter *change_p = NULL; 790289177Speter 791289177Speter SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); 792289177Speter 793289177Speter /* Check for a blank line. */ 794289177Speter if (eof || (line->len == 0)) 795289177Speter return SVN_NO_ERROR; 796289177Speter 797289177Speter change = apr_pcalloc(result_pool, sizeof(*change)); 798289177Speter last_str = line->data; 799289177Speter 800289177Speter /* Get the node-id of the change. */ 801289177Speter str = svn_cstring_tokenize(" ", &last_str); 802289177Speter if (str == NULL) 803289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 804289177Speter _("Invalid changes line in rev-file")); 805289177Speter 806289177Speter SVN_ERR(svn_fs_x__id_parse(&change->noderev_id, str)); 807289177Speter 808289177Speter /* Get the change type. */ 809289177Speter str = svn_cstring_tokenize(" ", &last_str); 810289177Speter if (str == NULL) 811289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 812289177Speter _("Invalid changes line in rev-file")); 813289177Speter 814289177Speter /* Don't bother to check the format number before looking for 815289177Speter * node-kinds: just read them if you find them. */ 816289177Speter change->node_kind = svn_node_unknown; 817289177Speter kind_str = strchr(str, '-'); 818289177Speter if (kind_str) 819289177Speter { 820289177Speter /* Cap off the end of "str" (the action). */ 821289177Speter *kind_str = '\0'; 822289177Speter kind_str++; 823289177Speter if (strcmp(kind_str, SVN_FS_X__KIND_FILE) == 0) 824289177Speter change->node_kind = svn_node_file; 825289177Speter else if (strcmp(kind_str, SVN_FS_X__KIND_DIR) == 0) 826289177Speter change->node_kind = svn_node_dir; 827289177Speter else 828289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 829289177Speter _("Invalid changes line in rev-file")); 830289177Speter } 831289177Speter 832289177Speter if (strcmp(str, ACTION_MODIFY) == 0) 833289177Speter { 834289177Speter change->change_kind = svn_fs_path_change_modify; 835289177Speter } 836289177Speter else if (strcmp(str, ACTION_ADD) == 0) 837289177Speter { 838289177Speter change->change_kind = svn_fs_path_change_add; 839289177Speter } 840289177Speter else if (strcmp(str, ACTION_DELETE) == 0) 841289177Speter { 842289177Speter change->change_kind = svn_fs_path_change_delete; 843289177Speter } 844289177Speter else if (strcmp(str, ACTION_REPLACE) == 0) 845289177Speter { 846289177Speter change->change_kind = svn_fs_path_change_replace; 847289177Speter } 848289177Speter else if (strcmp(str, ACTION_RESET) == 0) 849289177Speter { 850289177Speter change->change_kind = svn_fs_path_change_reset; 851289177Speter } 852289177Speter else 853289177Speter { 854289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 855289177Speter _("Invalid change kind in rev file")); 856289177Speter } 857289177Speter 858289177Speter /* Get the text-mod flag. */ 859289177Speter str = svn_cstring_tokenize(" ", &last_str); 860289177Speter if (str == NULL) 861289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 862289177Speter _("Invalid changes line in rev-file")); 863289177Speter 864289177Speter if (strcmp(str, FLAG_TRUE) == 0) 865289177Speter { 866289177Speter change->text_mod = TRUE; 867289177Speter } 868289177Speter else if (strcmp(str, FLAG_FALSE) == 0) 869289177Speter { 870289177Speter change->text_mod = FALSE; 871289177Speter } 872289177Speter else 873289177Speter { 874289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 875289177Speter _("Invalid text-mod flag in rev-file")); 876289177Speter } 877289177Speter 878289177Speter /* Get the prop-mod flag. */ 879289177Speter str = svn_cstring_tokenize(" ", &last_str); 880289177Speter if (str == NULL) 881289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 882289177Speter _("Invalid changes line in rev-file")); 883289177Speter 884289177Speter if (strcmp(str, FLAG_TRUE) == 0) 885289177Speter { 886289177Speter change->prop_mod = TRUE; 887289177Speter } 888289177Speter else if (strcmp(str, FLAG_FALSE) == 0) 889289177Speter { 890289177Speter change->prop_mod = FALSE; 891289177Speter } 892289177Speter else 893289177Speter { 894289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 895289177Speter _("Invalid prop-mod flag in rev-file")); 896289177Speter } 897289177Speter 898289177Speter /* Get the mergeinfo-mod flag. */ 899289177Speter str = svn_cstring_tokenize(" ", &last_str); 900289177Speter if (str == NULL) 901289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 902289177Speter _("Invalid changes line in rev-file")); 903289177Speter 904289177Speter if (strcmp(str, FLAG_TRUE) == 0) 905289177Speter { 906289177Speter change->mergeinfo_mod = svn_tristate_true; 907289177Speter } 908289177Speter else if (strcmp(str, FLAG_FALSE) == 0) 909289177Speter { 910289177Speter change->mergeinfo_mod = svn_tristate_false; 911289177Speter } 912289177Speter else 913289177Speter { 914289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 915289177Speter _("Invalid mergeinfo-mod flag in rev-file")); 916289177Speter } 917289177Speter 918289177Speter /* Get the changed path. */ 919289177Speter if (!svn_fspath__is_canonical(last_str)) 920289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 921289177Speter _("Invalid path in changes line")); 922289177Speter 923289177Speter change->path.data = auto_unescape_path(apr_pstrmemdup(result_pool, 924289177Speter last_str, 925289177Speter strlen(last_str)), 926289177Speter result_pool); 927289177Speter change->path.len = strlen(change->path.data); 928289177Speter 929289177Speter /* Read the next line, the copyfrom line. */ 930289177Speter SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool)); 931289177Speter change->copyfrom_known = TRUE; 932289177Speter if (eof || line->len == 0) 933289177Speter { 934289177Speter change->copyfrom_rev = SVN_INVALID_REVNUM; 935289177Speter change->copyfrom_path = NULL; 936289177Speter } 937289177Speter else 938289177Speter { 939289177Speter last_str = line->data; 940289177Speter SVN_ERR(parse_revnum(&change->copyfrom_rev, (const char **)&last_str)); 941289177Speter 942289177Speter if (!svn_fspath__is_canonical(last_str)) 943289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 944289177Speter _("Invalid copy-from path in changes line")); 945289177Speter 946289177Speter change->copyfrom_path = auto_unescape_path(last_str, result_pool); 947289177Speter } 948289177Speter 949289177Speter *change_p = change; 950289177Speter 951289177Speter return SVN_NO_ERROR; 952289177Speter} 953289177Speter 954289177Spetersvn_error_t * 955289177Spetersvn_fs_x__read_changes(apr_array_header_t **changes, 956289177Speter svn_stream_t *stream, 957289177Speter apr_pool_t *result_pool, 958289177Speter apr_pool_t *scratch_pool) 959289177Speter{ 960289177Speter svn_fs_x__change_t *change; 961289177Speter apr_pool_t *iterpool; 962289177Speter 963289177Speter /* Pre-allocate enough room for most change lists. 964289177Speter (will be auto-expanded as necessary). 965289177Speter 966289177Speter Chose the default to just below 2^N such that the doubling reallocs 967289177Speter will request roughly 2^M bytes from the OS without exceeding the 968289177Speter respective two-power by just a few bytes (leaves room array and APR 969289177Speter node overhead for large enough M). 970289177Speter */ 971289177Speter *changes = apr_array_make(result_pool, 63, sizeof(svn_fs_x__change_t *)); 972289177Speter 973289177Speter SVN_ERR(read_change(&change, stream, result_pool, scratch_pool)); 974289177Speter iterpool = svn_pool_create(scratch_pool); 975289177Speter while (change) 976289177Speter { 977289177Speter APR_ARRAY_PUSH(*changes, svn_fs_x__change_t*) = change; 978289177Speter SVN_ERR(read_change(&change, stream, result_pool, iterpool)); 979289177Speter svn_pool_clear(iterpool); 980289177Speter } 981289177Speter svn_pool_destroy(iterpool); 982289177Speter 983289177Speter return SVN_NO_ERROR; 984289177Speter} 985289177Speter 986289177Spetersvn_error_t * 987289177Spetersvn_fs_x__read_changes_incrementally(svn_stream_t *stream, 988289177Speter svn_fs_x__change_receiver_t 989289177Speter change_receiver, 990289177Speter void *change_receiver_baton, 991289177Speter apr_pool_t *scratch_pool) 992289177Speter{ 993289177Speter svn_fs_x__change_t *change; 994289177Speter apr_pool_t *iterpool; 995289177Speter 996289177Speter iterpool = svn_pool_create(scratch_pool); 997289177Speter do 998289177Speter { 999289177Speter svn_pool_clear(iterpool); 1000289177Speter 1001289177Speter SVN_ERR(read_change(&change, stream, iterpool, iterpool)); 1002289177Speter if (change) 1003289177Speter SVN_ERR(change_receiver(change_receiver_baton, change, iterpool)); 1004289177Speter } 1005289177Speter while (change); 1006289177Speter svn_pool_destroy(iterpool); 1007289177Speter 1008289177Speter return SVN_NO_ERROR; 1009289177Speter} 1010289177Speter 1011289177Speter/* Write a single change entry, path PATH, change CHANGE, to STREAM. 1012289177Speter 1013289177Speter All temporary allocations are in SCRATCH_POOL. */ 1014289177Speterstatic svn_error_t * 1015289177Speterwrite_change_entry(svn_stream_t *stream, 1016289177Speter svn_fs_x__change_t *change, 1017289177Speter apr_pool_t *scratch_pool) 1018289177Speter{ 1019289177Speter const char *idstr; 1020289177Speter const char *change_string = NULL; 1021289177Speter const char *kind_string = ""; 1022289177Speter svn_stringbuf_t *buf; 1023289177Speter apr_size_t len; 1024289177Speter 1025289177Speter switch (change->change_kind) 1026289177Speter { 1027289177Speter case svn_fs_path_change_modify: 1028289177Speter change_string = ACTION_MODIFY; 1029289177Speter break; 1030289177Speter case svn_fs_path_change_add: 1031289177Speter change_string = ACTION_ADD; 1032289177Speter break; 1033289177Speter case svn_fs_path_change_delete: 1034289177Speter change_string = ACTION_DELETE; 1035289177Speter break; 1036289177Speter case svn_fs_path_change_replace: 1037289177Speter change_string = ACTION_REPLACE; 1038289177Speter break; 1039289177Speter case svn_fs_path_change_reset: 1040289177Speter change_string = ACTION_RESET; 1041289177Speter break; 1042289177Speter default: 1043289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 1044289177Speter _("Invalid change type %d"), 1045289177Speter change->change_kind); 1046289177Speter } 1047289177Speter 1048289177Speter idstr = svn_fs_x__id_unparse(&change->noderev_id, scratch_pool)->data; 1049289177Speter 1050289177Speter SVN_ERR_ASSERT(change->node_kind == svn_node_dir 1051289177Speter || change->node_kind == svn_node_file); 1052289177Speter kind_string = apr_psprintf(scratch_pool, "-%s", 1053289177Speter change->node_kind == svn_node_dir 1054289177Speter ? SVN_FS_X__KIND_DIR 1055289177Speter : SVN_FS_X__KIND_FILE); 1056289177Speter 1057289177Speter buf = svn_stringbuf_createf(scratch_pool, "%s %s%s %s %s %s %s\n", 1058289177Speter idstr, change_string, kind_string, 1059289177Speter change->text_mod ? FLAG_TRUE : FLAG_FALSE, 1060289177Speter change->prop_mod ? FLAG_TRUE : FLAG_FALSE, 1061289177Speter change->mergeinfo_mod == svn_tristate_true 1062289177Speter ? FLAG_TRUE : FLAG_FALSE, 1063289177Speter auto_escape_path(change->path.data, scratch_pool)); 1064289177Speter 1065289177Speter if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) 1066289177Speter { 1067289177Speter svn_stringbuf_appendcstr(buf, apr_psprintf(scratch_pool, "%ld %s", 1068289177Speter change->copyfrom_rev, 1069289177Speter auto_escape_path(change->copyfrom_path, 1070289177Speter scratch_pool))); 1071289177Speter } 1072289177Speter 1073289177Speter svn_stringbuf_appendbyte(buf, '\n'); 1074289177Speter 1075289177Speter /* Write all change info in one write call. */ 1076289177Speter len = buf->len; 1077289177Speter return svn_error_trace(svn_stream_write(stream, buf->data, &len)); 1078289177Speter} 1079289177Speter 1080289177Spetersvn_error_t * 1081289177Spetersvn_fs_x__write_changes(svn_stream_t *stream, 1082289177Speter svn_fs_t *fs, 1083289177Speter apr_hash_t *changes, 1084289177Speter svn_boolean_t terminate_list, 1085289177Speter apr_pool_t *scratch_pool) 1086289177Speter{ 1087289177Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1088289177Speter apr_array_header_t *sorted_changed_paths; 1089289177Speter int i; 1090289177Speter 1091289177Speter /* For the sake of the repository administrator sort the changes so 1092289177Speter that the final file is deterministic and repeatable, however the 1093289177Speter rest of the FSX code doesn't require any particular order here. 1094289177Speter 1095289177Speter Also, this sorting is only effective in writing all entries with 1096289177Speter a single call as write_final_changed_path_info() does. For the 1097289177Speter list being written incrementally during transaction, we actually 1098289177Speter *must not* change the order of entries from different calls. 1099289177Speter */ 1100289177Speter sorted_changed_paths = svn_sort__hash(changes, 1101289177Speter svn_sort_compare_items_lexically, 1102289177Speter scratch_pool); 1103289177Speter 1104289177Speter /* Write all items to disk in the new order. */ 1105289177Speter for (i = 0; i < sorted_changed_paths->nelts; ++i) 1106289177Speter { 1107289177Speter svn_fs_x__change_t *change; 1108289177Speter 1109289177Speter svn_pool_clear(iterpool); 1110289177Speter change = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).value; 1111289177Speter 1112289177Speter /* Write out the new entry into the final rev-file. */ 1113289177Speter SVN_ERR(write_change_entry(stream, change, iterpool)); 1114289177Speter } 1115289177Speter 1116289177Speter if (terminate_list) 1117289177Speter svn_stream_puts(stream, "\n"); 1118289177Speter 1119289177Speter svn_pool_destroy(iterpool); 1120289177Speter 1121289177Speter return SVN_NO_ERROR; 1122289177Speter} 1123289177Speter 1124