diff options
Diffstat (limited to 'subversion/svnrdump')
-rw-r--r-- | subversion/svnrdump/dump_editor.c | 532 | ||||
-rw-r--r-- | subversion/svnrdump/load_editor.c | 309 | ||||
-rw-r--r-- | subversion/svnrdump/svnrdump.c | 403 | ||||
-rw-r--r-- | subversion/svnrdump/svnrdump.h | 1 | ||||
-rw-r--r-- | subversion/svnrdump/util.c | 4 |
5 files changed, 449 insertions, 800 deletions
diff --git a/subversion/svnrdump/dump_editor.c b/subversion/svnrdump/dump_editor.c index faf029f..bf4f81c 100644 --- a/subversion/svnrdump/dump_editor.c +++ b/subversion/svnrdump/dump_editor.c @@ -30,6 +30,7 @@ #include "svn_subst.h" #include "svn_dirent_uri.h" +#include "private/svn_repos_private.h" #include "private/svn_subr_private.h" #include "private/svn_dep_compat.h" #include "private/svn_editor.h" @@ -39,28 +40,16 @@ #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r)) -#if 0 -#define LDR_DBG(x) SVN_DBG(x) -#else -#define LDR_DBG(x) while(0) -#endif /* A directory baton used by all directory-related callback functions * in the dump editor. */ struct dir_baton { struct dump_edit_baton *eb; - struct dir_baton *parent_dir_baton; /* Pool for per-directory allocations */ apr_pool_t *pool; - /* is this directory a new addition to this revision? */ - svn_boolean_t added; - - /* has this directory been written to the output stream? */ - svn_boolean_t written_out; - /* the path to this directory */ const char *repos_relpath; /* a relpath */ @@ -68,6 +57,9 @@ struct dir_baton const char *copyfrom_path; /* a relpath */ svn_revnum_t copyfrom_rev; + /* Headers accumulated so far for this directory */ + svn_repos__dumpfile_headers_t *headers; + /* Properties which were modified during change_dir_prop. */ apr_hash_t *props; @@ -80,9 +72,8 @@ struct dir_baton us, although they're all really within this directory. */ apr_hash_t *deleted_entries; - /* Flags to trigger dumping props and record termination newlines. */ + /* Flag to trigger dumping props. */ svn_boolean_t dump_props; - svn_boolean_t dump_newlines; }; /* A file baton used by all file-related callback functions in the dump @@ -90,7 +81,6 @@ struct dir_baton struct file_baton { struct dump_edit_baton *eb; - struct dir_baton *parent_dir_baton; /* Pool for per-file allocations */ apr_pool_t *pool; @@ -120,13 +110,6 @@ struct file_baton svn_boolean_t dump_props; }; -/* A handler baton to be used in window_handler(). */ -struct handler_baton -{ - svn_txdelta_window_handler_t apply_handler; - void *apply_baton; -}; - /* The baton used by the dump editor. */ struct dump_edit_baton { /* The output stream we write the dumpfile to */ @@ -155,11 +138,10 @@ struct dump_edit_baton { /* The revision we're currently dumping. */ svn_revnum_t current_revision; - /* The kind (file or directory) and baton of the item whose block of + /* The baton of the directory node whose block of dump stream data has not been fully completed; NULL if there's no such item. */ - svn_node_kind_t pending_kind; - void *pending_baton; + struct dir_baton *pending_db; }; /* Make a directory baton to represent the directory at PATH (relative @@ -171,16 +153,15 @@ struct dump_edit_baton { * copy source. * * PB is the directory baton of this directory's parent, or NULL if - * this is the top-level directory of the edit. ADDED indicates if - * this directory is newly added in this revision. Perform all - * allocations in POOL. */ + * this is the top-level directory of the edit. + * + * Perform all allocations in POOL. */ static struct dir_baton * make_dir_baton(const char *path, const char *copyfrom_path, svn_revnum_t copyfrom_rev, void *edit_baton, struct dir_baton *pb, - svn_boolean_t added, apr_pool_t *pool) { struct dump_edit_baton *eb = edit_baton; @@ -199,15 +180,13 @@ make_dir_baton(const char *path, copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool); new_db->eb = eb; - new_db->parent_dir_baton = pb; new_db->pool = pool; new_db->repos_relpath = repos_relpath; new_db->copyfrom_path = copyfrom_path ? svn_relpath_canonicalize(copyfrom_path, pool) : NULL; new_db->copyfrom_rev = copyfrom_rev; - new_db->added = added; - new_db->written_out = FALSE; + new_db->headers = NULL; new_db->props = apr_hash_make(pool); new_db->deleted_props = apr_hash_make(pool); new_db->deleted_entries = apr_hash_make(pool); @@ -227,7 +206,6 @@ make_file_baton(const char *path, struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb)); new_fb->eb = pb->eb; - new_fb->parent_dir_baton = pb; new_fb->pool = pool; new_fb->repos_relpath = svn_relpath_canonicalize(path, pool); new_fb->props = apr_hash_make(pool); @@ -240,9 +218,11 @@ make_file_baton(const char *path, return new_fb; } -/* Return in *HEADER and *CONTENT the headers and content for PROPS. */ +/* Append to HEADERS the required headers, and set *CONTENT to the property + * content section, to represent the property delta of PROPS/DELETED_PROPS. + */ static svn_error_t * -get_props_content(svn_stringbuf_t **header, +get_props_content(svn_repos__dumpfile_headers_t *headers, svn_stringbuf_t **content, apr_hash_t *props, apr_hash_t *deleted_props, @@ -251,10 +231,8 @@ get_props_content(svn_stringbuf_t **header, { svn_stream_t *content_stream; apr_hash_t *normal_props; - const char *buf; *content = svn_stringbuf_create_empty(result_pool); - *header = svn_stringbuf_create_empty(result_pool); content_stream = svn_stream_from_stringbuf(*content, scratch_pool); @@ -265,101 +243,63 @@ get_props_content(svn_stringbuf_t **header, SVN_ERR(svn_stream_close(content_stream)); /* Prop-delta: true */ - *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA - ": true\n"); - - /* Prop-content-length: 193 */ - buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", (*content)->len); - svn_stringbuf_appendcstr(*header, buf); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true"); return SVN_NO_ERROR; } -/* Extract and dump properties stored in PROPS and property deletions - * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to - * FALSE. +/* A special case of dump_node(), for a delete record. * - * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing - * the content block of the property changes; otherwise, dump that to - * the stream, too. + * The only thing special about this version is it only writes one blank + * line, not two, after the headers. Why? Historical precedent for the + * case where a delete record is used as part of a (delete + add-with-history) + * in implementing a replacement. */ static svn_error_t * -do_dump_props(svn_stringbuf_t **propstring, - svn_stream_t *stream, - apr_hash_t *props, - apr_hash_t *deleted_props, - svn_boolean_t *trigger_var, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +dump_node_delete(svn_stream_t *stream, + const char *node_relpath, + apr_pool_t *pool) { - svn_stringbuf_t *header; - svn_stringbuf_t *content; - apr_size_t len; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); - if (trigger_var && !*trigger_var) - return SVN_NO_ERROR; - - SVN_ERR(get_props_content(&header, &content, props, deleted_props, - result_pool, scratch_pool)); - len = header->len; - SVN_ERR(svn_stream_write(stream, header->data, &len)); + assert(svn_relpath_is_canonical(node_relpath)); - if (propstring) - { - *propstring = content; - } - else - { - /* Content-length: 14 */ - SVN_ERR(svn_stream_printf(stream, scratch_pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - content->len)); - - len = content->len; - SVN_ERR(svn_stream_write(stream, content->data, &len)); - - /* No text is going to be dumped. Write a couple of newlines and - wait for the next node/ revision. */ - SVN_ERR(svn_stream_puts(stream, "\n\n")); - - /* Cleanup so that data is never dumped twice. */ - apr_hash_clear(props); - apr_hash_clear(deleted_props); - if (trigger_var) - *trigger_var = FALSE; - } + /* Node-path: ... */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); - return SVN_NO_ERROR; -} + /* Node-action: delete */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); -static svn_error_t * -do_dump_newlines(struct dump_edit_baton *eb, - svn_boolean_t *trigger_var, - apr_pool_t *pool) -{ - if (trigger_var && *trigger_var) - { - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - *trigger_var = FALSE; - } + SVN_ERR(svn_repos__dump_node_record(stream, headers, + NULL, FALSE, 0, /* props & text */ + FALSE /*content_length_always*/, pool)); return SVN_NO_ERROR; } -/* - * Write out a node record for PATH of type KIND under EB->FS_ROOT. +/* Set *HEADERS_P to contain some headers for the node at PATH of type KIND. + * * ACTION describes what is happening to the node (see enum - * svn_node_action). Write record to writable EB->STREAM, using - * EB->BUFFER to write in chunks. + * svn_node_action). * * If the node was itself copied, IS_COPY is TRUE and the * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV. * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this * node is part of a copied subtree. + * + * Iff ACTION is svn_node_action_replace and IS_COPY, then first write a + * complete deletion record to the dump stream. + * + * If ACTION is svn_node_action_delete, then the node record will be + * complete. (The caller may want to write two blank lines after the + * header block.) */ static svn_error_t * -dump_node(struct dump_edit_baton *eb, +dump_node(svn_repos__dumpfile_headers_t **headers_p, + struct dump_edit_baton *eb, const char *repos_relpath, struct dir_baton *db, struct file_baton *fb, @@ -370,6 +310,8 @@ dump_node(struct dump_edit_baton *eb, apr_pool_t *pool) { const char *node_relpath = repos_relpath; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); assert(svn_relpath_is_canonical(repos_relpath)); assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path)); @@ -381,17 +323,16 @@ dump_node(struct dump_edit_baton *eb, node_relpath, pool); /* Node-path: ... */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - node_relpath)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); /* Node-kind: "file" | "dir" */ if (fb) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file"); else if (db) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Write the appropriate Node-action header */ @@ -403,26 +344,22 @@ dump_node(struct dump_edit_baton *eb, do here but print node action information. Node-action: change. */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change"); + break; + + case svn_node_action_delete: + /* Node-action: delete */ + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); break; case svn_node_action_replace: - if (is_copy) - { - /* Delete the original, and then re-add the replacement as a - copy using recursive calls into this function. */ - SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete, - FALSE, NULL, SVN_INVALID_REVNUM, pool)); - SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add, - is_copy, copyfrom_path, copyfrom_rev, pool)); - } - else + if (! is_copy) { /* Node-action: replace */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION - ": replace\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace"); /* Wait for a change_*_prop to be called before dumping anything */ @@ -430,45 +367,36 @@ dump_node(struct dump_edit_baton *eb, fb->dump_props = TRUE; else if (db) db->dump_props = TRUE; + break; } - break; - - case svn_node_action_delete: - /* Node-action: delete */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); + else + { + /* More complex case: is_copy is true, and copyfrom_path/ + copyfrom_rev are present: delete the original, and then re-add + it */ + /* ### Why not write a 'replace' record? Don't know. */ - /* We can leave this routine quietly now. Nothing more to do- - print a couple of newlines because we're not dumping props or - text. */ - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); + /* ### Unusually, we end this 'delete' node record with only a single + blank line after the header block -- no extra blank line. */ + SVN_ERR(dump_node_delete(eb->stream, repos_relpath, pool)); - break; + /* The remaining action is a non-replacing add-with-history */ + /* action = svn_node_action_add; */ + } + /* FALL THROUGH to 'add' */ case svn_node_action_add: /* Node-action: add */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); if (is_copy) { /* Node-copyfrom-rev / Node-copyfrom-path */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV - ": %ld\n" - SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH - ": %s\n", - copyfrom_rev, copyfrom_path)); - - /* Ugly hack: If a directory was copied from a previous - revision, nothing like close_file() will be called to write two - blank lines. If change_dir_prop() is called, props are dumped - (along with the necessary PROPS-END\n\n and we're good. So - set DUMP_NEWLINES here to print the newlines unless - change_dir_prop() is called next otherwise the `svnadmin load` - parser will fail. */ - if (db) - db->dump_newlines = TRUE; + svn_repos__dumpfile_header_pushf( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", copyfrom_rev); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, copyfrom_path); } else { @@ -496,6 +424,11 @@ dump_node(struct dump_edit_baton *eb, break; } + + /* Return the headers so far. We don't necessarily have all the headers + yet -- there may be property-related and content length headers to + come, if this was not a 'delete' record. */ + *headers_p = headers; return SVN_NO_ERROR; } @@ -504,35 +437,29 @@ dump_mkdir(struct dump_edit_baton *eb, const char *repos_relpath, apr_pool_t *pool) { - svn_stringbuf_t *prop_header, *prop_content; - apr_size_t len; - const char *buf; + svn_stringbuf_t *prop_content; + svn_repos__dumpfile_headers_t *headers + = svn_repos__dumpfile_headers_create(pool); /* Node-path: ... */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", - repos_relpath)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_PATH, repos_relpath); /* Node-kind: dir */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); /* Node-action: add */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); /* Dump the (empty) property block. */ - SVN_ERR(get_props_content(&prop_header, &prop_content, + SVN_ERR(get_props_content(headers, &prop_content, apr_hash_make(pool), apr_hash_make(pool), pool, pool)); - len = prop_header->len; - SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len)); - len = prop_content->len; - buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", len); - SVN_ERR(svn_stream_puts(eb->stream, buf)); - SVN_ERR(svn_stream_puts(eb->stream, "\n")); - SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len)); + SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, prop_content, + FALSE, 0, FALSE /*content_length_always*/, + pool)); /* Newlines to tie it all off. */ SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); @@ -540,40 +467,43 @@ dump_mkdir(struct dump_edit_baton *eb, return SVN_NO_ERROR; } -/* Dump pending items from the specified node, to allow starting the dump - of a child node */ +/* Dump pending headers and properties for the directory EB->pending_db (if + * not null), to allow starting the dump of a child node */ static svn_error_t * -dump_pending(struct dump_edit_baton *eb, - apr_pool_t *scratch_pool) +dump_pending_dir(struct dump_edit_baton *eb, + apr_pool_t *scratch_pool) { - if (! eb->pending_baton) + struct dir_baton *db = eb->pending_db; + svn_stringbuf_t *prop_content = NULL; + + if (! db) return SVN_NO_ERROR; - if (eb->pending_kind == svn_node_dir) + /* Some pending properties to dump? */ + if (db->dump_props) { - struct dir_baton *db = eb->pending_baton; + SVN_ERR(get_props_content(db->headers, &prop_content, + db->props, db->deleted_props, + scratch_pool, scratch_pool)); + } + SVN_ERR(svn_repos__dump_node_record(eb->stream, db->headers, prop_content, + FALSE, 0, FALSE /*content_length_always*/, + scratch_pool)); - /* Some pending properties to dump? */ - SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props, - &(db->dump_props), db->pool, scratch_pool)); + /* No text is going to be dumped. Write a couple of newlines and + wait for the next node/ revision. */ + SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - /* Some pending newlines to dump? */ - SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool)); - } - else if (eb->pending_kind == svn_node_file) + if (db->dump_props) { - struct file_baton *fb = eb->pending_baton; - - /* Some pending properties to dump? */ - SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props, - &(fb->dump_props), fb->pool, scratch_pool)); + /* Cleanup so that data is never dumped twice. */ + apr_hash_clear(db->props); + apr_hash_clear(db->deleted_props); + db->dump_props = FALSE; } - else - abort(); /* Anything that was pending is pending no longer. */ - eb->pending_baton = NULL; - eb->pending_kind = svn_node_none; + eb->pending_db = NULL; return SVN_NO_ERROR; } @@ -594,8 +524,6 @@ open_root(void *edit_baton, /* Clear the per-revision pool after each revision */ svn_pool_clear(eb->pool); - LDR_DBG(("open_root %p\n", *root_baton)); - if (eb->update_anchor_relpath) { int i; @@ -628,16 +556,15 @@ open_root(void *edit_baton, /* ... but for the source directory itself, we'll defer to letting the typical plumbing handle this task. */ new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, TRUE, pool); - SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db, + edit_baton, NULL, pool); + SVN_ERR(dump_node(&new_db->headers, + eb, new_db->repos_relpath, new_db, NULL, svn_node_action_add, FALSE, NULL, SVN_INVALID_REVNUM, pool)); /* Remember that we've started but not yet finished handling this directory. */ - new_db->written_out = TRUE; - eb->pending_baton = new_db; - eb->pending_kind = svn_node_dir; + eb->pending_db = new_db; } } svn_pool_destroy(iterpool); @@ -646,7 +573,7 @@ open_root(void *edit_baton, if (! new_db) { new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, FALSE, pool); + edit_baton, NULL, pool); } *root_baton = new_db; @@ -661,15 +588,13 @@ delete_entry(const char *path, { struct dir_baton *pb = parent_baton; - LDR_DBG(("delete_entry %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* We don't dump this deletion immediate. Rather, we add this path to the deleted_entries of the parent directory baton. That way, we can tell (later) an addition from a replacement. All the real deletions get handled in close_directory(). */ - svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path), pb); + svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->pool, path), pb); return SVN_NO_ERROR; } @@ -683,40 +608,37 @@ add_directory(const char *path, void **child_baton) { struct dir_baton *pb = parent_baton; - void *val; + void *was_deleted; struct dir_baton *new_db; svn_boolean_t is_copy; - LDR_DBG(("add_directory %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, - pb, TRUE, pb->eb->pool); + pb, pb->pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect an add-with-history */ is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); /* Dump the node */ - SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL, - val ? svn_node_action_replace : svn_node_action_add, + SVN_ERR(dump_node(&new_db->headers, + pb->eb, new_db->repos_relpath, new_db, NULL, + was_deleted ? svn_node_action_replace : svn_node_action_add, is_copy, is_copy ? new_db->copyfrom_path : NULL, is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, pool)); - if (val) + if (was_deleted) /* Delete the path, it's now been dumped */ svn_hash_sets(pb->deleted_entries, path, NULL); /* Remember that we've started, but not yet finished handling this directory. */ - new_db->written_out = TRUE; - pb->eb->pending_baton = new_db; - pb->eb->pending_kind = svn_node_dir; + pb->eb->pending_db = new_db; *child_baton = new_db; return SVN_NO_ERROR; @@ -734,9 +656,7 @@ open_directory(const char *path, const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; - LDR_DBG(("open_directory %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* If the parent directory has explicit comparison path and rev, record the same for this one. */ @@ -744,12 +664,12 @@ open_directory(const char *path, { copyfrom_path = svn_relpath_join(pb->copyfrom_path, svn_relpath_basename(path, NULL), - pb->eb->pool); + pb->pool); copyfrom_rev = pb->copyfrom_rev; } new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, - FALSE, pb->eb->pool); + pb->pool); *child_baton = new_db; return SVN_NO_ERROR; @@ -763,12 +683,10 @@ close_directory(void *dir_baton, apr_hash_index_t *hi; svn_boolean_t this_pending; - LDR_DBG(("close_directory %p\n", dir_baton)); - /* Remember if this directory is the one currently pending. */ - this_pending = (db->eb->pending_baton == db); + this_pending = (db->eb->pending_db == db); - SVN_ERR(dump_pending(db->eb, pool)); + SVN_ERR(dump_pending_dir(db->eb, pool)); /* If this directory was pending, then dump_pending() should have taken care of all the props and such. Of course, the only way @@ -781,22 +699,23 @@ close_directory(void *dir_baton, directory. */ if ((! this_pending) && (db->dump_props)) { - SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL, + SVN_ERR(dump_node(&db->headers, + db->eb, db->repos_relpath, db, NULL, svn_node_action_change, FALSE, NULL, SVN_INVALID_REVNUM, pool)); - db->eb->pending_baton = db; - db->eb->pending_kind = svn_node_dir; - SVN_ERR(dump_pending(db->eb, pool)); + db->eb->pending_db = db; + SVN_ERR(dump_pending_dir(db->eb, pool)); } /* Dump the deleted directory entries */ for (hi = apr_hash_first(pool, db->deleted_entries); hi; hi = apr_hash_next(hi)) { - const char *path = svn__apr_hash_index_key(hi); + const char *path = apr_hash_this_key(hi); - SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete, - FALSE, NULL, SVN_INVALID_REVNUM, pool)); + SVN_ERR(dump_node_delete(db->eb->stream, path, pool)); + /* This deletion record is complete -- write an extra newline */ + SVN_ERR(svn_stream_puts(db->eb->stream, "\n")); } /* ### should be unnecessary */ @@ -815,17 +734,15 @@ add_file(const char *path, { struct dir_baton *pb = parent_baton; struct file_baton *fb; - void *val; + void *was_deleted; - LDR_DBG(("add_file %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* Make the file baton. */ fb = make_file_baton(path, pb, pool); /* This might be a replacement -- is the path already deleted? */ - val = svn_hash_gets(pb->deleted_entries, path); + was_deleted = svn_hash_gets(pb->deleted_entries, path); /* Detect add-with-history. */ if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev)) @@ -834,10 +751,10 @@ add_file(const char *path, fb->copyfrom_rev = copyfrom_rev; fb->is_copy = TRUE; } - fb->action = val ? svn_node_action_replace : svn_node_action_add; + fb->action = was_deleted ? svn_node_action_replace : svn_node_action_add; /* Delete the path, it's now been dumped. */ - if (val) + if (was_deleted) svn_hash_sets(pb->deleted_entries, path, NULL); *file_baton = fb; @@ -854,9 +771,7 @@ open_file(const char *path, struct dir_baton *pb = parent_baton; struct file_baton *fb; - LDR_DBG(("open_file %s\n", path)); - - SVN_ERR(dump_pending(pb->eb, pool)); + SVN_ERR(dump_pending_dir(pb->eb, pool)); /* Make the file baton. */ fb = make_file_baton(path, pb, pool); @@ -867,7 +782,7 @@ open_file(const char *path, { fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path, svn_relpath_basename(path, NULL), - pb->eb->pool); + pb->pool); fb->copyfrom_rev = pb->copyfrom_rev; } @@ -884,13 +799,11 @@ change_dir_prop(void *parent_baton, struct dir_baton *db = parent_baton; svn_boolean_t this_pending; - LDR_DBG(("change_dir_prop %p\n", parent_baton)); - /* This directory is not pending, but something else is, so handle the "something else". */ - this_pending = (db->eb->pending_baton == db); + this_pending = (db->eb->pending_db == db); if (! this_pending) - SVN_ERR(dump_pending(db->eb, pool)); + SVN_ERR(dump_pending_dir(db->eb, pool)); if (svn_property_kind2(name) != svn_prop_regular_kind) return SVN_NO_ERROR; @@ -902,9 +815,7 @@ change_dir_prop(void *parent_baton, else svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), ""); - /* Make sure we eventually output the props, and disable printing - a couple of extra newlines */ - db->dump_newlines = FALSE; + /* Make sure we eventually output the props */ db->dump_props = TRUE; return SVN_NO_ERROR; @@ -918,8 +829,6 @@ change_file_prop(void *file_baton, { struct file_baton *fb = file_baton; - LDR_DBG(("change_file_prop %p\n", file_baton)); - if (svn_property_kind2(name) != svn_prop_regular_kind) return SVN_NO_ERROR; @@ -939,22 +848,6 @@ change_file_prop(void *file_baton, } static svn_error_t * -window_handler(svn_txdelta_window_t *window, void *baton) -{ - struct handler_baton *hb = baton; - static svn_error_t *err; - - err = hb->apply_handler(window, hb->apply_baton); - if (window != NULL && !err) - return SVN_NO_ERROR; - - if (err) - SVN_ERR(err); - - return SVN_NO_ERROR; -} - -static svn_error_t * apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, @@ -962,31 +855,19 @@ apply_textdelta(void *file_baton, const char *base_checksum, { struct file_baton *fb = file_baton; struct dump_edit_baton *eb = fb->eb; - struct handler_baton *hb; svn_stream_t *delta_filestream; - LDR_DBG(("apply_textdelta %p\n", file_baton)); - - /* This is custom handler_baton, allocated from a separate pool. */ - hb = apr_pcalloc(eb->pool, sizeof(*hb)); - /* Use a temporary file to measure the Text-content-length */ delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool); /* Prepare to write the delta to the delta_filestream */ - svn_txdelta_to_svndiff3(&(hb->apply_handler), &(hb->apply_baton), + svn_txdelta_to_svndiff3(handler, handler_baton, delta_filestream, 0, SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); /* Record that there's text to be dumped, and its base checksum. */ fb->dump_text = TRUE; - fb->base_checksum = apr_pstrdup(eb->pool, base_checksum); - - /* The actual writing takes place when this function has - finished. Set handler and handler_baton now so for - window_handler() */ - *handler = window_handler; - *handler_baton = hb; + fb->base_checksum = apr_pstrdup(fb->pool, base_checksum); return SVN_NO_ERROR; } @@ -999,22 +880,25 @@ close_file(void *file_baton, struct file_baton *fb = file_baton; struct dump_edit_baton *eb = fb->eb; apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t)); - svn_stringbuf_t *propstring; + svn_stringbuf_t *propstring = NULL; + svn_repos__dumpfile_headers_t *headers; - LDR_DBG(("close_file %p\n", file_baton)); + SVN_ERR(dump_pending_dir(eb, pool)); - SVN_ERR(dump_pending(eb, pool)); - - /* Dump the node. */ - SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb, + /* Start dumping this node, by collecting some basic headers for it. */ + SVN_ERR(dump_node(&headers, eb, fb->repos_relpath, NULL, fb, fb->action, fb->is_copy, fb->copyfrom_path, fb->copyfrom_rev, pool)); /* Some pending properties to dump? We'll dump just the headers for now, then dump the actual propchange content only after dumping the text headers too (if present). */ - SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props, - &(fb->dump_props), pool, pool)); + if (fb->dump_props) + { + SVN_ERR(get_props_content(headers, &propstring, + fb->props, fb->deleted_props, + pool, pool)); + } /* Dump the text headers */ if (fb->dump_text) @@ -1022,9 +906,8 @@ close_file(void *file_baton, apr_status_t err; /* Text-delta: true */ - SVN_ERR(svn_stream_puts(eb->stream, - SVN_REPOS_DUMPFILE_TEXT_DELTA - ": true\n")); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true"); err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file); if (err) @@ -1032,43 +915,22 @@ close_file(void *file_baton, if (fb->base_checksum) /* Text-delta-base-md5: */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 - ": %s\n", - fb->base_checksum)); - - /* Text-content-length: 39 */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH - ": %lu\n", - (unsigned long)info->size)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, fb->base_checksum); /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */ - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 - ": %s\n", - text_checksum)); + svn_repos__dumpfile_header_push( + headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, text_checksum); } - /* Content-length: 1549 */ - /* If both text and props are absent, skip this header */ - if (fb->dump_props) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %ld\n\n", - (unsigned long)info->size + propstring->len)); - else if (fb->dump_text) - SVN_ERR(svn_stream_printf(eb->stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %ld\n\n", - (unsigned long)info->size)); - - /* Dump the props now */ + /* Dump the headers and props now */ + SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, propstring, + fb->dump_text, info->size, + FALSE /*content_length_always*/, + pool)); + if (fb->dump_props) { - SVN_ERR(svn_stream_write(eb->stream, propstring->data, - &(propstring->len))); - /* Cleanup */ fb->dump_props = FALSE; apr_hash_clear(fb->props); @@ -1235,7 +1097,7 @@ svn_rdump__get_dump_editor(const svn_delta_editor_t **editor, eb->ra_session = ra_session; eb->update_anchor_relpath = update_anchor_relpath; eb->current_revision = revision; - eb->pending_kind = svn_node_none; + eb->pending_db = NULL; /* Create a special per-revision pool */ eb->pool = svn_pool_create(pool); diff --git a/subversion/svnrdump/load_editor.c b/subversion/svnrdump/load_editor.c index c35a289..15dac6e 100644 --- a/subversion/svnrdump/load_editor.c +++ b/subversion/svnrdump/load_editor.c @@ -43,14 +43,7 @@ #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r)) -#if 0 -#define LDR_DBG(x) SVN_DBG(x) -#else -#define LDR_DBG(x) while(0) -#endif - - /** * General baton used by the parser functions. */ @@ -68,9 +61,6 @@ struct parse_baton /* To bleep, or not to bleep? (What kind of question is that?) */ svn_boolean_t quiet; - /* UUID found in the dumpstream, if any; NULL otherwise. */ - const char *uuid; - /* Root URL of the target repository. */ const char *root_url; @@ -93,17 +83,20 @@ struct parse_baton /* The oldest revision loaded from the dump stream, or SVN_INVALID_REVNUM if none have been loaded. */ svn_revnum_t oldest_dumpstream_rev; + + /* An hash containing specific revision properties to skip while + loading. */ + apr_hash_t *skip_revprops; }; /** * Use to wrap the dir_context_t in commit.c so we can keep track of - * depth, relpath and parent for open_directory and close_directory. + * relpath and parent for open_directory and close_directory. */ struct directory_baton { void *baton; const char *relpath; - int depth; /* The copy-from source of this directory, no matter whether it is copied explicitly (the root node of a copy) or implicitly (being an @@ -190,160 +183,6 @@ get_revision_mapping(apr_hash_t *rev_map, } -/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with - PARENT_DIR, and return it in *MERGEINFO_VAL. */ -/* ### FIXME: Consider somehow sharing code with - ### libsvn_repos/load-fs-vtable.c:prefix_mergeinfo_paths() */ -static svn_error_t * -prefix_mergeinfo_paths(svn_string_t **mergeinfo_val, - const svn_string_t *mergeinfo_orig, - const char *parent_dir, - apr_pool_t *pool) -{ - apr_hash_t *prefixed_mergeinfo, *mergeinfo; - apr_hash_index_t *hi; - void *rangelist; - - SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool)); - prefixed_mergeinfo = apr_hash_make(pool); - for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) - { - const void *key; - const char *path, *merge_source; - - apr_hash_this(hi, &key, NULL, &rangelist); - merge_source = svn_relpath_canonicalize(key, pool); - - /* The svn:mergeinfo property syntax demands a repos abspath */ - path = svn_fspath__canonicalize(svn_relpath_join(parent_dir, - merge_source, pool), - pool); - svn_hash_sets(prefixed_mergeinfo, path, rangelist); - } - return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool); -} - - -/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists - as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL - (allocated from POOL). */ -/* ### FIXME: Consider somehow sharing code with - ### libsvn_repos/load-fs-vtable.c:renumber_mergeinfo_revs() */ -static svn_error_t * -renumber_mergeinfo_revs(svn_string_t **final_val, - const svn_string_t *initial_val, - struct revision_baton *rb, - apr_pool_t *pool) -{ - apr_pool_t *subpool = svn_pool_create(pool); - svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo; - svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool); - apr_hash_index_t *hi; - - SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool)); - - /* Issue #3020 - http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16 - Remove mergeinfo older than the oldest revision in the dump stream - and adjust its revisions by the difference between the head rev of - the target repository and the current dump stream rev. */ - if (rb->pb->oldest_dumpstream_rev > 1) - { - SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( - &predates_stream_mergeinfo, mergeinfo, - rb->pb->oldest_dumpstream_rev - 1, 0, - TRUE, subpool, subpool)); - SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( - &mergeinfo, mergeinfo, - rb->pb->oldest_dumpstream_rev - 1, 0, - FALSE, subpool, subpool)); - SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists( - &predates_stream_mergeinfo, - predates_stream_mergeinfo, - -rb->rev_offset, subpool, subpool)); - } - else - { - predates_stream_mergeinfo = NULL; - } - - for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi)) - { - svn_rangelist_t *rangelist; - struct parse_baton *pb = rb->pb; - int i; - const void *path; - apr_ssize_t pathlen; - void *val; - - apr_hash_this(hi, &path, &pathlen, &val); - rangelist = val; - - /* Possibly renumber revisions in merge source's rangelist. */ - for (i = 0; i < rangelist->nelts; i++) - { - svn_revnum_t rev_from_map; - svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, - svn_merge_range_t *); - rev_from_map = get_revision_mapping(pb->rev_map, range->start); - if (SVN_IS_VALID_REVNUM(rev_from_map)) - { - range->start = rev_from_map; - } - else if (range->start == pb->oldest_dumpstream_rev - 1) - { - /* Since the start revision of svn_merge_range_t are not - inclusive there is one possible valid start revision that - won't be found in the PB->REV_MAP mapping of load stream - revsions to loaded revisions: The revision immediately - preceeding the oldest revision from the load stream. - This is a valid revision for mergeinfo, but not a valid - copy from revision (which PB->REV_MAP also maps for) so it - will never be in the mapping. - - If that is what we have here, then find the mapping for the - oldest rev from the load stream and subtract 1 to get the - renumbered, non-inclusive, start revision. */ - rev_from_map = get_revision_mapping(pb->rev_map, - pb->oldest_dumpstream_rev); - if (SVN_IS_VALID_REVNUM(rev_from_map)) - range->start = rev_from_map - 1; - } - else - { - /* If we can't remap the start revision then don't even bother - trying to remap the end revision. It's possible we might - actually succeed at the latter, which can result in invalid - mergeinfo with a start rev > end rev. If that gets into the - repository then a world of bustage breaks loose anytime that - bogus mergeinfo is parsed. See - http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16. - */ - continue; - } - - rev_from_map = get_revision_mapping(pb->rev_map, range->end); - if (SVN_IS_VALID_REVNUM(rev_from_map)) - range->end = rev_from_map; - } - apr_hash_set(final_mergeinfo, path, pathlen, rangelist); - } - - if (predates_stream_mergeinfo) - { - SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, - subpool, subpool)); - } - - SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool)); - - SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool)); - svn_pool_destroy(subpool); - - return SVN_NO_ERROR; -} - - static svn_error_t * commit_callback(const svn_commit_info_t *commit_info, void *baton, @@ -564,8 +403,8 @@ new_revision_record(void **revision_baton, for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi)) { - const char *hname = svn__apr_hash_index_key(hi); - const char *hval = svn__apr_hash_index_val(hi); + const char *hname = apr_hash_this_key(hi); + const char *hval = apr_hash_this_val(hi); if (strcmp(hname, SVN_REPOS_DUMPFILE_REVISION_NUMBER) == 0) rb->rev = atoi(hval); @@ -606,9 +445,6 @@ uuid_record(const char *uuid, void *parse_baton, apr_pool_t *pool) { - struct parse_baton *pb; - pb = parse_baton; - pb->uuid = apr_pstrdup(pool, uuid); return SVN_NO_ERROR; } @@ -704,8 +540,6 @@ new_node_record(void **node_baton, rb->rev - rb->rev_offset - 1, rb->pool, &child_baton)); - LDR_DBG(("Opened root %p\n", child_baton)); - /* child_baton corresponds to the root directory baton here */ push_directory(rb, child_baton, "", TRUE /*is_added*/, NULL, SVN_INVALID_REVNUM); @@ -713,8 +547,8 @@ new_node_record(void **node_baton, for (hi = apr_hash_first(rb->pool, headers); hi; hi = apr_hash_next(hi)) { - const char *hname = svn__apr_hash_index_key(hi); - const char *hval = svn__apr_hash_index_val(hi); + const char *hname = apr_hash_this_key(hi); + const char *hval = apr_hash_this_val(hi); /* Parse the different kinds of headers we can encounter and stuff them into the node_baton for writing later */ @@ -741,6 +575,9 @@ new_node_record(void **node_baton, nb->copyfrom_path = apr_pstrdup(rb->pool, hval); } + /* Before handling the new node, ensure depth-first editing order by + traversing the directory hierarchy from the old node's to the new + node's parent directory. */ nb_dirname = svn_relpath_dirname(nb->path, pool); if (svn_path_compare_paths(nb_dirname, rb->db->relpath) != 0) @@ -751,9 +588,6 @@ new_node_record(void **node_baton, int i; apr_size_t n; - /* Before attempting to handle the action, call open_directory - for all the path components and set the directory baton - accordingly */ ancestor_path = svn_relpath_get_longest_ancestor(nb_dirname, rb->db->relpath, pool); @@ -771,7 +605,6 @@ new_node_record(void **node_baton, /* Don't worry about destroying the actual rb->db object, since the pool we're using has the lifetime of one revision anyway */ - LDR_DBG(("Closing dir %p\n", rb->db->baton)); SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool)); rb->db = rb->db->parent; } @@ -786,7 +619,6 @@ new_node_record(void **node_baton, rb->db->baton, rb->rev - rb->rev_offset - 1, rb->pool, &child_baton)); - LDR_DBG(("Opened dir %p\n", child_baton)); push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/, NULL, SVN_INVALID_REVNUM); } @@ -816,6 +648,7 @@ new_node_record(void **node_baton, if (rb->pb->parent_dir) nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, nb->copyfrom_path, rb->pool); + /* Convert to a URL, as the commit editor requires. */ nb->copyfrom_url = svn_path_url_add_component2(rb->pb->root_url, nb->copyfrom_path, rb->pool); @@ -826,7 +659,6 @@ new_node_record(void **node_baton, { case svn_node_action_delete: case svn_node_action_replace: - LDR_DBG(("Deleting entry %s in %p\n", nb->path, rb->db->baton)); SVN_ERR(commit_editor->delete_entry(nb->path, rb->rev - rb->rev_offset - 1, rb->db->baton, rb->pool)); @@ -843,16 +675,12 @@ new_node_record(void **node_baton, nb->copyfrom_url, nb->copyfrom_rev, rb->pool, &(nb->file_baton))); - LDR_DBG(("Added file %s to dir %p as %p\n", - nb->path, rb->db->baton, nb->file_baton)); break; case svn_node_dir: SVN_ERR(commit_editor->add_directory(nb->path, rb->db->baton, nb->copyfrom_url, nb->copyfrom_rev, rb->pool, &child_baton)); - LDR_DBG(("Added dir %s to dir %p as %p\n", - nb->path, rb->db->baton, child_baton)); push_directory(rb, child_baton, nb->path, TRUE /*is_added*/, nb->copyfrom_path, nb->copyfrom_rev); break; @@ -896,11 +724,13 @@ set_revision_property(void *baton, if (rb->rev > 0) { - svn_hash_sets(rb->revprop_table, - apr_pstrdup(rb->pool, name), - svn_string_dup(value, rb->pool)); + if (! svn_hash_gets(rb->pb->skip_revprops, name)) + svn_hash_sets(rb->revprop_table, + apr_pstrdup(rb->pool, name), + svn_string_dup(value, rb->pool)); } - else if (rb->rev_offset == -1) + else if (rb->rev_offset == -1 + && ! svn_hash_gets(rb->pb->skip_revprops, name)) { /* Special case: set revision 0 properties directly (which is safe because the commit_editor hasn't been created yet), but @@ -925,51 +755,30 @@ set_node_property(void *baton, const svn_string_t *value) { struct node_baton *nb = baton; + struct revision_baton *rb = nb->rb; + struct parse_baton *pb = rb->pb; apr_pool_t *pool = nb->rb->pool; svn_prop_t *prop; if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) { - svn_string_t *renumbered_mergeinfo; - svn_string_t prop_val; - - /* Tolerate mergeinfo with "\r\n" line endings because some - dumpstream sources might contain as much. If so normalize - the line endings to '\n' and make a notification to - PARSE_BATON->FEEDBACK_STREAM that we have made this - correction. */ - if (strstr(value->data, "\r")) + svn_string_t *new_value; + svn_error_t *err; + + err = svn_repos__adjust_mergeinfo_property(&new_value, value, + pb->parent_dir, + pb->rev_map, + pb->oldest_dumpstream_rev, + rb->rev_offset, + NULL, NULL, /*notify*/ + pool, pool); + if (err) { - const char *prop_eol_normalized; - - SVN_ERR(svn_subst_translate_cstring2(value->data, - &prop_eol_normalized, - "\n", /* translate to LF */ - FALSE, /* no repair */ - NULL, /* no keywords */ - FALSE, /* no expansion */ - pool)); - prop_val.data = prop_eol_normalized; - prop_val.len = strlen(prop_eol_normalized); - value = &prop_val; - - /* ### TODO: notify? */ + return svn_error_quick_wrap(err, + _("Invalid svn:mergeinfo value")); } - /* Renumber mergeinfo as appropriate. */ - SVN_ERR(renumber_mergeinfo_revs(&renumbered_mergeinfo, value, - nb->rb, pool)); - value = renumbered_mergeinfo; - - if (nb->rb->pb->parent_dir) - { - /* Prefix the merge source paths with PB->parent_dir. */ - /* ASSUMPTION: All source paths are included in the dump stream. */ - svn_string_t *mergeinfo_val; - SVN_ERR(prefix_mergeinfo_paths(&mergeinfo_val, value, - nb->rb->pb->parent_dir, pool)); - value = mergeinfo_val; - } + value = new_value; } SVN_ERR(svn_rdump__normalize_prop(name, &value, pool)); @@ -978,7 +787,7 @@ set_node_property(void *baton, prop = apr_palloc(nb->rb->pool, sizeof (*prop)); prop->name = apr_pstrdup(pool, name); - prop->value = value ? svn_string_dup(value, pool) : NULL; + prop->value = svn_string_dup(value, pool); svn_hash_sets(nb->prop_changes, prop->name, prop); return SVN_NO_ERROR; @@ -1024,7 +833,6 @@ remove_node_props(void *baton) /* Find the path and revision that has the node's original properties */ if (ARE_VALID_COPY_ARGS(nb->copyfrom_path, nb->copyfrom_rev)) { - LDR_DBG(("using nb->copyfrom %s@%ld", nb->copyfrom_path, nb->copyfrom_rev)); orig_path = nb->copyfrom_path; orig_rev = nb->copyfrom_rev; } @@ -1033,8 +841,6 @@ remove_node_props(void *baton) { /* If this is a dir, then it's described by rb->db; if this is a file, then it's a child of the dir in rb->db. */ - LDR_DBG(("using rb->db->copyfrom (k=%d) %s@%ld", - nb->kind, rb->db->copyfrom_path, rb->db->copyfrom_rev)); orig_path = (nb->kind == svn_node_dir) ? rb->db->copyfrom_path : svn_relpath_join(rb->db->copyfrom_path, @@ -1044,13 +850,11 @@ remove_node_props(void *baton) } else { - LDR_DBG(("using self.path@head %s@%ld", nb->path, SVN_INVALID_REVNUM)); /* ### Should we query at a known, fixed, "head" revision number instead of passing SVN_INVALID_REVNUM and getting a moving target? */ orig_path = nb->path; orig_rev = SVN_INVALID_REVNUM; } - LDR_DBG(("Trying %s@%ld", orig_path, orig_rev)); if ((nb->action == svn_node_action_add || nb->action == svn_node_action_replace) @@ -1071,7 +875,7 @@ remove_node_props(void *baton) for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); + const char *name = apr_hash_this_key(hi); svn_prop_kind_t kind = svn_property_kind2(name); if (kind == svn_prop_regular_kind) @@ -1091,7 +895,6 @@ set_fulltext(svn_stream_t **stream, void *handler_baton; apr_pool_t *pool = nb->rb->pool; - LDR_DBG(("Setting fulltext for %p\n", nb->file_baton)); SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum, pool, &handler, &handler_baton)); *stream = svn_txdelta_target_push(handler, handler_baton, @@ -1108,7 +911,6 @@ apply_textdelta(svn_txdelta_window_handler_t *handler, const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor; apr_pool_t *pool = nb->rb->pool; - LDR_DBG(("Applying textdelta to %p\n", nb->file_baton)); SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum, pool, handler, handler_baton)); @@ -1126,8 +928,8 @@ close_node(void *baton) for (hi = apr_hash_first(pool, nb->prop_changes); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - svn_prop_t *prop = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + svn_prop_t *prop = apr_hash_this_val(hi); switch (nb->kind) { @@ -1148,7 +950,6 @@ close_node(void *baton) deleted the file (which doesn't require us to open it). */ if ((nb->kind == svn_node_file) && (nb->file_baton)) { - LDR_DBG(("Closing file %p\n", nb->file_baton)); SVN_ERR(commit_editor->close_file(nb->file_baton, NULL, nb->rb->pool)); } @@ -1178,12 +979,10 @@ close_revision(void *baton) session itself */ while (rb->db && rb->db->parent) { - LDR_DBG(("Closing dir %p\n", rb->db->baton)); SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool)); rb->db = rb->db->parent; } /* root dir's baton */ - LDR_DBG(("Closing edit on %p\n", commit_edit_baton)); SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool)); SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool)); } @@ -1201,8 +1000,6 @@ close_revision(void *baton) rb->rev - rb->rev_offset - 1, rb->pool, &child_baton)); - LDR_DBG(("Opened root %p\n", child_baton)); - LDR_DBG(("Closing edit on %p\n", commit_edit_baton)); SVN_ERR(commit_editor->close_directory(child_baton, rb->pool)); SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool)); } @@ -1222,16 +1019,22 @@ close_revision(void *baton) if (SVN_IS_VALID_REVNUM(committed_rev)) { - SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_DATE, - rb->datestamp, rb->pool)); - SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev, - SVN_PROP_REVISION_DATE, - NULL, rb->datestamp, rb->pool)); - SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_AUTHOR, - rb->author, rb->pool)); - SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev, - SVN_PROP_REVISION_AUTHOR, - NULL, rb->author, rb->pool)); + if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_DATE)) + { + SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_DATE, + rb->datestamp, rb->pool)); + SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev, + SVN_PROP_REVISION_DATE, + NULL, rb->datestamp, rb->pool)); + } + if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_AUTHOR)) + { + SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_AUTHOR, + rb->author, rb->pool)); + SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev, + SVN_PROP_REVISION_AUTHOR, + NULL, rb->author, rb->pool)); + } } svn_pool_destroy(rb->pool); @@ -1244,6 +1047,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream, svn_ra_session_t *session, svn_ra_session_t *aux_session, svn_boolean_t quiet, + apr_hash_t *skip_revprops, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) @@ -1287,6 +1091,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream, parse_baton->rev_map = apr_hash_make(pool); parse_baton->last_rev_mapped = SVN_INVALID_REVNUM; parse_baton->oldest_dumpstream_rev = SVN_INVALID_REVNUM; + parse_baton->skip_revprops = skip_revprops; err = svn_repos_parse_dumpstream3(stream, parser, parse_baton, FALSE, cancel_func, cancel_baton, pool); diff --git a/subversion/svnrdump/svnrdump.c b/subversion/svnrdump/svnrdump.c index 6bf409c..df0286b 100644 --- a/subversion/svnrdump/svnrdump.c +++ b/subversion/svnrdump/svnrdump.c @@ -39,6 +39,7 @@ #include "svnrdump.h" +#include "private/svn_repos_private.h" #include "private/svn_cmdline_private.h" #include "private/svn_ra_private.h" @@ -80,9 +81,11 @@ enum svn_svnrdump__longopt_t opt_auth_password, opt_auth_nocache, opt_non_interactive, + opt_skip_revprop, opt_force_interactive, opt_incremental, opt_trust_server_cert, + opt_trust_server_cert_failures, opt_version }; @@ -92,6 +95,7 @@ enum svn_svnrdump__longopt_t opt_auth_password, \ opt_auth_nocache, \ opt_trust_server_cert, \ + opt_trust_server_cert_failures, \ opt_non_interactive, \ opt_force_interactive @@ -106,7 +110,7 @@ static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] = { "load", load_cmd, { 0 }, N_("usage: svnrdump load URL\n\n" "Load a 'dumpfile' given on stdin to a repository at remote URL.\n"), - { 'q', SVN_SVNRDUMP__BASE_OPTIONS } }, + { 'q', opt_skip_revprop, SVN_SVNRDUMP__BASE_OPTIONS } }, { "help", 0, { "?", "h" }, N_("usage: svnrdump help [SUBCOMMAND...]\n\n" "Describe the usage of this program or its subcommands.\n"), @@ -122,6 +126,8 @@ static const apr_getopt_option_t svnrdump__options[] = N_("no progress (only errors) to stderr")}, {"incremental", opt_incremental, 0, N_("dump incrementally")}, + {"skip-revprop", opt_skip_revprop, 1, + N_("skip revision property ARG (e.g., \"svn:author\")")}, {"config-dir", opt_config_dir, 1, N_("read user configuration files from directory ARG")}, {"username", opt_auth_username, 1, @@ -150,12 +156,24 @@ static const apr_getopt_option_t svnrdump__options[] = "For example:\n" " " " servers:global:http-library=serf")}, - {"trust-server-cert", opt_trust_server_cert, 0, - N_("accept SSL server certificates from unknown\n" - " " - "certificate authorities without prompting (but only\n" - " " - "with '--non-interactive')") }, + {"trust-server-cert", opt_trust_server_cert, 0, + N_("deprecated; same as\n" + " " + "--trust-server-cert-failures=unknown-ca")}, + {"trust-server-cert-failures", opt_trust_server_cert_failures, 1, + N_("with --non-interactive, accept SSL server\n" + " " + "certificates with failures; ARG is comma-separated\n" + " " + "list of 'unknown-ca' (Unknown Authority),\n" + " " + "'cn-mismatch' (Hostname mismatch), 'expired'\n" + " " + "(Expired certificate), 'not-yet-valid' (Not yet\n" + " " + "valid certificate) and 'other' (all other not\n" + " " + "separately classified certificate errors).")}, {0, 0, 0, 0} }; @@ -182,6 +200,7 @@ typedef struct opt_baton_t { svn_opt_revision_t end_revision; svn_boolean_t quiet; svn_boolean_t incremental; + apr_hash_t *skip_revprops; } opt_baton_t; /* Print dumpstream-formatted information about REVISION. @@ -197,38 +216,13 @@ replay_revstart(svn_revnum_t revision, { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; - svn_stringbuf_t *propstring; - svn_stream_t *stdout_stream; - svn_stream_t *revprop_stream; - - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - /* Revision-number: 19 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); + /* Normalize and dump the revprops */ SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); - propstring = svn_stringbuf_create_ensure(0, pool); - revprop_stream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(revprop_stream)); - - /* Prop-content-length: 13 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", propstring->len)); - - /* Content-length: 29 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); - - /* Property data. */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - SVN_ERR(svn_stream_close(stdout_stream)); + SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, NULL, + normal_props, + TRUE /*props_section_always*/, + pool)); SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision, rb->stdout_stream, rb->extra_ra_session, @@ -271,38 +265,13 @@ replay_revstart_v2(svn_revnum_t revision, { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; - svn_stringbuf_t *propstring; - svn_stream_t *stdout_stream; - svn_stream_t *revprop_stream; - - SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); - /* Revision-number: 19 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); + /* Normalize and dump the revprops */ SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); - propstring = svn_stringbuf_create_ensure(0, pool); - revprop_stream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(revprop_stream)); - - /* Prop-content-length: 13 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", propstring->len)); - - /* Content-length: 29 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); - - /* Property data. */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - SVN_ERR(svn_stream_close(stdout_stream)); + SVN_ERR(svn_repos__dump_revision_record(rb->stdout_stream, revision, + normal_props, + TRUE /*props_section_always*/, + pool)); SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision, rb->stdout_stream, @@ -348,7 +317,11 @@ init_client_context(svn_client_ctx_t **ctx_p, const char *config_dir, const char *repos_url, svn_boolean_t no_auth_cache, - svn_boolean_t trust_server_cert, + svn_boolean_t trust_unknown_ca, + svn_boolean_t trust_cn_mismatch, + svn_boolean_t trust_expired, + svn_boolean_t trust_not_yet_valid, + svn_boolean_t trust_other_failure, apr_array_header_t *config_options, apr_pool_t *pool) { @@ -412,11 +385,14 @@ init_client_context(svn_client_ctx_t **ctx_p, ctx->cancel_func = check_cancel; /* Default authentication providers for non-interactive use */ - SVN_ERR(svn_cmdline_create_auth_baton(&(ctx->auth_baton), non_interactive, - username, password, config_dir, - no_auth_cache, trust_server_cert, - cfg_config, ctx->cancel_func, - ctx->cancel_baton, pool)); + SVN_ERR(svn_cmdline_create_auth_baton2(&(ctx->auth_baton), non_interactive, + username, password, config_dir, + no_auth_cache, trust_unknown_ca, + trust_cn_mismatch, trust_expired, + trust_not_yet_valid, + trust_other_failure, + cfg_config, ctx->cancel_func, + ctx->cancel_baton, pool)); *ctx_p = ctx; return SVN_NO_ERROR; } @@ -432,35 +408,12 @@ dump_revision_header(svn_ra_session_t *session, apr_pool_t *pool) { apr_hash_t *prophash; - svn_stringbuf_t *propstring; - svn_stream_t *propstream; - - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_REVISION_NUMBER - ": %ld\n", revision)); - prophash = apr_hash_make(pool); - propstring = svn_stringbuf_create_empty(pool); SVN_ERR(svn_ra_rev_proplist(session, revision, &prophash, pool)); - - propstream = svn_stream_from_stringbuf(propstring, pool); - SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool)); - SVN_ERR(svn_stream_close(propstream)); - - /* Property-content-length: 14; Content-length: 14 */ - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n", - propstring->len)); - SVN_ERR(svn_stream_printf(stdout_stream, pool, - SVN_REPOS_DUMPFILE_CONTENT_LENGTH - ": %" APR_SIZE_T_FMT "\n\n", - propstring->len)); - /* The properties */ - SVN_ERR(svn_stream_write(stdout_stream, propstring->data, - &(propstring->len))); - SVN_ERR(svn_stream_puts(stdout_stream, "\n")); - + SVN_ERR(svn_repos__dump_revision_record(stdout_stream, revision, NULL, + prophash, + TRUE /*props_section_always*/, + pool)); return SVN_NO_ERROR; } @@ -608,6 +561,7 @@ load_revisions(svn_ra_session_t *session, svn_ra_session_t *aux_session, const char *url, svn_boolean_t quiet, + apr_hash_t *skip_revprops, apr_pool_t *pool) { apr_file_t *stdin_file; @@ -617,7 +571,8 @@ load_revisions(svn_ra_session_t *session, stdin_stream = svn_stream_from_aprfile2(stdin_file, FALSE, pool); SVN_ERR(svn_rdump__load_dumpstream(stdin_stream, session, aux_session, - quiet, check_cancel, NULL, pool)); + quiet, skip_revprops, + check_cancel, NULL, pool)); SVN_ERR(svn_stream_close(stdin_stream)); @@ -667,23 +622,6 @@ version(const char *progname, } -/* A statement macro, similar to @c SVN_ERR, but returns an integer. - * Evaluate @a expr. If it yields an error, handle that error and - * return @c EXIT_FAILURE. - */ -#define SVNRDUMP_ERR(expr) \ - do \ - { \ - svn_error_t *svn_err__temp = (expr); \ - if (svn_err__temp) \ - { \ - svn_handle_error2(svn_err__temp, stderr, FALSE, "svnrdump: "); \ - svn_error_clear(svn_err__temp); \ - return EXIT_FAILURE; \ - } \ - } \ - while (0) - /* Handle the "dump" subcommand. Implements `svn_opt_subcommand_t'. */ static svn_error_t * dump_cmd(apr_getopt_t *os, @@ -718,7 +656,7 @@ load_cmd(apr_getopt_t *os, SVN_ERR(svn_client_open_ra_session2(&aux_session, opt_baton->url, NULL, opt_baton->ctx, pool, pool)); return load_revisions(opt_baton->session, aux_session, opt_baton->url, - opt_baton->quiet, pool); + opt_baton->quiet, opt_baton->skip_revprops, pool); } /* Handle the "help" subcommand. Implements `svn_opt_subcommand_t'. */ @@ -729,6 +667,7 @@ help_cmd(apr_getopt_t *os, { const char *header = _("general usage: svnrdump SUBCOMMAND URL [-r LOWER[:UPPER]]\n" + "Subversion remote repository dump and load tool.\n" "Type 'svnrdump help <subcommand>' for help on a specific subcommand.\n" "Type 'svnrdump --version' to see the program version and RA modules.\n" "\n" @@ -831,19 +770,27 @@ validate_and_resolve_revisions(opt_baton_t *opt_baton, return SVN_NO_ERROR; } -int -main(int argc, const char **argv) +/* + * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, + * either return an error to be displayed, or set *EXIT_CODE to non-zero and + * return SVN_NO_ERROR. + */ +static svn_error_t * +sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; const svn_opt_subcommand_desc2_t *subcommand = NULL; opt_baton_t *opt_baton; svn_revnum_t latest_revision = SVN_INVALID_REVNUM; - apr_pool_t *pool = NULL; const char *config_dir = NULL; const char *username = NULL; const char *password = NULL; svn_boolean_t no_auth_cache = FALSE; - svn_boolean_t trust_server_cert = FALSE; + svn_boolean_t trust_unknown_ca = FALSE; + svn_boolean_t trust_cn_mismatch = FALSE; + svn_boolean_t trust_expired = FALSE; + svn_boolean_t trust_not_yet_valid = FALSE; + svn_boolean_t trust_other_failure = FALSE; svn_boolean_t non_interactive = FALSE; svn_boolean_t force_interactive = FALSE; apr_array_header_t *config_options = NULL; @@ -852,20 +799,13 @@ main(int argc, const char **argv) apr_array_header_t *received_opts; int i; - if (svn_cmdline_init ("svnrdump", stderr) != EXIT_SUCCESS) - return EXIT_FAILURE; - - /* Create our top-level pool. Use a separate mutexless allocator, - * given this application is single threaded. - */ - pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); - opt_baton = apr_pcalloc(pool, sizeof(*opt_baton)); opt_baton->start_revision.kind = svn_opt_revision_unspecified; opt_baton->end_revision.kind = svn_opt_revision_unspecified; opt_baton->url = NULL; + opt_baton->skip_revprops = apr_hash_make(pool); - SVNRDUMP_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); + SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); os->interleave = TRUE; /* Options and arguments can be interleaved */ @@ -905,8 +845,9 @@ main(int argc, const char **argv) break; if (status != APR_SUCCESS) { - SVNRDUMP_ERR(usage(argv[0], pool)); - exit(EXIT_FAILURE); + SVN_ERR(usage(argv[0], pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } /* Stash the option code in an array before parsing it. */ @@ -919,11 +860,10 @@ main(int argc, const char **argv) /* Make sure we've not seen -r already. */ if (opt_baton->start_revision.kind != svn_opt_revision_unspecified) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Multiple revision arguments " - "encountered; try '-r N:M' instead " - "of '-r N -r M'")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Multiple revision arguments " + "encountered; try '-r N:M' instead " + "of '-r N -r M'")); } /* Parse the -r argument. */ if (svn_opt_parse_revision(&(opt_baton->start_revision), @@ -931,12 +871,10 @@ main(int argc, const char **argv) opt_arg, pool) != 0) { const char *utf8_opt_arg; - err = svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool); - if (! err) - err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("Syntax error in revision " - "argument '%s'"), utf8_opt_arg); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Syntax error in revision " + "argument '%s'"), utf8_opt_arg); } } break; @@ -953,10 +891,10 @@ main(int argc, const char **argv) opt_baton->help = TRUE; break; case opt_auth_username: - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&username, opt_arg, pool)); break; case opt_auth_password: - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&password, opt_arg, pool)); break; case opt_auth_nocache: no_auth_cache = TRUE; @@ -970,8 +908,22 @@ main(int argc, const char **argv) case opt_incremental: opt_baton->incremental = TRUE; break; - case opt_trust_server_cert: - trust_server_cert = TRUE; + case opt_skip_revprop: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + svn_hash_sets(opt_baton->skip_revprops, opt_arg, opt_arg); + break; + case opt_trust_server_cert: /* backward compat */ + trust_unknown_ca = TRUE; + break; + case opt_trust_server_cert_failures: + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_trust_options( + &trust_unknown_ca, + &trust_cn_mismatch, + &trust_expired, + &trust_not_yet_valid, + &trust_other_failure, + opt_arg, pool)); break; case opt_config_option: if (!config_options) @@ -979,9 +931,11 @@ main(int argc, const char **argv) apr_array_make(pool, 1, sizeof(svn_cmdline__config_argument_t*)); - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); - SVNRDUMP_ERR(svn_cmdline__parse_config_option(config_options, - opt_arg, pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, opt_arg, pool)); + SVN_ERR(svn_cmdline__parse_config_option(config_options, + opt_arg, + "svnrdump: ", + pool)); } } @@ -989,10 +943,9 @@ main(int argc, const char **argv) * exclusive. */ if (non_interactive && force_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--non-interactive and --force-interactive " - "are mutually exclusive")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--non-interactive and --force-interactive " + "are mutually exclusive")); } if (opt_baton->help) @@ -1017,9 +970,9 @@ main(int argc, const char **argv) else { - SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } else @@ -1031,16 +984,15 @@ main(int argc, const char **argv) if (subcommand == NULL) { const char *first_arg_utf8; - err = svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, pool); - if (err) - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8, first_arg, + pool)); svn_error_clear( svn_cmdline_fprintf(stderr, pool, _("Unknown subcommand: '%s'\n"), first_arg_utf8)); - SVNRDUMP_ERR(help_cmd(NULL, NULL, pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_ERR(help_cmd(NULL, NULL, pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } } @@ -1065,69 +1017,64 @@ main(int argc, const char **argv) subcommand, pool); svn_opt_format_option(&optstr, badopt, FALSE, pool); if (subcommand->name[0] == '-') - SVN_INT_ERR(help_cmd(NULL, NULL, pool)); + SVN_ERR(help_cmd(NULL, NULL, pool)); else svn_error_clear(svn_cmdline_fprintf( stderr, pool, _("Subcommand '%s' doesn't accept option '%s'\n" "Type 'svnrdump help %s' for usage.\n"), subcommand->name, optstr, subcommand->name)); - svn_pool_destroy(pool); - return EXIT_FAILURE; + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } } - if (subcommand && strcmp(subcommand->name, "--version") == 0) + if (strcmp(subcommand->name, "--version") == 0) { - SVNRDUMP_ERR(version(argv[0], opt_baton->quiet, pool)); - svn_pool_destroy(pool); - exit(EXIT_SUCCESS); + SVN_ERR(version(argv[0], opt_baton->quiet, pool)); + return SVN_NO_ERROR; } - if (subcommand && strcmp(subcommand->name, "help") == 0) + if (strcmp(subcommand->name, "help") == 0) { - SVNRDUMP_ERR(help_cmd(os, opt_baton, pool)); - svn_pool_destroy(pool); - exit(EXIT_SUCCESS); + SVN_ERR(help_cmd(os, opt_baton, pool)); + return SVN_NO_ERROR; } - /* --trust-server-cert can only be used with --non-interactive */ - if (trust_server_cert && !non_interactive) + /* --trust-* can only be used with --non-interactive */ + if (!non_interactive) { - err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("--trust-server-cert requires " - "--non-interactive")); - return svn_cmdline_handle_exit_error(err, pool, "svnrdump: "); + if (trust_unknown_ca || trust_cn_mismatch || trust_expired + || trust_not_yet_valid || trust_other_failure) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--trust-server-cert-failures requires " + "--non-interactive")); } /* Expect one more non-option argument: the repository URL. */ if (os->ind != os->argc - 1) { - SVNRDUMP_ERR(usage(argv[0], pool)); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + SVN_ERR(usage(argv[0], pool)); + *exit_code = EXIT_FAILURE; + return SVN_NO_ERROR; } else { const char *repos_url; - SVNRDUMP_ERR(svn_utf_cstring_to_utf8(&repos_url, - os->argv[os->ind], pool)); + SVN_ERR(svn_utf_cstring_to_utf8(&repos_url, os->argv[os->ind], pool)); if (! svn_path_is_url(repos_url)) { - err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, 0, - "Target '%s' is not a URL", - repos_url); - SVNRDUMP_ERR(err); - svn_pool_destroy(pool); - exit(EXIT_FAILURE); + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, 0, + "Target '%s' is not a URL", + repos_url); } opt_baton->url = svn_uri_canonicalize(repos_url, pool); } if (strcmp(subcommand->name, "load") == 0) { - /* + /* * By default (no --*-interactive options given), the 'load' subcommand * is interactive unless username and password were provided on the * command line. This allows prompting for auth creds to work without @@ -1141,16 +1088,20 @@ main(int argc, const char **argv) non_interactive = !svn_cmdline__be_interactive(non_interactive, force_interactive); - SVNRDUMP_ERR(init_client_context(&(opt_baton->ctx), - non_interactive, - username, - password, - config_dir, - opt_baton->url, - no_auth_cache, - trust_server_cert, - config_options, - pool)); + SVN_ERR(init_client_context(&(opt_baton->ctx), + non_interactive, + username, + password, + config_dir, + opt_baton->url, + no_auth_cache, + trust_unknown_ca, + trust_cn_mismatch, + trust_expired, + trust_not_yet_valid, + trust_other_failure, + config_options, + pool)); err = svn_client_open_ra_session2(&(opt_baton->session), opt_baton->url, NULL, @@ -1171,15 +1122,45 @@ main(int argc, const char **argv) if (err && err->apr_err == SVN_ERR_AUTHN_FAILED && non_interactive) { - err = svn_error_quick_wrap(err, - _("Authentication failed and interactive" - " prompting is disabled; see the" - " --force-interactive option")); + return svn_error_quick_wrap(err, + _("Authentication failed and interactive" + " prompting is disabled; see the" + " --force-interactive option")); } + else if (err) + return err; + else + return SVN_NO_ERROR; +} - SVNRDUMP_ERR(err); +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + int exit_code = EXIT_SUCCESS; + svn_error_t *err; - svn_pool_destroy(pool); + /* Initialize the app. */ + if (svn_cmdline_init("svnrdump", stderr) != EXIT_SUCCESS) + return EXIT_FAILURE; + + /* Create our top-level pool. Use a separate mutexless allocator, + * given this application is single threaded. + */ + pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); + + err = sub_main(&exit_code, argc, argv, pool); + + /* Flush stdout and report if it fails. It would be flushed on exit anyway + but this makes sure that output is not silently lost if it fails. */ + err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); - return EXIT_SUCCESS; + if (err) + { + exit_code = EXIT_FAILURE; + svn_cmdline_handle_exit_error(err, NULL, "svnrdump: "); + } + + svn_pool_destroy(pool); + return exit_code; } diff --git a/subversion/svnrdump/svnrdump.h b/subversion/svnrdump/svnrdump.h index 2a81014..919ea5e 100644 --- a/subversion/svnrdump/svnrdump.h +++ b/subversion/svnrdump/svnrdump.h @@ -89,6 +89,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream, svn_ra_session_t *session, svn_ra_session_t *aux_session, svn_boolean_t quiet, + apr_hash_t *skip_revprops, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool); diff --git a/subversion/svnrdump/util.c b/subversion/svnrdump/util.c index 2586cd1..9a89c89 100644 --- a/subversion/svnrdump/util.c +++ b/subversion/svnrdump/util.c @@ -61,8 +61,8 @@ svn_rdump__normalize_props(apr_hash_t **normal_props, for (hi = apr_hash_first(result_pool, props); hi; hi = apr_hash_next(hi)) { - const char *key = svn__apr_hash_index_key(hi); - const svn_string_t *value = svn__apr_hash_index_val(hi); + const char *key = apr_hash_this_key(hi); + const svn_string_t *value = apr_hash_this_val(hi); SVN_ERR(svn_rdump__normalize_prop(key, &value, result_pool)); |