static svn_error_t * new_node_record(void **node_baton, apr_hash_t *headers, void *revision_baton, apr_pool_t *pool) { struct revision_baton *rb = revision_baton; const struct svn_delta_editor_t *commit_editor = rb->pb->commit_editor; void *commit_edit_baton = rb->pb->commit_edit_baton; struct node_baton *nb; apr_hash_index_t *hi; void *child_baton; const char *nb_dirname; nb = apr_pcalloc(rb->pool, sizeof(*nb)); nb->rb = rb; nb->is_added = FALSE; nb->copyfrom_path = NULL; nb->copyfrom_url = NULL; nb->copyfrom_rev = SVN_INVALID_REVNUM; nb->prop_changes = apr_hash_make(rb->pool); /* If the creation of commit_editor is pending, create it now and open_root on it; also create a top-level directory baton. */ if (!commit_editor) { /* The revprop_table should have been filled in with important information like svn:log in set_revision_property. We can now use it all this information to create our commit_editor. But first, clear revprops that we aren't allowed to set with the commit_editor. We'll set them separately using the RA API after closing the editor (see close_revision). */ svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_AUTHOR, NULL); svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_DATE, NULL); SVN_ERR(svn_ra__register_editor_shim_callbacks(rb->pb->session, get_shim_callbacks(rb, rb->pool))); SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor, &commit_edit_baton, rb->revprop_table, commit_callback, revision_baton, NULL, FALSE, rb->pool)); rb->pb->commit_editor = commit_editor; rb->pb->commit_edit_baton = commit_edit_baton; SVN_ERR(commit_editor->open_root(commit_edit_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); } 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); /* Parse the different kinds of headers we can encounter and stuff them into the node_baton for writing later */ if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_PATH) == 0) nb->path = apr_pstrdup(rb->pool, hval); if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_KIND) == 0) nb->kind = strcmp(hval, "file") == 0 ? svn_node_file : svn_node_dir; if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_ACTION) == 0) { if (strcmp(hval, "add") == 0) nb->action = svn_node_action_add; if (strcmp(hval, "change") == 0) nb->action = svn_node_action_change; if (strcmp(hval, "delete") == 0) nb->action = svn_node_action_delete; if (strcmp(hval, "replace") == 0) nb->action = svn_node_action_replace; } if (strcmp(hname, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5) == 0) nb->base_checksum = apr_pstrdup(rb->pool, hval); if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV) == 0) nb->copyfrom_rev = atoi(hval); if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH) == 0) nb->copyfrom_path = apr_pstrdup(rb->pool, hval); } nb_dirname = svn_relpath_dirname(nb->path, pool); if (svn_path_compare_paths(nb_dirname, rb->db->relpath) != 0) { char *ancestor_path; apr_size_t residual_close_count; apr_array_header_t *residual_open_path; 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); residual_close_count = svn_path_component_count(svn_relpath_skip_ancestor(ancestor_path, rb->db->relpath)); residual_open_path = svn_path_decompose(svn_relpath_skip_ancestor(ancestor_path, nb_dirname), pool); /* First close all as many directories as there are after skip_ancestor, and then open fresh directories */ for (n = 0; n < residual_close_count; n ++) { /* 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; } for (i = 0; i < residual_open_path->nelts; i ++) { char *relpath_compose = svn_relpath_join(rb->db->relpath, APR_ARRAY_IDX(residual_open_path, i, const char *), rb->pool); SVN_ERR(commit_editor->open_directory(relpath_compose, 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); } }
svn_error_t * svn_ra_local__split_URL(svn_repos_t **repos, const char **repos_url, const char **fs_path, const char *URL, apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; const char *repos_dirent; const char *repos_root_dirent; svn_stringbuf_t *urlbuf; apr_size_t root_end; SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dirent, URL, pool)); /* Search for a repository in the full path. */ repos_root_dirent = svn_repos_find_root_path(repos_dirent, pool); if (!repos_root_dirent) return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, NULL, _("Unable to open repository '%s'"), URL); /* Attempt to open a repository at URL. */ err = svn_repos_open2(repos, repos_root_dirent, NULL, pool); if (err) return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err, _("Unable to open repository '%s'"), URL); /* Assert capabilities directly, since client == server. */ { apr_array_header_t *caps = apr_array_make(pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(caps, const char *) = SVN_RA_CAPABILITY_MERGEINFO; SVN_ERR(svn_repos_remember_client_capabilities(*repos, caps)); } /* = apr_pstrcat(pool, "/", svn_dirent_skip_ancestor(repos_root_dirent, repos_dirent), (const char *)NULL); */ root_end = strlen(repos_root_dirent); if (! repos_dirent[root_end]) *fs_path = "/"; else if (repos_dirent[root_end] == '/') *fs_path = &repos_dirent[root_end]; else { /* On Windows "C:/" is the parent directory of "C:/dir" */ *fs_path = &repos_dirent[root_end-1]; SVN_ERR_ASSERT((*fs_path)[0] == '/'); } /* Remove the path components after the root dirent from the original URL, to get a URL to the repository root. We don't use svn_uri_get_file_url_from_dirent() here as that would transform several uris to form a differently formed url than svn_uri_canonicalize would. E.g. file://localhost/C:/dir -> file:///C:/dir (a transform that was originally supported directly by this function, before the implementation moved) On on Windows: file:///dir -> file:///E:/dir (When E: is the current disk) */ urlbuf = svn_stringbuf_create(URL, pool); svn_path_remove_components(urlbuf, svn_path_component_count(repos_dirent) - svn_path_component_count(repos_root_dirent)); *repos_url = urlbuf->data; /* Configure hook script environment variables. */ SVN_ERR(svn_repos_hooks_setenv(*repos, NULL, pool)); return SVN_NO_ERROR; }
/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * getlocks_closed(svn_ra_serf__xml_estate_t *xes, void *baton, int leaving_state, const svn_string_t *cdata, apr_hash_t *attrs, apr_pool_t *scratch_pool) { lock_context_t *lock_ctx = baton; if (leaving_state == LOCK) { const char *path = svn_hash_gets(attrs, "path"); svn_boolean_t save_lock = FALSE; /* Filter out unwanted paths. Since Subversion only allows locks on files, we can treat depth=immediates the same as depth=files for filtering purposes. Meaning, we'll keep this lock if: a) its path is the very path we queried, or b) we've asked for a fully recursive answer, or c) we've asked for depth=files or depth=immediates, and this lock is on an immediate child of our query path. */ if (strcmp(lock_ctx->path, path) == 0 || lock_ctx->requested_depth == svn_depth_infinity) { save_lock = TRUE; } else if (lock_ctx->requested_depth == svn_depth_files || lock_ctx->requested_depth == svn_depth_immediates) { const char *relpath = svn_fspath__skip_ancestor(lock_ctx->path, path); if (relpath && (svn_path_component_count(relpath) == 1)) save_lock = TRUE; } if (save_lock) { /* We get to put the structure on the stack rather than using svn_lock_create(). Bwahahaha.... */ svn_lock_t lock = { 0 }; const char *date; svn_lock_t *result_lock; /* Note: these "attributes" came from child elements. Some of them may have not been sent, so the value will be NULL. */ lock.path = path; lock.token = svn_hash_gets(attrs, "token"); lock.owner = svn_hash_gets(attrs, "owner"); lock.comment = svn_hash_gets(attrs, "comment"); date = svn_hash_gets(attrs, SVN_DAV__CREATIONDATE); if (date) SVN_ERR(svn_time_from_cstring(&lock.creation_date, date, scratch_pool)); date = svn_hash_gets(attrs, "expirationdate"); if (date) SVN_ERR(svn_time_from_cstring(&lock.expiration_date, date, scratch_pool)); result_lock = svn_lock_dup(&lock, lock_ctx->pool); svn_hash_sets(lock_ctx->hash, result_lock->path, result_lock); } } else { const char *name; SVN_ERR_ASSERT(cdata != NULL); if (leaving_state == PATH) name = "path"; else if (leaving_state == TOKEN) name = "token"; else if (leaving_state == OWNER) name = "owner"; else if (leaving_state == COMMENT) name = "comment"; else if (leaving_state == CREATION_DATE) name = SVN_DAV__CREATIONDATE; else if (leaving_state == EXPIRATION_DATE) name = "expirationdate"; else SVN_ERR_MALFUNCTION(); /* Store the lock information onto the LOCK elemstate. */ svn_ra_serf__xml_note(xes, LOCK, name, cdata->data); } return SVN_NO_ERROR; }
svn_error_t * svn_ra_local__split_URL(svn_repos_t **repos, const char **repos_url, const char **fs_path, const char *URL, apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; const char *repos_root; const char *hostname, *path; svn_stringbuf_t *urlbuf; /* Verify that the URL is well-formed (loosely) */ /* First, check for the "file://" prefix. */ if (strncmp(URL, "file://", 7) != 0) return svn_error_createf (SVN_ERR_RA_ILLEGAL_URL, NULL, _("Local URL '%s' does not contain 'file://' prefix"), URL); /* Then, skip what's between the "file://" prefix and the next occurance of '/' -- this is the hostname, and we are considering everything from that '/' until the end of the URL to be the absolute path portion of the URL. If we got just "file://", treat it the same as "file:///". */ hostname = URL + 7; if (*hostname == '\0') { path = "/"; hostname = NULL; } else { path = strchr(hostname, '/'); if (! path) return svn_error_createf (SVN_ERR_RA_ILLEGAL_URL, NULL, _("Local URL '%s' contains only a hostname, no path"), URL); /* Treat localhost as an empty hostname. */ if (hostname != path) { hostname = svn_path_uri_decode(apr_pstrmemdup(pool, hostname, path - hostname), pool); if (strncmp(hostname, "localhost", 9) == 0) hostname = NULL; } else hostname = NULL; } /* Duplicate the URL, starting at the top of the path. At the same time, we URI-decode the path. */ #if defined(WIN32) || defined(__CYGWIN__) /* On Windows, we'll typically have to skip the leading / if the path starts with a drive letter. Like most Web browsers, We support two variants of this scheme: file:///X:/path and file:///X|/path Note that, at least on WinNT and above, file:////./X:/path will also work, so we must make sure the transformation doesn't break that, and file:///path (that looks within the current drive only) should also keep working. If we got a non-empty hostname other than localhost, we convert this into an UNC path. In this case, we obviously don't strip the slash even if the path looks like it starts with a drive letter. Another thing to remember is that the form file:///\machine/share was the only way to access UNC paths in svn before 1.2. We need to support that for compatibility with old working copies. */ { static const char valid_drive_letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* Casting away const! */ char *dup_path = (char *)svn_path_uri_decode(path, pool); if (!hostname && dup_path[1] && strchr(valid_drive_letters, dup_path[1]) && (dup_path[2] == ':' || dup_path[2] == '|') && dup_path[3] == '/') { /* Skip the leading slash. */ ++dup_path; /* We're using path below to calculate fs_path, so keep it in sync. */ ++path; if (dup_path[1] == '|') dup_path[1] = ':'; } if (hostname) /* We still know that the path starts with a slash. */ repos_root = apr_pstrcat(pool, "//", hostname, path, NULL); else repos_root = dup_path; } #else /* Currently, the only hostnames we are allowing on non-Win32 platforms are the empty string and 'localhost'. */ if (hostname) return svn_error_createf (SVN_ERR_RA_ILLEGAL_URL, NULL, _("Local URL '%s' contains unsupported hostname"), URL); repos_root = svn_path_uri_decode(path, pool); #endif /* Search for a repository in the full path. */ repos_root = svn_repos_find_root_path(repos_root, pool); if (!repos_root) return svn_error_createf (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, NULL, _("Unable to open repository '%s'"), URL); /* Attempt to open a repository at URL. */ err = svn_repos_open(repos, repos_root, pool); if (err) return svn_error_createf (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, err, _("Unable to open repository '%s'"), URL); /* Assert capabilities directly, since client == server. */ { apr_array_header_t *caps = apr_array_make(pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(caps, const char *) = SVN_RA_CAPABILITY_MERGEINFO; SVN_ERR(svn_repos_remember_client_capabilities(*repos, caps)); } /* What remains of URL after being hacked at in the previous step is REPOS_URL. FS_PATH is what we've hacked off in the process. Note that path is not encoded and what we gave to svn_root_find_root_path may have been destroyed by that function. So we have to decode it once more. But then, it is ours... We want the suffix of path after the repos root part. Note that repos_root may contain //hostname, but path doesn't. */ *fs_path = svn_path_uri_decode(path, pool) + (strlen(repos_root) - (hostname ? strlen(hostname) + 2 : 0)); /* Ensure that *FS_PATH has its leading slash. */ if (**fs_path != '/') *fs_path = apr_pstrcat(pool, "/", *fs_path, NULL); /* Remove the path components in *fs_path from the original URL, to get the URL to the repository root. */ urlbuf = svn_stringbuf_create(URL, pool); svn_path_remove_components(urlbuf, svn_path_component_count(*fs_path)); *repos_url = urlbuf->data; return SVN_NO_ERROR; }
/* This implements the `svn_ra_neon__endelm_cb_t' prototype. */ static svn_error_t * getlocks_end_element(void *userdata, int state, const char *ns, const char *ln) { get_locks_baton_t *baton = userdata; const svn_ra_neon__xml_elm_t *elm; elm = svn_ra_neon__lookup_xml_elem(getlocks_report_elements, ns, ln); /* Just skip unknown elements. */ if (elm == NULL) return SVN_NO_ERROR; switch (elm->id) { case ELEM_lock: /* is the final svn_lock_t valid? all fields must be present except for 'comment' and 'expiration_date'. */ if ((! baton->current_lock->path) || (! baton->current_lock->token) || (! baton->current_lock->owner) || (! baton->current_lock->creation_date)) SVN_ERR(svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Incomplete lock data returned"))); /* Filter out unwanted paths. Since Subversion only allows locks on files, we can treat depth=immediates the same as depth=files for filtering purposes. Meaning, we'll keep this lock if: a) its path is the very path we queried, or b) we've asked for a fully recursive answer, or c) we've asked for depth=files or depth=immediates, and this lock is on an immediate child of our query path. */ if ((strcmp(baton->path, baton->current_lock->path) == 0) || (baton->requested_depth == svn_depth_infinity)) { apr_hash_set(baton->lock_hash, baton->current_lock->path, APR_HASH_KEY_STRING, baton->current_lock); } else if ((baton->requested_depth == svn_depth_files) || (baton->requested_depth == svn_depth_immediates)) { const char *rel_uri = svn_fspath__is_child(baton->path, baton->current_lock->path, baton->scratchpool); if (rel_uri && (svn_path_component_count(rel_uri) == 1)) apr_hash_set(baton->lock_hash, baton->current_lock->path, APR_HASH_KEY_STRING, baton->current_lock); svn_pool_clear(baton->scratchpool); } break; case ELEM_lock_path: /* neon has already xml-unescaped the cdata for us. */ baton->current_lock->path = svn_fspath__canonicalize(apr_pstrmemdup(baton->scratchpool, baton->cdata_accum->data, baton->cdata_accum->len), baton->pool); /* clean up the accumulator. */ svn_stringbuf_setempty(baton->cdata_accum); svn_pool_clear(baton->scratchpool); break; case ELEM_lock_token: /* neon has already xml-unescaped the cdata for us. */ baton->current_lock->token = apr_pstrmemdup(baton->pool, baton->cdata_accum->data, baton->cdata_accum->len); /* clean up the accumulator. */ svn_stringbuf_setempty(baton->cdata_accum); svn_pool_clear(baton->scratchpool); break; case ELEM_lock_creationdate: SVN_ERR(svn_time_from_cstring(&(baton->current_lock->creation_date), baton->cdata_accum->data, baton->scratchpool)); /* clean up the accumulator. */ svn_stringbuf_setempty(baton->cdata_accum); svn_pool_clear(baton->scratchpool); break; case ELEM_lock_expirationdate: SVN_ERR(svn_time_from_cstring(&(baton->current_lock->expiration_date), baton->cdata_accum->data, baton->scratchpool)); /* clean up the accumulator. */ svn_stringbuf_setempty(baton->cdata_accum); svn_pool_clear(baton->scratchpool); break; case ELEM_lock_owner: case ELEM_lock_comment: { const char *final_val; if (baton->encoding) { /* Possibly recognize other encodings someday. */ if (strcmp(baton->encoding, "base64") == 0) { svn_string_t *encoded_val; const svn_string_t *decoded_val; encoded_val = svn_string_create_from_buf(baton->cdata_accum, baton->scratchpool); decoded_val = svn_base64_decode_string(encoded_val, baton->scratchpool); final_val = decoded_val->data; } else return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got unrecognized encoding '%s'"), baton->encoding); baton->encoding = NULL; } else { /* neon has already xml-unescaped the cdata for us. */ final_val = baton->cdata_accum->data; } if (elm->id == ELEM_lock_owner) baton->current_lock->owner = apr_pstrdup(baton->pool, final_val); if (elm->id == ELEM_lock_comment) baton->current_lock->comment = apr_pstrdup(baton->pool, final_val); /* clean up the accumulator. */ svn_stringbuf_setempty(baton->cdata_accum); svn_pool_clear(baton->scratchpool); break; } default: break; } return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__locks_get(svn_fs_t *fs, const char *path, svn_depth_t depth, svn_fs_get_locks_callback_t get_locks_func, void *get_locks_baton, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBC *cursor; DBT key, value; int db_err, db_c_err; apr_pool_t *subpool = svn_pool_create(pool); const char *lock_token; svn_lock_t *lock; svn_error_t *err; const char *lookup_path = path; /* First, try to lookup PATH itself. */ err = svn_fs_bdb__lock_token_get(&lock_token, fs, path, trail, pool); if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN) || (err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK))) { svn_error_clear(err); } else if (err) { return svn_error_trace(err); } else { SVN_ERR(get_lock(&lock, fs, path, lock_token, trail, pool)); if (lock && get_locks_func) SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); } /* If we're only looking at PATH itself (depth = empty), stop here. */ if (depth == svn_depth_empty) return SVN_NO_ERROR; /* Now go hunt for possible children of PATH. */ svn_fs_base__trail_debug(trail, "lock-tokens", "cursor"); db_err = bfd->lock_tokens->cursor(bfd->lock_tokens, trail->db_txn, &cursor, 0); SVN_ERR(BDB_WRAP(fs, "creating cursor for reading lock tokens", db_err)); /* Since the key is going to be returned as well as the value make sure BDB malloc's the returned key. */ svn_fs_base__str_to_dbt(&key, lookup_path); key.flags |= DB_DBT_MALLOC; /* Get the first matching key that is either equal or greater than the one passed in, by passing in the DB_RANGE_SET flag. */ db_err = svn_bdb_dbc_get(cursor, &key, svn_fs_base__result_dbt(&value), DB_SET_RANGE); if (!svn_fspath__is_root(path, strlen(path))) lookup_path = apr_pstrcat(pool, path, "/", (char *)NULL); /* As long as the prefix of the returned KEY matches LOOKUP_PATH we know it is either LOOKUP_PATH or a decendant thereof. */ while ((! db_err) && strncmp(lookup_path, key.data, strlen(lookup_path)) == 0) { const char *child_path; svn_pool_clear(subpool); svn_fs_base__track_dbt(&key, subpool); svn_fs_base__track_dbt(&value, subpool); /* Create a usable path and token in temporary memory. */ child_path = apr_pstrmemdup(subpool, key.data, key.size); lock_token = apr_pstrmemdup(subpool, value.data, value.size); if ((depth == svn_depth_files) || (depth == svn_depth_immediates)) { /* On the assumption that we only store locks for files, depth=files and depth=immediates should boil down to the same set of results. So just see if CHILD_PATH is an immediate child of PATH. If not, we don't care about this item. */ const char *rel_path = svn_fspath__is_child(path, child_path, subpool); if (!rel_path || (svn_path_component_count(rel_path) != 1)) goto loop_it; } /* Get the lock for CHILD_PATH. */ err = get_lock(&lock, fs, child_path, lock_token, trail, subpool); if (err) { svn_bdb_dbc_close(cursor); return svn_error_trace(err); } /* Lock is verified, hand it off to our callback. */ if (lock && get_locks_func) { err = get_locks_func(get_locks_baton, lock, subpool); if (err) { svn_bdb_dbc_close(cursor); return svn_error_trace(err); } } loop_it: svn_fs_base__result_dbt(&key); svn_fs_base__result_dbt(&value); db_err = svn_bdb_dbc_get(cursor, &key, &value, DB_NEXT); } svn_pool_destroy(subpool); db_c_err = svn_bdb_dbc_close(cursor); if (db_err && (db_err != DB_NOTFOUND)) SVN_ERR(BDB_WRAP(fs, "fetching lock tokens", db_err)); if (db_c_err) SVN_ERR(BDB_WRAP(fs, "fetching lock tokens (closing cursor)", db_c_err)); return SVN_NO_ERROR; }