util_lock.c revision e7a83ac97aed916dc6da9441f24d5630caad249f
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
** DAV repository-independent lock functions
*/
#include "apr.h"
#include "apr_strings.h"
#include "mod_dav.h"
#include "http_log.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_core.h"
/* ---------------------------------------------------------------
**
** Property-related lock functions
**
*/
/*
** dav_lock_get_activelock: Returns a <lockdiscovery> containing
** an activelock element for every item in the lock_discovery tree
*/
{
int count = 0;
dav_buffer work_buf = { 0 };
apr_pool_t *p = r->pool;
/* If no locks or no lock provider, there are no locks */
/*
** Since resourcediscovery is defined with (activelock)*,
** <D:activelock/> shouldn't be necessary for an empty lock.
*/
return "";
}
/*
** Note: it could be interesting to sum the lengths of the owners
** and locktokens during this loop. However, the buffer
** mechanism provides some rough padding so that we don't
** really need to have an exact size. Further, constructing
** locktoken strings could be relatively expensive.
*/
count++;
/* if a buffer was not provided, then use an internal buffer */
/* reset the length before we start appending stuff */
/* prep the buffer with a "good" size */
char tmp[100];
#if DAV_DEBUG
/* ### crap. design error */
"DESIGN ERROR: attempted to product an "
"activelock element from a partial, indirect "
"lock record. Creating an XML parsing error "
"to ease detection of this situation: <");
}
#endif
case DAV_LOCKTYPE_WRITE:
break;
default:
/* ### internal error. log something? */
break;
}
case DAV_LOCKSCOPE_EXCLUSIVE:
break;
case DAV_LOCKSCOPE_SHARED:
break;
default:
/* ### internal error. log something? */
break;
}
/*
** This contains a complete, self-contained <DAV:owner> element,
** with namespace declarations and xml:lang handling. Just drop
** it in.
*/
}
}
else {
/*
** Check if the timeout is not, for any reason, already elapsed.
** (e.g., because of a large collection, or disk under heavy load...)
*/
}
else {
}
}
"</D:timeout>" DEBUG_CR
"<D:locktoken>" DEBUG_CR
"<D:href>");
"</D:href>" DEBUG_CR
"</D:locktoken>" DEBUG_CR
"</D:activelock>" DEBUG_CR);
}
}
/*
** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a
** lockinfo XML element, then populates a dav_lock structure
** with its contents.
*/
const dav_resource *resource,
const apr_xml_doc *doc,
{
apr_pool_t *p = r->pool;
return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
"The request body contains an unexpected "
"XML root element.");
}
"Could not parse the lockinfo due to an "
"internal problem creating a lock structure.",
err);
}
return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
"An invalid Depth header was specified.");
}
/* Parse elements in the XML body */
&& child->first_child
continue;
}
}
&& child->first_child
continue;
}
const char *text;
/* quote all the values in the <DAV:owner> element */
apr_xml_quote_elem(p, child);
/*
** Store a full <DAV:owner> element with namespace definitions
** and an xml:lang definition, if applicable.
*/
continue;
}
return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, 0,
apr_psprintf(p,
"The server cannot satisfy the "
"LOCK request due to an unknown XML "
"element (\"%s\") within the "
"DAV:lockinfo element.",
}
*lock_request = lock;
return NULL;
}
/* ---------------------------------------------------------------
**
** General lock functions
**
*/
/* dav_lock_walker: Walker callback function to record indirect locks */
{
/* We don't want to set indirects on the target */
return NULL;
/* ### add a higher-level description? */
return err;
}
/* add to the multistatus response */
/*
** ### actually, this is probably wrong: we want to fail the whole
** ### LOCK process if something goes bad. maybe the caller should
** ### do a dav_unlock() (e.g. a rollback) if any errors occurred.
*/
}
return NULL;
}
/*
** dav_add_lock: Add a direct lock for resource, and indirect locks for
** all children, bounded by depth.
** ### assume request only contains one lock
*/
const dav_resource *resource,
{
/* Requested lock can be:
* Depth: 0 for null resource, existing resource, or existing collection
* Depth: Inf for existing collection
*/
/*
** 2518 9.2 says to ignore depth if target is not a collection (it has
** no internal children); pretend the client gave the correct depth.
*/
if (!resource->collection) {
depth = 0;
}
/* In all cases, first add direct entry in lockdb */
/*
** Append the new (direct) lock to the resource's existing locks.
**
** Note: this also handles locknull resources
*/
/* ### maybe add a higher-level description */
return err;
}
if (depth > 0) {
/* Walk existing collection and set indirect locks */
dav_walker_ctx ctx = { { 0 } };
ctx.r = r;
/* implies a 5xx status code occurred. screw the multistatus */
return err;
}
if (multi_status != NULL) {
/* manufacture a 207 error for the multistatus response */
*response = multi_status;
"Error(s) occurred on resources during the "
"addition of a depth lock.");
}
}
return NULL;
}
/*
** dav_lock_query: Opens the lock database. Returns a linked list of
** dav_lock structures for all direct locks on path.
*/
const dav_resource *resource,
{
/* If no lock database, return empty result */
return NULL;
}
/* ### insert a higher-level description? */
locks);
}
/* dav_unlock_walker: Walker callback function to remove indirect locks */
{
/* Before removing the lock, do any auto-checkin required */
/* ### get rid of this typecast */
!= NULL) {
return err;
}
}
/* ### should we stop or return a multistatus? looks like STOP */
/* ### add a higher-level description? */
return err;
}
return NULL;
}
/*
** dav_get_direct_resource:
**
** Find a lock on the specified resource, then return the resource the
** lock was applied to (in other words, given a (possibly) indirect lock,
** return the direct lock's corresponding resource).
**
** If the lock is an indirect lock, this usually means traversing up the
** namespace [repository] hierarchy. Note that some lock providers may be
** able to return this information with a traversal.
*/
const dav_locktoken *locktoken,
const dav_resource *resource,
const dav_resource **direct_resource)
{
}
*direct_resource = NULL;
/* Find the top of this lock-
* If r->filename's direct locks include locktoken, use r->filename.
* If r->filename's indirect locks include locktoken, retry r->filename/..
* Else fail.
*/
/*
** Find the lock specified by <locktoken> on <resource>. If it is
** an indirect lock, then partial results are okay. We're just
** trying to find the thing and know whether it is a direct or
** an indirect lock.
*/
/* ### add a higher-level desc? */
return err;
}
/* not found! that's an error. */
return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
"The specified locktoken does not correspond "
"to an existing lock on this resource.");
}
/* we found the direct lock. return this resource. */
return NULL;
}
/* the lock was indirect. move up a level in the URL namespace */
/* ### add a higher-level desc? */
return err;
}
}
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
"The lock database is corrupt. A direct lock could "
"not be found for the corresponding indirect lock "
"on this resource.");
}
/*
** dav_unlock: Removes all direct and indirect locks for r->filename,
** with given locktoken. If locktoken == null_locktoken, all locks
** are removed. If r->filename represents an indirect lock,
** we must unlock the appropriate direct lock.
** Returns OK or appropriate HTTP_* response and logs any errors.
**
** ### We've already crawled the tree to ensure everything was locked
** by us; there should be no need to incorporate a rollback.
*/
const dav_locktoken *locktoken)
{
int result;
dav_walker_ctx ctx = { { 0 } };
/* If no locks provider, then there is nothing to unlock. */
return OK;
}
* point to an indirect lock. We need resource of the _direct_
* lock in order to walk down the tree and remove the locks. So,
* If locktoken != null_locktoken,
* Walk up the resource hierarchy until we see a direct lock.
* and do a subrequest. I think walking up is faster and will work
* all the time.
* Else
* Just start removing all locks at and below resource.
*/
/* ### return err! maybe add a higher-level desc */
/* ### map result to something nice; log an error */
return HTTP_INTERNAL_SERVER_ERROR;
}
&lock_resource)) != NULL) {
/* ### add a higher-level desc? */
/* ### should return err! */
}
/* At this point, lock_resource/locktoken refers to a direct lock (key), ie
* the root of a depth > 0 lock, or locktoken is null.
*/
ctx.r = r;
/* ### fix this! */
/* ### do something with multi_status */
return result;
}
/* dav_inherit_walker: Walker callback function to inherit locks */
{
return NULL;
}
/* ### maybe add a higher-level desc */
}
/*
** dav_inherit_locks: When a resource or collection is added to a collection,
** locks on the collection should be inherited to the resource/collection.
** (MOVE, MKCOL, etc) Here we propagate any direct or indirect locks from
** parent of resource to resource and below.
*/
const dav_resource *resource,
int use_parent)
{
const dav_resource *which_resource;
dav_walker_ctx ctx = { { 0 } };
if (use_parent) {
/* ### add a higher-level desc? */
return err;
}
/* ### map result to something nice; log an error */
"Could not fetch parent resource. Unable to "
"inherit locks from the parent and apply "
"them to this resource.");
}
}
else {
}
/* ### maybe add a higher-level desc */
return err;
}
/* No locks to propagate, just return */
return NULL;
}
/*
** (1) Copy all indirect locks from our parent;
** (2) Create indirect locks for the depth infinity, direct locks
** in our parent.
**
** The append_locks call in the walker callback will do the indirect
** conversion, but we need to remove any direct locks that are NOT
** depth "infinity".
*/
else
}
}
/* <locks> has all our new locks. Walk down and propagate them. */
ctx.r = r;
/* ### do something with multi_status */
}
/* ---------------------------------------------------------------
**
** Functions dealing with lock-null resources
**
*/
/*
** dav_get_resource_state: Returns the state of the resource
** r->filename: DAV_RESOURCE_NULL, DAV_RESOURCE_LOCK_NULL,
** or DAV_RESOURCE_EXIST.
**
** Returns DAV_RESOURCE_ERROR if an error occurs.
*/
const dav_resource *resource)
{
return DAV_RESOURCE_EXISTS;
int locks_present;
/*
** A locknull resource has the form:
**
** known-dir "/" locknull-file
**
** It would be nice to look into <resource> to verify this form,
** but it does not have enough information for us. Instead, we
** can look at the path_info. If the form does not match, then
** there is no way we could have a locknull resource -- it must
** be a plain, null resource.
**
** Apache sets r->filename to known-dir/unknown-file and r->path_info
** to "" for the "proper" case. If anything is in path_info, then
** it can't be a locknull resource.
**
** ### I bet this path_info hack doesn't work for repositories.
** ### Need input from repository implementors! What kind of
** ### restructure do we need? New provider APIs?
*/
return DAV_RESOURCE_NULL;
}
/* note that we might see some expired locks... *shrug* */
}
/* ### don't log an error. return err. add higher-level desc. */
"Failed to query lock-null status for %s",
r->filename);
return DAV_RESOURCE_ERROR;
}
if (locks_present)
return DAV_RESOURCE_LOCK_NULL;
}
return DAV_RESOURCE_NULL;
}
const dav_resource *resource,
int resource_state,
int depth)
{
if (resource_state == DAV_RESOURCE_LOCK_NULL) {
/*
** The resource is no longer a locknull resource. This will remove
** the special marker.
**
** Note that a locknull resource has already inherited all of the
** locks from the parent. We do not need to call dav_inherit_locks.
**
** NOTE: some lock providers record locks for locknull resources using
** a different key than for regular resources. this will shift
** the lock information between the two key types.
*/
/*
** There are resources under this one, which are new. We must
** propagate the locks down to the new resources.
*/
if (depth > 0 &&
/* ### add a higher level desc? */
return err;
}
}
else if (resource_state == DAV_RESOURCE_NULL) {
/* ### should pass depth to dav_inherit_locks so that it can
** ### optimize for the depth==0 case.
*/
/* this resource should inherit locks from its parent */
"The resource was created successfully, but "
"there was a problem inheriting locks from "
"the parent resource.",
err);
return err;
}
}
/* else the resource already exists and its locks are correct. */
return NULL;
}