📄 repos.c
字号:
const char *root_path,
const char *label,
int use_checked_in,
dav_resource **resource)
{
const char *fs_path;
const char *repo_name;
const char *xslt_uri;
const char *fs_parent_path;
dav_resource_combined *comb;
dav_svn_repos *repos;
const char *cleaned_uri;
const char *repos_name;
const char *relative;
const char *repos_path;
const char *repos_key;
const char *version_name;
svn_error_t *serr;
dav_error *err;
int had_slash;
repo_name = dav_svn_get_repo_name(r);
xslt_uri = dav_svn_get_xslt_uri(r);
/* This does all the work of interpreting/splitting the request uri. */
err = dav_svn_split_uri (r, r->uri, root_path,
&cleaned_uri, &had_slash,
&repos_name, &relative, &repos_path);
if (err)
return err;
/* The path that we will eventually try to open as an svn
repository. Normally defined by the SVNPath directive. */
fs_path = dav_svn_get_fs_path(r);
/* If the SVNParentPath directive was used instead... */
fs_parent_path = dav_svn_get_fs_parent_path(r);
if (fs_parent_path != NULL)
{
/* ...then the URL to the repository is actually one implicit
component longer... */
root_path = svn_path_join(root_path, repos_name, r->pool);
/* ...and we need to specify exactly what repository to open. */
fs_path = svn_path_join(fs_parent_path, repos_name, r->pool);
}
/* Start building and filling a 'combination' object. */
comb = apr_pcalloc(r->pool, sizeof(*comb));
comb->res.info = &comb->priv;
comb->res.hooks = &dav_svn_hooks_repos;
comb->res.pool = r->pool;
comb->res.uri = cleaned_uri;
/* Original request, off which to generate subrequests later. */
comb->priv.r = r;
/* ### ugly hack to carry over Content-Type data to the open_stream, which
### does not have access to the request headers. */
{
const char *ct = apr_table_get(r->headers_in, "content-type");
comb->priv.is_svndiff =
ct != NULL
&& strcmp(ct, SVN_SVNDIFF_MIME_TYPE) == 0;
}
/* ### and another hack for computing diffs to send to the client */
comb->priv.delta_base = apr_table_get(r->headers_in,
SVN_DAV_DELTA_BASE_HEADER);
/* Gather any options requested by an svn client. */
comb->priv.svn_client_options = apr_table_get(r->headers_in,
SVN_DAV_OPTIONS_HEADER);
/* See if the client sent a custom 'version name' request header. */
version_name = apr_table_get(r->headers_in, SVN_DAV_VERSION_NAME_HEADER);
comb->priv.version_name
= version_name ? SVN_STR_TO_REV(version_name): SVN_INVALID_REVNUM;
/* Remember checksums, if any. */
comb->priv.base_checksum =
apr_table_get (r->headers_in, SVN_DAV_BASE_FULLTEXT_MD5_HEADER);
comb->priv.result_checksum =
apr_table_get (r->headers_in, SVN_DAV_RESULT_FULLTEXT_MD5_HEADER);
/* "relative" is part of the "uri" string, so it has the proper
lifetime to store here. */
/* ### that comment no longer applies. we're creating a string with its
### own lifetime now. so WHY are we using a string? hmm... */
comb->priv.uri_path = svn_stringbuf_create(relative, r->pool);
/* initialize this until we put something real here */
comb->priv.root.rev = SVN_INVALID_REVNUM;
/* create the repository structure and stash it away */
repos = apr_pcalloc(r->pool, sizeof(*repos));
repos->pool = r->pool;
comb->priv.repos = repos;
/* We are assuming the root_path will live at least as long as this
resource. Considering that it typically comes from the per-dir
config in mod_dav, this is valid for now. */
repos->root_path = svn_path_uri_encode(root_path, r->pool);
/* where is the SVN FS for this resource? */
repos->fs_path = fs_path;
/* A name for the repository */
repos->repo_name = repo_name;
/* An XSL transformation */
repos->xslt_uri = xslt_uri;
/* Is autoversioning active in this repos? */
repos->autoversioning = dav_svn_get_autoversioning_flag(r);
/* Remember various bits for later URL construction */
repos->base_url = ap_construct_url(r->pool, "", r);
repos->special_uri = dav_svn_get_special_uri(r);
/* Remember who is making this request */
repos->username = r->user;
/* Retrieve/cache open repository */
repos_key = apr_pstrcat(r->pool, "mod_dav_svn:", fs_path, NULL);
apr_pool_userdata_get((void **)&repos->repos, repos_key, r->connection->pool);
if (repos->repos == NULL)
{
serr = svn_repos_open(&(repos->repos), fs_path, r->connection->pool);
if (serr != NULL)
{
/* The error returned by svn_repos_open might contain the
actual path to the failed repository. We don't want to
leak that path back to the client, because that would be
a security risk, but we do want to log the real error on
the server side. */
const char *new_msg = "Could not open the requested SVN filesystem";
svn_error_t *sanitized_error = svn_error_create(serr->apr_err,
NULL, new_msg);
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
"%s", serr->message);
/* Return a slightly less informative error to dav. */
svn_error_clear(serr);
return dav_svn_convert_err (sanitized_error,
HTTP_INTERNAL_SERVER_ERROR,
apr_psprintf(r->pool, new_msg),
r->pool);
}
/* Cache the open repos for the next request on this connection */
apr_pool_userdata_set(repos->repos, repos_key,
NULL, r->connection->pool);
}
/* cache the filesystem object */
repos->fs = svn_repos_fs (repos->repos);
/* capture warnings during cleanup of the FS */
svn_fs_set_warning_func(repos->fs, log_warning, r);
/* Figure out the type of the resource. Note that we have a PARSE step
which is separate from a PREP step. This is because the PARSE can
map multiple URLs to the same resource type. The PREP operates on
the type of the resource. */
/* skip over the leading "/" in the relative URI */
if (dav_svn_parse_uri(comb, relative + 1, label, use_checked_in))
goto malformed_URI;
#ifdef SVN_DEBUG
if (comb->res.type == DAV_RESOURCE_TYPE_UNKNOWN)
{
/* Unknown URI. Return NULL to indicate "no resource" */
DBG0("DESIGN FAILURE: should not be UNKNOWN at this point");
*resource = NULL;
return NULL;
}
#endif
/* prepare the resource for operation */
if ((err = dav_svn_prep_resource(comb)) != NULL)
return err;
/* a GET request for a REGULAR collection resource MUST have a trailing
slash. Redirect to include one if it does not. */
if (comb->res.collection && comb->res.type == DAV_RESOURCE_TYPE_REGULAR
&& !had_slash && r->method_number == M_GET)
{
/* note that we drop r->args. we don't deal with them anyways */
const char *new_path = apr_pstrcat(r->pool,
ap_escape_uri(r->pool, r->uri),
"/",
NULL);
apr_table_setn(r->headers_out, "Location",
ap_construct_url(r->pool, new_path, r));
return dav_new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0,
"Requests for a collection must have a "
"trailing slash on the URI.");
}
*resource = &comb->res;
return NULL;
malformed_URI:
/* A malformed URI error occurs when a URI indicates the "special" area,
yet it has an improper construction. Generally, this is because some
doofus typed it in manually or has a buggy client. */
/* ### pick something other than HTTP_INTERNAL_SERVER_ERROR */
/* ### are SVN_ERR_APMOD codes within the right numeric space? */
return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR,
SVN_ERR_APMOD_MALFORMED_URI,
"The URI indicated a resource within Subversion's "
"special resource area, but does not exist. This is "
"generally caused by a problem in the client "
"software.");
}
static dav_error * dav_svn_get_parent_resource(const dav_resource *resource,
dav_resource **parent_resource)
{
svn_stringbuf_t *path = resource->info->uri_path;
/* the root of the repository has no parent */
if (path->len == 1 && *path->data == '/')
{
*parent_resource = NULL;
return NULL;
}
switch (resource->type)
{
case DAV_RESOURCE_TYPE_WORKING:
case DAV_RESOURCE_TYPE_REGULAR:
/* The "/" occurring within the URL of working resources is part of
its identifier; it does not establish parent resource relationships.
All working resources have the same parent, which is:
http://host.name/path2repos/$svn/wrk/
*/
*parent_resource =
dav_svn_create_private_resource(resource,
DAV_SVN_RESTYPE_WRK_COLLECTION);
break;
case DAV_RESOURCE_TYPE_ACTIVITY:
*parent_resource =
dav_svn_create_private_resource(resource,
DAV_SVN_RESTYPE_ACT_COLLECTION);
break;
default:
/* ### needs more work. need parents for other resource types
###
### return an error so we can easily identify the cases where
### we've called this function unexpectedly. */
return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
apr_psprintf(resource->pool,
"get_parent_resource was called for "
"%s (type %d)",
resource->uri, resource->type));
break;
}
return NULL;
}
/* does RES2 live in the same repository as RES1? */
static int is_our_resource(const dav_resource *res1,
const dav_resource *res2)
{
if (res1->hooks != res2->hooks
|| strcmp(res1->info->repos->fs_path, res2->info->repos->fs_path) != 0)
{
/* a different provider, or a different FS repository */
return 0;
}
/* coalesce the repository */
if (res1->info->repos != res2->info->repos)
{
/* ### might be nice to have a pool which we can clear to toss
### out the old, redundant repos/fs. */
/* have res2 point to res1's filesystem */
res2->info->repos = res1->info->repos;
/* res2's fs_root object is now invalid. regenerate it using
the now-shared filesystem. */
if (res2->info->root.txn_name)
{
/* reopen the txn by name */
svn_error_clear (svn_fs_open_txn(&(res2->info->root.txn),
res2->info->repos->fs,
res2->info->root.txn_name,
res2->info->repos->pool));
/* regenerate the txn "root" object */
svn_error_clear (svn_fs_txn_root(&(res2->info->root.root),
res2->info->root.txn,
res2->info->repos->pool));
}
else if (res2->info->root.rev)
{
/* default: regenerate the revision "root" object */
svn_error_clear (svn_fs_revision_root(&(res2->info->root.root),
res2->info->repos->fs,
res2->info->root.rev,
res2->info->repos->pool));
}
}
return 1;
}
static int dav_svn_is_same_resource(const dav_resource *res1,
const dav_resource *res2)
{
if (!is_our_resource(res1, res2))
return 0;
/* ### what if the same resource were reached via two URIs? */
return svn_stringbuf_compare(res1->info->uri_path, res2->info->uri_path);
}
static int dav_svn_is_parent_resource(const dav_resource *res1,
const dav_resource *res2)
{
apr_size_t len1 = strlen(res1->info->uri_path->data);
apr_size_t len2;
if (!is_our_resource(res1, res2))
return 0;
/* ### what if a resource were reached via two URIs? we ought to define
### parent/child relations for resources independent of URIs.
### i.e. define a "canonical" location for each resource, then return
### the parent based on that location. */
/* res2 is one of our resources, we can use its ->info ptr */
len2 = strlen(res2->info->uri_path->data);
return (len2 > len1
&& memcmp(res1->info->uri_path->data, res2->info->uri_path->data,
len1) == 0
&& res2->info->uri_path->data[len1] == '/');
}
dav_error * dav_svn_resource_kind (request_rec *r,
const char *uri,
const char *root_path,
svn_node_kind_t *kind)
{
dav_error *derr;
svn_error_t *serr;
dav_resource *resource;
svn_revnum_t base_rev;
svn_fs_root_t *base_rev_root;
char *saved_uri;
/* Temporarily insert the uri that the user actually wants us to
convert into a resource. Typically, this is already r->uri, so
this is usually a no-op. But sometimes the caller may pass in
the Destination: header uri.
### WHAT WE REALLY WANT here is to refactor dav_svn_get_resource,
so that some alternate interface actually allows us to specify
the URI to process, i.e. not always process r->uri.
*/
saved_uri = r->uri;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -