📄 lock.c
字号:
/* * lock.c: mod_dav_svn locking provider functions * * ==================================================================== * Copyright (c) 2000-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <httpd.h>#include <http_log.h>#include <mod_dav.h>#include <apr_uuid.h>#include <apr_time.h>#include "svn_fs.h"#include "svn_repos.h"#include "svn_dav.h"#include "svn_time.h"#include "svn_pools.h"#include "dav_svn.h"struct dav_lockdb_private{ /* These represent 'custom' request hearders only sent by svn clients: */ svn_boolean_t lock_steal; svn_boolean_t lock_break; svn_boolean_t keep_locks; svn_revnum_t working_revnum; /* The original request, so we can set 'custom' output headers. */ request_rec *r;};/* Helper func: convert an svn_lock_t to a dav_lock, allocated in pool. EXISTS_P indicates whether slock->path actually exists or not. If HIDE_AUTH_USER is set, then do not return the svn lock's 'owner' as dlock->auth_user. */static voidsvn_lock_to_dav_lock(dav_lock **dlock, const svn_lock_t *slock, svn_boolean_t hide_auth_user, svn_boolean_t exists_p, apr_pool_t *pool){ dav_lock *lock = apr_pcalloc(pool, sizeof(*lock)); dav_locktoken *token = apr_pcalloc(pool, sizeof(*token)); lock->rectype = DAV_LOCKREC_DIRECT; lock->scope = DAV_LOCKSCOPE_EXCLUSIVE; lock->type = DAV_LOCKTYPE_WRITE; lock->depth = 0; lock->is_locknull = exists_p; token->uuid_str = apr_pstrdup(pool, slock->token); lock->locktoken = token; /* the svn_lock_t 'comment' field maps to the 'DAV:owner' field. */ if (slock->comment) { if (! slock->is_dav_comment) { /* This comment was originally given to us by an svn client, so, we need to wrap the naked comment with <DAV:owner>, and xml-escape it for safe transport, lest we send back an invalid http response. (mod_dav won't do it for us, because it assumes that it personally created every lock in the repository.) */ lock->owner = apr_pstrcat(pool, "<D:owner xmlns:D=\"DAV:\">", apr_xml_quote_string(pool, slock->comment, 1), "</D:owner>", NULL); } else { lock->owner = apr_pstrdup(pool, slock->comment); } } else lock->owner = NULL; /* the svn_lock_t 'owner' is the actual authenticated owner of the lock, and maps to the 'auth_user' field in the mod_dav lock. */ /* (If the client ran 'svn unlock --force', then we don't want to return lock->auth_user. Otherwise mod_dav will throw an error when lock->auth_user and r->user don't match.) */ if (! hide_auth_user) lock->auth_user = apr_pstrdup(pool, slock->owner); /* This is absurd. apr_time.h has an apr_time_t->time_t func, but not the reverse?? */ if (slock->expiration_date) lock->timeout = (time_t) (slock->expiration_date / APR_USEC_PER_SEC); else lock->timeout = DAV_TIMEOUT_INFINITE; *dlock = lock;}/* Helper func for dav_lock_to_svn_lock: take an incoming "<D:owner><foo></D:owner>" tag and convert it to "<foo>". */static dav_error *unescape_xml(const char **output, const char *input, apr_pool_t *pool){ apr_xml_parser *xml_parser = apr_xml_parser_create(pool); apr_xml_doc *xml_doc; apr_status_t apr_err; const char *xml_input = apr_pstrcat (pool, "<?xml version=\"1.0\" encoding=\"utf-8\"?>", input, NULL); apr_err = apr_xml_parser_feed(xml_parser, xml_input, strlen(xml_input)); if (!apr_err) apr_err = apr_xml_parser_done(xml_parser, &xml_doc); if (apr_err) { char errbuf[1024]; (void)apr_xml_parser_geterror(xml_parser, errbuf, sizeof(errbuf)); return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, DAV_ERR_LOCK_SAVE_LOCK, errbuf); } apr_xml_to_text(pool, xml_doc->root, APR_XML_X2T_INNER, xml_doc->namespaces, NULL, output, NULL); return SVN_NO_ERROR;}/* Helper func: convert a dav_lock to an svn_lock_t, allocated in pool. */static dav_error *dav_lock_to_svn_lock(svn_lock_t **slock, const dav_lock *dlock, const char *path, dav_lockdb_private *info, svn_boolean_t is_svn_client, apr_pool_t *pool){ svn_lock_t *lock; /* Sanity checks */ if (dlock->type != DAV_LOCKTYPE_WRITE) return dav_new_error(pool, HTTP_BAD_REQUEST, DAV_ERR_LOCK_SAVE_LOCK, "Only 'write' locks are supported."); if (dlock->scope != DAV_LOCKSCOPE_EXCLUSIVE) return dav_new_error(pool, HTTP_BAD_REQUEST, DAV_ERR_LOCK_SAVE_LOCK, "Only exclusive locks are supported."); lock = svn_lock_create(pool); lock->path = apr_pstrdup(pool, path); lock->token = apr_pstrdup(pool, dlock->locktoken->uuid_str); /* DAV has no concept of lock creationdate, so assume 'now' */ lock->creation_date = apr_time_now(); if (dlock->auth_user) lock->owner = apr_pstrdup(pool, dlock->auth_user); /* We need to be very careful about stripping the <D:owner> tag away from the cdata. It's okay to do for svn clients, but not other DAV clients! */ if (dlock->owner) { if (is_svn_client) { /* mod_dav has forcibly xml-escaped the comment before handing it to us; we need to xml-unescape it (and remove the <D:owner> wrapper) when storing in the repository, so it looks reasonable to the rest of svn. */ dav_error *derr; lock->is_dav_comment = 0; /* comment is NOT xml-wrapped. */ derr = unescape_xml(&(lock->comment), dlock->owner, pool); if (derr) return derr; } else { /* The comment comes from a non-svn client; don't touch this data at all. */ lock->comment = apr_pstrdup(pool, dlock->owner); lock->is_dav_comment = 1; /* comment IS xml-wrapped. */ } } if (dlock->timeout == DAV_TIMEOUT_INFINITE) lock->expiration_date = 0; /* never expires */ else lock->expiration_date = ((apr_time_t)dlock->timeout) * APR_USEC_PER_SEC; *slock = lock; return 0;}/* Helper func: invoke mod_dav_svn's authz_read callback on PATH in HEAD revision, return the readability result in *READABLE. */static dav_error *check_readability(svn_boolean_t *readable, request_rec *r, const dav_svn_repos *repos, const char *path, apr_pool_t *pool){ svn_error_t *serr; svn_fs_root_t *headroot; svn_revnum_t headrev; dav_svn_authz_read_baton arb; arb.r = r; arb.repos = repos; serr = svn_fs_youngest_rev(&headrev, repos->fs, pool); if (serr) return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to get youngest filesystem revision.", pool); serr = svn_fs_revision_root(&headroot, repos->fs, headrev, pool); if (serr) return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to open revision root for HEAD.", pool); serr = dav_svn_authz_read(readable, headroot, path, &arb, pool); if (serr) return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to check readability of a path.", pool); return 0;}/* ---------------------------------------------------------------- *//* mod_dav locking vtable starts here: *//* Return the supportedlock property for a resource */static const char *dav_svn_get_supportedlock(const dav_resource *resource){ /* This is imitating what mod_dav_fs is doing. Note that unlike mod_dav_fs, however, we don't support "shared" locks, only "exclusive" ones. Nor do we support locks on collections. */ static const char supported[] = DEBUG_CR "<D:lockentry>" DEBUG_CR "<D:lockscope><D:exclusive/></D:lockscope>" DEBUG_CR "<D:locktype><D:write/></D:locktype>" DEBUG_CR "</D:lockentry>" DEBUG_CR; if (resource->collection) return NULL; else return supported;}/* Parse a lock token URI, returning a lock token object allocated * in the given pool. */static dav_error *dav_svn_parse_locktoken(apr_pool_t *pool, const char *char_token, dav_locktoken **locktoken_p){ dav_locktoken *token = apr_pcalloc(pool, sizeof(*token)); /* libsvn_fs already produces a valid locktoken URI. */ token->uuid_str = apr_pstrdup(pool, char_token); *locktoken_p = token; return 0;}/* Format a lock token object into a URI string, allocated in * the given pool. * * Always returns non-NULL. */static const char *dav_svn_format_locktoken(apr_pool_t *p, const dav_locktoken *locktoken){ /* libsvn_fs already produces a valid locktoken URI. */ return apr_pstrdup(p, locktoken->uuid_str);}/* Compare two lock tokens. * * Result < 0 => lt1 < lt2 * Result == 0 => lt1 == lt2 * Result > 0 => lt1 > lt2 */static intdav_svn_compare_locktoken(const dav_locktoken *lt1, const dav_locktoken *lt2){ return strcmp(lt1->uuid_str, lt2->uuid_str);}/* Open the provider's lock database. * * The provider may or may not use a "real" database for locks * (a lock could be an attribute on a resource, for example). * * The provider may choose to use the value of the DAVLockDB directive * (as returned by dav_get_lockdb_path()) to decide where to place * any storage it may need. * * The request storage pool should be associated with the lockdb, * so it can be used in subsequent operations. * * If ro != 0, only readonly operations will be performed. * If force == 0, the open can be "lazy"; no subsequent locking operations * may occur. * If force != 0, locking operations will definitely occur. */static dav_error *dav_svn_open_lockdb(request_rec *r, int ro,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -