summaryrefslogtreecommitdiffstats
path: root/modules/dav/main/util_lock.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-08 19:09:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-08 19:09:22 +0000
commit2faa747e2303ee774a4b4aace961188e950e185a (patch)
tree604e79c7481956ce48f458e3546eaf1090b3ffff /modules/dav/main/util_lock.c
parentInitial commit. (diff)
downloadapache2-2faa747e2303ee774a4b4aace961188e950e185a.tar.xz
apache2-2faa747e2303ee774a4b4aace961188e950e185a.zip
Adding upstream version 2.4.58.upstream/2.4.58
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/dav/main/util_lock.c')
-rw-r--r--modules/dav/main/util_lock.c798
1 files changed, 798 insertions, 0 deletions
diff --git a/modules/dav/main/util_lock.c b/modules/dav/main/util_lock.c
new file mode 100644
index 0000000..1b3a647
--- /dev/null
+++ b/modules/dav/main/util_lock.c
@@ -0,0 +1,798 @@
+/* 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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"
+
+APLOG_USE_MODULE(dav);
+
+/* ---------------------------------------------------------------
+**
+** Property-related lock functions
+**
+*/
+
+/*
+** dav_lock_get_activelock: Returns a <lockdiscovery> containing
+** an activelock element for every item in the lock_discovery tree
+*/
+DAV_DECLARE(const char *) dav_lock_get_activelock(request_rec *r,
+ dav_lock *lock,
+ dav_buffer *pbuf)
+{
+ dav_lock *lock_scan;
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+ 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 */
+ if (lock == NULL || hooks == NULL) {
+ /*
+ ** 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.
+ */
+ for (lock_scan = lock; lock_scan != NULL; lock_scan = lock_scan->next)
+ count++;
+
+ /* if a buffer was not provided, then use an internal buffer */
+ if (pbuf == NULL)
+ pbuf = &work_buf;
+
+ /* reset the length before we start appending stuff */
+ pbuf->cur_len = 0;
+
+ /* prep the buffer with a "good" size */
+ dav_check_bufsize(p, pbuf, count * 300);
+
+ for (; lock != NULL; lock = lock->next) {
+ char tmp[100];
+
+#if DAV_DEBUG
+ if (lock->rectype == DAV_LOCKREC_INDIRECT_PARTIAL) {
+ /* ### crap. design error */
+ dav_buffer_append(p, pbuf,
+ "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
+
+ dav_buffer_append(p, pbuf, "<D:activelock>" DEBUG_CR "<D:locktype>");
+ switch (lock->type) {
+ case DAV_LOCKTYPE_WRITE:
+ dav_buffer_append(p, pbuf, "<D:write/>");
+ break;
+ default:
+ /* ### internal error. log something? */
+ break;
+ }
+ dav_buffer_append(p, pbuf, "</D:locktype>" DEBUG_CR "<D:lockscope>");
+ switch (lock->scope) {
+ case DAV_LOCKSCOPE_EXCLUSIVE:
+ dav_buffer_append(p, pbuf, "<D:exclusive/>");
+ break;
+ case DAV_LOCKSCOPE_SHARED:
+ dav_buffer_append(p, pbuf, "<D:shared/>");
+ break;
+ default:
+ /* ### internal error. log something? */
+ break;
+ }
+ dav_buffer_append(p, pbuf, "</D:lockscope>" DEBUG_CR);
+ apr_snprintf(tmp, sizeof(tmp), "<D:depth>%s</D:depth>" DEBUG_CR,
+ lock->depth == DAV_INFINITY ? "infinity" : "0");
+ dav_buffer_append(p, pbuf, tmp);
+
+ if (lock->owner) {
+ /*
+ ** This contains a complete, self-contained <DAV:owner> element,
+ ** with namespace declarations and xml:lang handling. Just drop
+ ** it in.
+ */
+ dav_buffer_append(p, pbuf, lock->owner);
+ }
+
+ dav_buffer_append(p, pbuf, "<D:timeout>");
+ if (lock->timeout == DAV_TIMEOUT_INFINITE) {
+ dav_buffer_append(p, pbuf, "Infinite");
+ }
+ else {
+ time_t now = time(NULL);
+
+ /*
+ ** Check if the timeout is not, for any reason, already elapsed.
+ ** (e.g., because of a large collection, or disk under heavy load...)
+ */
+ if (now >= lock->timeout) {
+ dav_buffer_append(p, pbuf, "Second-0");
+ }
+ else {
+ apr_snprintf(tmp, sizeof(tmp), "Second-%lu", (long unsigned int)(lock->timeout - now));
+ dav_buffer_append(p, pbuf, tmp);
+ }
+ }
+
+ dav_buffer_append(p, pbuf,
+ "</D:timeout>" DEBUG_CR
+ "<D:locktoken>" DEBUG_CR
+ "<D:href>");
+ dav_buffer_append(p, pbuf,
+ (*hooks->format_locktoken)(p, lock->locktoken));
+ dav_buffer_append(p, pbuf,
+ "</D:href>" DEBUG_CR
+ "</D:locktoken>" DEBUG_CR
+ "</D:activelock>" DEBUG_CR);
+ }
+
+ return pbuf->buf;
+}
+
+/*
+** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a
+** lockinfo XML element, then populates a dav_lock structure
+** with its contents.
+*/
+DAV_DECLARE(dav_error *) dav_lock_parse_lockinfo(request_rec *r,
+ const dav_resource *resource,
+ dav_lockdb *lockdb,
+ const apr_xml_doc *doc,
+ dav_lock **lock_request)
+{
+ apr_pool_t *p = r->pool;
+ dav_error *err;
+ apr_xml_elem *child;
+ dav_lock *lock;
+
+ if (!dav_validate_root(doc, "lockinfo")) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
+ "The request body contains an unexpected "
+ "XML root element.");
+ }
+
+ if ((err = (*lockdb->hooks->create_lock)(lockdb, resource,
+ &lock)) != NULL) {
+ return dav_push_error(p, err->status, 0,
+ "Could not parse the lockinfo due to an "
+ "internal problem creating a lock structure.",
+ err);
+ }
+
+ lock->depth = dav_get_depth(r, DAV_INFINITY);
+ if (lock->depth == -1) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
+ "An invalid Depth header was specified.");
+ }
+ lock->timeout = dav_get_timeout(r);
+
+ /* Parse elements in the XML body */
+ for (child = doc->root->first_child; child; child = child->next) {
+ if (strcmp(child->name, "locktype") == 0
+ && child->first_child
+ && lock->type == DAV_LOCKTYPE_UNKNOWN) {
+ if (strcmp(child->first_child->name, "write") == 0) {
+ lock->type = DAV_LOCKTYPE_WRITE;
+ continue;
+ }
+ }
+ if (strcmp(child->name, "lockscope") == 0
+ && child->first_child
+ && lock->scope == DAV_LOCKSCOPE_UNKNOWN) {
+ if (strcmp(child->first_child->name, "exclusive") == 0)
+ lock->scope = DAV_LOCKSCOPE_EXCLUSIVE;
+ else if (strcmp(child->first_child->name, "shared") == 0)
+ lock->scope = DAV_LOCKSCOPE_SHARED;
+ if (lock->scope != DAV_LOCKSCOPE_UNKNOWN)
+ continue;
+ }
+
+ if (strcmp(child->name, "owner") == 0 && lock->owner == NULL) {
+ 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.
+ */
+ apr_xml_to_text(p, child, APR_XML_X2T_FULL_NS_LANG, doc->namespaces,
+ NULL, &text, NULL);
+ lock->owner = text;
+
+ 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.",
+ child->name));
+ }
+
+ *lock_request = lock;
+ return NULL;
+}
+
+/* ---------------------------------------------------------------
+**
+** General lock functions
+**
+*/
+
+/* dav_lock_walker: Walker callback function to record indirect locks */
+static dav_error * dav_lock_walker(dav_walk_resource *wres, int calltype)
+{
+ dav_walker_ctx *ctx = wres->walk_ctx;
+ dav_error *err;
+
+ /* We don't want to set indirects on the target */
+ if ((*wres->resource->hooks->is_same_resource)(wres->resource,
+ ctx->w.root))
+ return NULL;
+
+ if ((err = (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb,
+ wres->resource, 1,
+ ctx->lock)) != NULL) {
+ if (ap_is_HTTP_SERVER_ERROR(err->status)) {
+ /* ### add a higher-level description? */
+ return err;
+ }
+
+ /* add to the multistatus response */
+ dav_add_response(wres, err->status, NULL);
+
+ /*
+ ** ### 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
+*/
+DAV_DECLARE(dav_error *) dav_add_lock(request_rec *r,
+ const dav_resource *resource,
+ dav_lockdb *lockdb, dav_lock *lock,
+ dav_response **response)
+{
+ dav_error *err;
+ int depth = lock->depth;
+
+ *response = NULL;
+
+ /* 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
+ */
+ if ((err = (*lockdb->hooks->append_locks)(lockdb, resource, 0,
+ lock)) != NULL) {
+ /* ### maybe add a higher-level description */
+ return err;
+ }
+
+ if (depth > 0) {
+ /* Walk existing collection and set indirect locks */
+ dav_walker_ctx ctx = { { 0 } };
+ dav_response *multi_status;
+
+ ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH;
+ ctx.w.func = dav_lock_walker;
+ ctx.w.walk_ctx = &ctx;
+ ctx.w.pool = r->pool;
+ ctx.w.root = resource;
+ ctx.w.lockdb = lockdb;
+
+ ctx.r = r;
+ ctx.lock = lock;
+
+ err = (*resource->hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
+ if (err != NULL) {
+ /* 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;
+ return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, 0,
+ "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.
+*/
+DAV_DECLARE(dav_error*) dav_lock_query(dav_lockdb *lockdb,
+ const dav_resource *resource,
+ dav_lock **locks)
+{
+ /* If no lock database, return empty result */
+ if (lockdb == NULL) {
+ *locks = NULL;
+ return NULL;
+ }
+
+ /* ### insert a higher-level description? */
+ return (*lockdb->hooks->get_locks)(lockdb, resource,
+ DAV_GETLOCKS_RESOLVED,
+ locks);
+}
+
+/* dav_unlock_walker: Walker callback function to remove indirect locks */
+static dav_error * dav_unlock_walker(dav_walk_resource *wres, int calltype)
+{
+ dav_walker_ctx *ctx = wres->walk_ctx;
+ dav_error *err;
+
+ /* Before removing the lock, do any auto-checkin required */
+ if (wres->resource->working) {
+ /* ### get rid of this typecast */
+ if ((err = dav_auto_checkin(ctx->r, (dav_resource *) wres->resource,
+ 0 /*undo*/, 1 /*unlock*/, NULL))
+ != NULL) {
+ return err;
+ }
+ }
+
+ if ((err = (*ctx->w.lockdb->hooks->remove_lock)(ctx->w.lockdb,
+ wres->resource,
+ ctx->locktoken)) != NULL) {
+ /* ### 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.
+*/
+static dav_error * dav_get_direct_resource(apr_pool_t *p,
+ dav_lockdb *lockdb,
+ const dav_locktoken *locktoken,
+ const dav_resource *resource,
+ const dav_resource **direct_resource)
+{
+ if (lockdb->hooks->lookup_resource != NULL) {
+ return (*lockdb->hooks->lookup_resource)(lockdb, locktoken,
+ 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.
+ */
+ while (resource != NULL) {
+ dav_error *err;
+ dav_lock *lock;
+ dav_resource *parent;
+
+ /*
+ ** 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.
+ */
+ if ((err = (*lockdb->hooks->find_lock)(lockdb, resource, locktoken,
+ 1, &lock)) != NULL) {
+ /* ### add a higher-level desc? */
+ return err;
+ }
+
+ /* not found! that's an error. */
+ if (lock == NULL) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
+ "The specified locktoken does not correspond "
+ "to an existing lock on this resource.");
+ }
+
+ if (lock->rectype == DAV_LOCKREC_DIRECT) {
+ /* we found the direct lock. return this resource. */
+
+ *direct_resource = resource;
+ return NULL;
+ }
+
+ /* the lock was indirect. move up a level in the URL namespace */
+ if ((err = (*resource->hooks->get_parent_resource)(resource,
+ &parent)) != NULL) {
+ /* ### add a higher-level desc? */
+ return err;
+ }
+ resource = parent;
+ }
+
+ 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.
+*/
+DAV_DECLARE(int) dav_unlock(request_rec *r, const dav_resource *resource,
+ const dav_locktoken *locktoken)
+{
+ int result;
+ dav_lockdb *lockdb;
+ const dav_resource *lock_resource = resource;
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+ const dav_hooks_repository *repos_hooks = resource->hooks;
+ dav_walker_ctx ctx = { { 0 } };
+ dav_response *multi_status;
+ dav_error *err;
+
+ /* If no locks provider, then there is nothing to unlock. */
+ if (hooks == NULL) {
+ return OK;
+ }
+
+ /* 2518 requires the entire lock to be removed if resource/locktoken
+ * 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.
+ * Or, we could get the direct lock's db/key, pick out the URL
+ * 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.
+ */
+
+ if ((err = (*hooks->open_lockdb)(r, 0, 1, &lockdb)) != NULL) {
+ /* ### return err! maybe add a higher-level desc */
+ /* ### map result to something nice; log an error */
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (locktoken != NULL
+ && (err = dav_get_direct_resource(r->pool, lockdb,
+ locktoken, resource,
+ &lock_resource)) != NULL) {
+ /* ### add a higher-level desc? */
+ /* ### should return err! */
+ return err->status;
+ }
+
+ /* 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.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL;
+ ctx.w.func = dav_unlock_walker;
+ ctx.w.walk_ctx = &ctx;
+ ctx.w.pool = r->pool;
+ ctx.w.root = lock_resource;
+ ctx.w.lockdb = lockdb;
+
+ ctx.r = r;
+ ctx.locktoken = locktoken;
+
+ err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
+
+ /* ### fix this! */
+ /* ### do something with multi_status */
+ result = err == NULL ? OK : err->status;
+
+ (*hooks->close_lockdb)(lockdb);
+
+ return result;
+}
+
+/* dav_inherit_walker: Walker callback function to inherit locks */
+static dav_error * dav_inherit_walker(dav_walk_resource *wres, int calltype)
+{
+ dav_walker_ctx *ctx = wres->walk_ctx;
+
+ if (ctx->skip_root
+ && (*wres->resource->hooks->is_same_resource)(wres->resource,
+ ctx->w.root)) {
+ return NULL;
+ }
+
+ /* ### maybe add a higher-level desc */
+ return (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb,
+ wres->resource, 1,
+ ctx->lock);
+}
+
+/*
+** 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.
+*/
+static dav_error * dav_inherit_locks(request_rec *r, dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int use_parent)
+{
+ dav_error *err;
+ const dav_resource *which_resource;
+ dav_lock *locks;
+ dav_lock *scan;
+ dav_lock *prev;
+ dav_walker_ctx ctx = { { 0 } };
+ const dav_hooks_repository *repos_hooks = resource->hooks;
+ dav_response *multi_status;
+
+ if (use_parent) {
+ dav_resource *parent;
+ if ((err = (*repos_hooks->get_parent_resource)(resource,
+ &parent)) != NULL) {
+ /* ### add a higher-level desc? */
+ return err;
+ }
+ if (parent == NULL) {
+ /* ### map result to something nice; log an error */
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
+ "Could not fetch parent resource. Unable to "
+ "inherit locks from the parent and apply "
+ "them to this resource.");
+ }
+ which_resource = parent;
+ }
+ else {
+ which_resource = resource;
+ }
+
+ if ((err = (*lockdb->hooks->get_locks)(lockdb, which_resource,
+ DAV_GETLOCKS_PARTIAL,
+ &locks)) != NULL) {
+ /* ### maybe add a higher-level desc */
+ return err;
+ }
+
+ if (locks == NULL) {
+ /* 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".
+ */
+ for (scan = locks, prev = NULL;
+ scan != NULL;
+ prev = scan, scan = scan->next) {
+
+ if (scan->rectype == DAV_LOCKREC_DIRECT
+ && scan->depth != DAV_INFINITY) {
+
+ if (prev == NULL)
+ locks = scan->next;
+ else
+ prev->next = scan->next;
+ }
+ }
+
+ /* <locks> has all our new locks. Walk down and propagate them. */
+
+ ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL;
+ ctx.w.func = dav_inherit_walker;
+ ctx.w.walk_ctx = &ctx;
+ ctx.w.pool = r->pool;
+ ctx.w.root = resource;
+ ctx.w.lockdb = lockdb;
+
+ ctx.r = r;
+ ctx.lock = locks;
+ ctx.skip_root = !use_parent;
+
+ /* ### do something with multi_status */
+ return (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &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.
+*/
+DAV_DECLARE(int) dav_get_resource_state(request_rec *r,
+ const dav_resource *resource)
+{
+ const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
+
+ if (resource->exists)
+ return DAV_RESOURCE_EXISTS;
+
+ if (hooks != NULL) {
+ dav_error *err;
+ dav_lockdb *lockdb;
+ 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?
+ */
+ if (r->path_info != NULL && *r->path_info != '\0') {
+ return DAV_RESOURCE_NULL;
+ }
+
+ if ((err = (*hooks->open_lockdb)(r, 1, 1, &lockdb)) == NULL) {
+ /* note that we might see some expired locks... *shrug* */
+ err = (*hooks->has_locks)(lockdb, resource, &locks_present);
+ (*hooks->close_lockdb)(lockdb);
+ }
+
+ if (err != NULL) {
+ /* ### don't log an error. return err. add higher-level desc. */
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00623)
+ "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;
+}
+
+DAV_DECLARE(dav_error *) dav_notify_created(request_rec *r,
+ dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int resource_state,
+ int depth)
+{
+ dav_error *err;
+
+ 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.
+ */
+ (void)(*lockdb->hooks->remove_locknull_state)(lockdb, resource);
+
+ /*
+ ** There are resources under this one, which are new. We must
+ ** propagate the locks down to the new resources.
+ */
+ if (depth > 0 &&
+ (err = dav_inherit_locks(r, lockdb, resource, 0)) != NULL) {
+ /* ### 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 */
+ if ((err = dav_inherit_locks(r, lockdb, resource, 1)) != NULL) {
+
+ err = dav_push_error(r->pool, err->status, 0,
+ "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;
+}