/* Retrieves a record */ mdatum_t mukv_fetch(mukv_t *kv, mdatum_t key, apr_pool_t *pool) { entry_t *entry; mdatum_t val; val.dptr = NULL; val.dsize = 0; entry = rhash_get(kv->index, key.dptr, key.dsize); if (entry == NULL) { return val; } if (ftell(kv->file) != entry->off) { if (fseek(kv->file, entry->off, SEEK_SET) != 0) { return val; } } val.dptr = apr_palloc(pool, entry->size); if (fread(val.dptr, 1, entry->size, kv->file) != entry->size) { val.dptr = NULL; return val; } val.dsize = entry->size; return val; }
/* Deletes a record (from the index, not from the disk) */ int mukv_delete(mukv_t *kv, mdatum_t key) { entry_t *entry = rhash_get(kv->index, key.dptr, key.dsize); if (entry) { rhash_set(kv->index, key.dptr, key.dsize, NULL, 0); } return 0; }
/* Subversion delta editor callback */ static svn_error_t *de_close_edit(void *edit_baton, apr_pool_t *pool) { de_baton_t *de_baton = (de_baton_t *)edit_baton; apr_hash_index_t *hi; svn_error_t *err; /* Recursively dump all nodes touched by this revision */ if ((err = delta_dump_node_recursive((de_node_baton_t *)de_baton->root_node))) { return err; } /* * There are probably some deleted nodes that haven't been dumped yet. * This will happen if nodes whose parent is a copy destination have been * deleted. */ for (hi = apr_hash_first(pool, de_baton->log_revision->changed_paths); hi; hi = apr_hash_next(hi)) { const char *path; svn_log_changed_path_t *log; apr_hash_this(hi, (const void **)(void *)&path, NULL, (void **)(void *)&log); DEBUG_MSG("Checking %s (%c)\n", path, log->action); if (log->action == 'D') { /* We can unlink a possible temporary file now */ char *filename = rhash_get(delta_hash, path, APR_HASH_KEY_STRING); if (filename) { #ifndef DUMP_DEBUG apr_file_remove(filename, pool); #endif rhash_set(delta_hash, path, APR_HASH_KEY_STRING, NULL, 0); } if (apr_hash_get(de_baton->dumped_entries, path, APR_HASH_KEY_STRING) == NULL) { de_node_baton_t *node = delta_create_node_no_parent(path, de_baton, pool); node->action = log->action; node->dump_needed = 1; DEBUG_MSG("Post-dumping %s\n", path); if ((err = delta_dump_node(node))) { return err; } } } } #ifdef USE_TIMING DEBUG_MSG("apply_text_delta: %f seconds\n", tm_de_apply_textdelta); #endif return SVN_NO_ERROR; }
/* Subversion delta editor callback */ static svn_error_t *de_apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, void **handler_baton) { apr_file_t *src_file = NULL, *dest_file = NULL; svn_stream_t *src_stream, *dest_stream; de_node_baton_t *node = (de_node_baton_t *)file_baton; dump_options_t *opts = node->de_baton->opts; #ifdef USE_TIMING stopwatch_t watch = stopwatch_create(); #endif char *filename; DEBUG_MSG("de_apply_textdelta(%s)\n", node->path); /* Create a new temporary file to write to */ node->filename = apr_psprintf(node->pool, "%s/XXXXXX", opts->temp_dir); apr_file_mktemp(&dest_file, node->filename, APR_CREATE | APR_READ | APR_WRITE | APR_EXCL, pool); dest_stream = svn_stream_from_aprfile2(dest_file, FALSE, pool); /* Update the local copy */ filename = rhash_get(delta_hash, node->path, APR_HASH_KEY_STRING); if (filename == NULL) { src_stream = svn_stream_empty(pool); } else { apr_file_open(&src_file, filename, APR_READ, 0600, pool); src_stream = svn_stream_from_aprfile2(src_file, FALSE, pool); } svn_txdelta_apply(src_stream, dest_stream, node->md5sum, node->path, pool, handler, handler_baton); node->old_filename = apr_pstrdup(node->pool, filename); rhash_set(delta_hash, node->path, APR_HASH_KEY_STRING, node->filename, RHASH_VAL_STRING); DEBUG_MSG("applied delta: %s -> %s\n", node->old_filename, node->filename); node->applied_delta = 1; node->dump_needed = 1; #ifdef USE_TIMING tm_de_apply_textdelta += stopwatch_elapsed(&watch); #endif return SVN_NO_ERROR; }
/* Loads the properties of a node to a temporary file */ static svn_error_t *delta_load_properties(de_node_baton_t *node) { char *filename; apr_file_t *file = NULL; apr_status_t status; apr_pool_t *pool = svn_pool_create(node->pool); filename = rhash_get(prop_hash, node->path, APR_HASH_KEY_STRING); if (filename == NULL) { /* No properties is ok, too */ svn_pool_destroy(pool); return SVN_NO_ERROR; } status = apr_file_open(&file, filename, APR_READ, 0600, pool); if (status) { apr_pool_t *epool = svn_pool_create(NULL); char *errbuf = apr_palloc(epool, ERRBUFFER_SIZE); svn_pool_destroy(pool); return svn_error_create(status, NULL, apr_strerror(status, errbuf, ERRBUFFER_SIZE)); } if (property_hash_load(node->properties, file, node->pool)) { apr_file_close(file); DEBUG_MSG("ERROR reading from %s\n", filename); return svn_error_create(1, NULL, "Error reading properties file"); } apr_file_close(file); /* Delete the old file if it exists */ apr_file_remove(filename, pool); rhash_set(prop_hash, node->path, APR_HASH_KEY_STRING, NULL, 0); svn_pool_destroy(pool); return SVN_NO_ERROR; }
/* Checks whether a record exists */ int mukv_exists(mukv_t *kv, mdatum_t key) { return (rhash_get(kv->index, key.dptr, key.dsize) != NULL); }
/* Dumps a node */ static svn_error_t *delta_dump_node(de_node_baton_t *node) { de_baton_t *de_baton = node->de_baton; session_t *session = de_baton->session; dump_options_t *opts = de_baton->opts; char *path = node->path; unsigned long prop_len, content_len; char dump_content = 0, dump_props = 0; apr_hash_index_t *hi; /* Check if the node needs to be dumped at all */ if (!node->dump_needed) { DEBUG_MSG("delta_dump_node(%s): aborting: dump not needed\n", node->path); return SVN_NO_ERROR; } /* Check if this is a dry run */ if (opts->flags & DF_DRY_RUN) { node->dump_needed = 0; DEBUG_MSG("delta_dump_node(%s): aborting: DF_DRY_RUN\n", node->path); return SVN_NO_ERROR; } /* If the node is a directory and no properties have been changed, we don't need to dump it */ if ((node->action == 'M') && (node->kind == svn_node_dir) && (!node->props_changed)) { node->dump_needed = 0; DEBUG_MSG("delta_dump_node(%s): aborting: directory && action == 'M' && !props_changed\n", node->path); return SVN_NO_ERROR; } /* If the node's parent has been copied, we don't need to dump it if its contents haven't changed. Addionally, make sure the node doesn't contain extra copyfrom information. */ if ((node->cp_info == CPI_COPY) && (node->action == 'A') && (node->copyfrom_path == NULL)) { node->dump_needed = 0; DEBUG_MSG("delta_dump_node(%s): aborting: cp_info == CPI_COPY && action == 'A'\n", node->path); return SVN_NO_ERROR; } DEBUG_MSG("delta_dump_node(%s): started\n", node->path); /* Check for potential copy. This is neede here because it might change the action. */ delta_check_copy(node); if (node->action == 'R') { /* Special handling for replacements */ DEBUG_MSG("delta_dump_node(%s): running delta_dump_replace()\n", node->path); return delta_dump_replace(node); } /* Dump node path */ if (opts->prefix != NULL) { printf("%s: %s%s\n", SVN_REPOS_DUMPFILE_NODE_PATH, opts->prefix, path); } else { printf("%s: %s\n", SVN_REPOS_DUMPFILE_NODE_PATH, path); } /* Dump node kind */ if (node->action != 'D') { printf("%s: %s\n", SVN_REPOS_DUMPFILE_NODE_KIND, node->kind == svn_node_file ? "file" : "dir"); } /* Dump action */ printf("%s: ", SVN_REPOS_DUMPFILE_NODE_ACTION); switch (node->action) { case 'M': printf("change\n"); if ((de_baton->opts->verbosity > 0) && !(de_baton->opts->flags & DF_DRY_RUN)) { fprintf(stderr, _(" * editing path : %s ... "), path); } break; case 'A': printf("add\n"); if ((de_baton->opts->verbosity > 0) && !(de_baton->opts->flags & DF_DRY_RUN)) { fprintf(stderr, _(" * adding path : %s ... "), path); } break; case 'D': printf("delete\n"); if ((de_baton->opts->verbosity > 0) && !(de_baton->opts->flags & DF_DRY_RUN)) { fprintf(stderr, _(" * deleting path : %s ... "), path); } /* We can finish early here */ printf("\n\n"); delta_mark_node(node); DEBUG_MSG("delta_dump_node(%s): deleted -> finished\n", node->path); return SVN_NO_ERROR; case 'R': printf("replace\n"); break; } /* Check if the node content needs to be dumped */ if (node->kind == svn_node_file && node->applied_delta) { DEBUG_MSG("delta_dump_node(%s): dump_content = 1\n", node->path); dump_content = 1; } /* Check if the node properties need to be dumped */ if ((node->props_changed) || (node->action == 'A')) { DEBUG_MSG("delta_dump_node(%s): dump_props = 1\n", node->path); dump_props = 1; } /* Output copy information if neccessary */ if (node->cp_info == CPI_COPY) { const char *copyfrom_path = delta_get_local_copyfrom_path(session->prefix, node->copyfrom_path); printf("%s: %ld\n", SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, node->copyfrom_rev_local); if (opts->prefix != NULL) { printf("%s: %s%s\n", SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, opts->prefix, copyfrom_path); } else { printf("%s: %s\n", SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, copyfrom_path); } /* Maybe we don't need to dump the contents */ if ((node->action == 'A') && (node->kind == svn_node_file)) { unsigned char *prev_md5 = rhash_get(md5_hash, copyfrom_path, APR_HASH_KEY_STRING); if (prev_md5 && !memcmp(node->md5sum, prev_md5, APR_MD5_DIGESTSIZE)) { DEBUG_MSG("md5sum matches\n"); dump_content = 0; } else { #ifdef DEBUG if (prev_md5) { DEBUG_MSG("md5sum doesn't match: (%s != %s)\n", svn_md5_digest_to_cstring(node->md5sum, node->pool), svn_md5_digest_to_cstring(prev_md5, node->pool)); } else { DEBUG_MSG("md5sum of %s not available\n", copyfrom_path); } #endif dump_content = 1; } } if (!dump_content && !node->props_changed) { printf("\n\n"); delta_mark_node(node); return 0; } else if (node->kind == svn_node_dir) { dump_content = 0; } } /* Deltify? */ if (dump_content && (opts->flags & DF_USE_DELTAS)) { svn_error_t *err; if ((err = delta_deltify_node(node))) { return err; } } #ifdef DUMP_DEBUG /* Dump some extra debug info */ if (dump_content) { printf("Debug-filename: %s\n", node->filename); if (node->old_filename) { printf("Debug-old-filename: %s\n", node->old_filename); } if (opts->flags & DF_USE_DELTAS) { printf("Debug-delta-filename: %s\n", node->delta_filename); } } #endif /* Dump properties & content */ prop_len = 0; content_len = 0; /* Dump property size */ for (hi = apr_hash_first(node->pool, node->properties); hi; hi = apr_hash_next(hi)) { const char *key; svn_string_t *value; apr_hash_this(hi, (const void **)(void *)&key, NULL, (void **)(void *)&value); /* Don't dump the property if it has been deleted */ if (apr_hash_get(node->del_properties, key, APR_HASH_KEY_STRING) != NULL) { continue; } prop_len += property_strlen(node->pool, key, value->data); } /* In dump format version 3, deleted properties should be dumped, too */ if (opts->dump_format == 3) { for (hi = apr_hash_first(node->pool, node->del_properties); hi; hi = apr_hash_next(hi)) { const char *key; void *value; apr_hash_this(hi, (const void **)(void *)&key, NULL, &value); prop_len += property_del_strlen(node->pool, key); } } if ((prop_len > 0)) { dump_props = 1; } if (dump_props) { if (opts->dump_format == 3) { printf("%s: true\n", SVN_REPOS_DUMPFILE_PROP_DELTA); } prop_len += PROPS_END_LEN; printf("%s: %lu\n", SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH, prop_len); } /* Dump content size */ if (dump_content) { char *fpath = (opts->flags & DF_USE_DELTAS) ? node->delta_filename : node->filename; apr_finfo_t *info = apr_pcalloc(node->pool, sizeof(apr_finfo_t)); if (apr_stat(info, fpath, APR_FINFO_SIZE, node->pool) != APR_SUCCESS) { DEBUG_MSG("delta_dump_node: FATAL: cannot stat %s\n", node->filename); return svn_error_create(1, NULL, apr_psprintf(session->pool, "Cannot stat %s", node->filename)); } content_len = (unsigned long)info->size; if (opts->flags & DF_USE_DELTAS) { printf("%s: true\n", SVN_REPOS_DUMPFILE_TEXT_DELTA); } printf("%s: %lu\n", SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH, content_len); if (*node->md5sum != 0x00) { printf("%s: %s\n", SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, svn_md5_digest_to_cstring(node->md5sum, node->pool)); } } printf("%s: %lu\n\n", SVN_REPOS_DUMPFILE_CONTENT_LENGTH, (unsigned long)prop_len+content_len); /* Dump properties */ if (dump_props) { for (hi = apr_hash_first(node->pool, node->properties); hi; hi = apr_hash_next(hi)) { const char *key; svn_string_t *value; apr_hash_this(hi, (const void **)(void *)&key, NULL, (void **)(void *)&value); /* Don't dump the property if it has been deleted */ if (apr_hash_get(node->del_properties, key, APR_HASH_KEY_STRING) != NULL) { continue; } property_dump(key, value->data); } /* In dump format version 3, deleted properties should be dumped, too */ if (opts->dump_format == 3) { for (hi = apr_hash_first(node->pool, node->del_properties); hi; hi = apr_hash_next(hi)) { const char *key; void *value; apr_hash_this(hi, (const void **)(void *)&key, NULL, &value); property_del_dump(key); } } printf(PROPS_END); } /* Dump content */ if (dump_content) { svn_error_t *err; apr_pool_t *pool = svn_pool_create(node->pool); const char *fpath = (opts->flags & DF_USE_DELTAS) ? node->delta_filename : node->filename; fflush(stdout); if ((err = delta_cat_file(pool, fpath))) { return err; } fflush(stdout); svn_pool_destroy(pool); #ifndef DUMP_DEBUG if (opts->flags & DF_USE_DELTAS) { apr_file_remove(node->delta_filename, node->pool); } #endif } printf("\n\n"); delta_mark_node(node); return SVN_NO_ERROR; }