static svn_error_t * close_file(void *file_baton, const char *text_digest, apr_pool_t *pool) { struct file_baton *fb = file_baton; if (text_digest) { svn_checksum_t *checksum; svn_checksum_t *text_checksum; SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, fb->edit_baton->txn_root, fb->path, TRUE, pool)); SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5, text_digest, pool)); if (!svn_checksum_match(text_checksum, checksum)) return svn_checksum_mismatch_err(text_checksum, checksum, pool, _("Checksum mismatch for resulting fulltext\n(%s)"), fb->path); } return SVN_NO_ERROR; }
svn_error_t * svn_fs_fs__dag_finalize_edits(dag_node_t *file, const svn_checksum_t *checksum, apr_pool_t *pool) { if (checksum) { svn_checksum_t *file_checksum; SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file, checksum->kind, pool)); if (!svn_checksum_match(checksum, file_checksum)) return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL, _("Checksum mismatch, file '%s':\n" " expected: %s\n" " actual: %s\n"), file->created_path, svn_checksum_to_cstring_display(checksum, pool), svn_checksum_to_cstring_display(file_checksum, pool)); } return SVN_NO_ERROR; }
/* Increment records[rep] if both are non-NULL and REP contains a sha1. * Allocate keys and values in RESULT_POOL. */ static svn_error_t *record(apr_hash_t *records, representation_t *rep, apr_pool_t *result_pool) { struct key_t *key; struct value_t *value; /* Skip if we ignore this particular kind of reps, or if the rep doesn't * exist or doesn't have the checksum we are after. (The latter case * often corresponds to node_rev->kind == svn_node_dir.) */ if (records == NULL || rep == NULL || rep->sha1_checksum == NULL) return SVN_NO_ERROR; /* Construct the key. * * Must use calloc() because apr_hash_* pay attention to padding bytes too. */ key = apr_pcalloc(result_pool, sizeof(*key)); key->revision = rep->revision; key->offset = rep->offset; /* Update or create the value. */ if ((value = apr_hash_get(records, key, sizeof(*key)))) { /* Paranoia. */ SVN_ERR_ASSERT(value->sha1_checksum != NULL); SVN_ERR_ASSERT(svn_checksum_match(value->sha1_checksum, rep->sha1_checksum)); /* Real work. */ value->refcount++; } else { value = apr_palloc(result_pool, sizeof(*value)); value->sha1_checksum = svn_checksum_dup(rep->sha1_checksum, result_pool); value->refcount = 1; } /* Store them. */ apr_hash_set(records, key, sizeof(*key), value); return SVN_NO_ERROR; }
svn_error_t * svn_fs_x__dag_finalize_edits(dag_node_t *file, const svn_checksum_t *checksum, apr_pool_t *pool) { if (checksum) { svn_checksum_t *file_checksum; SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file, checksum->kind, pool)); if (!svn_checksum_match(checksum, file_checksum)) return svn_checksum_mismatch_err(checksum, file_checksum, pool, _("Checksum mismatch for '%s'"), file->created_path); } return SVN_NO_ERROR; }
static svn_error_t * file_close(void *file_baton, const char *text_checksum, apr_pool_t *scratch_pool) { struct file_baton_t *fb = file_baton; struct edit_baton_t *eb = fb->eb; struct dir_baton_t *pb = fb->pb; SVN_ERR(ensure_added(pb, fb->pool)); if (text_checksum) { svn_checksum_t *expected_checksum; svn_checksum_t *actual_checksum; SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5, text_checksum, fb->pool)); actual_checksum = svn_checksum__from_digest_md5(fb->digest, fb->pool); if (! svn_checksum_match(expected_checksum, actual_checksum)) return svn_error_trace( svn_checksum_mismatch_err(expected_checksum, actual_checksum, fb->pool, _("Checksum mismatch for '%s'"), svn_dirent_local_style( fb->local_abspath, fb->pool))); } SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, fb->local_abspath, fb->properties, TRUE /* skip checks */, eb->notify_func, eb->notify_baton, fb->pool)); svn_pool_destroy(fb->pool); SVN_ERR(maybe_done(pb)); return SVN_NO_ERROR; }
static svn_error_t * file_close(void *file_baton, const char *text_checksum, apr_pool_t *scratch_pool) { struct file_baton_t *fb = file_baton; struct dir_baton_t *pb = fb->pb; if (fb->writing_file) SVN_ERR(svn_io_file_rename2(fb->writing_file, fb->local_abspath, FALSE /*flush*/, scratch_pool)); if (text_checksum) { svn_checksum_t *expected_checksum; svn_checksum_t *actual_checksum; SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5, text_checksum, fb->pool)); actual_checksum = svn_checksum__from_digest_md5(fb->digest, fb->pool); if (! svn_checksum_match(expected_checksum, actual_checksum)) return svn_error_trace( svn_checksum_mismatch_err(expected_checksum, actual_checksum, fb->pool, _("Checksum mismatch for '%s'"), svn_dirent_local_style( fb->local_abspath, fb->pool))); } SVN_ERR(ensure_added_file(fb, fb->pool)); svn_pool_destroy(fb->pool); SVN_ERR(maybe_done(pb)); return SVN_NO_ERROR; }
/* Perform a copy or a plain add. * * For a copy, also adjust the copy-from rev, check any copy-source checksum, * and send a notification. */ static svn_error_t * maybe_add_with_history(struct node_baton *nb, struct revision_baton *rb, apr_pool_t *pool) { struct parse_baton *pb = rb->pb; if ((nb->copyfrom_path == NULL) || (! pb->use_history)) { /* Add empty file or dir, without history. */ if (nb->kind == svn_node_file) SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool)); else if (nb->kind == svn_node_dir) SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool)); } else { /* Hunt down the source revision in this fs. */ svn_fs_root_t *copy_root; svn_revnum_t copyfrom_rev; /* Try to find the copyfrom revision in the revision map; failing that, fall back to the revision offset approach. */ copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev); if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) copyfrom_rev = nb->copyfrom_rev - rb->rev_offset; if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, _("Relative source revision %ld is not" " available in current repository"), copyfrom_rev); SVN_ERR(svn_fs_revision_root(©_root, pb->fs, copyfrom_rev, pool)); if (nb->copy_source_checksum) { svn_checksum_t *checksum; SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root, nb->copyfrom_path, TRUE, pool)); if (!svn_checksum_match(nb->copy_source_checksum, checksum)) return svn_checksum_mismatch_err(nb->copy_source_checksum, checksum, pool, _("Copy source checksum mismatch on copy from '%s'@%ld\n" "to '%s' in rev based on r%ld"), nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev); } SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path, rb->txn_root, nb->path, pool)); if (pb->notify_func) { /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ svn_repos_notify_t *notify = svn_repos_notify_create( svn_repos_notify_load_copied_node, pb->notify_pool); pb->notify_func(pb->notify_baton, notify, pb->notify_pool); svn_pool_clear(pb->notify_pool); } } return SVN_NO_ERROR; }
static svn_error_t * zero_match(apr_pool_t *pool) { svn_checksum_t *zero_md5; svn_checksum_t *zero_sha1; svn_checksum_t *A_md5; svn_checksum_t *B_md5; svn_checksum_t *A_sha1; svn_checksum_t *B_sha1; zero_md5 = svn_checksum_create(svn_checksum_md5, pool); SVN_ERR(svn_checksum_clear(zero_md5)); SVN_ERR(svn_checksum(&A_md5, svn_checksum_md5, "A", 1, pool)); SVN_ERR(svn_checksum(&B_md5, svn_checksum_md5, "B", 1, pool)); zero_sha1 = svn_checksum_create(svn_checksum_sha1, pool); SVN_ERR(svn_checksum_clear(zero_sha1)); SVN_ERR(svn_checksum(&A_sha1, svn_checksum_sha1, "A", 1, pool)); SVN_ERR(svn_checksum(&B_sha1, svn_checksum_sha1, "B", 1, pool)); /* Different non-zero don't match. */ SVN_TEST_ASSERT(!svn_checksum_match(A_md5, B_md5)); SVN_TEST_ASSERT(!svn_checksum_match(A_sha1, B_sha1)); SVN_TEST_ASSERT(!svn_checksum_match(A_md5, A_sha1)); SVN_TEST_ASSERT(!svn_checksum_match(A_md5, B_sha1)); /* Zero matches anything of the same kind. */ SVN_TEST_ASSERT(svn_checksum_match(A_md5, zero_md5)); SVN_TEST_ASSERT(svn_checksum_match(zero_md5, B_md5)); SVN_TEST_ASSERT(svn_checksum_match(A_sha1, zero_sha1)); SVN_TEST_ASSERT(svn_checksum_match(zero_sha1, B_sha1)); /* Zero doesn't match anything of a different kind... */ SVN_TEST_ASSERT(!svn_checksum_match(zero_md5, A_sha1)); SVN_TEST_ASSERT(!svn_checksum_match(zero_sha1, A_md5)); /* ...even another zero. */ SVN_TEST_ASSERT(!svn_checksum_match(zero_md5, zero_sha1)); return SVN_NO_ERROR; }
/* Exercise the pristine text API with a simple write and read. */ static svn_error_t * pristine_write_read(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_wc__db_t *db; const char *wc_abspath; svn_wc__db_install_data_t *install_data; svn_stream_t *pristine_stream; apr_size_t sz; const char data[] = "Blah"; svn_string_t *data_string = svn_string_create(data, pool); svn_checksum_t *data_sha1, *data_md5; SVN_ERR(create_repos_and_wc(&wc_abspath, &db, "pristine_write_read", opts, pool)); /* Write DATA into a new temporary pristine file, set PRISTINE_TMP_ABSPATH * to its path and set DATA_SHA1 and DATA_MD5 to its checksums. */ SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, &install_data, &data_sha1, &data_md5, db, wc_abspath, pool, pool)); sz = strlen(data); SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); SVN_ERR(svn_stream_close(pristine_stream)); /* Ensure it's not already in the store. */ { svn_boolean_t present; SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, pool)); SVN_TEST_ASSERT(! present); } /* Install the new pristine file, referenced by its checksum. */ SVN_ERR(svn_wc__db_pristine_install(install_data, data_sha1, data_md5, pool)); /* Ensure it is now found in the store. */ { svn_boolean_t present; SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, pool)); SVN_TEST_ASSERT(present); } /* Look up its MD-5 from its SHA-1, and check it's the same MD-5. */ { const svn_checksum_t *looked_up_md5; SVN_ERR(svn_wc__db_pristine_get_md5(&looked_up_md5, db, wc_abspath, data_sha1, pool, pool)); SVN_TEST_ASSERT(looked_up_md5->kind == svn_checksum_md5); SVN_TEST_ASSERT(svn_checksum_match(data_md5, looked_up_md5)); } /* Read the pristine text back and verify it's the same content. */ { svn_stream_t *data_stream = svn_stream_from_string(data_string, pool); svn_stream_t *data_read_back; svn_boolean_t same; SVN_ERR(svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, data_sha1, pool, pool)); SVN_ERR(svn_stream_contents_same2(&same, data_read_back, data_stream, pool)); SVN_TEST_ASSERT(same); } /* Trivially test the "remove if unreferenced" API: it's not referenced so we should be able to remove it. */ { svn_error_t *err; svn_stream_t *data_read_back; SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool)); err = svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, data_sha1, pool, pool); SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); } /* Ensure it's no longer found in the store. */ { svn_boolean_t present; SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, pool)); SVN_TEST_ASSERT(! present); } return SVN_NO_ERROR; }
static svn_error_t * stream_window_test(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool) { /* Note: put these in data segment, not the stack */ static char source[109001]; static char target[109001]; int i; char *p = &source[9]; svn_checksum_t *expected; svn_checksum_t *actual; svn_string_t source_str; svn_string_t target_str; svn_stream_t *source_stream; svn_stream_t *target_stream; svn_txdelta_stream_t *txstream; *msg = "txdelta stream and windows test"; if (msg_only) return SVN_NO_ERROR; memcpy(source, "a\nb\nc\nd\ne", 9); for (i = 100; i--; ) *p++ = '\n'; for (i = 999; i--; p += 109) memcpy(p, source, 109); source[109000] = '\0'; memcpy(target, source, 109001); for (i = 1000; i--; ) target[i*109 + 4] = 'X'; SVN_ERR(svn_checksum(&expected, svn_checksum_md5, target, 109000, pool)); /* f6fd44565e14c6e44b35292719deb77e */ printf("expected: %s\n", svn_checksum_to_cstring(expected, pool)); source_str.data = source; source_str.len = 109000; source_stream = svn_stream_from_string(&source_str, pool); target_str.data = target; target_str.len = 109000; target_stream = svn_stream_from_string(&target_str, pool); svn_txdelta(&txstream, source_stream, target_stream, pool); while (1) { svn_txdelta_window_t *window; SVN_ERR(svn_txdelta_next_window(&window, txstream, pool)); if (window == NULL) break; /* ### examine the window */ } actual = svn_checksum__from_digest(svn_txdelta_md5_digest(txstream), svn_checksum_md5, pool);; printf(" actual: %s\n", svn_checksum_to_cstring(actual, pool)); if (!svn_checksum_match(expected, actual)) { return svn_error_create(SVN_ERR_TEST_FAILED, NULL, "Checksums did not match."); } return SVN_NO_ERROR; }
/* An svn_delta_editor_t function. When the file is closed we have a temporary * file containing a pristine version of the repository file. This can * be compared against the working copy. * * ### Ignore TEXT_CHECKSUM for now. Someday we can use it to verify * ### the integrity of the file being diffed. Done efficiently, this * ### would probably involve calculating the checksum as the data is * ### received, storing the final checksum in the file_baton, and * ### comparing against it here. */ static svn_error_t * close_file(void *file_baton, const char *expected_md5_digest, apr_pool_t *pool) { struct file_baton *b = file_baton; struct edit_baton *eb = b->edit_baton; svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown; svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; apr_pool_t *scratch_pool; /* Skip *everything* within a newly tree-conflicted directory. */ if (b->skip) { svn_pool_destroy(b->pool); return SVN_NO_ERROR; } scratch_pool = b->pool; if (expected_md5_digest && eb->text_deltas) { svn_checksum_t *expected_md5_checksum; SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5, expected_md5_digest, scratch_pool)); if (!svn_checksum_match(expected_md5_checksum, b->result_md5_checksum)) return svn_error_trace(svn_checksum_mismatch_err( expected_md5_checksum, b->result_md5_checksum, pool, _("Checksum mismatch for '%s'"), b->path)); } if (!b->added && b->propchanges->nelts > 0) { if (!b->pristine_props) { /* We didn't receive a text change, so we have no pristine props. Retrieve just the props now. */ SVN_ERR(get_file_from_ra(b, TRUE, scratch_pool)); } remove_non_prop_changes(b->pristine_props, b->propchanges); } if (b->path_end_revision || b->propchanges->nelts > 0) { const char *mimetype1, *mimetype2; get_file_mime_types(&mimetype1, &mimetype2, b); if (b->added) SVN_ERR(eb->diff_callbacks->file_added( &content_state, &prop_state, &b->tree_conflicted, b->path, b->path_end_revision ? b->path_start_revision : NULL, b->path_end_revision, 0, b->edit_baton->target_revision, mimetype1, mimetype2, NULL, SVN_INVALID_REVNUM, b->propchanges, b->pristine_props, b->edit_baton->diff_cmd_baton, scratch_pool)); else SVN_ERR(eb->diff_callbacks->file_changed( &content_state, &prop_state, &b->tree_conflicted, b->path, b->path_end_revision ? b->path_start_revision : NULL, b->path_end_revision, b->edit_baton->revision, b->edit_baton->target_revision, mimetype1, mimetype2, b->propchanges, b->pristine_props, b->edit_baton->diff_cmd_baton, scratch_pool)); } if (eb->notify_func) { deleted_path_notify_t *dpn; svn_wc_notify_t *notify; svn_wc_notify_action_t action; svn_node_kind_t kind = svn_node_file; /* Find out if a pending delete notification for this path is * still around. */ dpn = apr_hash_get(eb->deleted_paths, b->path, APR_HASH_KEY_STRING); if (dpn) { /* If any was found, we will handle the pending 'deleted path * notification' (DPN) here. Remove it from the list. */ apr_hash_set(eb->deleted_paths, b->path, APR_HASH_KEY_STRING, NULL); /* the pending delete might be on a different node kind. */ kind = dpn->kind; content_state = prop_state = dpn->state; } /* Determine what the notification (ACTION) should be. * In case of a pending 'delete', this might become a 'replace'. */ if (b->tree_conflicted) action = svn_wc_notify_tree_conflict; else if (dpn) { if (dpn->action == svn_wc_notify_update_delete && b->added) action = svn_wc_notify_update_replace; else /* Note: dpn->action might be svn_wc_notify_tree_conflict */ action = dpn->action; } else if ((content_state == svn_wc_notify_state_missing) || (content_state == svn_wc_notify_state_obstructed)) action = svn_wc_notify_skip; else if (b->added) action = svn_wc_notify_update_add; else action = svn_wc_notify_update_update; notify = svn_wc_create_notify(b->path, action, scratch_pool); notify->kind = kind; notify->content_state = content_state; notify->prop_state = prop_state; (*eb->notify_func)(eb->notify_baton, notify, scratch_pool); } svn_pool_destroy(b->pool); /* Destroy file and scratch pool */ return SVN_NO_ERROR; }
/* An svn_delta_editor_t function. */ static svn_error_t * apply_textdelta(void *file_baton, const char *base_md5_digest, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, void **handler_baton) { struct file_baton *b = file_baton; svn_stream_t *src_stream; svn_stream_t *result_stream; apr_pool_t *scratch_pool = b->pool; /* Skip *everything* within a newly tree-conflicted directory. */ if (b->skip) { *handler = svn_delta_noop_window_handler; *handler_baton = NULL; return SVN_NO_ERROR; } /* If we're not sending file text, then ignore any that we receive. */ if (! b->edit_baton->text_deltas) { /* Supply valid paths to indicate there is a text change. */ SVN_ERR(get_empty_file(b->edit_baton, &b->path_start_revision)); SVN_ERR(get_empty_file(b->edit_baton, &b->path_end_revision)); *handler = svn_delta_noop_window_handler; *handler_baton = NULL; return SVN_NO_ERROR; } /* We need the expected pristine file, so go get it */ if (!b->added) SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool)); else SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision))); SVN_ERR_ASSERT(b->path_start_revision != NULL); if (base_md5_digest != NULL) { svn_checksum_t *base_md5_checksum; SVN_ERR(svn_checksum_parse_hex(&base_md5_checksum, svn_checksum_md5, base_md5_digest, scratch_pool)); if (!svn_checksum_match(base_md5_checksum, b->start_md5_checksum)) return svn_error_trace(svn_checksum_mismatch_err( base_md5_checksum, b->start_md5_checksum, scratch_pool, _("Base checksum mismatch for '%s'"), b->path)); } /* Open the file to be used as the base for second revision */ SVN_ERR(svn_stream_open_readonly(&src_stream, b->path_start_revision, scratch_pool, scratch_pool)); /* Open the file that will become the second revision after applying the text delta, it starts empty */ SVN_ERR(svn_stream_open_unique(&result_stream, &b->path_end_revision, NULL, svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); svn_txdelta_apply(src_stream, result_stream, b->result_digest, b->path, b->pool, &(b->apply_handler), &(b->apply_baton)); *handler = window_handler; *handler_baton = file_baton; return SVN_NO_ERROR; }