1251881Speter/* lock.c :  functions for manipulating filesystem locks.
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter
24251881Speter#include "svn_hash.h"
25251881Speter#include "svn_pools.h"
26251881Speter#include "svn_error.h"
27251881Speter#include "svn_fs.h"
28251881Speter#include "svn_private_config.h"
29251881Speter
30251881Speter#include <apr_uuid.h>
31251881Speter
32251881Speter#include "lock.h"
33251881Speter#include "tree.h"
34251881Speter#include "err.h"
35251881Speter#include "bdb/locks-table.h"
36251881Speter#include "bdb/lock-tokens-table.h"
37251881Speter#include "util/fs_skels.h"
38251881Speter#include "../libsvn_fs/fs-loader.h"
39251881Speter#include "private/svn_fs_util.h"
40251881Speter#include "private/svn_subr_private.h"
41251881Speter#include "private/svn_dep_compat.h"
42251881Speter
43251881Speter
44251881Speter/* Add LOCK and its associated LOCK_TOKEN (associated with PATH) as
45251881Speter   part of TRAIL. */
46251881Speterstatic svn_error_t *
47251881Speteradd_lock_and_token(svn_lock_t *lock,
48251881Speter                   const char *lock_token,
49251881Speter                   const char *path,
50251881Speter                   trail_t *trail)
51251881Speter{
52251881Speter  SVN_ERR(svn_fs_bdb__lock_add(trail->fs, lock_token, lock,
53251881Speter                               trail, trail->pool));
54251881Speter  return svn_fs_bdb__lock_token_add(trail->fs, path, lock_token,
55251881Speter                                    trail, trail->pool);
56251881Speter}
57251881Speter
58251881Speter
59251881Speter/* Delete LOCK_TOKEN and its corresponding lock (associated with PATH,
60251881Speter   whose KIND is supplied), as part of TRAIL. */
61251881Speterstatic svn_error_t *
62251881Speterdelete_lock_and_token(const char *lock_token,
63251881Speter                      const char *path,
64251881Speter                      trail_t *trail)
65251881Speter{
66251881Speter  SVN_ERR(svn_fs_bdb__lock_delete(trail->fs, lock_token,
67251881Speter                                  trail, trail->pool));
68251881Speter  return svn_fs_bdb__lock_token_delete(trail->fs, path,
69251881Speter                                       trail, trail->pool);
70251881Speter}
71251881Speter
72251881Speter
73251881Speterstruct lock_args
74251881Speter{
75251881Speter  svn_lock_t **lock_p;
76251881Speter  const char *path;
77251881Speter  const char *token;
78251881Speter  const char *comment;
79251881Speter  svn_boolean_t is_dav_comment;
80251881Speter  svn_boolean_t steal_lock;
81251881Speter  apr_time_t expiration_date;
82251881Speter  svn_revnum_t current_rev;
83251881Speter};
84251881Speter
85251881Speter
86251881Speterstatic svn_error_t *
87251881Spetertxn_body_lock(void *baton, trail_t *trail)
88251881Speter{
89251881Speter  struct lock_args *args = baton;
90251881Speter  svn_node_kind_t kind = svn_node_file;
91251881Speter  svn_lock_t *existing_lock;
92251881Speter  svn_lock_t *lock;
93251881Speter
94251881Speter  SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool));
95251881Speter
96251881Speter  /* Until we implement directory locks someday, we only allow locks
97251881Speter     on files or non-existent paths. */
98251881Speter  if (kind == svn_node_dir)
99251881Speter    return SVN_FS__ERR_NOT_FILE(trail->fs, args->path);
100251881Speter
101251881Speter  /* While our locking implementation easily supports the locking of
102251881Speter     nonexistent paths, we deliberately choose not to allow such madness. */
103251881Speter  if (kind == svn_node_none)
104251881Speter    {
105251881Speter      if (SVN_IS_VALID_REVNUM(args->current_rev))
106251881Speter        return svn_error_createf(
107251881Speter          SVN_ERR_FS_OUT_OF_DATE, NULL,
108251881Speter          _("Path '%s' doesn't exist in HEAD revision"),
109251881Speter          args->path);
110251881Speter      else
111251881Speter        return svn_error_createf(
112251881Speter          SVN_ERR_FS_NOT_FOUND, NULL,
113251881Speter          _("Path '%s' doesn't exist in HEAD revision"),
114251881Speter          args->path);
115251881Speter    }
116251881Speter
117251881Speter  /* There better be a username attached to the fs. */
118251881Speter  if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
119251881Speter    return SVN_FS__ERR_NO_USER(trail->fs);
120251881Speter
121251881Speter  /* Is the caller attempting to lock an out-of-date working file? */
122251881Speter  if (SVN_IS_VALID_REVNUM(args->current_rev))
123251881Speter    {
124251881Speter      svn_revnum_t created_rev;
125251881Speter      SVN_ERR(svn_fs_base__get_path_created_rev(&created_rev, args->path,
126251881Speter                                                trail, trail->pool));
127251881Speter
128251881Speter      /* SVN_INVALID_REVNUM means the path doesn't exist.  So
129251881Speter         apparently somebody is trying to lock something in their
130251881Speter         working copy, but somebody else has deleted the thing
131251881Speter         from HEAD.  That counts as being 'out of date'. */
132251881Speter      if (! SVN_IS_VALID_REVNUM(created_rev))
133251881Speter        return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
134251881Speter                                 "Path '%s' doesn't exist in HEAD revision",
135251881Speter                                 args->path);
136251881Speter
137251881Speter      if (args->current_rev < created_rev)
138251881Speter        return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
139251881Speter                                 "Lock failed: newer version of '%s' exists",
140251881Speter                                 args->path);
141251881Speter    }
142251881Speter
143251881Speter  /* If the caller provided a TOKEN, we *really* need to see
144251881Speter     if a lock already exists with that token, and if so, verify that
145251881Speter     the lock's path matches PATH.  Otherwise we run the risk of
146251881Speter     breaking the 1-to-1 mapping of lock tokens to locked paths. */
147251881Speter  if (args->token)
148251881Speter    {
149251881Speter      svn_lock_t *lock_from_token;
150251881Speter      svn_error_t *err = svn_fs_bdb__lock_get(&lock_from_token, trail->fs,
151251881Speter                                              args->token, trail,
152251881Speter                                              trail->pool);
153251881Speter      if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
154251881Speter                  || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
155251881Speter        {
156251881Speter          svn_error_clear(err);
157251881Speter        }
158251881Speter      else
159251881Speter        {
160251881Speter          SVN_ERR(err);
161251881Speter          if (strcmp(lock_from_token->path, args->path) != 0)
162251881Speter            return svn_error_create(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
163251881Speter                                    "Lock failed: token refers to existing "
164251881Speter                                    "lock with non-matching path.");
165251881Speter        }
166251881Speter    }
167251881Speter
168251881Speter  /* Is the path already locked?
169251881Speter
170251881Speter     Note that this next function call will automatically ignore any
171251881Speter     errors about {the path not existing as a key, the path's token
172251881Speter     not existing as a key, the lock just having been expired}.  And
173251881Speter     that's totally fine.  Any of these three errors are perfectly
174251881Speter     acceptable to ignore; it means that the path is now free and
175251881Speter     clear for locking, because the bdb funcs just cleared out both
176251881Speter     of the tables for us.   */
177251881Speter  SVN_ERR(svn_fs_base__get_lock_helper(&existing_lock, args->path,
178251881Speter                                       trail, trail->pool));
179251881Speter  if (existing_lock)
180251881Speter    {
181251881Speter      if (! args->steal_lock)
182251881Speter        {
183251881Speter          /* Sorry, the path is already locked. */
184251881Speter          return SVN_FS__ERR_PATH_ALREADY_LOCKED(trail->fs,
185251881Speter                                                 existing_lock);
186251881Speter        }
187251881Speter      else
188251881Speter        {
189251881Speter          /* ARGS->steal_lock is set, so fs_username is "stealing" the
190251881Speter             lock from lock->owner.  Destroy the existing lock. */
191251881Speter          SVN_ERR(delete_lock_and_token(existing_lock->token,
192251881Speter                                        existing_lock->path, trail));
193251881Speter        }
194251881Speter    }
195251881Speter
196251881Speter  /* Create a new lock, and add it to the tables. */
197251881Speter  lock = svn_lock_create(trail->pool);
198251881Speter  if (args->token)
199251881Speter    lock->token = apr_pstrdup(trail->pool, args->token);
200251881Speter  else
201251881Speter    SVN_ERR(svn_fs_base__generate_lock_token(&(lock->token), trail->fs,
202251881Speter                                             trail->pool));
203251881Speter  lock->path = apr_pstrdup(trail->pool, args->path);
204251881Speter  lock->owner = apr_pstrdup(trail->pool, trail->fs->access_ctx->username);
205251881Speter  lock->comment = apr_pstrdup(trail->pool, args->comment);
206251881Speter  lock->is_dav_comment = args->is_dav_comment;
207251881Speter  lock->creation_date = apr_time_now();
208251881Speter  lock->expiration_date = args->expiration_date;
209251881Speter  SVN_ERR(add_lock_and_token(lock, lock->token, args->path, trail));
210251881Speter  *(args->lock_p) = lock;
211251881Speter
212251881Speter  return SVN_NO_ERROR;
213251881Speter}
214251881Speter
215251881Speter
216251881Speter
217251881Spetersvn_error_t *
218251881Spetersvn_fs_base__lock(svn_lock_t **lock,
219251881Speter                  svn_fs_t *fs,
220251881Speter                  const char *path,
221251881Speter                  const char *token,
222251881Speter                  const char *comment,
223251881Speter                  svn_boolean_t is_dav_comment,
224251881Speter                  apr_time_t expiration_date,
225251881Speter                  svn_revnum_t current_rev,
226251881Speter                  svn_boolean_t steal_lock,
227251881Speter                  apr_pool_t *pool)
228251881Speter{
229251881Speter  struct lock_args args;
230251881Speter
231251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
232251881Speter
233251881Speter  args.lock_p = lock;
234251881Speter  args.path = svn_fs__canonicalize_abspath(path, pool);
235251881Speter  args.token = token;
236251881Speter  args.comment = comment;
237251881Speter  args.is_dav_comment = is_dav_comment;
238251881Speter  args.steal_lock = steal_lock;
239251881Speter  args.expiration_date = expiration_date;
240251881Speter  args.current_rev = current_rev;
241251881Speter
242251881Speter  return svn_fs_base__retry_txn(fs, txn_body_lock, &args, FALSE, pool);
243251881Speter}
244251881Speter
245251881Speter
246251881Spetersvn_error_t *
247251881Spetersvn_fs_base__generate_lock_token(const char **token,
248251881Speter                                 svn_fs_t *fs,
249251881Speter                                 apr_pool_t *pool)
250251881Speter{
251251881Speter  /* Notice that 'fs' is currently unused.  But perhaps someday, we'll
252251881Speter     want to use the fs UUID + some incremented number?  For now, we
253251881Speter     generate a URI that matches the DAV RFC.  We could change this to
254251881Speter     some other URI scheme someday, if we wish. */
255251881Speter  *token = apr_pstrcat(pool, "opaquelocktoken:",
256251881Speter                       svn_uuid_generate(pool), (char *)NULL);
257251881Speter  return SVN_NO_ERROR;
258251881Speter}
259251881Speter
260251881Speter
261251881Speterstruct unlock_args
262251881Speter{
263251881Speter  const char *path;
264251881Speter  const char *token;
265251881Speter  svn_boolean_t break_lock;
266251881Speter};
267251881Speter
268251881Speter
269251881Speterstatic svn_error_t *
270251881Spetertxn_body_unlock(void *baton, trail_t *trail)
271251881Speter{
272251881Speter  struct unlock_args *args = baton;
273251881Speter  const char *lock_token;
274251881Speter  svn_lock_t *lock;
275251881Speter
276251881Speter  /* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */
277251881Speter  SVN_ERR(svn_fs_bdb__lock_token_get(&lock_token, trail->fs, args->path,
278251881Speter                                     trail, trail->pool));
279251881Speter
280251881Speter  /* If not breaking the lock, we need to do some more checking. */
281251881Speter  if (!args->break_lock)
282251881Speter    {
283251881Speter      /* Sanity check: The lock token must exist, and must match. */
284251881Speter      if (args->token == NULL)
285251881Speter        return svn_fs_base__err_no_lock_token(trail->fs, args->path);
286251881Speter      else if (strcmp(lock_token, args->token) != 0)
287251881Speter        return SVN_FS__ERR_NO_SUCH_LOCK(trail->fs, args->path);
288251881Speter
289251881Speter      SVN_ERR(svn_fs_bdb__lock_get(&lock, trail->fs, lock_token,
290251881Speter                                   trail, trail->pool));
291251881Speter
292251881Speter      /* There better be a username attached to the fs. */
293251881Speter      if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
294251881Speter        return SVN_FS__ERR_NO_USER(trail->fs);
295251881Speter
296251881Speter      /* And that username better be the same as the lock's owner. */
297251881Speter      if (strcmp(trail->fs->access_ctx->username, lock->owner) != 0)
298251881Speter        return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
299251881Speter           trail->fs,
300251881Speter           trail->fs->access_ctx->username,
301251881Speter           lock->owner);
302251881Speter    }
303251881Speter
304251881Speter  /* Remove a row from each of the locking tables. */
305251881Speter  return delete_lock_and_token(lock_token, args->path, trail);
306251881Speter}
307251881Speter
308251881Speter
309251881Spetersvn_error_t *
310251881Spetersvn_fs_base__unlock(svn_fs_t *fs,
311251881Speter                    const char *path,
312251881Speter                    const char *token,
313251881Speter                    svn_boolean_t break_lock,
314251881Speter                    apr_pool_t *pool)
315251881Speter{
316251881Speter  struct unlock_args args;
317251881Speter
318251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
319251881Speter
320251881Speter  args.path = svn_fs__canonicalize_abspath(path, pool);
321251881Speter  args.token = token;
322251881Speter  args.break_lock = break_lock;
323251881Speter  return svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, pool);
324251881Speter}
325251881Speter
326251881Speter
327251881Spetersvn_error_t *
328251881Spetersvn_fs_base__get_lock_helper(svn_lock_t **lock_p,
329251881Speter                             const char *path,
330251881Speter                             trail_t *trail,
331251881Speter                             apr_pool_t *pool)
332251881Speter{
333251881Speter  const char *lock_token;
334251881Speter  svn_error_t *err;
335251881Speter
336251881Speter  err = svn_fs_bdb__lock_token_get(&lock_token, trail->fs, path,
337251881Speter                                   trail, pool);
338251881Speter
339251881Speter  /* We've deliberately decided that this function doesn't tell the
340251881Speter     caller *why* the lock is unavailable.  */
341251881Speter  if (err && ((err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK)
342251881Speter              || (err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
343251881Speter              || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
344251881Speter    {
345251881Speter      svn_error_clear(err);
346251881Speter      *lock_p = NULL;
347251881Speter      return SVN_NO_ERROR;
348251881Speter    }
349251881Speter  else
350251881Speter    SVN_ERR(err);
351251881Speter
352251881Speter  /* Same situation here.  */
353251881Speter  err = svn_fs_bdb__lock_get(lock_p, trail->fs, lock_token, trail, pool);
354251881Speter  if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
355251881Speter              || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
356251881Speter    {
357251881Speter      svn_error_clear(err);
358251881Speter      *lock_p = NULL;
359251881Speter      return SVN_NO_ERROR;
360251881Speter    }
361251881Speter  else
362251881Speter    SVN_ERR(err);
363251881Speter
364251881Speter  return svn_error_trace(err);
365251881Speter}
366251881Speter
367251881Speter
368251881Speterstruct lock_token_get_args
369251881Speter{
370251881Speter  svn_lock_t **lock_p;
371251881Speter  const char *path;
372251881Speter};
373251881Speter
374251881Speter
375251881Speterstatic svn_error_t *
376251881Spetertxn_body_get_lock(void *baton, trail_t *trail)
377251881Speter{
378251881Speter  struct lock_token_get_args *args = baton;
379251881Speter  return svn_fs_base__get_lock_helper(args->lock_p, args->path,
380251881Speter                                      trail, trail->pool);
381251881Speter}
382251881Speter
383251881Speter
384251881Spetersvn_error_t *
385251881Spetersvn_fs_base__get_lock(svn_lock_t **lock,
386251881Speter                      svn_fs_t *fs,
387251881Speter                      const char *path,
388251881Speter                      apr_pool_t *pool)
389251881Speter{
390251881Speter  struct lock_token_get_args args;
391251881Speter
392251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
393251881Speter
394251881Speter  args.path = svn_fs__canonicalize_abspath(path, pool);
395251881Speter  args.lock_p = lock;
396251881Speter  return svn_fs_base__retry_txn(fs, txn_body_get_lock, &args, FALSE, pool);
397251881Speter}
398251881Speter
399251881Speter/* Implements `svn_fs_get_locks_callback_t', spooling lock information
400251881Speter   to a stream as the filesystem provides it.  BATON is an 'svn_stream_t *'
401251881Speter   object pointing to the stream.  We'll write the spool stream with a
402251881Speter   format like so:
403251881Speter
404251881Speter      SKEL1_LEN "\n" SKEL1 "\n" SKEL2_LEN "\n" SKEL2 "\n" ...
405251881Speter
406251881Speter   where each skel is a lock skel (the same format we use to store
407251881Speter   locks in the `locks' table). */
408251881Speterstatic svn_error_t *
409251881Speterspool_locks_info(void *baton,
410251881Speter                 svn_lock_t *lock,
411251881Speter                 apr_pool_t *pool)
412251881Speter{
413251881Speter  svn_skel_t *lock_skel;
414251881Speter  svn_stream_t *stream = baton;
415251881Speter  const char *skel_len;
416251881Speter  svn_stringbuf_t *skel_buf;
417251881Speter  apr_size_t len;
418251881Speter
419251881Speter  SVN_ERR(svn_fs_base__unparse_lock_skel(&lock_skel, lock, pool));
420251881Speter  skel_buf = svn_skel__unparse(lock_skel, pool);
421251881Speter  skel_len = apr_psprintf(pool, "%" APR_SIZE_T_FMT "\n", skel_buf->len);
422251881Speter  len = strlen(skel_len);
423251881Speter  SVN_ERR(svn_stream_write(stream, skel_len, &len));
424251881Speter  len = skel_buf->len;
425251881Speter  SVN_ERR(svn_stream_write(stream, skel_buf->data, &len));
426251881Speter  len = 1;
427251881Speter  return svn_stream_write(stream, "\n", &len);
428251881Speter}
429251881Speter
430251881Speter
431251881Speterstruct locks_get_args
432251881Speter{
433251881Speter  const char *path;
434251881Speter  svn_depth_t depth;
435251881Speter  svn_stream_t *stream;
436251881Speter};
437251881Speter
438251881Speter
439251881Speterstatic svn_error_t *
440251881Spetertxn_body_get_locks(void *baton, trail_t *trail)
441251881Speter{
442251881Speter  struct locks_get_args *args = baton;
443251881Speter  return svn_fs_bdb__locks_get(trail->fs, args->path, args->depth,
444251881Speter                               spool_locks_info, args->stream,
445251881Speter                               trail, trail->pool);
446251881Speter}
447251881Speter
448251881Speter
449251881Spetersvn_error_t *
450251881Spetersvn_fs_base__get_locks(svn_fs_t *fs,
451251881Speter                       const char *path,
452251881Speter                       svn_depth_t depth,
453251881Speter                       svn_fs_get_locks_callback_t get_locks_func,
454251881Speter                       void *get_locks_baton,
455251881Speter                       apr_pool_t *pool)
456251881Speter{
457251881Speter  struct locks_get_args args;
458251881Speter  svn_stream_t *stream;
459251881Speter  svn_stringbuf_t *buf;
460251881Speter  svn_boolean_t eof;
461251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
462251881Speter
463251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
464251881Speter
465251881Speter  args.path = svn_fs__canonicalize_abspath(path, pool);
466251881Speter  args.depth = depth;
467251881Speter  /* Enough for 100+ locks if the comments are small. */
468251881Speter  args.stream = svn_stream__from_spillbuf(4 * 1024  /* blocksize */,
469251881Speter                                          64 * 1024 /* maxsize */,
470251881Speter                                          pool);
471251881Speter  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool));
472251881Speter
473251881Speter  /* Read the stream calling GET_LOCKS_FUNC(). */
474251881Speter  stream = args.stream;
475251881Speter
476251881Speter  while (1)
477251881Speter    {
478251881Speter      apr_size_t len, skel_len;
479251881Speter      char c, *skel_buf;
480251881Speter      svn_skel_t *lock_skel;
481251881Speter      svn_lock_t *lock;
482251881Speter      apr_uint64_t ui64;
483251881Speter      svn_error_t *err;
484251881Speter
485251881Speter      svn_pool_clear(iterpool);
486251881Speter
487251881Speter      /* Read a skel length line and parse it for the skel's length.  */
488251881Speter      SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool));
489251881Speter      if (eof)
490251881Speter        break;
491251881Speter      err = svn_cstring_strtoui64(&ui64, buf->data, 0, APR_SIZE_MAX, 10);
492251881Speter      if (err)
493251881Speter        return svn_error_create(SVN_ERR_MALFORMED_FILE, err, NULL);
494251881Speter      skel_len = (apr_size_t)ui64;
495251881Speter
496251881Speter      /* Now read that much into a buffer. */
497251881Speter      skel_buf = apr_palloc(pool, skel_len + 1);
498251881Speter      SVN_ERR(svn_stream_read(stream, skel_buf, &skel_len));
499251881Speter      skel_buf[skel_len] = '\0';
500251881Speter
501251881Speter      /* Read the extra newline that follows the skel. */
502251881Speter      len = 1;
503251881Speter      SVN_ERR(svn_stream_read(stream, &c, &len));
504251881Speter      if (c != '\n')
505251881Speter        return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL);
506251881Speter
507251881Speter      /* Parse the skel into a lock, and notify the caller. */
508251881Speter      lock_skel = svn_skel__parse(skel_buf, skel_len, iterpool);
509251881Speter      SVN_ERR(svn_fs_base__parse_lock_skel(&lock, lock_skel, iterpool));
510251881Speter      SVN_ERR(get_locks_func(get_locks_baton, lock, iterpool));
511251881Speter    }
512251881Speter
513251881Speter  SVN_ERR(svn_stream_close(stream));
514251881Speter  svn_pool_destroy(iterpool);
515251881Speter  return SVN_NO_ERROR;
516251881Speter}
517251881Speter
518251881Speter
519251881Speter
520251881Speter/* Utility function:  verify that a lock can be used.
521251881Speter
522251881Speter   If no username is attached to the FS, return SVN_ERR_FS_NO_USER.
523251881Speter
524251881Speter   If the FS username doesn't match LOCK's owner, return
525251881Speter   SVN_ERR_FS_LOCK_OWNER_MISMATCH.
526251881Speter
527251881Speter   If FS hasn't been supplied with a matching lock-token for LOCK,
528251881Speter   return SVN_ERR_FS_BAD_LOCK_TOKEN.
529251881Speter
530251881Speter   Otherwise return SVN_NO_ERROR.
531251881Speter */
532251881Speterstatic svn_error_t *
533251881Speterverify_lock(svn_fs_t *fs,
534251881Speter            svn_lock_t *lock,
535251881Speter            apr_pool_t *pool)
536251881Speter{
537251881Speter  if ((! fs->access_ctx) || (! fs->access_ctx->username))
538251881Speter    return svn_error_createf
539251881Speter      (SVN_ERR_FS_NO_USER, NULL,
540251881Speter       _("Cannot verify lock on path '%s'; no username available"),
541251881Speter       lock->path);
542251881Speter
543251881Speter  else if (strcmp(fs->access_ctx->username, lock->owner) != 0)
544251881Speter    return svn_error_createf
545251881Speter      (SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL,
546251881Speter       _("User '%s' does not own lock on path '%s' (currently locked by '%s')"),
547251881Speter       fs->access_ctx->username, lock->path, lock->owner);
548251881Speter
549251881Speter  else if (svn_hash_gets(fs->access_ctx->lock_tokens, lock->token) == NULL)
550251881Speter    return svn_error_createf
551251881Speter      (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
552251881Speter       _("Cannot verify lock on path '%s'; no matching lock-token available"),
553251881Speter       lock->path);
554251881Speter
555251881Speter  return SVN_NO_ERROR;
556251881Speter}
557251881Speter
558251881Speter
559251881Speter/* This implements the svn_fs_get_locks_callback_t interface, where
560251881Speter   BATON is just an svn_fs_t object. */
561251881Speterstatic svn_error_t *
562251881Speterget_locks_callback(void *baton,
563251881Speter                   svn_lock_t *lock,
564251881Speter                   apr_pool_t *pool)
565251881Speter{
566251881Speter  return verify_lock(baton, lock, pool);
567251881Speter}
568251881Speter
569251881Speter
570251881Speter/* The main routine for lock enforcement, used throughout libsvn_fs_base. */
571251881Spetersvn_error_t *
572251881Spetersvn_fs_base__allow_locked_operation(const char *path,
573251881Speter                                    svn_boolean_t recurse,
574251881Speter                                    trail_t *trail,
575251881Speter                                    apr_pool_t *pool)
576251881Speter{
577251881Speter  if (recurse)
578251881Speter    {
579251881Speter      /* Discover all locks at or below the path. */
580251881Speter      SVN_ERR(svn_fs_bdb__locks_get(trail->fs, path, svn_depth_infinity,
581251881Speter                                    get_locks_callback,
582251881Speter                                    trail->fs, trail, pool));
583251881Speter    }
584251881Speter  else
585251881Speter    {
586251881Speter      svn_lock_t *lock;
587251881Speter
588251881Speter      /* Discover any lock attached to the path. */
589251881Speter      SVN_ERR(svn_fs_base__get_lock_helper(&lock, path, trail, pool));
590251881Speter      if (lock)
591251881Speter        SVN_ERR(verify_lock(trail->fs, lock, pool));
592251881Speter    }
593251881Speter  return SVN_NO_ERROR;
594251881Speter}
595