~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_base/lock.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* lock.c :  functions for manipulating filesystem locks.
 
2
 *
 
3
 * ====================================================================
 
4
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
5
 *
 
6
 * This software is licensed as described in the file COPYING, which
 
7
 * you should have received as part of this distribution.  The terms
 
8
 * are also available at http://subversion.tigris.org/license-1.html.
 
9
 * If newer versions of this license are posted there, you may use a
 
10
 * newer version instead, at your option.
 
11
 *
 
12
 * This software consists of voluntary contributions made by many
 
13
 * individuals.  For exact contribution history, see the revision
 
14
 * history and logs, available at http://subversion.tigris.org/.
 
15
 * ====================================================================
 
16
 */
 
17
 
 
18
 
 
19
#include "svn_pools.h"
 
20
#include "svn_error.h"
 
21
#include "svn_fs.h"
 
22
#include "svn_private_config.h"
 
23
 
 
24
#include "apr_uuid.h"
 
25
 
 
26
#include "lock.h"
 
27
#include "tree.h"
 
28
#include "err.h"
 
29
#include "bdb/locks-table.h"
 
30
#include "bdb/lock-tokens-table.h"
 
31
#include "../libsvn_fs/fs-loader.h"
 
32
 
 
33
 
 
34
/* Add LOCK and its associated LOCK_TOKEN (associated with PATH) as
 
35
   part of TRAIL. */
 
36
static svn_error_t *
 
37
add_lock_and_token (svn_lock_t *lock,
 
38
                    const char *lock_token,
 
39
                    const char *path,
 
40
                    trail_t *trail)
 
41
{
 
42
  SVN_ERR (svn_fs_bdb__lock_add (trail->fs, lock_token, lock, 
 
43
                                 trail, trail->pool));
 
44
  SVN_ERR (svn_fs_bdb__lock_token_add (trail->fs, path, lock_token, 
 
45
                                       trail, trail->pool));
 
46
  return SVN_NO_ERROR;
 
47
}
 
48
 
 
49
 
 
50
/* Delete LOCK_TOKEN and its corresponding lock (associated with PATH,
 
51
   whose KIND is supplied), as part of TRAIL. */
 
52
static svn_error_t *
 
53
delete_lock_and_token (const char *lock_token,
 
54
                       const char *path,
 
55
                       trail_t *trail)
 
56
{
 
57
  SVN_ERR (svn_fs_bdb__lock_delete (trail->fs, lock_token, 
 
58
                                    trail, trail->pool));
 
59
  SVN_ERR (svn_fs_bdb__lock_token_delete (trail->fs, path,
 
60
                                          trail, trail->pool));
 
61
  return SVN_NO_ERROR;
 
62
}
 
63
 
 
64
 
 
65
struct lock_args
 
66
{
 
67
  svn_lock_t **lock_p;
 
68
  const char *path;
 
69
  const char *token;
 
70
  const char *comment;
 
71
  svn_boolean_t is_dav_comment;
 
72
  svn_boolean_t steal_lock;
 
73
  apr_time_t expiration_date;
 
74
  svn_revnum_t current_rev;
 
75
};
 
76
 
 
77
 
 
78
static svn_error_t *
 
79
txn_body_lock (void *baton, trail_t *trail)
 
80
{
 
81
  struct lock_args *args = baton;
 
82
  svn_node_kind_t kind = svn_node_file;
 
83
  svn_lock_t *existing_lock;
 
84
  const char *fs_username;
 
85
  svn_lock_t *lock;
 
86
 
 
87
  SVN_ERR (svn_fs_base__get_path_kind (&kind, args->path, trail, trail->pool));
 
88
 
 
89
  /* Until we implement directory locks someday, we only allow locks
 
90
     on files or non-existent paths. */
 
91
  if (kind == svn_node_dir)
 
92
    return svn_fs_base__err_not_file (trail->fs, args->path);
 
93
 
 
94
  /* While our locking implementation easily supports the locking of
 
95
     nonexistent paths, we deliberately choose not to allow such madness. */
 
96
  if (kind == svn_node_none)
 
97
    return svn_error_createf (SVN_ERR_FS_NOT_FOUND, NULL,
 
98
                              "Path '%s' doesn't exist in HEAD revision",
 
99
                              args->path);
 
100
 
 
101
  /* There better be a username attached to the fs. */
 
102
  if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
 
103
    return svn_fs_base__err_no_user (trail->fs);
 
104
  else
 
105
    fs_username = trail->fs->access_ctx->username; /* for convenience */
 
106
 
 
107
  /* Is the caller attempting to lock an out-of-date working file? */
 
108
  if (SVN_IS_VALID_REVNUM(args->current_rev))
 
109
    {
 
110
      svn_revnum_t created_rev;
 
111
      SVN_ERR (svn_fs_base__get_path_created_rev (&created_rev, args->path,
 
112
                                                  trail, trail->pool));
 
113
 
 
114
      /* SVN_INVALID_REVNUM means the path doesn't exist.  So
 
115
         apparently somebody is trying to lock something in their
 
116
         working copy, but somebody else has deleted the thing
 
117
         from HEAD.  That counts as being 'out of date'. */     
 
118
      if (! SVN_IS_VALID_REVNUM(created_rev))
 
119
        return svn_error_createf (SVN_ERR_FS_OUT_OF_DATE, NULL,
 
120
                                  "Path '%s' doesn't exist in HEAD revision",
 
121
                                  args->path);
 
122
 
 
123
      if (args->current_rev < created_rev)
 
124
        return svn_error_createf (SVN_ERR_FS_OUT_OF_DATE, NULL,
 
125
                                  "Lock failed: newer version of '%s' exists",
 
126
                                  args->path);
 
127
    }
 
128
 
 
129
  /* If the caller provided a TOKEN, we *really* need to see
 
130
     if a lock already exists with that token, and if so, verify that
 
131
     the lock's path matches PATH.  Otherwise we run the risk of
 
132
     breaking the 1-to-1 mapping of lock tokens to locked paths. */
 
133
  if (args->token)
 
134
    {
 
135
      svn_lock_t *lock_from_token;
 
136
      svn_error_t *err = svn_fs_bdb__lock_get (&lock_from_token, trail->fs,
 
137
                                               args->token, trail, 
 
138
                                               trail->pool);
 
139
      if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
 
140
                  || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
 
141
        {
 
142
          svn_error_clear (err);
 
143
        }
 
144
      else
 
145
        {
 
146
          SVN_ERR (err);
 
147
          if (strcmp (lock_from_token->path, args->path) != 0)
 
148
            return svn_error_create (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
 
149
                                     "Lock failed: token refers to existing "
 
150
                                     "lock with non-matching path.");
 
151
        }
 
152
    }
 
153
 
 
154
  /* Is the path already locked?   
 
155
 
 
156
     Note that this next function call will automatically ignore any
 
157
     errors about {the path not existing as a key, the path's token
 
158
     not existing as a key, the lock just having been expired}.  And
 
159
     that's totally fine.  Any of these three errors are perfectly
 
160
     acceptable to ignore; it means that the path is now free and
 
161
     clear for locking, because the bdb funcs just cleared out both
 
162
     of the tables for us.   */
 
163
  SVN_ERR (svn_fs_base__get_lock_helper (&existing_lock, args->path, 
 
164
                                         trail, trail->pool));
 
165
  if (existing_lock)
 
166
    {
 
167
      if (! args->steal_lock)
 
168
        {
 
169
          /* Sorry, the path is already locked. */
 
170
          return svn_fs_base__err_path_already_locked (trail->fs,
 
171
                                                       existing_lock);
 
172
        }
 
173
      else
 
174
        {
 
175
          /* ARGS->steal_lock is set, so fs_username is "stealing" the
 
176
             lock from lock->owner.  Destroy the existing lock. */
 
177
          SVN_ERR (delete_lock_and_token (existing_lock->token,
 
178
                                          existing_lock->path, trail));
 
179
        }          
 
180
    }
 
181
 
 
182
  /* Create a new lock, and add it to the tables. */    
 
183
  lock = svn_lock_create (trail->pool);
 
184
  if (args->token)
 
185
    lock->token = apr_pstrdup (trail->pool, args->token);
 
186
  else
 
187
    SVN_ERR (svn_fs_base__generate_lock_token (&(lock->token), trail->fs, 
 
188
                                               trail->pool));
 
189
  lock->path = apr_pstrdup (trail->pool, args->path);
 
190
  lock->owner = apr_pstrdup (trail->pool, trail->fs->access_ctx->username);
 
191
  lock->comment = apr_pstrdup (trail->pool, args->comment);
 
192
  lock->is_dav_comment = args->is_dav_comment;
 
193
  lock->creation_date = apr_time_now();
 
194
  lock->expiration_date = args->expiration_date;
 
195
  SVN_ERR (add_lock_and_token (lock, lock->token, args->path, trail));
 
196
  *(args->lock_p) = lock;
 
197
 
 
198
  return SVN_NO_ERROR;
 
199
}
 
200
 
 
201
 
 
202
 
 
203
svn_error_t *
 
204
svn_fs_base__lock (svn_lock_t **lock,
 
205
                   svn_fs_t *fs,
 
206
                   const char *path,
 
207
                   const char *token,
 
208
                   const char *comment,
 
209
                   svn_boolean_t is_dav_comment,
 
210
                   apr_time_t expiration_date,
 
211
                   svn_revnum_t current_rev,
 
212
                   svn_boolean_t steal_lock,
 
213
                   apr_pool_t *pool)
 
214
{
 
215
  struct lock_args args;
 
216
 
 
217
  SVN_ERR (svn_fs_base__check_fs (fs));
 
218
 
 
219
  args.lock_p = lock;
 
220
  args.path = svn_fs_base__canonicalize_abspath (path, pool);
 
221
  args.token = token;
 
222
  args.comment = comment;
 
223
  args.is_dav_comment = is_dav_comment;
 
224
  args.steal_lock = steal_lock;
 
225
  args.expiration_date = expiration_date;
 
226
  args.current_rev = current_rev;
 
227
 
 
228
  return svn_fs_base__retry_txn (fs, txn_body_lock, &args, pool);
 
229
 
 
230
}
 
231
 
 
232
 
 
233
svn_error_t *
 
234
svn_fs_base__generate_lock_token (const char **token,
 
235
                                  svn_fs_t *fs,
 
236
                                  apr_pool_t *pool)
 
237
{
 
238
  /* Notice that 'fs' is currently unused.  But perhaps someday,
 
239
     we'll want to use the fs UUID + some incremented number?  */
 
240
  apr_uuid_t uuid;
 
241
  char *uuid_str = apr_pcalloc (pool, APR_UUID_FORMATTED_LENGTH + 1);
 
242
 
 
243
  apr_uuid_get (&uuid);
 
244
  apr_uuid_format (uuid_str, &uuid);
 
245
 
 
246
  /* For now, we generate a URI that matches the DAV RFC.  We could
 
247
     change this to some other URI scheme someday, if we wish. */
 
248
  *token = apr_pstrcat (pool, "opaquelocktoken:", uuid_str, NULL);
 
249
  return SVN_NO_ERROR;
 
250
}
 
251
 
 
252
 
 
253
struct unlock_args
 
254
{
 
255
  const char *path;
 
256
  const char *token;
 
257
  svn_boolean_t break_lock;
 
258
};
 
259
 
 
260
 
 
261
static svn_error_t *
 
262
txn_body_unlock (void *baton, trail_t *trail)
 
263
{
 
264
  struct unlock_args *args = baton;
 
265
  const char *lock_token;
 
266
  svn_lock_t *lock;
 
267
 
 
268
  /* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */
 
269
  SVN_ERR (svn_fs_bdb__lock_token_get (&lock_token, trail->fs, args->path,
 
270
                                       trail, trail->pool));
 
271
 
 
272
  /* If not breaking the lock, we need to do some more checking. */
 
273
  if (!args->break_lock)
 
274
    {
 
275
      /* Sanity check: The lock token must exist, and must match. */
 
276
      if (args->token == NULL)
 
277
        return svn_fs_base__err_no_lock_token (trail->fs, args->path);
 
278
      else if (strcmp (lock_token, args->token) != 0)
 
279
        return svn_fs_base__err_no_such_lock (trail->fs, args->path);
 
280
 
 
281
      SVN_ERR (svn_fs_bdb__lock_get (&lock, trail->fs, lock_token, 
 
282
                                     trail, trail->pool));
 
283
 
 
284
      /* There better be a username attached to the fs. */
 
285
      if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
 
286
        return svn_fs_base__err_no_user (trail->fs);
 
287
 
 
288
      /* And that username better be the same as the lock's owner. */
 
289
      if (strcmp(trail->fs->access_ctx->username, lock->owner) != 0)
 
290
        return svn_fs_base__err_lock_owner_mismatch
 
291
          (trail->fs,
 
292
           trail->fs->access_ctx->username,
 
293
           lock->owner);
 
294
    }
 
295
 
 
296
  /* Remove a row from each of the locking tables. */
 
297
  SVN_ERR (delete_lock_and_token (lock_token, args->path, trail));
 
298
  return SVN_NO_ERROR;
 
299
}
 
300
 
 
301
 
 
302
svn_error_t *
 
303
svn_fs_base__unlock (svn_fs_t *fs,
 
304
                     const char *path,
 
305
                     const char *token,
 
306
                     svn_boolean_t break_lock,
 
307
                     apr_pool_t *pool)
 
308
{
 
309
  struct unlock_args args;
 
310
 
 
311
  SVN_ERR (svn_fs_base__check_fs (fs));
 
312
 
 
313
  args.path = svn_fs_base__canonicalize_abspath (path, pool);
 
314
  args.token = token;
 
315
  args.break_lock = break_lock;
 
316
  return svn_fs_base__retry_txn (fs, txn_body_unlock, &args, pool);
 
317
}
 
318
 
 
319
 
 
320
svn_error_t *
 
321
svn_fs_base__get_lock_helper (svn_lock_t **lock_p,
 
322
                              const char *path,
 
323
                              trail_t *trail,
 
324
                              apr_pool_t *pool)
 
325
{
 
326
  const char *lock_token;
 
327
  svn_error_t *err;
 
328
  
 
329
  err = svn_fs_bdb__lock_token_get (&lock_token, trail->fs, path,
 
330
                                    trail, pool);
 
331
 
 
332
  /* We've deliberately decided that this function doesn't tell the
 
333
     caller *why* the lock is unavailable.  */
 
334
  if (err && ((err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK)
 
335
              || (err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
 
336
              || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
 
337
    {
 
338
      svn_error_clear (err);
 
339
      *lock_p = NULL;
 
340
      return SVN_NO_ERROR;
 
341
    }
 
342
  else
 
343
    SVN_ERR (err);
 
344
 
 
345
  /* Same situation here.  */
 
346
  err = svn_fs_bdb__lock_get (lock_p, trail->fs, lock_token, trail, pool);
 
347
  if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED)
 
348
              || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN)))
 
349
    {
 
350
      svn_error_clear (err);
 
351
      *lock_p = NULL;
 
352
      return SVN_NO_ERROR;
 
353
    }
 
354
  else
 
355
    SVN_ERR (err);
 
356
 
 
357
  return err;
 
358
}
 
359
 
 
360
 
 
361
struct lock_token_get_args
 
362
{
 
363
  svn_lock_t **lock_p;
 
364
  const char *path;
 
365
};
 
366
 
 
367
 
 
368
static svn_error_t *
 
369
txn_body_get_lock (void *baton, trail_t *trail)
 
370
{
 
371
  struct lock_token_get_args *args = baton;
 
372
  return svn_fs_base__get_lock_helper (args->lock_p, args->path, 
 
373
                                       trail, trail->pool);
 
374
}
 
375
 
 
376
 
 
377
svn_error_t *
 
378
svn_fs_base__get_lock (svn_lock_t **lock,
 
379
                       svn_fs_t *fs,
 
380
                       const char *path,
 
381
                       apr_pool_t *pool)
 
382
{
 
383
  struct lock_token_get_args args;
 
384
  svn_error_t *err;
 
385
 
 
386
  SVN_ERR (svn_fs_base__check_fs (fs));
 
387
  
 
388
  args.path = svn_fs_base__canonicalize_abspath (path, pool);
 
389
  args.lock_p = lock;  
 
390
  return svn_fs_base__retry_txn (fs, txn_body_get_lock, &args, pool);
 
391
  return err;
 
392
}
 
393
 
 
394
 
 
395
struct locks_get_args
 
396
{
 
397
  const char *path;
 
398
  svn_fs_get_locks_callback_t get_locks_func;
 
399
  void *get_locks_baton;
 
400
};
 
401
 
 
402
 
 
403
static svn_error_t *
 
404
txn_body_get_locks (void *baton, trail_t *trail)
 
405
{
 
406
  struct locks_get_args *args = baton;
 
407
  return svn_fs_bdb__locks_get (trail->fs, args->path,
 
408
                                args->get_locks_func, args->get_locks_baton,
 
409
                                trail, trail->pool);
 
410
}
 
411
 
 
412
 
 
413
svn_error_t *
 
414
svn_fs_base__get_locks (svn_fs_t *fs,
 
415
                        const char *path,
 
416
                        svn_fs_get_locks_callback_t get_locks_func,
 
417
                        void *get_locks_baton,
 
418
                        apr_pool_t *pool)
 
419
{
 
420
  struct locks_get_args args;
 
421
 
 
422
  SVN_ERR (svn_fs_base__check_fs (fs));
 
423
  args.path = svn_fs_base__canonicalize_abspath (path, pool);
 
424
  args.get_locks_func = get_locks_func;
 
425
  args.get_locks_baton = get_locks_baton;
 
426
  return svn_fs_base__retry_txn (fs, txn_body_get_locks, &args, pool);
 
427
}
 
428
 
 
429
 
 
430
 
 
431
/* Utility function:  verify that a lock can be used.
 
432
 
 
433
   If no username is attached to the FS, return SVN_ERR_FS_NO_USER.
 
434
 
 
435
   If the FS username doesn't match LOCK's owner, return
 
436
   SVN_ERR_FS_LOCK_OWNER_MISMATCH.
 
437
 
 
438
   If FS hasn't been supplied with a matching lock-token for LOCK,
 
439
   return SVN_ERR_FS_BAD_LOCK_TOKEN.
 
440
 
 
441
   Otherwise return SVN_NO_ERROR.
 
442
 */
 
443
static svn_error_t *
 
444
verify_lock (svn_fs_t *fs,
 
445
             svn_lock_t *lock,
 
446
             apr_pool_t *pool)
 
447
{
 
448
  if ((! fs->access_ctx) || (! fs->access_ctx->username))
 
449
    return svn_error_createf 
 
450
      (SVN_ERR_FS_NO_USER, NULL,
 
451
       _("Cannot verify lock on path '%s'; no username available"),
 
452
       lock->path);
 
453
  
 
454
  else if (strcmp (fs->access_ctx->username, lock->owner) != 0)
 
455
    return svn_error_createf 
 
456
      (SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL,
 
457
       _("User %s does not own lock on path '%s' (currently locked by %s)"),
 
458
       fs->access_ctx->username, lock->path, lock->owner);
 
459
 
 
460
  else if (apr_hash_get (fs->access_ctx->lock_tokens, lock->token,
 
461
                         APR_HASH_KEY_STRING) == NULL)
 
462
    return svn_error_createf 
 
463
      (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
 
464
       _("Cannot verify lock on path '%s'; no matching lock-token available"),
 
465
       lock->path);
 
466
    
 
467
  return SVN_NO_ERROR;
 
468
}
 
469
 
 
470
 
 
471
/* This implements the svn_fs_get_locks_callback_t interface, where
 
472
   BATON is just an svn_fs_t object. */
 
473
static svn_error_t *
 
474
get_locks_callback (void *baton, 
 
475
                    svn_lock_t *lock, 
 
476
                    apr_pool_t *pool)
 
477
{
 
478
  return verify_lock (baton, lock, pool);
 
479
}
 
480
 
 
481
 
 
482
/* The main routine for lock enforcement, used throughout libsvn_fs_base. */
 
483
svn_error_t *
 
484
svn_fs_base__allow_locked_operation (const char *path,
 
485
                                     svn_boolean_t recurse,
 
486
                                     trail_t *trail,
 
487
                                     apr_pool_t *pool)
 
488
{
 
489
  if (recurse)
 
490
    {
 
491
      /* Discover all locks at or below the path. */
 
492
      SVN_ERR (svn_fs_bdb__locks_get (trail->fs, path, get_locks_callback, 
 
493
                                      trail->fs, trail, pool));
 
494
    }
 
495
  else
 
496
    {
 
497
      svn_lock_t *lock;
 
498
 
 
499
      /* Discover any lock attached to the path. */
 
500
      SVN_ERR (svn_fs_base__get_lock_helper (&lock, path, trail, pool));
 
501
      if (lock)
 
502
        SVN_ERR (verify_lock (trail->fs, lock, pool));
 
503
    }
 
504
  return SVN_NO_ERROR;
 
505
}