Exemplo n.º 1
0
svn_error_t *
svn_fs_fs__read_rep_header(svn_fs_fs__rep_header_t **header,
                           svn_stream_t *stream,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
{
  svn_stringbuf_t *buffer;
  char *str, *last_str;
  apr_int64_t val;
  svn_boolean_t eol = FALSE;

  SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, scratch_pool));

  *header = apr_pcalloc(result_pool, sizeof(**header));
  (*header)->header_size = buffer->len + 1;
  if (strcmp(buffer->data, REP_PLAIN) == 0)
    {
      (*header)->type = svn_fs_fs__rep_plain;
      return SVN_NO_ERROR;
    }

  if (strcmp(buffer->data, REP_DELTA) == 0)
    {
      /* This is a delta against the empty stream. */
      (*header)->type = svn_fs_fs__rep_self_delta;
      return SVN_NO_ERROR;
    }

  (*header)->type = svn_fs_fs__rep_delta;

  /* We have hopefully a DELTA vs. a non-empty base revision. */
  last_str = buffer->data;
  str = svn_cstring_tokenize(" ", &last_str);
  if (! str || (strcmp(str, REP_DELTA) != 0))
    goto error;

  SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str));

  str = svn_cstring_tokenize(" ", &last_str);
  if (! str)
    goto error;
  SVN_ERR(svn_cstring_atoi64(&val, str));
  (*header)->base_item_index = (apr_off_t)val;

  str = svn_cstring_tokenize(" ", &last_str);
  if (! str)
    goto error;
  SVN_ERR(svn_cstring_atoi64(&val, str));
  (*header)->base_length = (svn_filesize_t)val;

  return SVN_NO_ERROR;

 error:
  return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                           _("Malformed representation header"));
}
Exemplo n.º 2
0
svn_error_t *
svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
                        svn_checksum_t **l2p_checksum,
                        apr_off_t *p2l_offset,
                        svn_checksum_t **p2l_checksum,
                        svn_stringbuf_t *footer,
                        svn_revnum_t rev,
                        apr_pool_t *result_pool)
{
  apr_int64_t val;
  char *last_str = footer->data;

  /* 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"));

  SVN_ERR(svn_cstring_atoi64(&val, str));
  *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"));

  SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str,
                                 result_pool));

  /* 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"));

  SVN_ERR(svn_cstring_atoi64(&val, str));
  *p2l_offset = (apr_off_t)val;

  /* 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"));

  SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str,
                                 result_pool));

  return SVN_NO_ERROR;
}
Exemplo n.º 3
0
Arquivo: id.c Projeto: Ranga123/test1
svn_fs_id_t *
svn_fs_base__id_parse(const char *data,
                      apr_size_t len,
                      apr_pool_t *pool)
{
    svn_fs_id_t *id;
    id_private_t *pvt;
    char *data_copy, *str;

    /* Dup the ID data into POOL.  Our returned ID will have references
       into this memory. */
    data_copy = apr_pstrmemdup(pool, data, len);

    /* Alloc a new svn_fs_id_t structure. */
    id = apr_palloc(pool, sizeof(*id));
    pvt = apr_palloc(pool, sizeof(*pvt));
    id->vtable = &id_vtable;
    id->fsap_data = pvt;

    /* Now, we basically just need to "split" this data on `.'
       characters.  We will use svn_cstring_tokenize, which will put
       terminators where each of the '.'s used to be.  Then our new
       id field will reference string locations inside our duplicate
       string.*/

    /* Node Id */
    str = svn_cstring_tokenize(".", &data_copy);
    if (str == NULL)
        return NULL;
    pvt->node_id = str;

    /* Copy Id */
    str = svn_cstring_tokenize(".", &data_copy);
    if (str == NULL)
        return NULL;
    pvt->copy_id = str;

    /* Txn Id */
    str = svn_cstring_tokenize(".", &data_copy);
    if (str == NULL)
        return NULL;
    pvt->txn_id = str;

    return id;
}
Exemplo n.º 4
0
void
svn_cstring_split_append(apr_array_header_t *array,
                         const char *input,
                         const char *sep_chars,
                         svn_boolean_t chop_whitespace,
                         apr_pool_t *pool)
{
  char *pats;
  char *p;

  pats = apr_pstrdup(pool, input);  /* strtok wants non-const data */
  p = svn_cstring_tokenize(sep_chars, &pats);

  while (p)
    {
      if (chop_whitespace)
        {
          while (svn_ctype_isspace(*p))
            p++;

          {
            char *e = p + (strlen(p) - 1);
            while ((e >= p) && (svn_ctype_isspace(*e)))
              e--;
            *(++e) = '\0';
          }
        }

      if (p[0] != '\0')
        APR_ARRAY_PUSH(array, const char *) = p;

      p = svn_cstring_tokenize(sep_chars, &pats);
    }

  return;
}
Exemplo n.º 5
0
/* Load the setting http-auth-types from the global or server specific
   section, parse its value and set the types of authentication we should
   accept from the server. */
static svn_error_t *
load_http_auth_types(apr_pool_t *pool, svn_config_t *config,
                     const char *server_group,
                     int *authn_types)
{
  const char *http_auth_types = NULL;
  *authn_types = SERF_AUTHN_NONE;

  svn_config_get(config, &http_auth_types, SVN_CONFIG_SECTION_GLOBAL,
               SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, NULL);

  if (server_group)
    {
      svn_config_get(config, &http_auth_types, server_group,
                     SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, http_auth_types);
    }

  if (http_auth_types)
    {
      char *token;
      char *auth_types_list = apr_palloc(pool, strlen(http_auth_types) + 1);
      apr_collapse_spaces(auth_types_list, http_auth_types);
      while ((token = svn_cstring_tokenize(";", &auth_types_list)) != NULL)
        {
          if (svn_cstring_casecmp("basic", token) == 0)
            *authn_types |= SERF_AUTHN_BASIC;
          else if (svn_cstring_casecmp("digest", token) == 0)
            *authn_types |= SERF_AUTHN_DIGEST;
          else if (svn_cstring_casecmp("ntlm", token) == 0)
            *authn_types |= SERF_AUTHN_NTLM;
          else if (svn_cstring_casecmp("negotiate", token) == 0)
            *authn_types |= SERF_AUTHN_NEGOTIATE;
          else
            return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
                                     _("Invalid config: unknown %s "
                                       "'%s'"),
                                     SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, token);
      }
    }
  else
    {
      /* Nothing specified by the user, so accept all types. */
      *authn_types = SERF_AUTHN_ALL;
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 6
0
svn_error_t *
svn_fs_fs__parse_representation(representation_t **rep_p,
                                svn_stringbuf_t *text,
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool)
{
  representation_t *rep;
  char *str;
  apr_int64_t val;
  char *string = text->data;
  svn_checksum_t *checksum;
  const char *end;

  rep = apr_pcalloc(result_pool, sizeof(*rep));
  *rep_p = rep;

  SVN_ERR(parse_revnum(&rep->revision, (const char **)&string));

  /* initialize transaction info (never stored) */
  svn_fs_fs__id_txn_reset(&rep->txn_id);

  /* while in transactions, it is legal to simply write "-1" */
  str = svn_cstring_tokenize(" ", &string);
  if (str == NULL)
    {
      if (rep->revision == SVN_INVALID_REVNUM)
        return SVN_NO_ERROR;

      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                              _("Malformed text representation offset line in node-rev"));
    }

  SVN_ERR(svn_cstring_atoi64(&val, str));
  rep->item_index = (apr_uint64_t)val;

  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_cstring_atoi64(&val, str));
  rep->size = (svn_filesize_t)val;

  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_cstring_atoi64(&val, str));
  rep->expanded_size = (svn_filesize_t)val;

  /* Read in the MD5 hash. */
  str = svn_cstring_tokenize(" ", &string);
  if ((str == NULL) || (strlen(str) != (APR_MD5_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_md5, str,
                                 scratch_pool));
  memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest));

  /* The remaining fields are only used for formats >= 4, so check that. */
  str = svn_cstring_tokenize(" ", &string);
  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"));

  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str,
                                 scratch_pool));
  rep->has_sha1 = checksum != NULL;
  memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));

  /* Read the uniquifier. */
  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));

  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"));

  ++str;
  rep->uniquifier.number = svn__base36toui64(&end, str);

  if (*end)
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Malformed text representation offset line in node-rev"));

  return SVN_NO_ERROR;
}
Exemplo n.º 7
0
/* Read the next entry in the changes record from file FILE and store
   the resulting change in *CHANGE_P.  If there is no next record,
   store NULL there.  Perform all allocations from POOL. */
static svn_error_t *
read_change(change_t **change_p,
            svn_stream_t *stream,
            apr_pool_t *result_pool,
            apr_pool_t *scratch_pool)
{
  svn_stringbuf_t *line;
  svn_boolean_t eof = TRUE;
  change_t *change;
  char *str, *last_str, *kind_str;
  svn_fs_path_change2_t *info;

  /* Default return value. */
  *change_p = NULL;

  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));

  /* Check for a blank line. */
  if (eof || (line->len == 0))
    return SVN_NO_ERROR;

  change = apr_pcalloc(result_pool, sizeof(*change));
  info = &change->info;
  last_str = line->data;

  /* Get the node-id of the change. */
  str = svn_cstring_tokenize(" ", &last_str);
  if (str == NULL)
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Invalid changes line in rev-file"));

  SVN_ERR(svn_fs_fs__id_parse(&info->node_rev_id, str, result_pool));
  if (info->node_rev_id == NULL)
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Invalid changes line in rev-file"));

  /* Get the change type. */
  str = svn_cstring_tokenize(" ", &last_str);
  if (str == NULL)
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Invalid changes line in rev-file"));

  /* Don't bother to check the format number before looking for
   * node-kinds: just read them if you find them. */
  info->node_kind = svn_node_unknown;
  kind_str = strchr(str, '-');
  if (kind_str)
    {
      /* Cap off the end of "str" (the action). */
      *kind_str = '\0';
      kind_str++;
      if (strcmp(kind_str, SVN_FS_FS__KIND_FILE) == 0)
        info->node_kind = svn_node_file;
      else if (strcmp(kind_str, SVN_FS_FS__KIND_DIR) == 0)
        info->node_kind = svn_node_dir;
      else
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                                _("Invalid changes line in rev-file"));
    }

  if (strcmp(str, ACTION_MODIFY) == 0)
    {
      info->change_kind = svn_fs_path_change_modify;
    }
  else if (strcmp(str, ACTION_ADD) == 0)
    {
      info->change_kind = svn_fs_path_change_add;
    }
  else if (strcmp(str, ACTION_DELETE) == 0)
    {
      info->change_kind = svn_fs_path_change_delete;
    }
  else if (strcmp(str, ACTION_REPLACE) == 0)
    {
      info->change_kind = svn_fs_path_change_replace;
    }
  else if (strcmp(str, ACTION_RESET) == 0)
    {
      info->change_kind = svn_fs_path_change_reset;
    }
  else
    {
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                              _("Invalid change kind in rev file"));
    }

  /* Get the text-mod flag. */
  str = svn_cstring_tokenize(" ", &last_str);
  if (str == NULL)
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Invalid changes line in rev-file"));

  if (strcmp(str, FLAG_TRUE) == 0)
    {
      info->text_mod = TRUE;
    }
  else if (strcmp(str, FLAG_FALSE) == 0)
    {
      info->text_mod = FALSE;
    }
  else
    {
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                              _("Invalid text-mod flag in rev-file"));
    }

  /* Get the prop-mod flag. */
  str = svn_cstring_tokenize(" ", &last_str);
  if (str == NULL)
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Invalid changes line in rev-file"));

  if (strcmp(str, FLAG_TRUE) == 0)
    {
      info->prop_mod = TRUE;
    }
  else if (strcmp(str, FLAG_FALSE) == 0)
    {
      info->prop_mod = FALSE;
    }
  else
    {
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                              _("Invalid prop-mod flag in rev-file"));
    }

  /* Get the mergeinfo-mod flag if given.  Otherwise, the next thing
     is the path starting with a slash.  Also, we must initialize the
     flag explicitly because 0 is not valid for a svn_tristate_t. */
  info->mergeinfo_mod = svn_tristate_unknown;
  if (*last_str != '/')
    {
      str = svn_cstring_tokenize(" ", &last_str);
      if (str == NULL)
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                                _("Invalid changes line in rev-file"));

      if (strcmp(str, FLAG_TRUE) == 0)
        {
          info->mergeinfo_mod = svn_tristate_true;
        }
      else if (strcmp(str, FLAG_FALSE) == 0)
        {
          info->mergeinfo_mod = svn_tristate_false;
        }
      else
        {
          return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                              _("Invalid mergeinfo-mod flag in rev-file"));
        }
    }

  /* Get the changed path. */
  if (!svn_fspath__is_canonical(last_str))
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                            _("Invalid path in changes line"));

  change->path.len = strlen(last_str);
  change->path.data = apr_pstrdup(result_pool, last_str);

  /* Read the next line, the copyfrom line. */
  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
  info->copyfrom_known = TRUE;
  if (eof || line->len == 0)
    {
      info->copyfrom_rev = SVN_INVALID_REVNUM;
      info->copyfrom_path = NULL;
    }
  else
    {
      last_str = line->data;
      SVN_ERR(parse_revnum(&info->copyfrom_rev, (const char **)&last_str));

      if (!svn_fspath__is_canonical(last_str))
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                                _("Invalid copy-from path in changes line"));

      info->copyfrom_path = apr_pstrdup(result_pool, last_str);
    }

  *change_p = change;

  return SVN_NO_ERROR;
}
Exemplo n.º 8
0
/* Return an ID resulting from parsing the string DATA, or NULL if DATA is
   an invalid ID string. *DATA will be modified / invalidated by this call. */
static svn_fs_id_t *
id_parse(char *data,
         apr_pool_t *pool)
{
  fs_fs__id_t *id;
  char *str;

  /* Alloc a new svn_fs_id_t structure. */
  id = apr_pcalloc(pool, sizeof(*id));
  id->generic_id.vtable = &id_vtable;
  id->generic_id.fsap_data = id;

  /* Now, we basically just need to "split" this data on `.'
     characters.  We will use svn_cstring_tokenize, which will put
     terminators where each of the '.'s used to be.  Then our new
     id field will reference string locations inside our duplicate
     string.*/

  /* Node Id */
  str = svn_cstring_tokenize(".", &data);
  if (str == NULL)
    return NULL;
  if (! part_parse(&id->private_id.node_id, str))
    return NULL;

  /* Copy Id */
  str = svn_cstring_tokenize(".", &data);
  if (str == NULL)
    return NULL;
  if (! part_parse(&id->private_id.copy_id, str))
    return NULL;

  /* Txn/Rev Id */
  str = svn_cstring_tokenize(".", &data);
  if (str == NULL)
    return NULL;

  if (str[0] == 'r')
    {
      apr_int64_t val;
      const char *tmp;
      svn_error_t *err;

      /* This is a revision type ID */
      id->private_id.txn_id.revision = SVN_INVALID_REVNUM;
      id->private_id.txn_id.number = 0;

      data = str + 1;
      str = svn_cstring_tokenize("/", &data);
      if (str == NULL)
        return NULL;
      if (!locale_independent_strtol(&id->private_id.rev_item.revision,
                                     str, &tmp))
        return NULL;

      err = svn_cstring_atoi64(&val, data);
      if (err)
        {
          svn_error_clear(err);
          return NULL;
        }
      id->private_id.rev_item.number = (apr_uint64_t)val;
    }
  else if (str[0] == 't')
    {
      /* This is a transaction type ID */
      id->private_id.rev_item.revision = SVN_INVALID_REVNUM;
      id->private_id.rev_item.number = 0;

      if (! txn_id_parse(&id->private_id.txn_id, str + 1))
        return NULL;
    }
  else
    return NULL;

  return (svn_fs_id_t *)id;
}
Exemplo n.º 9
0
svn_error_t *
svn_fs_x__parse_representation(svn_fs_x__representation_t **rep_p,
                               svn_stringbuf_t *text,
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool)
{
  svn_fs_x__representation_t *rep;
  char *str;
  apr_int64_t val;
  char *string = text->data;
  svn_checksum_t *checksum;

  rep = apr_pcalloc(result_pool, sizeof(*rep));
  *rep_p = rep;

  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_cstring_atoi64(&rep->id.change_set, str));

  /* while in transactions, it is legal to simply write "-1" */
  if (rep->id.change_set == -1)
    return SVN_NO_ERROR;

  str = svn_cstring_tokenize(" ", &string);
  if (str == NULL)
    {
      if (rep->id.change_set == SVN_FS_X__INVALID_CHANGE_SET)
        return SVN_NO_ERROR;

      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                              _("Malformed text representation offset line in node-rev"));
    }

  SVN_ERR(svn_cstring_atoi64(&val, str));
  rep->id.number = (apr_off_t)val;

  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_cstring_atoi64(&val, str));
  rep->size = (svn_filesize_t)val;

  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_cstring_atoi64(&val, str));
  rep->expanded_size = (svn_filesize_t)val;

  /* Read in the MD5 hash. */
  str = svn_cstring_tokenize(" ", &string);
  if ((str == NULL) || (strlen(str) != (APR_MD5_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_md5, str,
                                 scratch_pool));
  if (checksum)
    memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest));

  /* The remaining fields are only used for formats >= 4, so check that. */
  str = svn_cstring_tokenize(" ", &string);
  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"));

  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str,
                                 scratch_pool));
  rep->has_sha1 = checksum != NULL;
  if (checksum)
    memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));

  return SVN_NO_ERROR;
}
Exemplo n.º 10
0
svn_error_t *
svn_fs_x__parse_footer(apr_off_t *l2p_offset,
                       svn_checksum_t **l2p_checksum,
                       apr_off_t *p2l_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;
  char *last_str = footer->data;

  /* Get the L2P offset. */
  const char *str = svn_cstring_tokenize(" ", &last_str);
  if (str == NULL)
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                             "Invalid r%ld footer", rev);

  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_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));

  /* Get the P2L offset. */
  str = svn_cstring_tokenize(" ", &last_str);
  if (str == NULL)
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                             "Invalid r%ld footer", rev);

  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_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));

  return SVN_NO_ERROR;
}