📄 reps-strings.c
字号:
if (len != str->len) return svn_error_createf (SVN_ERR_FS_CORRUPT, NULL, _("Failure reading rep '%s'"), rep_key); /* Just the standard paranoia. */ { representation_t *rep; apr_md5_ctx_t md5_context; unsigned char checksum[APR_MD5_DIGESTSIZE]; apr_md5_init(&md5_context); apr_md5_update(&md5_context, str->data, str->len); apr_md5_final(checksum, &md5_context); SVN_ERR(svn_fs_bdb__read_rep(&rep, fs, rep_key, trail, pool)); 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"), rep_key, svn_md5_digest_to_cstring_display(rep->checksum, pool), svn_md5_digest_to_cstring_display(checksum, pool)); } return SVN_NO_ERROR;}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, trail->pool)); 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, trail->pool)); 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_display(rep->checksum, trail->pool), svn_md5_digest_to_cstring_display(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, apr_pool_t *pool){ representation_t *rep; SVN_ERR(svn_fs_bdb__read_rep(&rep, fs, rep_key, trail, pool)); 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, pool)); } 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 */ return UNKNOWN_NODE_KIND(rep_key); 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, trail->pool)); 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, trail->pool)); memcpy(rep->checksum, wb->md5_digest, APR_MD5_DIGESTSIZE); SVN_ERR(svn_fs_bdb__write_rep(wb->fs, wb->rep_key, rep, trail, trail->pool)); 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -