/* Getting just one file. */ static svn_error_t * svn_ra_local__get_file(svn_ra_session_t *session, const char *path, svn_revnum_t revision, svn_stream_t *stream, svn_revnum_t *fetched_rev, apr_hash_t **props, apr_pool_t *pool) { svn_fs_root_t *root; svn_stream_t *contents; svn_revnum_t youngest_rev; svn_ra_local__session_baton_t *sess = session->priv; const char *abs_path = svn_path_join(sess->fs_path->data, path, pool); /* Open the revision's root. */ if (! SVN_IS_VALID_REVNUM(revision)) { SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool)); SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool)); if (fetched_rev != NULL) *fetched_rev = youngest_rev; } else SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); if (stream) { /* Get a stream representing the file's contents. */ SVN_ERR(svn_fs_file_contents(&contents, root, abs_path, pool)); /* Now push data from the fs stream back at the caller's stream. Note that this particular RA layer does not computing a checksum as we go, and confirming it against the repository's checksum when done. That's because it calls svn_fs_file_contents() directly, which already checks the stored checksum, and all we're doing here is writing bytes in a loop. Truly, Nothing Can Go Wrong :-). But RA layers that go over a network should confirm the checksum. Note: we are not supposed to close the passed-in stream, so disown the thing. */ SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(stream, pool), sess->callbacks ? sess->callbacks->cancel_func : NULL, sess->callback_baton, pool)); } /* Handle props if requested. */ if (props) SVN_ERR(get_node_props(props, sess, root, abs_path, pool)); return SVN_NO_ERROR; }
static PyObject *fs_get_revision_root(FileSystemObject *self, PyObject *args) { svn_revnum_t rev; FileSystemRootObject *ret; apr_pool_t *pool; svn_fs_root_t *root; if (!PyArg_ParseTuple(args, "l", &rev)) return NULL; pool = Pool(NULL); if (pool == NULL) return NULL; RUN_SVN_WITH_POOL(pool, svn_fs_revision_root(&root, self->fs, rev, pool)); ret = PyObject_New(FileSystemRootObject, &FileSystemRoot_Type); if (ret == NULL) return NULL; ret->root = root; ret->pool = pool; return (PyObject *)ret; }
static svn_error_t * fetch_props_func(apr_hash_t **props, void *baton, const char *path, svn_revnum_t base_revision, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; svn_fs_root_t *fs_root; svn_error_t *err; SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, svn_fs_txn_base_revision(eb->txn), scratch_pool)); err = svn_fs_node_proplist(props, fs_root, path, result_pool); if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) { svn_error_clear(err); *props = apr_hash_make(result_pool); return SVN_NO_ERROR; } else if (err) return svn_error_trace(err); return SVN_NO_ERROR; }
/* This implements svn_editor_cb_copy_t */ static svn_error_t * copy_cb(void *baton, const char *src_relpath, svn_revnum_t src_revision, const char *dst_relpath, svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; const char *src_fspath = FSPATH(src_relpath, scratch_pool); const char *dst_fspath = FSPATH(dst_relpath, scratch_pool); svn_fs_root_t *root; svn_fs_root_t *src_root; SVN_ERR(get_root(&root, eb)); /* Check if we can we replace the maybe-specified destination (revision). */ if (SVN_IS_VALID_REVNUM(replaces_rev)) { SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool)); SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool)); } else { SVN_ERR(can_create(root, dst_fspath, scratch_pool)); } SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision, scratch_pool)); SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool)); svn_fs_close_root(src_root); return SVN_NO_ERROR; }
svn_error_t * svn_repos_fs_get_locks(apr_hash_t **locks, svn_repos_t *repos, const char *path, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { apr_hash_t *all_locks = apr_hash_make(pool); svn_revnum_t head_rev; struct get_locks_baton_t baton; /* Locks are always said to apply to HEAD revision, so we'll check to see if locked-paths are readable in HEAD as well. */ SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool)); /* Populate our callback baton. */ baton.fs = repos->fs; baton.locks = all_locks; baton.authz_read_func = authz_read_func; baton.authz_read_baton = authz_read_baton; SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs, head_rev, pool)); /* Get all the locks. */ SVN_ERR(svn_fs_get_locks(repos->fs, path, get_locks_callback, &baton, pool)); *locks = baton.locks; return SVN_NO_ERROR; }
svn_error_t * svn_repos_fs_get_locks2(apr_hash_t **locks, svn_repos_t *repos, const char *path, svn_depth_t depth, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { apr_hash_t *all_locks = apr_hash_make(pool); svn_revnum_t head_rev; struct get_locks_baton_t baton; SVN_ERR_ASSERT((depth == svn_depth_empty) || (depth == svn_depth_files) || (depth == svn_depth_immediates) || (depth == svn_depth_infinity)); SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool)); /* Populate our callback baton. */ baton.fs = repos->fs; baton.locks = all_locks; baton.authz_read_func = authz_read_func; baton.authz_read_baton = authz_read_baton; SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs, head_rev, pool)); /* Get all the locks. */ SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth, get_locks_callback, &baton, pool)); *locks = baton.locks; return SVN_NO_ERROR; }
void set_revision(svn_revnum_t revnum) { if (revnum == 0) { check_svn(svn_fs_youngest_rev(&revnum, fs, apr_pool)); } check_svn(svn_fs_revision_root(&fs_root, fs, revnum, apr_pool)); }
Repository(char const* path) { svn_repos_t *repos; check_svn(svn_repos_open(&repos, path, apr_pool)); fs = svn_repos_fs(repos); svn_revnum_t revnum; check_svn(svn_fs_youngest_rev(&revnum, fs, apr_pool)); check_svn(svn_fs_revision_root(&fs_root, fs, revnum, apr_pool)); }
svn_error_t * svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo, svn_repos_t *repos, const apr_array_header_t *paths, svn_revnum_t rev, svn_mergeinfo_inheritance_t inherit, svn_boolean_t include_descendants, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { /* Here we cast away 'const', but won't try to write through this pointer * without first allocating a new array. */ apr_array_header_t *readable_paths = (apr_array_header_t *) paths; svn_fs_root_t *root; apr_pool_t *iterpool = svn_pool_create(pool); if (!SVN_IS_VALID_REVNUM(rev)) SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool)); SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool)); /* Filter out unreadable paths before divining merge tracking info. */ if (authz_read_func) { int i; for (i = 0; i < paths->nelts; i++) { svn_boolean_t readable; const char *path = APR_ARRAY_IDX(paths, i, char *); svn_pool_clear(iterpool); SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton, iterpool)); if (readable && readable_paths != paths) APR_ARRAY_PUSH(readable_paths, const char *) = path; else if (!readable && readable_paths == paths) { /* Requested paths differ from readable paths. Fork list of readable paths from requested paths. */ int j; readable_paths = apr_array_make(pool, paths->nelts - 1, sizeof(char *)); for (j = 0; j < i; j++) { path = APR_ARRAY_IDX(paths, j, char *); APR_ARRAY_PUSH(readable_paths, const char *) = path; } } } }
static svn_error_t * delete_entry(const char *path, svn_revnum_t revision, void *parent_baton, apr_pool_t *pool) { struct node_baton *d = parent_baton; struct edit_baton *eb = d->edit_baton; svn_repos_node_t *node; const char *name; const char *base_path; svn_revnum_t base_rev; svn_fs_root_t *base_root; svn_node_kind_t kind; /* Get (or create) the change node and update it. */ name = svn_path_basename(path, pool); node = find_child_by_name(d->node, name); if (! node) node = create_child_node(d->node, name, eb->node_pool); node->action = 'D'; /* We need to look up this node's parents to see what its original path in the filesystem was. Why? Because if this deletion occurred underneath a copied path, the thing that was deleted probably lived at a different location (relative to the copy source). */ find_real_base_location(&base_path, &base_rev, node, pool); if (! SVN_IS_VALID_REVNUM(base_rev)) { /* No interesting base revision? We'll just look for the path in our base root. */ base_root = eb->base_root; } else { /* Oh. Perhaps some copy goodness happened somewhere? */ SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool)); } /* Now figure out if this thing was a file or a dir. */ SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, _("'%s' not found in filesystem"), path); node->kind = kind; return SVN_NO_ERROR; }
static svn_error_t * svn_ra_local__do_check_path(svn_ra_session_t *session, const char *path, svn_revnum_t revision, svn_node_kind_t *kind, apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; svn_fs_root_t *root; const char *abs_path = svn_path_join(sess->fs_path->data, path, pool); if (! SVN_IS_VALID_REVNUM(revision)) SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool)); SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); return svn_fs_check_path(kind, root, abs_path, pool); }
static svn_error_t * get_dump_editor(const svn_delta_editor_t **editor, void **edit_baton, svn_fs_t *fs, svn_revnum_t to_rev, const char *root_path, svn_stream_t *stream, svn_repos_notify_func_t notify_func, void *notify_baton, svn_revnum_t oldest_dumped_rev, svn_boolean_t use_deltas, svn_boolean_t verify, apr_pool_t *pool) { /* Allocate an edit baton to be stored in every directory baton. Set it up for the directory baton we create here, which is the root baton. */ struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb)); svn_delta_editor_t *dump_editor = svn_delta_default_editor(pool); /* Set up the edit baton. */ eb->stream = stream; eb->notify_func = notify_func; eb->notify_baton = notify_baton; eb->oldest_dumped_rev = oldest_dumped_rev; eb->bufsize = sizeof(eb->buffer); eb->path = apr_pstrdup(pool, root_path); SVN_ERR(svn_fs_revision_root(&(eb->fs_root), fs, to_rev, pool)); eb->current_rev = to_rev; eb->use_deltas = use_deltas; eb->verify = verify; /* Set up the editor. */ dump_editor->open_root = open_root; dump_editor->delete_entry = delete_entry; dump_editor->add_directory = add_directory; dump_editor->open_directory = open_directory; dump_editor->close_directory = close_directory; dump_editor->change_dir_prop = change_dir_prop; dump_editor->add_file = add_file; dump_editor->open_file = open_file; *edit_baton = eb; *editor = dump_editor; return SVN_NO_ERROR; }
static svn_error_t * read_packed_fs(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool) { svn_fs_t *fs; svn_stream_t *rstream; svn_stringbuf_t *rstring; svn_revnum_t i; *msg = "read from a packed FSFS filesystem"; if (msg_only) return SVN_NO_ERROR; /* Bail (with success) on known-untestable scenarios */ if ((strcmp(opts->fs_type, "fsfs") != 0) || (opts->server_minor_version && (opts->server_minor_version < 6))) return SVN_NO_ERROR; SVN_ERR(create_packed_filesystem(REPO_NAME, opts, 11, 5, pool)); SVN_ERR(svn_fs_open(&fs, REPO_NAME, NULL, pool)); for (i = 1; i < 12; i++) { svn_fs_root_t *rev_root; svn_stringbuf_t *sb; SVN_ERR(svn_fs_revision_root(&rev_root, fs, i, pool)); SVN_ERR(svn_fs_file_contents(&rstream, rev_root, "iota", pool)); SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool)); if (i == 1) sb = svn_stringbuf_create("This is the file 'iota'.\n", pool); else sb = svn_stringbuf_create(get_rev_contents(i, pool), pool); if (! svn_stringbuf_compare(rstring, sb)) return svn_error_createf(SVN_ERR_FS_GENERAL, NULL, "Bad data in revision %ld.", i); } return SVN_NO_ERROR; }
static svn_error_t * svn_ra_local__replay(svn_ra_session_t *session, svn_revnum_t revision, svn_revnum_t low_water_mark, svn_boolean_t send_deltas, const svn_delta_editor_t *editor, void *edit_baton, apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; svn_fs_root_t *root; SVN_ERR(svn_fs_revision_root(&root, svn_repos_fs(sess->repos), revision, pool)); return svn_repos_replay2(root, sess->fs_path->data, low_water_mark, send_deltas, editor, edit_baton, NULL, NULL, pool); }
static svn_error_t * fetch_kind_func(svn_node_kind_t *kind, void *baton, const char *path, svn_revnum_t base_revision, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; svn_fs_root_t *fs_root; if (!SVN_IS_VALID_REVNUM(base_revision)) base_revision = svn_fs_txn_base_revision(eb->txn); SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool)); SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool)); return SVN_NO_ERROR; }
/* Build the node-origins index any newly added items introduced in REVISION in FS. Set *COUNT to the number of new items found. */ static svn_error_t * index_revision_adds(int *count, svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool) { svn_fs_root_t *root; apr_hash_t *changes; apr_hash_index_t *hi; apr_pool_t *subpool; *count = 0; SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool)); SVN_ERR(svn_fs_paths_changed2(&changes, root, pool)); /* No paths changed in this revision? Nothing to do. */ if (apr_hash_count(changes) == 0) return SVN_NO_ERROR; subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi)) { const void *path; void *val; svn_fs_path_change2_t *change; svn_pool_clear(subpool); apr_hash_this(hi, &path, NULL, &val); change = val; if ((change->change_kind == svn_fs_path_change_add) || (change->change_kind == svn_fs_path_change_replace)) { if (! (change->copyfrom_path && SVN_IS_VALID_REVNUM(change->copyfrom_rev))) { svn_revnum_t origin; SVN_ERR(svn_fs_node_origin_rev(&origin, root, path, subpool)); (*count)++; } } } svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* This implements svn_editor_cb_move_t */ static svn_error_t * move_cb(void *baton, const char *src_relpath, svn_revnum_t src_revision, const char *dst_relpath, svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; const char *src_fspath = FSPATH(src_relpath, scratch_pool); const char *dst_fspath = FSPATH(dst_relpath, scratch_pool); svn_fs_root_t *root; svn_fs_root_t *src_root; SVN_ERR(get_root(&root, eb)); /* Check if we delete the specified source (revision), and can we replace the maybe-specified destination (revision). */ SVN_ERR(can_modify(root, src_fspath, src_revision, scratch_pool)); if (SVN_IS_VALID_REVNUM(replaces_rev)) { SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool)); SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool)); } else { SVN_ERR(can_create(root, dst_fspath, scratch_pool)); } /* ### would be nice to have svn_fs_move() */ /* Copy the src to the dst. */ SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision, scratch_pool)); SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool)); svn_fs_close_root(src_root); /* Notice: we're deleting the src repos path from the dst root. */ SVN_ERR(svn_fs_delete(root, src_fspath, scratch_pool)); return SVN_NO_ERROR; }
static svn_error_t * test_open_file(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *file_pool, void **file_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb)); svn_fs_root_t *rev_root = NULL; /* Fill in the file baton. */ fb->path = svn_path_join(eb->root_path, path, eb->pool); fb->edit_baton = eb; SVN_ERR(svn_fs_revision_root(&rev_root, eb->fs, base_revision, file_pool)); SVN_ERR(svn_fs_revision_link(rev_root, eb->txn_root, fb->path, file_pool)); *file_baton = fb; return SVN_NO_ERROR; }
static svn_error_t * fetch_base_func(const char **filename, void *baton, const char *path, svn_revnum_t base_revision, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { struct edit_baton *eb = baton; svn_stream_t *contents; svn_stream_t *file_stream; const char *tmp_filename; svn_fs_root_t *fs_root; svn_error_t *err; if (!SVN_IS_VALID_REVNUM(base_revision)) base_revision = svn_fs_txn_base_revision(eb->txn); SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool)); err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool); if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) { svn_error_clear(err); *filename = NULL; return SVN_NO_ERROR; } else if (err) return svn_error_trace(err); SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL, svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool)); *filename = apr_pstrdup(result_pool, tmp_filename); return SVN_NO_ERROR; }
static svn_error_t * test_open_directory(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *dir_pool, void **child_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db)); svn_fs_root_t *rev_root = NULL; /* Construct the full path of the new directory */ db->full_path = svn_path_join(eb->root_path, path, eb->pool); db->edit_baton = eb; SVN_ERR(svn_fs_revision_root(&rev_root, eb->fs, base_revision, dir_pool)); SVN_ERR(svn_fs_revision_link(rev_root, eb->txn_root, db->full_path, dir_pool)); *child_baton = db; return SVN_NO_ERROR; }
static svn_error_t * test_add_file(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *file_pool, void **file_baton) { struct dir_baton *db = parent_baton; struct edit_baton *eb = db->edit_baton; struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb)); /* Fill in the file baton. */ fb->path = svn_path_join(eb->root_path, path, eb->pool); fb->edit_baton = eb; if (copyfrom_path) /* add with history */ { svn_fs_root_t *rev_root = NULL; SVN_ERR(svn_fs_revision_root(&rev_root, eb->fs, copyfrom_revision, file_pool)); SVN_ERR(svn_fs_copy(rev_root, copyfrom_path, eb->txn_root, fb->path, file_pool)); } else /* add without history */ SVN_ERR(svn_fs_make_file(eb->txn_root, fb->path, file_pool)); *file_baton = fb; return SVN_NO_ERROR; }
static svn_error_t * test_add_directory(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *dir_pool, void **child_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db)); /* Construct the full path of the new directory */ db->full_path = svn_path_join(eb->root_path, path, eb->pool); db->edit_baton = eb; if (copyfrom_path) /* add with history */ { svn_fs_root_t *rev_root = NULL; SVN_ERR(svn_fs_revision_root(&rev_root, eb->fs, copyfrom_revision, dir_pool)); SVN_ERR(svn_fs_copy(rev_root, copyfrom_path, eb->txn_root, db->full_path, dir_pool)); } else /* add without history */ SVN_ERR(svn_fs_make_dir(eb->txn_root, db->full_path, dir_pool)); *child_baton = db; 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; }
dav_error * dav_svn__merge_response(ap_filter_t *output, const dav_svn_repos *repos, svn_revnum_t new_rev, char *post_commit_err, apr_xml_elem *prop_elem, svn_boolean_t disable_merge_response, apr_pool_t *pool) { apr_bucket_brigade *bb; svn_fs_root_t *root; svn_error_t *serr; const char *vcc; const char *rev; svn_string_t *creationdate, *creator_displayname; const char *post_commit_err_elem = NULL, *post_commit_header_info = NULL; serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not open the FS root for the " "revision just committed.", repos->pool); } bb = apr_brigade_create(pool, output->c->bucket_alloc); /* prep some strings */ /* the HREF for the baseline is actually the VCC */ vcc = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VCC, SVN_IGNORED_REVNUM, NULL, 0 /* add_href */, pool); /* the version-name of the baseline is the revision number */ rev = apr_psprintf(pool, "%ld", new_rev); /* get the post-commit hook stderr, if any */ if (post_commit_err) { post_commit_header_info = apr_psprintf(pool, " xmlns:S=\"%s\"", SVN_XML_NAMESPACE); post_commit_err_elem = apr_psprintf(pool, "<S:post-commit-err>%s" "</S:post-commit-err>", post_commit_err); } else { post_commit_header_info = "" ; post_commit_err_elem = "" ; } /* get the creationdate and creator-displayname of the new revision, too. */ serr = svn_fs_revision_prop(&creationdate, repos->fs, new_rev, SVN_PROP_REVISION_DATE, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not get date of newest revision", repos->pool); } serr = svn_fs_revision_prop(&creator_displayname, repos->fs, new_rev, SVN_PROP_REVISION_AUTHOR, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not get author of newest revision", repos->pool); } (void) ap_fputstrs(output, bb, DAV_XML_HEADER DEBUG_CR "<D:merge-response xmlns:D=\"DAV:\"", post_commit_header_info, ">" DEBUG_CR "<D:updated-set>" DEBUG_CR /* generate a response for the new baseline */ "<D:response>" DEBUG_CR "<D:href>", apr_xml_quote_string(pool, vcc, 1), "</D:href>" DEBUG_CR "<D:propstat><D:prop>" DEBUG_CR /* ### this is wrong. it's a VCC, not a baseline. but ### we need to tell the client to look at *this* ### resource for the version-name. */ "<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR, post_commit_err_elem, DEBUG_CR "<D:version-name>", rev, "</D:version-name>" DEBUG_CR, NULL); if (creationdate) { (void) ap_fputstrs(output, bb, "<D:creationdate>", apr_xml_quote_string(pool, creationdate->data, 1), "</D:creationdate>" DEBUG_CR, NULL); } if (creator_displayname) { (void) ap_fputstrs(output, bb, "<D:creator-displayname>", apr_xml_quote_string(pool, creator_displayname->data, 1), "</D:creator-displayname>" DEBUG_CR, NULL); } (void) ap_fputstrs(output, bb, "</D:prop>" DEBUG_CR "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR "</D:response>" DEBUG_CR, NULL); /* ONLY have dir_delta drive the editor if the caller asked us to generate a full MERGE response. svn clients can ask us to suppress this walk by sending specific request headers. */ if (! disable_merge_response) { /* Now we need to generate responses for all the resources which changed. This is done through a delta of the two roots. Note that a directory is not marked when open_dir is seen (since it typically is used just for changing members in that directory); instead, we want for a property change (the only reason the client would need to fetch a new directory). ### we probably should say something about the dirs, so that ### we can pass back the new version URL */ /* and go make me proud, boy! */ serr = do_resources(repos, root, new_rev, output, bb, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error constructing resource list.", repos->pool); } } /* wrap up the merge response */ (void) ap_fputs(output, bb, "</D:updated-set>" DEBUG_CR "</D:merge-response>" DEBUG_CR); /* send whatever is left in the brigade */ (void) ap_pass_brigade(output, bb); return SVN_NO_ERROR; }
/* The main dumper. */ svn_error_t * svn_repos_dump_fs3(svn_repos_t *repos, svn_stream_t *stream, svn_revnum_t start_rev, svn_revnum_t end_rev, svn_boolean_t incremental, svn_boolean_t use_deltas, svn_repos_notify_func_t notify_func, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) { const svn_delta_editor_t *dump_editor; void *dump_edit_baton = NULL; svn_revnum_t i; svn_fs_t *fs = svn_repos_fs(repos); apr_pool_t *subpool = svn_pool_create(pool); svn_revnum_t youngest; const char *uuid; int version; svn_boolean_t found_old_reference = FALSE; svn_boolean_t found_old_mergeinfo = FALSE; svn_repos_notify_t *notify; /* Determine the current youngest revision of the filesystem. */ SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); /* Use default vals if necessary. */ if (! SVN_IS_VALID_REVNUM(start_rev)) start_rev = 0; if (! SVN_IS_VALID_REVNUM(end_rev)) end_rev = youngest; if (! stream) stream = svn_stream_empty(pool); /* Validate the revisions. */ if (start_rev > end_rev) return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("Start revision %ld" " is greater than end revision %ld"), start_rev, end_rev); if (end_rev > youngest) return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("End revision %ld is invalid " "(youngest revision is %ld)"), end_rev, youngest); if ((start_rev == 0) && incremental) incremental = FALSE; /* revision 0 looks the same regardless of whether or not this is an incremental dump, so just simplify things. */ /* Write out the UUID. */ SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); /* If we're not using deltas, use the previous version, for compatibility with svn 1.0.x. */ version = SVN_REPOS_DUMPFILE_FORMAT_VERSION; if (!use_deltas) version--; /* Write out "general" metadata for the dumpfile. In this case, a magic header followed by a dumpfile format version. */ SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n", version)); SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid)); /* Create a notify object that we can reuse in the loop. */ if (notify_func) notify = svn_repos_notify_create(svn_repos_notify_dump_rev_end, pool); /* Main loop: we're going to dump revision i. */ for (i = start_rev; i <= end_rev; i++) { svn_revnum_t from_rev, to_rev; svn_fs_root_t *to_root; svn_boolean_t use_deltas_for_rev; svn_pool_clear(subpool); /* Check for cancellation. */ if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); /* Special-case the initial revision dump: it needs to contain *all* nodes, because it's the foundation of all future revisions in the dumpfile. */ if ((i == start_rev) && (! incremental)) { /* Special-special-case a dump of revision 0. */ if (i == 0) { /* Just write out the one revision 0 record and move on. The parser might want to use its properties. */ SVN_ERR(write_revision_record(stream, fs, 0, subpool)); to_rev = 0; goto loop_end; } /* Compare START_REV to revision 0, so that everything appears to be added. */ from_rev = 0; to_rev = i; } else { /* In the normal case, we want to compare consecutive revs. */ from_rev = i - 1; to_rev = i; } /* Write the revision record. */ SVN_ERR(write_revision_record(stream, fs, to_rev, subpool)); /* Fetch the editor which dumps nodes to a file. Regardless of what we've been told, don't use deltas for the first rev of a non-incremental dump. */ use_deltas_for_rev = use_deltas && (incremental || i != start_rev); SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, to_rev, "", stream, notify_func, notify_baton, start_rev, use_deltas_for_rev, FALSE, subpool)); /* Drive the editor in one way or another. */ SVN_ERR(svn_fs_revision_root(&to_root, fs, to_rev, subpool)); /* If this is the first revision of a non-incremental dump, we're in for a full tree dump. Otherwise, we want to simply replay the revision. */ if ((i == start_rev) && (! incremental)) { svn_fs_root_t *from_root; SVN_ERR(svn_fs_revision_root(&from_root, fs, from_rev, subpool)); SVN_ERR(svn_repos_dir_delta2(from_root, "", "", to_root, "", dump_editor, dump_edit_baton, NULL, NULL, FALSE, /* don't send text-deltas */ svn_depth_infinity, FALSE, /* don't send entry props */ FALSE, /* don't ignore ancestry */ subpool)); } else { SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE, dump_editor, dump_edit_baton, NULL, NULL, subpool)); } loop_end: if (notify_func) { notify->revision = to_rev; notify_func(notify_baton, notify, subpool); } if (dump_edit_baton) /* We never get an edit baton for r0. */ { if (((struct edit_baton *)dump_edit_baton)->found_old_reference) found_old_reference = TRUE; if (((struct edit_baton *)dump_edit_baton)->found_old_mergeinfo) found_old_mergeinfo = TRUE; } } if (notify_func) { /* Did we issue any warnings about references to revisions older than the oldest dumped revision? If so, then issue a final generic warning, since the inline warnings already issued might easily be missed. */ notify = svn_repos_notify_create(svn_repos_notify_dump_end, subpool); notify_func(notify_baton, notify, subpool); if (found_old_reference) { notify = svn_repos_notify_create(svn_repos_notify_warning, subpool); notify->warning = svn_repos_notify_warning_found_old_reference; notify->warning_str = _("The range of revisions dumped " "contained references to " "copy sources outside that " "range."); notify_func(notify_baton, notify, subpool); } /* Ditto if we issued any warnings about old revisions referenced in dumped mergeinfo. */ if (found_old_mergeinfo) { notify = svn_repos_notify_create(svn_repos_notify_warning, subpool); notify->warning = svn_repos_notify_warning_found_old_mergeinfo; notify->warning_str = _("The range of revisions dumped " "contained mergeinfo " "which reference revisions outside " "that range."); notify_func(notify_baton, notify, subpool); } } svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* Inspect the data and/or prop reps of revision REVNUM in FS. Store * reference count tallies in passed hashes (allocated in RESULT_POOL). * * If PROP_REPS or DATA_REPS is NULL, the respective kind of reps are not * tallied. * * Print progress report to STDERR unless QUIET is true. * * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * process_one_revision(svn_fs_t *fs, svn_revnum_t revnum, svn_boolean_t quiet, apr_hash_t *prop_reps, apr_hash_t *data_reps, apr_hash_t *both_reps, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_fs_root_t *rev_root; apr_hash_t *paths_changed; apr_hash_index_t *hi; if (! quiet) SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "processing r%ld\n", revnum)); /* Get the changed paths. */ SVN_ERR(svn_fs_revision_root(&rev_root, fs, revnum, scratch_pool)); SVN_ERR(svn_fs_paths_changed2(&paths_changed, rev_root, scratch_pool)); /* Iterate them. */ /* ### use iterpool? */ for (hi = apr_hash_first(scratch_pool, paths_changed); hi; hi = apr_hash_next(hi)) { const char *path; const svn_fs_path_change2_t *change; const svn_fs_id_t *node_rev_id1, *node_rev_id2; const svn_fs_id_t *the_id; node_revision_t *node_rev; path = svn__apr_hash_index_key(hi); change = svn__apr_hash_index_val(hi); if (! quiet) SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "processing r%ld:%s\n", revnum, path)); if (change->change_kind == svn_fs_path_change_delete) /* Can't ask for reps of PATH at REVNUM if the path no longer exists * at that revision! */ continue; /* Okay, we have two node_rev id's for this change: the txn one and * the revision one. We'll use the latter. */ node_rev_id1 = change->node_rev_id; SVN_ERR(svn_fs_node_id(&node_rev_id2, rev_root, path, scratch_pool)); SVN_ERR_ASSERT(svn_fs_fs__id_txn_id(node_rev_id1) != NULL); SVN_ERR_ASSERT(svn_fs_fs__id_rev(node_rev_id2) != SVN_INVALID_REVNUM); the_id = node_rev_id2; /* Get the node_rev using the chosen node_rev_id. */ SVN_ERR(svn_fs_fs__get_node_revision(&node_rev, fs, the_id, scratch_pool)); /* Maybe record the sha1's. */ SVN_ERR(record(prop_reps, node_rev->prop_rep, result_pool)); SVN_ERR(record(data_reps, node_rev->data_rep, result_pool)); SVN_ERR(record(both_reps, node_rev->prop_rep, result_pool)); SVN_ERR(record(both_reps, node_rev->data_rep, result_pool)); } return SVN_NO_ERROR; }
dav_error * dav_svn__replay_report(const dav_resource *resource, const apr_xml_doc *doc, dav_svn__output *output) { dav_error *derr = NULL; svn_revnum_t low_water_mark = SVN_INVALID_REVNUM; svn_revnum_t rev; const svn_delta_editor_t *editor; svn_boolean_t send_deltas = TRUE; dav_svn__authz_read_baton arb; const char *base_dir; apr_bucket_brigade *bb; apr_xml_elem *child; svn_fs_root_t *root; svn_error_t *err; void *edit_baton; int ns; /* In Subversion 1.8, we allowed this REPORT to be issue against a revision resource. Doing so means the REV is part of the request URL, and BASE_DIR is embedded in the request body. The old-school (and incorrect, see issue #4287 -- https://issues.apache.org/jira/browse/SVN-4287) way was to REPORT on the public URL of the BASE_DIR and embed the REV in the report body. */ if (resource->baselined && (resource->type == DAV_RESOURCE_TYPE_VERSION)) { rev = resource->info->root.rev; base_dir = NULL; } else { rev = SVN_INVALID_REVNUM; base_dir = resource->info->repos_path; } arb.r = resource->info->r; arb.repos = resource->info->repos; ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have an " "svn:revision element. That element is " "required"); for (child = doc->root->first_child; child != NULL; child = child->next) { if (child->ns == ns) { const char *cdata; if (strcmp(child->name, "revision") == 0) { if (SVN_IS_VALID_REVNUM(rev)) { /* Uh... we already have a revision to use, either because this tag is non-unique or because the request was submitted against a revision-bearing resource URL. Either way, something's not right. */ return malformed_element_error("revision", resource->pool); } cdata = dav_xml_get_cdata(child, resource->pool, 1); rev = SVN_STR_TO_REV(cdata); } else if (strcmp(child->name, "low-water-mark") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("low-water-mark", resource->pool); low_water_mark = SVN_STR_TO_REV(cdata); } else if (strcmp(child->name, "send-deltas") == 0) { apr_int64_t parsed_val; cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("send-deltas", resource->pool); err = svn_cstring_strtoi64(&parsed_val, cdata, 0, 1, 10); if (err) { svn_error_clear(err); return malformed_element_error("send-deltas", resource->pool); } send_deltas = parsed_val != 0; } else if (strcmp(child->name, "include-path") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if ((derr = dav_svn__test_canonical(cdata, resource->pool))) return derr; /* Force BASE_DIR to be a relative path, not an fspath. */ base_dir = svn_relpath_canonicalize(cdata, resource->pool); } } } if (! SVN_IS_VALID_REVNUM(rev)) return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, 0, "Request was missing the revision argument"); if (! SVN_IS_VALID_REVNUM(low_water_mark)) return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, 0, "Request was missing the low-water-mark argument"); if (! base_dir) base_dir = ""; bb = apr_brigade_create(resource->pool, dav_svn__output_get_bucket_alloc(output)); if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool))) { derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Couldn't retrieve revision root", resource->pool); goto cleanup; } make_editor(&editor, &edit_baton, bb, output, dav_svn__get_compression_level(resource->info->r), resource->info->svndiff_version, resource->pool); if ((err = svn_repos_replay2(root, base_dir, low_water_mark, send_deltas, editor, edit_baton, dav_svn__authz_read_func(&arb), &arb, resource->pool))) { derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem replaying revision", resource->pool); goto cleanup; } if ((err = end_report(edit_baton))) { derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem closing editor drive", resource->pool); goto cleanup; } cleanup: dav_svn__operational_log(resource->info, svn_log__replay(base_dir, rev, resource->info->r->pool)); /* Flush the brigade. */ return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* The caller wants to modify REVISION of FSPATH. Is that allowed? */ static svn_error_t * can_modify(svn_fs_root_t *txn_root, const char *fspath, svn_revnum_t revision, apr_pool_t *scratch_pool) { svn_revnum_t created_rev; /* Out-of-dateness check: compare the created-rev of the node in the txn against the created-rev of FSPATH. */ SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath, scratch_pool)); /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination) have no (committed) revision number. Let the caller go ahead and modify these nodes. Note: strictly speaking, they might be performing an "illegal" edit in certain cases, but let's just assume they're Good Little Boys. If CREATED_REV is invalid, that means it's already mutable in the txn, which means it has already passed this out-of-dateness check. (Usually, this happens when looking at a parent directory of an already-modified node) */ if (!SVN_IS_VALID_REVNUM(created_rev)) return SVN_NO_ERROR; /* If the node is immutable (has a revision), then the caller should have supplied a valid revision number [that they expect to change]. The checks further below will determine the out-of-dateness of the specified revision. */ /* ### ugh. descendents of copy/move/rotate destinations carry along ### their original immutable state and (thus) a valid CREATED_REV. ### but they are logically uncommitted, so the caller will pass ### SVN_INVALID_REVNUM. (technically, the caller could provide ### ORIGINAL_REV, but that is semantically incorrect for the Ev2 ### API). ### ### for now, we will assume the caller knows what they are doing ### and an invalid revision implies such a descendent. in the ### future, we could examine the ancestor chain looking for a ### copy/move/rotate-here node and allow the modification (and the ### converse: if no such ancestor, the caller must specify the ### correct/intended revision to modify). */ #if 1 if (!SVN_IS_VALID_REVNUM(revision)) return SVN_NO_ERROR; #else if (!SVN_IS_VALID_REVNUM(revision)) /* ### use a custom error code? */ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, _("Revision for modifying '%s' is required"), fspath); #endif if (revision < created_rev) { /* We asked to change a node that is *older* than what we found in the transaction. The client is out of date. */ return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, _("'%s' is out of date; try updating"), fspath); } if (revision > created_rev) { /* We asked to change a node that is *newer* than what we found in the transaction. Given that the transaction was based off of 'youngest', then either: - the caller asked to modify a future node - the caller has committed more revisions since this txn was constructed, and is asking to modify a node in one of those new revisions. In either case, the node may not have changed in those new revisions; use the node's ID to determine this case. */ const svn_fs_id_t *txn_noderev_id; svn_fs_root_t *rev_root; const svn_fs_id_t *new_noderev_id; /* The ID of the node that we would be modifying in the txn */ SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath, scratch_pool)); /* Get the ID from the future/new revision. */ SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root), revision, scratch_pool)); SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath, scratch_pool)); svn_fs_close_root(rev_root); /* Has the target node changed in the future? */ if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0) { /* Restarting the commit will base the txn on the future/new revision, allowing the modification at REVISION. */ /* ### use a custom error code */ return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, _("'%s' has been modified since the " "commit began (restart the commit)"), fspath); } } return SVN_NO_ERROR; }
dav_error * dav_svn__get_inherited_props_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_error_t *serr; dav_error *derr = NULL; apr_xml_elem *child; apr_array_header_t *inherited_props; dav_svn__authz_read_baton arb; int ns; apr_bucket_brigade *bb; const char *path = "/"; svn_fs_root_t *root; int i; svn_revnum_t rev = SVN_INVALID_REVNUM; apr_pool_t *iterpool; /* Sanity check. */ if (!resource->info->repos_path) return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not specify a repository path"); ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " "certain required elements"); } iterpool = svn_pool_create(resource->pool); for (child = doc->root->first_child; child != NULL; child = child->next) { /* if this element isn't one of ours, then skip it */ if (child->ns != ns) continue; if (strcmp(child->name, SVN_DAV__REVISION) == 0) { rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, iterpool, 1)); } else if (strcmp(child->name, SVN_DAV__PATH) == 0) { path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(path, iterpool))) return derr; path = svn_fspath__join(resource->info->repos_path, path, resource->pool); } /* else unknown element; skip it */ } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build inherited property brigade */ bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); serr = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool); if (serr != NULL) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "couldn't retrieve revision root", resource->pool); serr = svn_repos_fs_get_inherited_props(&inherited_props, root, path, NULL, dav_svn__authz_read_func(&arb), &arb, resource->pool, iterpool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } serr = dav_svn__brigade_puts(bb, output, DAV_XML_HEADER DEBUG_CR "<S:" SVN_DAV__INHERITED_PROPS_REPORT " " "xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } for (i = 0; i < inherited_props->nelts; i++) { svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); svn_pool_clear(iterpool); serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_ITEM ">" DEBUG_CR "<S:" SVN_DAV__IPROP_PATH ">%s</S:" SVN_DAV__IPROP_PATH ">" DEBUG_CR, apr_xml_quote_string(resource->pool, elt->path_or_url, 0)); if (!serr) { apr_hash_index_t *hi; for (hi = apr_hash_first(resource->pool, elt->prop_hash); hi; hi = apr_hash_next(hi)) { const char *propname = apr_hash_this_key(hi); svn_string_t *propval = apr_hash_this_val(hi); const char *xml_safe; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPNAME ">%s</S:" SVN_DAV__IPROP_PROPNAME ">" DEBUG_CR, apr_xml_quote_string(iterpool, propname, 0)); if (!serr) { if (svn_xml_is_xml_safe(propval->data, propval->len)) { svn_stringbuf_t *tmp = NULL; svn_xml_escape_cdata_string(&tmp, propval, iterpool); xml_safe = tmp->data; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPVAL ">%s</S:" SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe); } else { xml_safe = svn_base64_encode_string2( propval, TRUE, iterpool)->data; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPVAL " encoding=\"base64\"" ">%s</S:" SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe); } } if (serr) break; } if (!serr) serr = dav_svn__brigade_printf(bb, output, "</S:" SVN_DAV__IPROP_ITEM ">" DEBUG_CR); } if (serr) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } } if ((serr = dav_svn__brigade_puts(bb, output, "</S:" SVN_DAV__INHERITED_PROPS_REPORT ">" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: /* Log this 'high level' svn action. */ dav_svn__operational_log(resource->info, svn_log__get_inherited_props(path, rev, resource->pool)); svn_pool_destroy(iterpool); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* This helper is the main "meat" of the editor -- it does all the work of writing a node record. Write out a node record for PATH of type KIND under EB->FS_ROOT. 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. If the node was itself copied, IS_COPY is TRUE and the path/revision of the copy source are in CMP_PATH/CMP_REV. If IS_COPY is FALSE, yet CMP_PATH/CMP_REV are valid, this node is part of a copied subtree. */ static svn_error_t * dump_node(struct edit_baton *eb, const char *path, svn_node_kind_t kind, enum svn_node_action action, svn_boolean_t is_copy, const char *cmp_path, svn_revnum_t cmp_rev, apr_pool_t *pool) { svn_stringbuf_t *propstring; svn_filesize_t content_length = 0; apr_size_t len; svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE; const char *compare_path = path; svn_revnum_t compare_rev = eb->current_rev - 1; svn_fs_root_t *compare_root = NULL; apr_file_t *delta_file = NULL; /* Maybe validate the path. */ if (eb->verify || eb->notify_func) { svn_error_t *err = svn_fs__path_valid(path, pool); if (err) { if (eb->notify_func) { char errbuf[512]; /* ### svn_strerror() magic number */ svn_repos_notify_t *notify; notify = svn_repos_notify_create(svn_repos_notify_warning, pool); notify->warning = svn_repos_notify_warning_invalid_fspath; notify->warning_str = apr_psprintf( pool, _("E%06d: While validating fspath '%s': %s"), err->apr_err, path, svn_err_best_message(err, errbuf, sizeof(errbuf))); eb->notify_func(eb->notify_baton, notify, pool); } /* Return the error in addition to notifying about it. */ if (eb->verify) return svn_error_trace(err); else svn_error_clear(err); } } /* Write out metadata headers for this file node. */ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path)); if (kind == svn_node_file) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); else if (kind == svn_node_dir) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); /* Remove leading slashes from copyfrom paths. */ if (cmp_path) cmp_path = svn_relpath_canonicalize(cmp_path, pool); /* Validate the comparison path/rev. */ if (ARE_VALID_COPY_ARGS(cmp_path, cmp_rev)) { compare_path = cmp_path; compare_rev = cmp_rev; } if (action == svn_node_action_change) { SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); /* either the text or props changed, or possibly both. */ SVN_ERR(svn_fs_revision_root(&compare_root, svn_fs_root_fs(eb->fs_root), compare_rev, pool)); SVN_ERR(svn_fs_props_changed(&must_dump_props, compare_root, compare_path, eb->fs_root, path, pool)); if (kind == svn_node_file) SVN_ERR(svn_fs_contents_changed(&must_dump_text, compare_root, compare_path, eb->fs_root, path, pool)); } else if (action == svn_node_action_replace) { if (! is_copy) { /* a simple delete+add, implied by a single 'replace' action. */ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": replace\n")); /* definitely need to dump all content for a replace. */ if (kind == svn_node_file) must_dump_text = TRUE; must_dump_props = TRUE; } else { /* more complex: delete original, then add-with-history. */ /* the path & kind headers have already been printed; just add a delete action, and end the current record.*/ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n\n")); /* recurse: print an additional add-with-history record. */ SVN_ERR(dump_node(eb, path, kind, svn_node_action_add, is_copy, compare_path, compare_rev, pool)); /* we can leave this routine quietly now, don't need to dump any content; that was already done in the second record. */ must_dump_text = FALSE; must_dump_props = FALSE; } } else if (action == svn_node_action_delete) { SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); /* we can leave this routine quietly now, don't need to dump any content. */ must_dump_text = FALSE; must_dump_props = FALSE; } else if (action == svn_node_action_add) { SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); if (! is_copy) { /* Dump all contents for a simple 'add'. */ if (kind == svn_node_file) must_dump_text = TRUE; must_dump_props = TRUE; } else { if (!eb->verify && cmp_rev < eb->oldest_dumped_rev && eb->notify_func) { svn_repos_notify_t *notify = svn_repos_notify_create(svn_repos_notify_warning, pool); notify->warning = svn_repos_notify_warning_found_old_reference; notify->warning_str = apr_psprintf( pool, _("Referencing data in revision %ld," " which is older than the oldest" " dumped revision (r%ld). Loading this dump" " into an empty repository" " will fail."), cmp_rev, eb->oldest_dumped_rev); eb->found_old_reference = TRUE; eb->notify_func(eb->notify_baton, notify, pool); } SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV ": %ld\n" SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH ": %s\n", cmp_rev, cmp_path)); SVN_ERR(svn_fs_revision_root(&compare_root, svn_fs_root_fs(eb->fs_root), compare_rev, pool)); /* Need to decide if the copied node had any extra textual or property mods as well. */ SVN_ERR(svn_fs_props_changed(&must_dump_props, compare_root, compare_path, eb->fs_root, path, pool)); if (kind == svn_node_file) { svn_checksum_t *checksum; const char *hex_digest; SVN_ERR(svn_fs_contents_changed(&must_dump_text, compare_root, compare_path, eb->fs_root, path, pool)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 ": %s\n", hex_digest)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1 ": %s\n", hex_digest)); } } } if ((! must_dump_text) && (! must_dump_props)) { /* If we're not supposed to dump text or props, so be it, we can just go home. However, if either one needs to be dumped, then our dumpstream format demands that at a *minimum*, we see a lone "PROPS-END" as a divider between text and props content within the content-block. */ len = 2; return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */ } /*** Start prepping content to dump... ***/ /* If we are supposed to dump properties, write out a property length header and generate a stringbuf that contains those property values here. */ if (must_dump_props) { apr_hash_t *prophash, *oldhash = NULL; apr_size_t proplen; svn_stream_t *propstream; SVN_ERR(svn_fs_node_proplist(&prophash, eb->fs_root, path, pool)); /* If this is a partial dump, then issue a warning if we dump mergeinfo properties that refer to revisions older than the first revision dumped. */ if (!eb->verify && eb->notify_func && eb->oldest_dumped_rev > 1) { svn_string_t *mergeinfo_str = apr_hash_get(prophash, SVN_PROP_MERGEINFO, APR_HASH_KEY_STRING); if (mergeinfo_str) { svn_mergeinfo_t mergeinfo, old_mergeinfo; SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_str->data, pool)); SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &old_mergeinfo, mergeinfo, eb->oldest_dumped_rev - 1, 0, TRUE, pool, pool)); if (apr_hash_count(old_mergeinfo)) { svn_repos_notify_t *notify = svn_repos_notify_create(svn_repos_notify_warning, pool); notify->warning = svn_repos_notify_warning_found_old_mergeinfo; notify->warning_str = apr_psprintf( pool, _("Mergeinfo referencing revision(s) prior " "to the oldest dumped revision (r%ld). " "Loading this dump may result in invalid " "mergeinfo."), eb->oldest_dumped_rev); eb->found_old_mergeinfo = TRUE; eb->notify_func(eb->notify_baton, notify, pool); } } } if (eb->use_deltas && compare_root) { /* Fetch the old property hash to diff against and output a header saying that our property contents are a delta. */ SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path, pool)); SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_PROP_DELTA ": true\n")); } else oldhash = apr_hash_make(pool); propstring = svn_stringbuf_create_ensure(0, pool); propstream = svn_stream_from_stringbuf(propstring, pool); SVN_ERR(svn_hash_write_incremental(prophash, oldhash, propstream, "PROPS-END", pool)); SVN_ERR(svn_stream_close(propstream)); proplen = propstring->len; content_length += proplen; SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n", proplen)); } /* If we are supposed to dump text, write out a text length header here, and an MD5 checksum (if available). */ if (must_dump_text && (kind == svn_node_file)) { svn_checksum_t *checksum; const char *hex_digest; svn_filesize_t textlen; if (eb->use_deltas) { /* Compute the text delta now and write it into a temporary file, so that we can find its length. Output a header saying our text contents are a delta. */ SVN_ERR(store_delta(&delta_file, &textlen, compare_root, compare_path, eb->fs_root, path, pool)); SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_DELTA ": true\n")); if (compare_root) { SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 ": %s\n", hex_digest)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1 ": %s\n", hex_digest)); } } else { /* Just fetch the length of the file. */ SVN_ERR(svn_fs_file_length(&textlen, eb->fs_root, path, pool)); } content_length += textlen; SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH ": %" SVN_FILESIZE_T_FMT "\n", textlen)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, eb->fs_root, path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 ": %s\n", hex_digest)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, eb->fs_root, path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1 ": %s\n", hex_digest)); } /* 'Content-length:' is the last header before we dump the content, and is the sum of the text and prop contents lengths. We write this only for the benefit of non-Subversion RFC-822 parsers. */ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH ": %" SVN_FILESIZE_T_FMT "\n\n", content_length)); /* Dump property content if we're supposed to do so. */ if (must_dump_props) { len = propstring->len; SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len)); } /* Dump text content */ if (must_dump_text && (kind == svn_node_file)) { svn_stream_t *contents; if (delta_file) { /* Make sure to close the underlying file when the stream is closed. */ contents = svn_stream_from_aprfile2(delta_file, FALSE, pool); } else SVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool)); SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool), NULL, NULL, pool)); } len = 2; return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */ }