diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c')
-rw-r--r-- | rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c | 791 |
1 files changed, 0 insertions, 791 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c b/rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c deleted file mode 100644 index cd39f067..00000000 --- a/rubbos/app/httpd-2.0.64/modules/dav/main/util_lock.c +++ /dev/null @@ -1,791 +0,0 @@ -/* 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" - -#if APR_HAVE_STDIO_H -#include <stdio.h> /* for sprintf() */ -#endif - -#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 -*/ -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); - sprintf(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); - sprintf(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, - "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, - "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, - 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, - "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, - "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, - "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, - "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, - "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; -} |