📄 reps-strings.c
字号:
}
struct read_rep_args
{
struct rep_read_baton *rb; /* The data source. */
char *buf; /* Where to put what we read. */
apr_size_t *len; /* How much to read / was read. */
};
/* BATON is of type `read_rep_args':
Read into BATON->rb->buf the *(BATON->len) bytes starting at
BATON->rb->offset from the data represented at BATON->rb->rep_key
in BATON->rb->fs, as part of TRAIL.
Afterwards, *(BATON->len) is the number of bytes actually read, and
BATON->rb->offset is incremented by that amount.
If BATON->rb->rep_key is null, this is assumed to mean the file's
contents have no representation, i.e., the file has no contents.
In that case, if BATON->rb->offset > 0, return the error
SVN_ERR_FS_FILE_CONTENTS_CHANGED, else just set *(BATON->len) to
zero and return. */
static svn_error_t *
txn_body_read_rep (void *baton, trail_t *trail)
{
struct read_rep_args *args = baton;
if (args->rb->rep_key)
{
SVN_ERR (rep_read_range (args->rb->fs,
args->rb->rep_key,
args->rb->offset,
args->buf,
args->len,
trail));
args->rb->offset += *(args->len);
/* We calculate the checksum just once, the moment we see the
* last byte of data. But we can't assume there was a short
* read. The caller may have known the length of the data and
* requested exactly that amount, so there would never be a
* short read. (That's why the read baton has to know the
* length of the data in advance.)
*
* On the other hand, some callers invoke the stream reader in a
* loop whose termination condition is that the read returned
* zero bytes of data -- which usually results in the read
* function being called one more time *after* the call that got
* a short read (indicating end-of-stream).
*
* The conditions below ensure that we compare checksums even
* when there is no short read associated with the last byte of
* data, while also ensuring that it's harmless to repeatedly
* read 0 bytes from the stream.
*/
if (! args->rb->checksum_finalized)
{
apr_md5_update (&(args->rb->md5_context), args->buf, *(args->len));
if (args->rb->offset == args->rb->size)
{
representation_t *rep;
unsigned char checksum[APR_MD5_DIGESTSIZE];
apr_md5_final (checksum, &(args->rb->md5_context));
args->rb->checksum_finalized = TRUE;
SVN_ERR (svn_fs_bdb__read_rep (&rep, args->rb->fs,
args->rb->rep_key, trail));
if (! svn_md5_digests_match (checksum, rep->checksum))
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
"Checksum mismatch on rep '%s':\n"
" expected: %s\n"
" actual: %s\n", args->rb->rep_key,
svn_md5_digest_to_cstring (rep->checksum, trail->pool),
svn_md5_digest_to_cstring (checksum, trail->pool));
}
}
}
else if (args->rb->offset > 0)
{
return
svn_error_create
(SVN_ERR_FS_REP_CHANGED, NULL,
"Null rep, but offset past zero already");
}
else
*(args->len) = 0;
return SVN_NO_ERROR;
}
static svn_error_t *
rep_read_contents (void *baton, char *buf, apr_size_t *len)
{
struct rep_read_baton *rb = baton;
struct read_rep_args args;
args.rb = rb;
args.buf = buf;
args.len = len;
/* If we got a trail, use it; else make one. */
if (rb->trail)
SVN_ERR (txn_body_read_rep (&args, rb->trail));
else
{
/* Hey, guess what? trails don't clear their own subpools. In
the case of reading from the db, any returned data should
live in our pre-allocated buffer, so the whole operation can
happen within a single malloc/free cycle. This prevents us
from creating millions of unnecessary trail subpools when
reading a big file. */
apr_pool_t *subpool = svn_pool_create (rb->pool);
SVN_ERR (svn_fs_base__retry_txn (rb->fs,
txn_body_read_rep,
&args,
subpool));
svn_pool_destroy (subpool);
}
return SVN_NO_ERROR;
}
/** Writing. **/
struct rep_write_baton
{
/* The FS in which we're writing. */
svn_fs_t *fs;
/* The representation skel whose contents we want to write. */
const char *rep_key;
/* The transaction id under which this write action will take
place. */
const char *txn_id;
/* If present, do the write as part of this trail, and use trail's
pool. Otherwise, see `pool' below. */
trail_t *trail;
/* MD5 checksum. Initialized when the baton is created, updated as
we write data, and finalized and stored when the stream is
closed. */
struct apr_md5_ctx_t md5_context;
unsigned char md5_digest[APR_MD5_DIGESTSIZE];
svn_boolean_t finalized;
/* Used for temporary allocations, iff `trail' (above) is null. */
apr_pool_t *pool;
};
static struct rep_write_baton *
rep_write_get_baton (svn_fs_t *fs,
const char *rep_key,
const char *txn_id,
trail_t *trail,
apr_pool_t *pool)
{
struct rep_write_baton *b;
b = apr_pcalloc (pool, sizeof (*b));
apr_md5_init (&(b->md5_context));
b->fs = fs;
b->trail = trail;
b->pool = pool;
b->rep_key = rep_key;
b->txn_id = txn_id;
return b;
}
/* Write LEN bytes from BUF into the end of the string represented via
REP_KEY in FS, as part of TRAIL. If the representation is not
mutable, return the error SVN_FS_REP_NOT_MUTABLE. */
static svn_error_t *
rep_write (svn_fs_t *fs,
const char *rep_key,
const char *buf,
apr_size_t len,
const char *txn_id,
trail_t *trail)
{
representation_t *rep;
SVN_ERR (svn_fs_bdb__read_rep (&rep, fs, rep_key, trail));
if (! rep_is_mutable (rep, txn_id))
return svn_error_createf
(SVN_ERR_FS_REP_NOT_MUTABLE, NULL,
"Rep '%s' is not mutable", rep_key);
if (rep->kind == rep_kind_fulltext)
{
SVN_ERR (svn_fs_bdb__string_append
(fs, &(rep->contents.fulltext.string_key), len, buf, trail));
}
else if (rep->kind == rep_kind_delta)
{
/* There should never be a case when we have a mutable
non-fulltext rep. The only code that creates mutable reps is
in this file, and it creates them fulltext. */
return svn_error_createf
(SVN_ERR_FS_CORRUPT, NULL,
"Rep '%s' both mutable and non-fulltext", rep_key);
}
else /* unknown kind */
abort ();
return SVN_NO_ERROR;
}
struct write_rep_args
{
struct rep_write_baton *wb; /* Destination. */
const char *buf; /* Data. */
apr_size_t len; /* How much to write. */
};
/* BATON is of type `write_rep_args':
Append onto BATON->wb->rep_key's contents BATON->len bytes of
data from BATON->wb->buf, in BATON->rb->fs, as part of TRAIL.
If the representation is not mutable, return the error
SVN_FS_REP_NOT_MUTABLE. */
static svn_error_t *
txn_body_write_rep (void *baton, trail_t *trail)
{
struct write_rep_args *args = baton;
SVN_ERR (rep_write (args->wb->fs,
args->wb->rep_key,
args->buf,
args->len,
args->wb->txn_id,
trail));
apr_md5_update (&(args->wb->md5_context), args->buf, args->len);
return SVN_NO_ERROR;
}
static svn_error_t *
rep_write_contents (void *baton,
const char *buf,
apr_size_t *len)
{
struct rep_write_baton *wb = baton;
struct write_rep_args args;
/* We toss LEN's indirectness because if not all the bytes are
written, it's an error, so we wouldn't be reporting anything back
through *LEN anyway. */
args.wb = wb;
args.buf = buf;
args.len = *len;
/* If we got a trail, use it; else make one. */
if (wb->trail)
SVN_ERR (txn_body_write_rep (&args, wb->trail));
else
{
/* Hey, guess what? trails don't clear their own subpools. In
the case of simply writing the rep to the db, we're *certain*
that there's no data coming back to us that needs to be
preserved... so the whole operation can happen within a
single malloc/free cycle. This prevents us from creating
millions of unnecessary trail subpools when writing a big
file. */
apr_pool_t *subpool = svn_pool_create (wb->pool);
SVN_ERR (svn_fs_base__retry_txn (wb->fs,
txn_body_write_rep,
&args,
subpool));
svn_pool_destroy (subpool);
}
return SVN_NO_ERROR;
}
/* Helper for rep_write_close_contents(); see that doc string for
more. BATON is of type `struct rep_write_baton'. */
static svn_error_t *
txn_body_write_close_rep (void *baton, trail_t *trail)
{
struct rep_write_baton *wb = baton;
representation_t *rep;
SVN_ERR (svn_fs_bdb__read_rep (&rep, wb->fs, wb->rep_key, trail));
memcpy (rep->checksum, wb->md5_digest, APR_MD5_DIGESTSIZE);
SVN_ERR (svn_fs_bdb__write_rep (wb->fs, wb->rep_key, rep, trail));
return SVN_NO_ERROR;
}
/* BATON is of type `struct rep_write_baton'.
*
* Finalize BATON->md5_context and store the resulting digest under
* BATON->rep_key.
*/
static svn_error_t *
rep_write_close_contents (void *baton)
{
struct rep_write_baton *wb = baton;
/* ### Thought: if we fixed apr-util MD5 contexts to allow repeated
digestification, then we wouldn't need a stream close function at
all -- instead, we could update the stored checksum each time a
write occurred, which would have the added advantage of making
interleaving reads and writes work. Currently, they'd fail with
a checksum mismatch, it just happens that our code never tries to
do that anyway. */
if (! wb->finalized)
{
apr_md5_final (wb->md5_digest, &wb->md5_context);
wb->finalized = TRUE;
}
/* If we got a trail, use it; else make one. */
if (wb->trail)
{
SVN_ERR (txn_body_write_close_rep (wb, wb->trail));
}
else
{
SVN_ERR (svn_fs_base__retry_txn (wb->fs,
txn_body_write_close_rep,
wb,
wb->pool));
}
return SVN_NO_ERROR;
}
/** Public read and write stream constructors. **/
svn_error_t *
svn_fs_base__rep_contents_read_stream (svn_stream_t **rs_p,
svn_fs_t *fs,
const char *rep_key,
svn_boolean_t use_trail_for_reads,
trail_t *trail,
apr_pool_t *pool)
{
struct rep_read_baton *rb;
SVN_ERR (rep_read_get_baton (&rb, fs, rep_key, use_trail_for_reads,
trail, pool));
*rs_p = svn_stream_create (rb, pool);
svn_stream_set_read (*rs_p, rep_read_contents);
return SVN_NO_ERROR;
}
/* Clear the contents of REP_KEY, so that it represents the empty
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -