📄 fs_fs.c
字号:
if (! txn_id)
return svn_error_create (SVN_ERR_FS_CORRUPT, NULL,
_("Attempted to write to non-transaction"));
SVN_ERR (svn_io_file_open (&noderev_file, path_txn_node_rev (fs, id, pool),
APR_WRITE | APR_CREATE | APR_TRUNCATE,
APR_OS_DEFAULT, pool));
SVN_ERR (write_noderev_txn (noderev_file, noderev, pool));
SVN_ERR (svn_io_file_close (noderev_file, pool));
return SVN_NO_ERROR;
}
/* This structure is used to hold the information associated with a
REP line. */
struct rep_args
{
svn_boolean_t is_delta;
svn_boolean_t is_delta_vs_empty;
svn_revnum_t base_revision;
apr_off_t base_offset;
apr_size_t base_length;
};
/* Read the next line from file FILE and parse it as a text
representation entry. Return the parsed entry in REP_ARGS_P.
Perform all allocations in POOL. */
static svn_error_t *
read_rep_line (struct rep_args **rep_args_p,
apr_file_t *file,
apr_pool_t *pool)
{
char buffer[160];
apr_size_t limit;
struct rep_args *rep_args;
char *str, *last_str;
limit = sizeof (buffer);
SVN_ERR (svn_io_read_length_line (file, buffer, &limit, pool));
rep_args = apr_pcalloc (pool, sizeof (*rep_args));
rep_args->is_delta = FALSE;
if (strcmp (buffer, REP_PLAIN) == 0)
{
*rep_args_p = rep_args;
return SVN_NO_ERROR;
}
if (strcmp (buffer, REP_DELTA) == 0)
{
/* This is a delta against the empty stream. */
rep_args->is_delta = TRUE;
rep_args->is_delta_vs_empty = TRUE;
*rep_args_p = rep_args;
return SVN_NO_ERROR;
}
rep_args->is_delta = TRUE;
rep_args->is_delta_vs_empty = FALSE;
/* We have hopefully a DELTA vs. a non-empty base revision. */
str = apr_strtok (buffer, " ", &last_str);
if (! str || (strcmp (str, REP_DELTA) != 0)) goto err;
str = apr_strtok (NULL, " ", &last_str);
if (! str) goto err;
rep_args->base_revision = atol (str);
str = apr_strtok (NULL, " ", &last_str);
if (! str) goto err;
rep_args->base_offset = (apr_off_t) apr_atoi64 (str);
str = apr_strtok (NULL, " ", &last_str);
if (! str) goto err;
rep_args->base_length = (apr_size_t) apr_atoi64 (str);
*rep_args_p = rep_args;
return SVN_NO_ERROR;
err:
return svn_error_create (SVN_ERR_FS_CORRUPT, NULL,
_("Malformed representation header"));
}
/* Given a revision file REV_FILE, find the Node-ID of the header
located at OFFSET and store it in *ID_P. Allocate temporary
variables from POOL. */
static svn_error_t *
get_fs_id_at_offset (svn_fs_id_t **id_p,
apr_file_t *rev_file,
apr_off_t offset,
apr_pool_t *pool)
{
svn_fs_id_t *id;
apr_hash_t *headers;
const char *node_id_str;
SVN_ERR (svn_io_file_seek (rev_file, APR_SET, &offset, pool));
SVN_ERR (read_header_block (&headers, rev_file, pool));
node_id_str = apr_hash_get (headers, HEADER_ID, APR_HASH_KEY_STRING);
if (node_id_str == NULL)
return svn_error_create (SVN_ERR_FS_CORRUPT, NULL,
_("Missing node-id in node-rev"));
id = svn_fs_fs__id_parse (node_id_str, strlen (node_id_str), pool);
if (id == NULL)
return svn_error_create (SVN_ERR_FS_CORRUPT, NULL,
_("Corrupt node-id in node-rev"));
*id_p = id;
return SVN_NO_ERROR;
}
/* Given an open revision file REV_FILE, locate the trailer that
specifies the offset to the root node-id and to the changed path
information. Store the root node offset in *ROOT_OFFSET and the
changed path offset in *CHANGES_OFFSET. If either of these
pointers is NULL, do nothing with it. Allocate temporary variables
from POOL. */
static svn_error_t *
get_root_changes_offset (apr_off_t *root_offset,
apr_off_t *changes_offset,
apr_file_t *rev_file,
apr_pool_t *pool)
{
apr_off_t offset;
char buf[65];
int i, num_bytes;
apr_size_t len;
/* We will assume that the last line containing the two offsets
will never be longer than 64 characters. */
offset = 0;
SVN_ERR (svn_io_file_seek (rev_file, APR_END, &offset, pool));
offset -= 64;
SVN_ERR (svn_io_file_seek (rev_file, APR_SET, &offset, pool));
/* Read in this last block, from which we will identify the last line. */
len=64;
SVN_ERR (svn_io_file_read (rev_file, buf, &len, pool));
/* This cast should be safe since the maximum amount read, 64, will
never be bigger than the size of an int. */
num_bytes = (int) len;
/* The last byte should be a newline. */
if (buf[num_bytes - 1] != '\n')
{
return svn_error_createf (SVN_ERR_FS_CORRUPT, NULL,
_("Revision file lacks trailing newline"));
}
/* Look for the next previous newline. */
for (i = num_bytes - 2; i >= 0; i--)
{
if (buf[i] == '\n') break;
}
if (i < 0)
{
return svn_error_createf (SVN_ERR_FS_CORRUPT, NULL,
_("Final line in revision file longer than 64 "
"characters"));
}
if (root_offset)
*root_offset = apr_atoi64 (&buf[i]);
/* find the next space */
for ( ; i < (num_bytes - 3) ; i++)
if (buf[i] == ' ') break;
if (i == (num_bytes - 2))
return svn_error_create (SVN_ERR_FS_CORRUPT, NULL,
_("Final line in revision file missing space"));
i++;
if (changes_offset)
*changes_offset = apr_atoi64 (&buf[i]);
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__rev_get_root (svn_fs_id_t **root_id_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
apr_file_t *revision_file;
apr_off_t root_offset;
svn_fs_id_t *root_id;
svn_error_t *err;
err = svn_io_file_open (&revision_file, path_rev (fs, rev, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
if (err && APR_STATUS_IS_ENOENT (err->apr_err))
{
svn_error_clear (err);
return svn_error_createf (SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("No such revision %ld"), rev);
}
else if (err)
return err;
SVN_ERR (get_root_changes_offset (&root_offset, NULL, revision_file, pool));
SVN_ERR (get_fs_id_at_offset (&root_id, revision_file, root_offset, pool));
SVN_ERR (svn_io_file_close (revision_file, pool));
*root_id_p = root_id;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__set_revision_proplist (svn_fs_t *fs,
svn_revnum_t rev,
apr_hash_t *proplist,
apr_pool_t *pool)
{
apr_file_t *revprop_file;
SVN_ERR (svn_io_file_open (&revprop_file, path_revprops (fs, rev, pool),
APR_WRITE | APR_TRUNCATE | APR_CREATE,
APR_OS_DEFAULT, pool));
SVN_ERR (svn_hash_write (proplist, revprop_file, pool));
SVN_ERR (svn_io_file_close (revprop_file, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__revision_proplist (apr_hash_t **proplist_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
apr_file_t *revprop_file;
apr_hash_t *proplist;
svn_error_t *err;
err = svn_io_file_open (&revprop_file, path_revprops (fs, rev, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
if (err && APR_STATUS_IS_ENOENT (err->apr_err))
{
svn_error_clear (err);
return svn_error_createf (SVN_ERR_FS_NO_SUCH_REVISION, NULL,
_("No such revision %ld"), rev);
}
else if (err)
return err;
proplist = apr_hash_make (pool);
SVN_ERR (svn_hash_read (proplist, revprop_file, pool));
SVN_ERR (svn_io_file_close (revprop_file, pool));
*proplist_p = proplist;
return SVN_NO_ERROR;
}
/* Represents where in the current svndiff data block each
representation is. */
struct rep_state
{
apr_file_t *file;
apr_off_t start; /* The starting offset for the raw
svndiff/plaintext data minus header. */
apr_off_t off; /* The current offset into the file. */
apr_off_t end; /* The end offset of the raw data. */
int ver; /* If a delta, what svndiff version? */
int chunk_index;
};
/* Build an array of rep_state structures in *LIST giving the delta
reps from first_rep to a plain-text or self-compressed rep. Set
*SRC_STATE to the plain-text rep we find at the end of the chain,
or to NULL if the final delta representation is self-compressed.
The representation to start from is designated by filesystem FS, id
ID, and representation REP. */
static svn_error_t *
build_rep_list (apr_array_header_t **list,
struct rep_state **src_state,
svn_fs_t *fs,
representation_t *first_rep,
apr_pool_t *pool)
{
representation_t rep;
struct rep_state *rs;
struct rep_args *rep_args;
apr_file_t *file;
unsigned char buf[4];
*list = apr_array_make (pool, 1, sizeof (struct rep_state *));
rep = *first_rep;
while (1)
{
SVN_ERR (open_and_seek_representation (&file, fs, &rep, pool));
SVN_ERR (read_rep_line (&rep_args, file, pool));
/* Create the rep_state for this representation. */
rs = apr_pcalloc (pool, sizeof (*rs));
rs->file = file;
SVN_ERR (get_file_offset (&rs->start, file, pool));
rs->off = rs->start;
rs->end = rs->start + rep.size;
if (rep_args->is_delta == FALSE)
{
/* This is a plaintext, so just return the current rep_state. */
*src_state = rs;
return SVN_NO_ERROR;
}
/* We are dealing with a delta, find out what version. */
SVN_ERR (svn_io_file_read_full (file, buf, 4, NULL, pool));
if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N')))
return svn_error_create
(SVN_ERR_FS_CORRUPT, NULL,
_("Malformed svndiff data in representation"));
rs->ver = buf[3];
rs->chunk_index = 0;
rs->off += 4;
/* Push this rep onto the list. If it's self-compressed, we're done. */
APR_ARRAY_PUSH (*list, struct rep_state *) = rs;
if (rep_args->is_delta_vs_empty)
{
*src_state = NULL;
return SVN_NO_ERROR;
}
rep.revision = rep_args->base_revision;
rep.offset = rep_args->base_offset;
rep.size = rep_args->base_length;
rep.txn_id = NULL;
}
}
struct rep_read_baton
{
/* The FS from which we're reading. */
svn_fs_t *fs;
/* The state of all prior delta representations. */
apr_array_header_t *rs_list;
/* The plaintext state, if there is a plaintext. */
struct rep_state *src_state;
/* The index of the current delta chunk, if we are reading a delta. */
int chunk_index;
/* The buffer where we store undeltified data. */
char *buf;
apr_size_t buf_pos;
apr_size_t buf_len;
/* An MD5 context for summing the data read in order to verify it. */
struct apr_md5_ctx_t md5_context;
svn_boolean_t checksum_finalized;
/* The stored checksum of the representation we are reading, its
length, and the amount we've read so far. Some of this
information is redundant with rs_list and src_state, but it's
convenient for the checksumming code to have it here. */
unsigned char checksum[APR_MD5_DIGESTSIZE];
svn_filesize_t len;
svn_filesize_t off;
/* Used for temporary allocations during the read. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -