📄 fs_fs.c
字号:
/* 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. */ apr_pool_t *pool; /* Pool used to store file handles and other data that is persistant for the entire stream read. */ apr_pool_t *filehandle_pool;};/* Create a rep_read_baton structure for node revision NODEREV in filesystem FS and store it in *RB_P. Perform all allocations in POOL. If rep is mutable, it must be for file contents. */static svn_error_t *rep_read_get_baton(struct rep_read_baton **rb_p, svn_fs_t *fs, representation_t *rep, apr_pool_t *pool){ struct rep_read_baton *b; b = apr_pcalloc(pool, sizeof(*b)); b->fs = fs; b->chunk_index = 0; b->buf = NULL; apr_md5_init(&(b->md5_context)); b->checksum_finalized = FALSE; memcpy(b->checksum, rep->checksum, sizeof(b->checksum)); b->len = rep->expanded_size; b->off = 0; b->pool = svn_pool_create(pool); b->filehandle_pool = svn_pool_create(pool); SVN_ERR(build_rep_list(&b->rs_list, &b->src_state, fs, rep, b->filehandle_pool)); /* Save our output baton. */ *rb_p = b; return SVN_NO_ERROR;}/* Skip forwards to THIS_CHUNK in REP_STATE and then read the next delta window into *NWIN. */static svn_error_t *read_window(svn_txdelta_window_t **nwin, int this_chunk, struct rep_state *rs, apr_pool_t *pool){ svn_stream_t *stream; assert(rs->chunk_index <= this_chunk); /* Skip windows to reach the current chunk if we aren't there yet. */ while (rs->chunk_index < this_chunk) { SVN_ERR(svn_txdelta_skip_svndiff_window(rs->file, rs->ver, pool)); rs->chunk_index++; SVN_ERR(get_file_offset(&rs->off, rs->file, pool)); if (rs->off >= rs->end) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Reading one svndiff window read " "beyond the end of the " "representation")); } /* Read the next window. */ stream = svn_stream_from_aprfile(rs->file, pool); SVN_ERR(svn_txdelta_read_svndiff_window(nwin, stream, rs->ver, pool)); rs->chunk_index++; SVN_ERR(get_file_offset(&rs->off, rs->file, pool)); if (rs->off > rs->end) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Reading one svndiff window read beyond " "the end of the representation")); return SVN_NO_ERROR;} /* Get one delta window that is a result of combining all but the last deltas from the current desired representation identified in *RB, to its final base representation. Store the window in *RESULT. */static svn_error_t *get_combined_window(svn_txdelta_window_t **result, struct rep_read_baton *rb){ apr_pool_t *pool, *new_pool; int i; svn_txdelta_window_t *window, *nwin; struct rep_state *rs; assert(rb->rs_list->nelts >= 2); pool = svn_pool_create(rb->pool); /* Read the next window from the original rep. */ rs = APR_ARRAY_IDX(rb->rs_list, 0, struct rep_state *); SVN_ERR(read_window(&window, rb->chunk_index, rs, pool)); /* Combine in the windows from the other delta reps, if needed. */ for (i = 1; i < rb->rs_list->nelts - 1; i++) { if (window->src_ops == 0) break; rs = APR_ARRAY_IDX(rb->rs_list, i, struct rep_state *); SVN_ERR(read_window(&nwin, rb->chunk_index, rs, pool)); /* Combine this window with the current one. Cycles pools so that we only need to hold three windows at a time. */ new_pool = svn_pool_create(rb->pool); window = svn_txdelta_compose_windows(nwin, window, new_pool); svn_pool_destroy(pool); pool = new_pool; } *result = window; return SVN_NO_ERROR;}static svn_error_t *rep_read_contents_close(void *baton){ struct rep_read_baton *rb = baton; svn_pool_destroy(rb->pool); svn_pool_destroy(rb->filehandle_pool); return SVN_NO_ERROR;}/* Return the next *LEN bytes of the rep and store them in *BUF. */static svn_error_t *get_contents(struct rep_read_baton *rb, char *buf, apr_size_t *len){ apr_size_t copy_len, remaining = *len, tlen; char *sbuf, *tbuf, *cur = buf; struct rep_state *rs; svn_txdelta_window_t *cwindow, *lwindow; /* Special case for when there are no delta reps, only a plain text. */ if (rb->rs_list->nelts == 0) { copy_len = remaining; rs = rb->src_state; if (((apr_off_t) copy_len) > rs->end - rs->off) copy_len = (apr_size_t) (rs->end - rs->off); SVN_ERR(svn_io_file_read_full(rs->file, cur, copy_len, NULL, rb->pool)); rs->off += copy_len; *len = copy_len; return SVN_NO_ERROR; } while (remaining > 0) { /* If we have buffered data from a previous chunk, use that. */ if (rb->buf) { /* Determine how much to copy from the buffer. */ copy_len = rb->buf_len - rb->buf_pos; if (copy_len > remaining) copy_len = remaining; /* Actually copy the data. */ memcpy(cur, rb->buf + rb->buf_pos, copy_len); rb->buf_pos += copy_len; cur += copy_len; remaining -= copy_len; /* If the buffer is all used up, clear it and empty the local pool. */ if (rb->buf_pos == rb->buf_len) { svn_pool_clear(rb->pool); rb->buf = NULL; } } else { rs = APR_ARRAY_IDX(rb->rs_list, 0, struct rep_state *); if (rs->off == rs->end) break; /* Get more buffered data by evaluating a chunk. */ if (rb->rs_list->nelts > 1) SVN_ERR(get_combined_window(&cwindow, rb)); else cwindow = NULL; if (!cwindow || cwindow->src_ops > 0) { rs = APR_ARRAY_IDX(rb->rs_list, rb->rs_list->nelts - 1, struct rep_state *); /* Read window from last representation in list. */ /* We apply this window directly instead of combining it with the others. We do this because vdelta is used for deltas against the empty stream, which will trigger quadratic behaviour in the delta combiner. */ SVN_ERR(read_window(&lwindow, rb->chunk_index, rs, rb->pool)); if (lwindow->src_ops > 0) { if (! rb->src_state) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("svndiff data requested " "non-existent source")); rs = rb->src_state; sbuf = apr_palloc(rb->pool, lwindow->sview_len); if (! ((rs->start + lwindow->sview_offset) < rs->end)) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("svndiff requested position " "beyond end of stream")); if ((rs->start + lwindow->sview_offset) != rs->off) { rs->off = rs->start + lwindow->sview_offset; SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, rb->pool)); } SVN_ERR(svn_io_file_read_full(rs->file, sbuf, lwindow->sview_len, NULL, rb->pool)); rs->off += lwindow->sview_len; } else sbuf = NULL; /* Apply lwindow to source. */ tlen = lwindow->tview_len; tbuf = apr_palloc(rb->pool, tlen); svn_txdelta_apply_instructions(lwindow, sbuf, tbuf, &tlen); if (tlen != lwindow->tview_len) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("svndiff window length is " "corrupt")); sbuf = tbuf; } else sbuf = NULL; rb->chunk_index++; if (cwindow) { rb->buf_len = cwindow->tview_len; rb->buf = apr_palloc(rb->pool, rb->buf_len); svn_txdelta_apply_instructions(cwindow, sbuf, rb->buf, &rb->buf_len); if (rb->buf_len != cwindow->tview_len) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("svndiff window length is " "corrupt")); } else { rb->buf_len = lwindow->tview_len; rb->buf = sbuf; } rb->buf_pos = 0; } } *len = cur - buf; return SVN_NO_ERROR;}/* BATON is of type `rep_read_baton'; read the next *LEN bytes of the representation and store them in *BUF. Sum as we read and verify the MD5 sum at the end. */static svn_error_t *rep_read_contents(void *baton, char *buf, apr_size_t *len){ struct rep_read_baton *rb = baton; /* Get the next block of data. */ SVN_ERR(get_contents(rb, buf, len)); /* Perform checksumming. We want to check the checksum as soon as the last byte of data is read, in case the caller never performs a short read, but we don't want to finalize the MD5 context twice. */ if (!rb->checksum_finalized) { apr_md5_update(&rb->md5_context, buf, *len); rb->off += *len; if (rb->off == rb->len) { unsigned char checksum[APR_MD5_DIGESTSIZE]; rb->checksum_finalized = TRUE; apr_md5_final(checksum, &rb->md5_context); if (! svn_md5_digests_match(checksum, rb->checksum)) return svn_error_createf (SVN_ERR_FS_CORRUPT, NULL, _("Checksum mismatch while reading representation:\n" " expected: %s\n" " actual: %s\n"), svn_md5_digest_to_cstring_display(rb->checksum, rb->pool), svn_md5_digest_to_cstring_display(checksum, rb->pool)); } } return SVN_NO_ERROR;}/* Return a stream in *CONTENTS_P that will read the contents of a representation stored at the location given by REP. Appropriate for any kind of immutable representation, but only for file contents (not props or directory contents) in mutable representations. If REP is NULL, the representation is assumed to be empty, and the empty stream is returned.*/static svn_error_t *read_representation(svn_stream_t **contents_p, svn_fs_t *fs, representation_t *rep, apr_pool_t *pool){ struct rep_read_baton *rb; if (! rep) { *contents_p = svn_stream_empty(pool); } else { SVN_ERR(rep_read_get_baton(&rb, fs, rep, pool)); *contents_p = svn_stream_create(rb, pool); svn_stream_set_read(*contents_p, rep_read_contents); svn_stream_set_close(*contents_p, rep_read_contents_close); } return SVN_NO_ERROR;}svn_error_t *svn_fs_fs__get_contents(svn_stream_t **contents_p, svn_fs_t *fs, node_revision_t *noderev, apr_pool_t *pool){ return read_representation(contents_p, fs, noderev->data_rep, pool);}/* Baton used when reading delta windows. */struct delta_read_baton{ struct rep_state *rs; unsigned char checksum[APR_MD5_DIGESTSIZE];};/* This implements the svn_txdelta_next_window_fn_t interface. */static svn_error_t *delta_read_next_window(svn_txdelta_window_t **window, void *baton, apr_pool_t *pool){ struct delta_read_baton *drb = baton; if (drb->rs->off == drb->rs->end) { *window = NULL; return SVN_NO_ERROR; } SVN_ERR(read_window(window, drb->rs->chunk_index, drb->rs, pool)); return SVN_NO_ERROR;}/* This implements the svn_txdelta_md5_digest_fn_t interface. */static const unsigned char *delta_read_md5_digest(void *baton){ struct delta_read_baton *drb = baton; return drb->checksum;}svn_er
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -