static svn_error_t * invoke_commit_cb(svn_commit_callback2_t commit_cb, void *commit_baton, svn_fs_t *fs, svn_revnum_t revision, const char *post_commit_errstr, apr_pool_t *scratch_pool) { /* FS interface returns non-const values. */ /* const */ svn_string_t *date; /* const */ svn_string_t *author; svn_commit_info_t *commit_info; if (commit_cb == NULL) return SVN_NO_ERROR; SVN_ERR(svn_fs_revision_prop(&date, fs, revision, SVN_PROP_REVISION_DATE, scratch_pool)); SVN_ERR(svn_fs_revision_prop(&author, fs, revision, SVN_PROP_REVISION_AUTHOR, scratch_pool)); commit_info = svn_create_commit_info(scratch_pool); /* fill up the svn_commit_info structure */ commit_info->revision = revision; commit_info->date = date ? date->data : NULL; commit_info->author = author ? author->data : NULL; commit_info->post_commit_err = post_commit_errstr; /* commit_info->repos_root is not set by the repos layer, only by RA layers */ return svn_error_trace(commit_cb(commit_info, commit_baton, scratch_pool)); }
svn_error_t * svn_repos_fs_revision_prop(svn_string_t **value_p, svn_repos_t *repos, svn_revnum_t rev, const char *propname, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { svn_repos_revision_access_level_t readability; SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, authz_read_func, authz_read_baton, pool)); if (readability == svn_repos_revision_access_none) { /* Property? What property? */ *value_p = NULL; } else if (readability == svn_repos_revision_access_partial) { /* Only svn:author and svn:date are fetchable. */ if ((strncmp(propname, SVN_PROP_REVISION_AUTHOR, strlen(SVN_PROP_REVISION_AUTHOR)) != 0) && (strncmp(propname, SVN_PROP_REVISION_DATE, strlen(SVN_PROP_REVISION_DATE)) != 0)) *value_p = NULL; else SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool)); } else /* wholly readable revision */ { SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool)); } return SVN_NO_ERROR; }
svn_error_t * svn_repos_fs_change_rev_prop3(svn_repos_t *repos, svn_revnum_t rev, const char *author, const char *name, const svn_string_t *new_value, svn_boolean_t use_pre_revprop_change_hook, svn_boolean_t use_post_revprop_change_hook, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { svn_string_t *old_value; svn_repos_revision_access_level_t readability; char action; SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, authz_read_func, authz_read_baton, pool)); if (readability == svn_repos_revision_access_full) { SVN_ERR(validate_prop(name, new_value, pool)); SVN_ERR(svn_fs_revision_prop(&old_value, repos->fs, rev, name, pool)); if (! new_value) action = 'D'; else if (! old_value) action = 'A'; else action = 'M'; if (use_pre_revprop_change_hook) SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, rev, author, name, new_value, action, pool)); SVN_ERR(svn_fs_change_rev_prop(repos->fs, rev, name, new_value, pool)); if (use_post_revprop_change_hook) SVN_ERR(svn_repos__hooks_post_revprop_change(repos, rev, author, name, old_value, action, pool)); } else /* rev is either unreadable or only partially readable */ { return svn_error_createf (SVN_ERR_AUTHZ_UNREADABLE, NULL, _("Write denied: not authorized to read all of revision %ld"), rev); } return SVN_NO_ERROR; }
static svn_error_t * get_set_revprop_packed_fs(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_fs_t *fs; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict; svn_revnum_t after_rev; svn_string_t *prop_value; apr_pool_t *subpool; /* Bail (with success) on known-untestable scenarios */ if ((strcmp(opts->fs_type, "fsfs") != 0) || (opts->server_minor_version && (opts->server_minor_version < 7))) return SVN_NO_ERROR; /* Create the packed FS and open it. */ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool)); SVN_ERR(svn_fs_open(&fs, REPO_NAME, NULL, pool)); subpool = svn_pool_create(pool); /* Do a commit to trigger packing. */ SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); SVN_ERR(svn_test__set_file_contents(txn_root, "iota", "new-iota", subpool)); SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool)); SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); svn_pool_clear(subpool); /* Pack the repository. */ SVN_ERR(svn_fs_pack(REPO_NAME, NULL, NULL, NULL, NULL, pool)); /* Try to get revprop for revision 0. */ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 0, SVN_PROP_REVISION_AUTHOR, pool)); /* Try to change revprop for revision 0. */ SVN_ERR(svn_fs_change_rev_prop(fs, 0, SVN_PROP_REVISION_AUTHOR, svn_string_create("tweaked-author", pool), 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; }
static svn_error_t * close_edit(void *edit_baton, apr_pool_t *pool) { struct edit_baton *eb = edit_baton; svn_revnum_t new_revision = SVN_INVALID_REVNUM; svn_error_t *err; const char *conflict; const char *post_commit_err = NULL; /* If no transaction has been created (ie. if open_root wasn't called before close_edit), abort the operation here with an error. */ if (! eb->txn) return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL, "No valid transaction supplied to close_edit"); /* Commit. */ err = svn_repos_fs_commit_txn(&conflict, eb->repos, &new_revision, eb->txn, pool); if (SVN_IS_VALID_REVNUM(new_revision)) { if (err) { /* If the error was in post-commit, then the commit itself succeeded. In which case, save the post-commit warning (to be reported back to the client, who will probably display it as a warning) and clear the error. */ post_commit_err = svn_repos__post_commit_error_str(err, pool); svn_error_clear(err); err = SVN_NO_ERROR; } } else { /* ### todo: we should check whether it really was a conflict, and return the conflict info if so? */ /* If the commit failed, it's *probably* due to a conflict -- that is, the txn being out-of-date. The filesystem gives us the ability to continue diddling the transaction and try again; but let's face it: that's not how the cvs or svn works from a user interface standpoint. Thus we don't make use of this fs feature (for now, at least.) So, in a nutshell: svn commits are an all-or-nothing deal. Each commit creates a new fs txn which either succeeds or is aborted completely. No second chances; the user simply needs to update and commit again :) */ eb->txn_aborted = TRUE; return svn_error_trace( svn_error_compose_create(err, svn_fs_abort_txn(eb->txn, pool))); } /* Pass new revision information to the caller's callback. */ { svn_string_t *date, *author; svn_commit_info_t *commit_info; /* Even if there was a post-commit hook failure, it's more serious if one of the calls here fails, so we explicitly check for errors here, while saving the possible post-commit error for later. */ err = svn_fs_revision_prop(&date, svn_repos_fs(eb->repos), new_revision, SVN_PROP_REVISION_DATE, pool); if (! err) { err = svn_fs_revision_prop(&author, svn_repos_fs(eb->repos), new_revision, SVN_PROP_REVISION_AUTHOR, pool); } if ((! err) && eb->commit_callback) { commit_info = svn_create_commit_info(pool); /* fill up the svn_commit_info structure */ commit_info->revision = new_revision; commit_info->date = date ? date->data : NULL; commit_info->author = author ? author->data : NULL; commit_info->post_commit_err = post_commit_err; err = (*eb->commit_callback)(commit_info, eb->commit_callback_baton, pool); } } return svn_error_trace(err); }
svn_error_t * svn_repos_fs_change_rev_prop4(svn_repos_t *repos, svn_revnum_t rev, const char *author, const char *name, const svn_string_t *const *old_value_p, const svn_string_t *new_value, svn_boolean_t use_pre_revprop_change_hook, svn_boolean_t use_post_revprop_change_hook, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, apr_pool_t *pool) { svn_repos_revision_access_level_t readability; SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, authz_read_func, authz_read_baton, pool)); if (readability == svn_repos_revision_access_full) { const svn_string_t *old_value; char action; apr_hash_t *hooks_env; SVN_ERR(svn_repos__validate_prop(name, new_value, pool)); /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */ if (old_value_p) { old_value = *old_value_p; } else { /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments * to the hooks to be accurate. */ svn_string_t *old_value2; SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool)); old_value = old_value2; } /* Prepare ACTION. */ if (! new_value) action = 'D'; else if (! old_value) action = 'A'; else action = 'M'; /* Parse the hooks-env file (if any, and if to be used). */ if (use_pre_revprop_change_hook || use_post_revprop_change_hook) SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, pool, pool)); /* ### currently not passing the old_value to hooks */ if (use_pre_revprop_change_hook) SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev, author, name, new_value, action, pool)); SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name, &old_value, new_value, pool)); if (use_post_revprop_change_hook) SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev, author, name, old_value, action, pool)); } else /* rev is either unreadable or only partially readable */ { return svn_error_createf (SVN_ERR_AUTHZ_UNREADABLE, NULL, _("Write denied: not authorized to read all of revision %ld"), rev); } return SVN_NO_ERROR; }