1
1
/* lock.c : functions for manipulating filesystem locks.
3
3
* ====================================================================
4
* Copyright (c) 2000-2007 CollabNet. All rights reserved.
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.
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/.
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
15
20
* ====================================================================
88
93
/* Until we implement directory locks someday, we only allow locks
89
94
on files or non-existent paths. */
90
95
if (kind == svn_node_dir)
91
return SVN_FS__ERR_NOT_FILE(trail->fs, args->path);
96
return SVN_FS__ERR_NOT_FILE(trail->fs, args->path, trail->pool);
93
98
/* While our locking implementation easily supports the locking of
94
99
nonexistent paths, we deliberately choose not to allow such madness. */
95
100
if (kind == svn_node_none)
96
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
97
"Path '%s' doesn't exist in HEAD revision",
102
if (SVN_IS_VALID_REVNUM(args->current_rev))
103
return svn_error_createf(
104
SVN_ERR_FS_OUT_OF_DATE, NULL,
105
_("Path '%s' doesn't exist in HEAD revision"),
108
return svn_error_createf(
109
SVN_ERR_FS_NOT_FOUND, NULL,
110
_("Path '%s' doesn't exist in HEAD revision"),
100
114
/* There better be a username attached to the fs. */
101
115
if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
102
return SVN_FS__ERR_NO_USER(trail->fs);
104
fs_username = trail->fs->access_ctx->username; /* for convenience */
116
return SVN_FS__ERR_NO_USER(trail->fs, trail->pool);
106
118
/* Is the caller attempting to lock an out-of-date working file? */
107
119
if (SVN_IS_VALID_REVNUM(args->current_rev))
269
282
if (args->token == NULL)
270
283
return svn_fs_base__err_no_lock_token(trail->fs, args->path);
271
284
else if (strcmp(lock_token, args->token) != 0)
272
return SVN_FS__ERR_NO_SUCH_LOCK(trail->fs, args->path);
285
return SVN_FS__ERR_NO_SUCH_LOCK(trail->fs, args->path, trail->pool);
274
287
SVN_ERR(svn_fs_bdb__lock_get(&lock, trail->fs, lock_token,
275
288
trail, trail->pool));
277
290
/* There better be a username attached to the fs. */
278
291
if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
279
return SVN_FS__ERR_NO_USER(trail->fs);
292
return SVN_FS__ERR_NO_USER(trail->fs, trail->pool);
281
294
/* And that username better be the same as the lock's owner. */
282
295
if (strcmp(trail->fs->access_ctx->username, lock->owner) != 0)
283
return SVN_FS__ERR_LOCK_OWNER_MISMATCH
296
return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
285
298
trail->fs->access_ctx->username,
289
303
/* Remove a row from each of the locking tables. */
381
395
return svn_fs_base__retry_txn(fs, txn_body_get_lock, &args, FALSE, pool);
398
/* Implements `svn_fs_get_locks_callback_t', spooling lock information
399
to disk as the filesystem provides it. BATON is an 'apr_file_t *'
400
object pointing to open, writable spool file. We'll write the
401
spool file with a format like so:
403
SKEL1_LEN "\n" SKEL1 "\n" SKEL2_LEN "\n" SKEL2 "\n" ...
405
where each skel is a lock skel (the same format we use to store
406
locks in the `locks' table). */
408
spool_locks_info(void *baton,
412
svn_skel_t *lock_skel;
413
apr_file_t *spool_file = (apr_file_t *)baton;
414
const char *skel_len;
415
svn_stringbuf_t *skel_buf;
417
SVN_ERR(svn_fs_base__unparse_lock_skel(&lock_skel, lock, pool));
418
skel_buf = svn_skel__unparse(lock_skel, pool);
419
skel_len = apr_psprintf(pool, "%" APR_SIZE_T_FMT "\n", skel_buf->len);
420
SVN_ERR(svn_io_file_write_full(spool_file, skel_len, strlen(skel_len),
422
SVN_ERR(svn_io_file_write_full(spool_file, skel_buf->data,
423
skel_buf->len, NULL, pool));
424
return svn_io_file_write_full(spool_file, "\n", 1, NULL, pool);
385
428
struct locks_get_args
387
430
const char *path;
388
svn_fs_get_locks_callback_t get_locks_func;
389
void *get_locks_baton;
432
apr_file_t *spool_file;
394
437
txn_body_get_locks(void *baton, trail_t *trail)
396
439
struct locks_get_args *args = baton;
397
return svn_fs_bdb__locks_get(trail->fs, args->path,
398
args->get_locks_func, args->get_locks_baton,
440
return svn_fs_bdb__locks_get(trail->fs, args->path, args->depth,
441
spool_locks_info, args->spool_file,
399
442
trail, trail->pool);
404
447
svn_fs_base__get_locks(svn_fs_t *fs,
405
448
const char *path,
406
450
svn_fs_get_locks_callback_t get_locks_func,
407
451
void *get_locks_baton,
408
452
apr_pool_t *pool)
410
454
struct locks_get_args args;
455
apr_off_t offset = 0;
456
svn_stream_t *stream;
457
svn_stringbuf_t *buf;
459
apr_pool_t *iterpool = svn_pool_create(pool);
412
461
SVN_ERR(svn_fs__check_fs(fs, TRUE));
413
463
args.path = svn_fs__canonicalize_abspath(path, pool);
414
args.get_locks_func = get_locks_func;
415
args.get_locks_baton = get_locks_baton;
416
return svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool);
465
SVN_ERR(svn_io_open_uniquely_named(&(args.spool_file), NULL, NULL, NULL,
466
NULL, svn_io_file_del_on_close,
468
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool));
470
/* Rewind the spool file, then re-read it, calling GET_LOCKS_FUNC(). */
471
SVN_ERR(svn_io_file_seek(args.spool_file, APR_SET, &offset, pool));
472
stream = svn_stream_from_aprfile2(args.spool_file, FALSE, pool);
476
apr_size_t len, skel_len;
477
char c, *end, *skel_buf;
478
svn_skel_t *lock_skel;
481
svn_pool_clear(iterpool);
483
/* Read a skel length line and parse it for the skel's length. */
484
SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eof, iterpool));
487
skel_len = (size_t) strtoul(buf->data, &end, 10);
488
if (skel_len == (size_t) ULONG_MAX || *end != '\0')
489
return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL);
491
/* Now read that much into a buffer. */
492
skel_buf = apr_palloc(pool, skel_len + 1);
493
SVN_ERR(svn_stream_read(stream, skel_buf, &skel_len));
494
skel_buf[skel_len] = '\0';
496
/* Read the extra newline that follows the skel. */
498
SVN_ERR(svn_stream_read(stream, &c, &len));
500
return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL);
502
/* Parse the skel into a lock, and notify the caller. */
503
lock_skel = svn_skel__parse(skel_buf, skel_len, iterpool);
504
SVN_ERR(svn_fs_base__parse_lock_skel(&lock, lock_skel, iterpool));
505
SVN_ERR(get_locks_func(get_locks_baton, lock, iterpool));
508
SVN_ERR(svn_stream_close(stream));
509
svn_pool_destroy(iterpool);
444
538
else if (strcmp(fs->access_ctx->username, lock->owner) != 0)
445
539
return svn_error_createf
446
540
(SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL,
447
_("User %s does not own lock on path '%s' (currently locked by %s)"),
541
_("User '%s' does not own lock on path '%s' (currently locked by '%s')"),
448
542
fs->access_ctx->username, lock->path, lock->owner);
450
544
else if (apr_hash_get(fs->access_ctx->lock_tokens, lock->token,