static svn_error_t * verify_directory_entry(void *baton, const void *key, apr_ssize_t klen, void *val, apr_pool_t *pool) { struct dir_baton *db = baton; char *path = svn_relpath_join(db->path, (const char *)key, pool); svn_node_kind_t kind; apr_hash_t *dirents; svn_filesize_t len; SVN_ERR(svn_fs_check_path(&kind, db->edit_baton->fs_root, path, pool)); switch (kind) { case svn_node_dir: /* Getting this directory's contents is enough to ensure that our link to it is correct. */ SVN_ERR(svn_fs_dir_entries(&dirents, db->edit_baton->fs_root, path, pool)); break; case svn_node_file: /* Getting this file's size is enough to ensure that our link to it is correct. */ SVN_ERR(svn_fs_file_length(&len, db->edit_baton->fs_root, path, pool)); break; default: return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("Unexpected node kind %d for '%s'"), kind, path); } return SVN_NO_ERROR; }
static svn_error_t * verify_close_directory(void *dir_baton, apr_pool_t *pool) { struct dir_baton *db = dir_baton; apr_hash_t *dirents; SVN_ERR(svn_fs_dir_entries(&dirents, db->edit_baton->fs_root, db->path, pool)); SVN_ERR(svn_iter_apr_hash(NULL, dirents, verify_directory_entry, dir_baton, pool)); return close_directory(dir_baton, pool); }
/* Getting a directory's entries */ static svn_error_t * svn_ra_local__get_dir(svn_ra_session_t *session, apr_hash_t **dirents, svn_revnum_t *fetched_rev, apr_hash_t **props, const char *path, svn_revnum_t revision, apr_uint32_t dirent_fields, apr_pool_t *pool) { svn_fs_root_t *root; svn_revnum_t youngest_rev; apr_hash_t *entries; apr_hash_index_t *hi; svn_ra_local__session_baton_t *sess = session->priv; apr_pool_t *subpool; 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 (dirents) { /* Get the dir's entries. */ SVN_ERR(svn_fs_dir_entries(&entries, root, abs_path, pool)); /* Loop over the fs dirents, and build a hash of general svn_dirent_t's. */ *dirents = apr_hash_make(pool); subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_hash_t *prophash; const char *datestring, *entryname, *fullpath; svn_fs_dirent_t *fs_entry; svn_dirent_t *entry = apr_pcalloc(pool, sizeof(*entry)); svn_pool_clear(subpool); apr_hash_this(hi, &key, NULL, &val); entryname = (const char *) key; fs_entry = (svn_fs_dirent_t *) val; fullpath = svn_path_join(abs_path, entryname, subpool); if (dirent_fields & SVN_DIRENT_KIND) { /* node kind */ entry->kind = fs_entry->kind; } if (dirent_fields & SVN_DIRENT_SIZE) { /* size */ if (entry->kind == svn_node_dir) entry->size = 0; else SVN_ERR(svn_fs_file_length(&(entry->size), root, fullpath, subpool)); } if (dirent_fields & SVN_DIRENT_HAS_PROPS) { /* has_props? */ SVN_ERR(svn_fs_node_proplist(&prophash, root, fullpath, subpool)); entry->has_props = (apr_hash_count(prophash) != 0); } if ((dirent_fields & SVN_DIRENT_TIME) || (dirent_fields & SVN_DIRENT_LAST_AUTHOR) || (dirent_fields & SVN_DIRENT_CREATED_REV)) { /* created_rev & friends */ SVN_ERR(svn_repos_get_committed_info(&(entry->created_rev), &datestring, &(entry->last_author), root, fullpath, subpool)); if (datestring) SVN_ERR(svn_time_from_cstring(&(entry->time), datestring, pool)); if (entry->last_author) entry->last_author = apr_pstrdup(pool, entry->last_author); } /* Store. */ apr_hash_set(*dirents, entryname, APR_HASH_KEY_STRING, entry); } svn_pool_destroy(subpool); } /* Handle props if requested. */ if (props) SVN_ERR(get_node_props(props, sess, root, abs_path, pool)); return SVN_NO_ERROR; }
/* Recursively traverse EDIT_PATH (as it exists under SOURCE_ROOT) emitting the appropriate editor calls to add it and its children without any history. This is meant to be used when either a subset of the tree has been ignored and we need to copy something from that subset to the part of the tree we do care about, or if a subset of the tree is unavailable because of authz and we need to use it as the source of a copy. */ static svn_error_t * add_subdir(svn_fs_root_t *source_root, svn_fs_root_t *target_root, const svn_delta_editor_t *editor, void *edit_baton, const char *edit_path, void *parent_baton, const char *source_fspath, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_hash_t *changed_paths, apr_pool_t *pool, void **dir_baton) { apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi, *phi; apr_hash_t *dirents; apr_hash_t *props; SVN_ERR(editor->add_directory(edit_path, parent_baton, NULL, SVN_INVALID_REVNUM, pool, dir_baton)); SVN_ERR(svn_fs_node_proplist(&props, target_root, edit_path, pool)); for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { const void *key; void *val; svn_pool_clear(subpool); apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_dir_prop(*dir_baton, key, val, subpool)); } /* We have to get the dirents from the source path, not the target, because we want nested copies from *readable* paths to be handled by path_driver_cb_func, not add_subdir (in order to preserve history). */ SVN_ERR(svn_fs_dir_entries(&dirents, source_root, source_fspath, pool)); for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { svn_fs_path_change2_t *change; svn_boolean_t readable = TRUE; svn_fs_dirent_t *dent; const char *copyfrom_path = NULL; svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; const char *new_edit_path; void *val; svn_pool_clear(subpool); apr_hash_this(hi, NULL, NULL, &val); dent = val; new_edit_path = svn_relpath_join(edit_path, dent->name, subpool); /* If a file or subdirectory of the copied directory is listed as a changed path (because it was modified after the copy but before the commit), we remove it from the changed_paths hash so that future calls to path_driver_cb_func will ignore it. */ change = apr_hash_get(changed_paths, new_edit_path, APR_HASH_KEY_STRING); if (change) { apr_hash_set(changed_paths, new_edit_path, APR_HASH_KEY_STRING, NULL); /* If it's a delete, skip this entry. */ if (change->change_kind == svn_fs_path_change_delete) continue; /* If it's a replacement, check for copyfrom info (if we don't have it already. */ if (change->change_kind == svn_fs_path_change_replace) { if (! change->copyfrom_known) { SVN_ERR(svn_fs_copied_from(&change->copyfrom_rev, &change->copyfrom_path, target_root, new_edit_path, pool)); change->copyfrom_known = TRUE; } copyfrom_path = change->copyfrom_path; copyfrom_rev = change->copyfrom_rev; } } if (authz_read_func) SVN_ERR(authz_read_func(&readable, target_root, new_edit_path, authz_read_baton, pool)); if (! readable) continue; if (dent->kind == svn_node_dir) { svn_fs_root_t *new_source_root; const char *new_source_fspath; void *new_dir_baton; if (copyfrom_path) { svn_fs_t *fs = svn_fs_root_fs(source_root); SVN_ERR(svn_fs_revision_root(&new_source_root, fs, copyfrom_rev, pool)); new_source_fspath = copyfrom_path; } else { new_source_root = source_root; new_source_fspath = svn_fspath__join(source_fspath, dent->name, subpool); } /* ### authz considerations? * * I think not; when path_driver_cb_func() calls add_subdir(), it * passes SOURCE_ROOT and SOURCE_FSPATH that are unreadable. */ if (change && change->change_kind == svn_fs_path_change_replace && copyfrom_path == NULL) { SVN_ERR(editor->add_directory(new_edit_path, *dir_baton, NULL, SVN_INVALID_REVNUM, subpool, &new_dir_baton)); } else { SVN_ERR(add_subdir(new_source_root, target_root, editor, edit_baton, new_edit_path, *dir_baton, new_source_fspath, authz_read_func, authz_read_baton, changed_paths, subpool, &new_dir_baton)); } SVN_ERR(editor->close_directory(new_dir_baton, subpool)); } else if (dent->kind == svn_node_file) { svn_txdelta_window_handler_t delta_handler; void *delta_handler_baton, *file_baton; svn_txdelta_stream_t *delta_stream; svn_checksum_t *checksum; SVN_ERR(editor->add_file(new_edit_path, *dir_baton, NULL, SVN_INVALID_REVNUM, pool, &file_baton)); SVN_ERR(svn_fs_node_proplist(&props, target_root, new_edit_path, subpool)); for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi)) { const void *key; apr_hash_this(phi, &key, NULL, &val); SVN_ERR(editor->change_file_prop(file_baton, key, val, subpool)); } SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool, &delta_handler, &delta_handler_baton)); SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, NULL, NULL, target_root, new_edit_path, pool)); SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler, delta_handler_baton, pool)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, target_root, new_edit_path, TRUE, pool)); SVN_ERR(editor->close_file(file_baton, svn_checksum_to_cstring(checksum, pool), pool)); } else SVN_ERR_MALFUNCTION(); } svn_pool_destroy(subpool); return SVN_NO_ERROR; }