aboutsummaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_fs/low_level.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_fs/low_level.c')
-rw-r--r--subversion/libsvn_fs_fs/low_level.c238
1 files changed, 180 insertions, 58 deletions
diff --git a/subversion/libsvn_fs_fs/low_level.c b/subversion/libsvn_fs_fs/low_level.c
index d21e312faa2e..2854bc6e2d84 100644
--- a/subversion/libsvn_fs_fs/low_level.c
+++ b/subversion/libsvn_fs_fs/low_level.c
@@ -189,6 +189,19 @@ svn_fs_fs__unparse_revision_trailer(apr_off_t root_offset,
changes_offset);
}
+/* If ERR is not NULL, wrap it MESSAGE. The latter must have an %ld
+ * format parameter that will be filled with REV. */
+static svn_error_t *
+wrap_footer_error(svn_error_t *err,
+ const char *message,
+ svn_revnum_t rev)
+{
+ if (err)
+ return svn_error_quick_wrapf(err, message, rev);
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
svn_checksum_t **l2p_checksum,
@@ -196,6 +209,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
svn_checksum_t **p2l_checksum,
svn_stringbuf_t *footer,
svn_revnum_t rev,
+ apr_off_t footer_offset,
apr_pool_t *result_pool)
{
apr_int64_t val;
@@ -204,17 +218,20 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
/* Get the L2P offset. */
const char *str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("Invalid revision footer"));
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ "Invalid r%ld footer", rev);
- SVN_ERR(svn_cstring_atoi64(&val, str));
+ SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0,
+ footer_offset - 1, 10),
+ "Invalid L2P offset in r%ld footer",
+ rev));
*l2p_offset = (apr_off_t)val;
/* Get the L2P checksum. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("Invalid revision footer"));
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ "Invalid r%ld footer", rev);
SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str,
result_pool));
@@ -222,17 +239,33 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
/* Get the P2L offset. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("Invalid revision footer"));
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ "Invalid r%ld footer", rev);
- SVN_ERR(svn_cstring_atoi64(&val, str));
+ SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0,
+ footer_offset - 1, 10),
+ "Invalid P2L offset in r%ld footer",
+ rev));
*p2l_offset = (apr_off_t)val;
+ /* The P2L indes follows the L2P index */
+ if (*p2l_offset <= *l2p_offset)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ "P2L offset %s must be larger than L2P offset %s"
+ " in r%ld footer",
+ apr_psprintf(result_pool,
+ "%" APR_UINT64_T_HEX_FMT,
+ (apr_uint64_t)*p2l_offset),
+ apr_psprintf(result_pool,
+ "%" APR_UINT64_T_HEX_FMT,
+ (apr_uint64_t)*l2p_offset),
+ rev);
+
/* Get the P2L checksum. */
str = svn_cstring_tokenize(" ", &last_str);
if (str == NULL)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("Invalid revision footer"));
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ "Invalid r%ld footer", rev);
SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str,
result_pool));
@@ -449,10 +482,10 @@ read_change(change_t **change_p,
svn_error_t *
svn_fs_fs__read_changes(apr_array_header_t **changes,
svn_stream_t *stream,
+ int max_count,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- change_t *change;
apr_pool_t *iterpool;
/* Pre-allocate enough room for most change lists.
@@ -465,13 +498,16 @@ svn_fs_fs__read_changes(apr_array_header_t **changes,
*/
*changes = apr_array_make(result_pool, 63, sizeof(change_t *));
- SVN_ERR(read_change(&change, stream, result_pool, scratch_pool));
iterpool = svn_pool_create(scratch_pool);
- while (change)
+ for (; max_count > 0; --max_count)
{
- APR_ARRAY_PUSH(*changes, change_t*) = change;
- SVN_ERR(read_change(&change, stream, result_pool, iterpool));
+ change_t *change;
svn_pool_clear(iterpool);
+ SVN_ERR(read_change(&change, stream, result_pool, iterpool));
+ if (!change)
+ break;
+
+ APR_ARRAY_PUSH(*changes, change_t*) = change;
}
svn_pool_destroy(iterpool);
@@ -705,6 +741,9 @@ read_header_block(apr_hash_t **headers,
return SVN_NO_ERROR;
}
+/* ### Ouch! The implementation of this function currently modifies
+ ### the input string when tokenizing it (so the input cannot be
+ ### used after that). */
svn_error_t *
svn_fs_fs__parse_representation(representation_t **rep_p,
svn_stringbuf_t *text,
@@ -775,13 +814,21 @@ svn_fs_fs__parse_representation(representation_t **rep_p,
if (str == NULL)
return SVN_NO_ERROR;
- /* Read the SHA1 hash. */
- if (strlen(str) != (APR_SHA1_DIGESTSIZE * 2))
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("Malformed text representation offset line in node-rev"));
+ /* Is the SHA1 hash present? */
+ if (str[0] == '-' && str[1] == 0)
+ {
+ checksum = NULL;
+ }
+ else
+ {
+ /* Read the SHA1 hash. */
+ if (strlen(str) != (APR_SHA1_DIGESTSIZE * 2))
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Malformed text representation offset line in node-rev"));
- SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str,
- scratch_pool));
+ SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str,
+ scratch_pool));
+ }
/* We do have a valid SHA1 but it might be all 0.
We cannot be sure where that came from (Alas! legacy), so let's not
@@ -793,21 +840,36 @@ svn_fs_fs__parse_representation(representation_t **rep_p,
if (checksum)
memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));
- /* Read the uniquifier. */
- str = svn_cstring_tokenize("/", &string);
+ str = svn_cstring_tokenize(" ", &string);
if (str == NULL)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed text representation offset line in node-rev"));
- SVN_ERR(svn_fs_fs__id_txn_parse(&rep->uniquifier.noderev_txn_id, str));
+ /* Is the uniquifier present? */
+ if (str[0] == '-' && str[1] == 0)
+ {
+ end = string;
+ }
+ else
+ {
+ char *substring = str;
- str = svn_cstring_tokenize(" ", &string);
- if (str == NULL || *str != '_')
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("Malformed text representation offset line in node-rev"));
+ /* Read the uniquifier. */
+ str = svn_cstring_tokenize("/", &substring);
+ if (str == NULL)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Malformed text representation offset line in node-rev"));
- ++str;
- rep->uniquifier.number = svn__base36toui64(&end, str);
+ SVN_ERR(svn_fs_fs__id_txn_parse(&rep->uniquifier.noderev_txn_id, str));
+
+ str = svn_cstring_tokenize(" ", &substring);
+ if (str == NULL || *str != '_')
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Malformed text representation offset line in node-rev"));
+
+ ++str;
+ rep->uniquifier.number = svn__base36toui64(&end, str);
+ }
if (*end)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -998,25 +1060,37 @@ svn_fs_fs__read_noderev(node_revision_t **noderev_p,
}
/* Return a textual representation of the DIGEST of given KIND.
- * If IS_NULL is TRUE, no digest is available.
* Allocate the result in RESULT_POOL.
*/
static const char *
format_digest(const unsigned char *digest,
svn_checksum_kind_t kind,
- svn_boolean_t is_null,
apr_pool_t *result_pool)
{
svn_checksum_t checksum;
checksum.digest = digest;
checksum.kind = kind;
- if (is_null)
- return "(null)";
-
return svn_checksum_to_cstring_display(&checksum, result_pool);
}
+/* Return a textual representation of the uniquifier represented
+ * by NODEREV_TXN_ID and NUMBER. Use POOL for the allocations.
+ */
+static const char *
+format_uniquifier(const svn_fs_fs__id_part_t *noderev_txn_id,
+ apr_uint64_t number,
+ apr_pool_t *pool)
+{
+ char buf[SVN_INT64_BUFFER_SIZE];
+ const char *txn_id_str;
+
+ txn_id_str = svn_fs_fs__id_txn_unparse(noderev_txn_id, pool);
+ svn__ui64tobase36(buf, number);
+
+ return apr_psprintf(pool, "%s/_%s", txn_id_str, buf);
+}
+
svn_stringbuf_t *
svn_fs_fs__unparse_representation(representation_t *rep,
int format,
@@ -1024,32 +1098,80 @@ svn_fs_fs__unparse_representation(representation_t *rep,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- char buffer[SVN_INT64_BUFFER_SIZE];
+ svn_stringbuf_t *str;
+ const char *sha1_str;
+ const char *uniquifier_str;
+
if (svn_fs_fs__id_txn_used(&rep->txn_id) && mutable_rep_truncated)
return svn_stringbuf_ncreate("-1", 2, result_pool);
- if (format < SVN_FS_FS__MIN_REP_SHARING_FORMAT || !rep->has_sha1)
- return svn_stringbuf_createf
- (result_pool, "%ld %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT
- " %" SVN_FILESIZE_T_FMT " %s",
- rep->revision, rep->item_index, rep->size,
- rep->expanded_size,
- format_digest(rep->md5_digest, svn_checksum_md5, FALSE,
- scratch_pool));
-
- svn__ui64tobase36(buffer, rep->uniquifier.number);
- return svn_stringbuf_createf
- (result_pool, "%ld %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT
- " %" SVN_FILESIZE_T_FMT " %s %s %s/_%s",
- rep->revision, rep->item_index, rep->size,
- rep->expanded_size,
- format_digest(rep->md5_digest, svn_checksum_md5,
- FALSE, scratch_pool),
- format_digest(rep->sha1_digest, svn_checksum_sha1,
- !rep->has_sha1, scratch_pool),
- svn_fs_fs__id_txn_unparse(&rep->uniquifier.noderev_txn_id,
- scratch_pool),
- buffer);
+ /* Format of the string:
+ <rev> <item_index> <size> <expanded-size> <md5> [<sha1>] [<uniquifier>]
+ */
+ str = svn_stringbuf_createf(
+ result_pool,
+ "%ld"
+ " %" APR_UINT64_T_FMT
+ " %" SVN_FILESIZE_T_FMT
+ " %" SVN_FILESIZE_T_FMT
+ " %s",
+ rep->revision,
+ rep->item_index,
+ rep->size,
+ rep->expanded_size,
+ format_digest(rep->md5_digest, svn_checksum_md5, scratch_pool));
+
+ /* Compatibility: these formats don't understand <sha1> and <uniquifier>. */
+ if (format < SVN_FS_FS__MIN_REP_SHARING_FORMAT)
+ return str;
+
+ if (format < SVN_FS_FS__MIN_REP_STRING_OPTIONAL_VALUES_FORMAT)
+ {
+ /* Compatibility: these formats can only have <sha1> and <uniquifier>
+ present simultaneously, or don't have them at all. */
+ if (rep->has_sha1)
+ {
+ sha1_str = format_digest(rep->sha1_digest, svn_checksum_sha1,
+ scratch_pool);
+ uniquifier_str = format_uniquifier(&rep->uniquifier.noderev_txn_id,
+ rep->uniquifier.number,
+ scratch_pool);
+ svn_stringbuf_appendbyte(str, ' ');
+ svn_stringbuf_appendcstr(str, sha1_str);
+ svn_stringbuf_appendbyte(str, ' ');
+ svn_stringbuf_appendcstr(str, uniquifier_str);
+ }
+ return str;
+ }
+
+ /* The most recent formats support optional <sha1> and <uniquifier> values. */
+ if (rep->has_sha1)
+ {
+ sha1_str = format_digest(rep->sha1_digest, svn_checksum_sha1,
+ scratch_pool);
+ }
+ else
+ sha1_str = "-";
+
+ if (rep->uniquifier.number == 0 &&
+ rep->uniquifier.noderev_txn_id.number == 0 &&
+ rep->uniquifier.noderev_txn_id.revision == 0)
+ {
+ uniquifier_str = "-";
+ }
+ else
+ {
+ uniquifier_str = format_uniquifier(&rep->uniquifier.noderev_txn_id,
+ rep->uniquifier.number,
+ scratch_pool);
+ }
+
+ svn_stringbuf_appendbyte(str, ' ');
+ svn_stringbuf_appendcstr(str, sha1_str);
+ svn_stringbuf_appendbyte(str, ' ');
+ svn_stringbuf_appendcstr(str, uniquifier_str);
+
+ return str;
}