📄 repos.c
字号:
/*
* COMB is the resource that we are constructing. Any elements that
* can be determined from the PATH may be set in COMB. However, further
* operations are not allowed (we don't want anything besides a parse
* error to occur).
*
* At a minimum, the parse function must set COMB->res.type and
* COMB->priv.repos_path.
*
* PATH does not contain a leading slash. Given "/root/$svn/xxx/the/path"
* as the request URI, the PATH variable will be "the/path"
*/
int (*parse)(dav_resource_combined *comb, const char *path,
const char *label, int use_checked_in);
/* The number of subcompenents after the !svn/xxx/... before we
reach the actual path within the repository. */
int numcomponents;
/* Boolean: are the subcomponents followed by a repos path? */
int has_repos_path;
/* The private resource type for the /$svn/xxx/ collection. */
enum dav_svn_private_restype restype;
} special_subdirs[] =
{
{ "ver", dav_svn_parse_version_uri,
1, TRUE, DAV_SVN_RESTYPE_VER_COLLECTION },
{ "his", dav_svn_parse_history_uri,
0, FALSE, DAV_SVN_RESTYPE_HIS_COLLECTION },
{ "wrk", dav_svn_parse_working_uri,
1, TRUE, DAV_SVN_RESTYPE_WRK_COLLECTION },
{ "act", dav_svn_parse_activity_uri,
1, FALSE, DAV_SVN_RESTYPE_ACT_COLLECTION },
{ "vcc", dav_svn_parse_vcc_uri,
1, FALSE, DAV_SVN_RESTYPE_VCC_COLLECTION },
{ "bc", dav_svn_parse_baseline_coll_uri,
1, TRUE, DAV_SVN_RESTYPE_BC_COLLECTION },
{ "bln", dav_svn_parse_baseline_uri,
1, FALSE, DAV_SVN_RESTYPE_BLN_COLLECTION },
{ "wbl", dav_svn_parse_wrk_baseline_uri,
2, FALSE, DAV_SVN_RESTYPE_WBL_COLLECTION },
{ NULL } /* sentinel */
};
/*
* dav_svn_parse_uri: parse the provided URI into its various bits
*
* URI will contain a path relative to our configured root URI. It should
* not have a leading "/". The root is identified by "".
*
* On output: *COMB will contain all of the information parsed out of
* the URI -- the resource type, activity ID, path, etc.
*
* Note: this function will only parse the URI. Validation of the pieces,
* opening data stores, etc, are not part of this function.
*
* TRUE is returned if a parsing error occurred. FALSE for success.
*/
static int dav_svn_parse_uri(dav_resource_combined *comb,
const char *uri,
const char *label,
int use_checked_in)
{
const char *special_uri = comb->priv.repos->special_uri;
apr_size_t len1;
apr_size_t len2;
char ch;
len1 = strlen(uri);
len2 = strlen(special_uri);
if (len1 > len2
&& ((ch = uri[len2]) == '/' || ch == '\0')
&& memcmp(uri, special_uri, len2) == 0)
{
if (ch == '\0')
{
/* URI was "/root/!svn". It exists, but has restricted usage. */
comb->res.type = DAV_RESOURCE_TYPE_PRIVATE;
comb->priv.restype = DAV_SVN_RESTYPE_ROOT_COLLECTION;
}
else
{
const struct special_defn *defn;
/* skip past the "!svn/" prefix */
uri += len2 + 1;
len1 -= len2 + 1;
for (defn = special_subdirs ; defn->name != NULL; ++defn)
{
apr_size_t len3 = strlen(defn->name);
if (len1 >= len3 && memcmp(uri, defn->name, len3) == 0)
{
if (uri[len3] == '\0')
{
/* URI was "/root/!svn/XXX". The location exists, but
has restricted usage. */
comb->res.type = DAV_RESOURCE_TYPE_PRIVATE;
/* store the resource type so that we can PROPFIND
on this collection. */
comb->priv.restype = defn->restype;
}
else if (uri[len3] == '/')
{
if ((*defn->parse)(comb, uri + len3 + 1, label,
use_checked_in))
return TRUE;
}
else
{
/* e.g. "/root/!svn/activity" (we just know "act") */
return TRUE;
}
break;
}
}
/* if completed the loop, then it is an unrecognized subdir */
if (defn->name == NULL)
return TRUE;
}
}
else
{
/* Anything under the root, but not under "!svn". These are all
version-controlled resources. */
comb->res.type = DAV_RESOURCE_TYPE_REGULAR;
comb->res.versioned = TRUE;
/* The location of these resources corresponds directly to the URI,
and we keep the leading "/". */
comb->priv.repos_path = comb->priv.uri_path->data;
}
return FALSE;
}
static dav_error * dav_svn_prep_regular(dav_resource_combined *comb)
{
apr_pool_t *pool = comb->res.pool;
dav_svn_repos *repos = comb->priv.repos;
svn_error_t *serr;
svn_node_kind_t kind;
/* A REGULAR resource might have a specific revision already (e.g. if it
is part of a baseline collection). However, if it doesn't, then we
will assume that we need the youngest revision.
### other cases besides a BC? */
if (comb->priv.root.rev == SVN_INVALID_REVNUM)
{
serr = svn_fs_youngest_rev(&comb->priv.root.rev, repos->fs, pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not determine the proper "
"revision to access",
pool);
}
}
/* get the root of the tree */
serr = svn_fs_revision_root(&comb->priv.root.root, repos->fs,
comb->priv.root.rev, pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not open the root of the "
"repository",
pool);
}
serr = svn_fs_check_path(&kind, comb->priv.root.root,
comb->priv.repos_path, pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
apr_psprintf (pool, "Error checking kind of "
"path '%s' in repository",
comb->priv.repos_path),
pool);
}
comb->res.exists = (kind == svn_node_none) ? FALSE : TRUE;
comb->res.collection = (kind == svn_node_dir) ? TRUE : FALSE;
return NULL;
}
static dav_error * dav_svn_prep_version(dav_resource_combined *comb)
{
svn_error_t *serr;
apr_pool_t *pool = comb->res.pool;
/* we are accessing the Version Resource by REV/PATH */
/* ### assert: .baselined = TRUE */
/* if we don't have a revision, then assume the youngest */
if (!SVN_IS_VALID_REVNUM(comb->priv.root.rev))
{
serr = svn_fs_youngest_rev(&comb->priv.root.rev,
comb->priv.repos->fs,
pool);
if (serr != NULL)
{
/* ### might not be a baseline */
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not fetch 'youngest' revision "
"to enable accessing the latest "
"baseline resource.",
pool);
}
}
/* ### baselines have no repos_path, and we don't need to open
### a root (yet). we just needed to ensure that we have the proper
### revision number. */
if (!comb->priv.root.root)
{
serr = svn_fs_revision_root(&comb->priv.root.root,
comb->priv.repos->fs,
comb->priv.root.rev,
pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not open a revision root.",
pool);
}
}
/* ### we should probably check that the revision is valid */
comb->res.exists = TRUE;
/* Set up the proper URI. Most likely, we arrived here via a VCC,
so the URI will be incorrect. Set the canonical form. */
/* ### assuming a baseline */
comb->res.uri = dav_svn_build_uri(comb->priv.repos,
DAV_SVN_BUILD_URI_BASELINE,
comb->priv.root.rev, NULL,
0 /* add_href */,
pool);
return NULL;
}
static dav_error * dav_svn_prep_history(dav_resource_combined *comb)
{
return NULL;
}
static dav_error * dav_svn_prep_working(dav_resource_combined *comb)
{
const char *txn_name = dav_svn_get_txn(comb->priv.repos,
comb->priv.root.activity_id);
apr_pool_t *pool = comb->res.pool;
svn_error_t *serr;
svn_node_kind_t kind;
if (txn_name == NULL)
{
/* ### HTTP_BAD_REQUEST is probably wrong */
return dav_new_error(pool, HTTP_BAD_REQUEST, 0,
"An unknown activity was specified in the URL. "
"This is generally caused by a problem in the "
"client software.");
}
comb->priv.root.txn_name = txn_name;
/* get the FS transaction, given its name */
serr = svn_fs_open_txn(&comb->priv.root.txn, comb->priv.repos->fs, txn_name,
pool);
if (serr != NULL)
{
if (serr->apr_err == SVN_ERR_FS_NO_SUCH_TRANSACTION)
{
svn_error_clear(serr);
return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0,
"An activity was specified and found, but the "
"corresponding SVN FS transaction was not "
"found.");
}
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not open the SVN FS transaction "
"corresponding to the specified activity.",
pool);
}
if (comb->res.baselined)
{
/* a Working Baseline */
/* if the transaction exists, then the working resource exists */
comb->res.exists = TRUE;
return NULL;
}
/* Set the txn author if not previously set. Protect against multi-author
* commits by verifying authenticated user associated with the current
* request is the same as the txn author.
* Note that anonymous requests are being excluded as being a change
* in author, because the commit may touch areas of the repository
* that are anonymous writeable as well as areas that are not.
*/
if (comb->priv.repos->username)
{
svn_string_t *current_author;
svn_string_t request_author;
serr = svn_fs_txn_prop(¤t_author, comb->priv.root.txn,
SVN_PROP_REVISION_AUTHOR, pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Failed to retrieve author of the SVN FS transaction "
"corresponding to the specified activity.",
pool);
}
request_author.data = comb->priv.repos->username;
request_author.len = strlen(request_author.data);
if (!current_author)
{
serr = svn_fs_change_txn_prop(comb->priv.root.txn,
SVN_PROP_REVISION_AUTHOR, &request_author, pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Failed to set the author of the SVN FS transaction "
"corresponding to the specified activity.",
pool);
}
}
else if (!svn_string_compare(current_author, &request_author))
{
return dav_new_error(pool, HTTP_NOT_IMPLEMENTED, 0,
"Multi-author commits not supported.");
}
}
/* get the root of the tree */
serr = svn_fs_txn_root(&comb->priv.root.root, comb->priv.root.txn, pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not open the (transaction) root of "
"the repository",
pool);
}
serr = svn_fs_check_path (&kind, comb->priv.root.root,
comb->priv.repos_path, pool);
if (serr != NULL)
{
return dav_svn_convert_err
(serr, HTTP_INTERNAL_SERVER_ERROR,
apr_psprintf (pool, "Error checking kind of path '%s' in repository",
comb->priv.repos_path),
pool);
}
comb->res.exists = (kind == svn_node_none) ? FALSE : TRUE;
comb->res.collection = (kind == svn_node_dir) ? TRUE : FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -