📄 repos.c
字号:
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; /* Remember if the requesting client is a Subversion client */ { const char *ua = apr_table_get(r->headers_in, "User-Agent"); if (ua && (ap_strstr_c(ua, "SVN/") == ua)) repos->is_svn_client = TRUE; } /* Retrieve/cache open repository */ repos_key = apr_pstrcat(r->pool, "mod_dav_svn:", fs_path, NULL); apr_pool_userdata_get(&userdata, repos_key, r->connection->pool); repos->repos = userdata; 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. */ return dav_svn__sanitize_error(serr, "Could not open the requested " "SVN filesystem", HTTP_INTERNAL_SERVER_ERROR, r); } /* 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); /* if an authenticated username is present, attach it to the FS */ if (r->user) { svn_fs_access_t *access_ctx; /* The fs is cached in connection->pool, but the fs access context lives in r->pool. Because the username or token could change on each request, we need to make sure that the fs points to a NULL access context after the request is gone. */ cleanup_baton = apr_pcalloc(r->pool, sizeof(*cleanup_baton)); cleanup_baton->pool = r->pool; cleanup_baton->fs = repos->fs; apr_pool_cleanup_register(r->pool, cleanup_baton, cleanup_fs_access, apr_pool_cleanup_null); /* Create an access context based on the authenticated username. */ serr = svn_fs_create_access(&access_ctx, r->user, r->pool); if (serr) { return dav_svn__sanitize_error(serr, "Could not create fs access context", HTTP_INTERNAL_SERVER_ERROR, r); } /* Attach the access context to the fs. */ serr = svn_fs_set_access(repos->fs, access_ctx); if (serr) { return dav_svn__sanitize_error(serr, "Could not attach access " "context to fs", HTTP_INTERNAL_SERVER_ERROR, r); } } /* Look for locktokens in the "If:" request header. */ err = dav_get_locktoken_list(r, <l); /* dav_get_locktoken_list claims to return a NULL list when no locktokens are present. But it actually throws this error instead! So we're deliberately trapping/ignoring it. This is a workaround for a bug in mod_dav. Remove this when the bug is fixed in mod_dav. See Subversion Issue #2248 */ if (err && (err->error_id != DAV_ERR_IF_ABSENT)) return err; /* If one or more locktokens are present in the header, push them into the filesystem access context. */ if (ltl) { svn_fs_access_t *access_ctx; dav_locktoken_list *list = ltl; serr = svn_fs_get_access(&access_ctx, repos->fs); if (serr) { return dav_svn__sanitize_error(serr, "Lock token is in request, " "but no user name", HTTP_BAD_REQUEST, r); } do { serr = svn_fs_access_add_lock_token(access_ctx, list->locktoken->uuid_str); if (serr) return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error pushing token into filesystem.", r->pool); list = list->next; } while (list); } /* 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.");}/* Helper func: return the parent of PATH, allocated in POOL. */static const char *get_parent_path(const char *path, apr_pool_t *pool){ apr_size_t len; const char *parentpath, *base_name; char *tmp = apr_pstrdup(pool, path); len = strlen(tmp); if (len > 0) { /* Remove any trailing slash; else svn_path_split() asserts. */ if (tmp[len-1] == '/') tmp[len-1] = '\0'; svn_path_split(tmp, &parentpath, &base_name, pool); return parentpath; } return path;}static dav_error * dav_svn_get_parent_resource(const dav_resource *resource, dav_resource **parent_resource){ dav_resource *parent; dav_resource_private *parentinfo; 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_REGULAR: parent = apr_pcalloc(resource->pool, sizeof(*parent)); parentinfo = apr_pcalloc(resource->pool, sizeof(*parentinfo)); parent->type = DAV_RESOURCE_TYPE_REGULAR; parent->exists = 1; parent->collection = 1; parent->versioned = 1; parent->hooks = resource->hooks; parent->pool = resource->pool; parent->uri = get_parent_path(resource->uri, resource->pool); parent->info = parentinfo; parentinfo->pool = resource->info->pool; parentinfo->uri_path = svn_stringbuf_create(get_parent_path(resource->info->uri_path->data, resource->pool), resource->pool); parentinfo->repos = resource->info->repos; parentinfo->root = resource->info->root; parentinfo->r = resource->info->r; parentinfo->svn_client_options = resource->info->svn_client_options; parentinfo->repos_path = get_parent_path(resource->info->repos_path, resource->pool); *parent_resource = parent; break; case DAV_RESOURCE_TYPE_WORKING: /* 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; r->uri = apr_pstrdup(r->pool, uri); /* parse the uri and prep the associated resource. */ derr = dav_svn_get_resource(r, root_path, /* ### I can't believe that every single parser ignores the LABEL and USE_CHECKED_IN args below!! */ "ignored_labe
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -