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#include "svn_pools.h" 24251881Speter#include "svn_error.h" 25251881Speter#include "svn_dirent_uri.h" 26251881Speter#include "svn_path.h" 27251881Speter#include "svn_fs.h" 28251881Speter#include "svn_hash.h" 29251881Speter#include "svn_time.h" 30251881Speter#include "svn_utf.h" 31251881Speter 32251881Speter#include <apr_uuid.h> 33251881Speter#include <apr_file_io.h> 34251881Speter#include <apr_file_info.h> 35251881Speter 36251881Speter#include "lock.h" 37251881Speter#include "tree.h" 38251881Speter#include "fs_fs.h" 39289180Speter#include "util.h" 40251881Speter#include "../libsvn_fs/fs-loader.h" 41251881Speter 42251881Speter#include "private/svn_fs_util.h" 43251881Speter#include "private/svn_fspath.h" 44289180Speter#include "private/svn_sorts_private.h" 45251881Speter#include "svn_private_config.h" 46251881Speter 47251881Speter/* Names of hash keys used to store a lock for writing to disk. */ 48251881Speter#define PATH_KEY "path" 49251881Speter#define TOKEN_KEY "token" 50251881Speter#define OWNER_KEY "owner" 51251881Speter#define CREATION_DATE_KEY "creation_date" 52251881Speter#define EXPIRATION_DATE_KEY "expiration_date" 53251881Speter#define COMMENT_KEY "comment" 54251881Speter#define IS_DAV_COMMENT_KEY "is_dav_comment" 55251881Speter#define CHILDREN_KEY "children" 56251881Speter 57251881Speter/* Number of characters from the head of a digest file name used to 58251881Speter calculate a subdirectory in which to drop that file. */ 59251881Speter#define DIGEST_SUBDIR_LEN 3 60251881Speter 61251881Speter 62251881Speter 63251881Speter/*** Generic helper functions. ***/ 64251881Speter 65251881Speter/* Set *DIGEST to the MD5 hash of STR. */ 66251881Speterstatic svn_error_t * 67251881Spetermake_digest(const char **digest, 68251881Speter const char *str, 69251881Speter apr_pool_t *pool) 70251881Speter{ 71251881Speter svn_checksum_t *checksum; 72251881Speter 73251881Speter SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, strlen(str), pool)); 74251881Speter 75251881Speter *digest = svn_checksum_to_cstring_display(checksum, pool); 76251881Speter return SVN_NO_ERROR; 77251881Speter} 78251881Speter 79251881Speter 80251881Speter/* Set the value of KEY (whose size is KEY_LEN, or APR_HASH_KEY_STRING 81251881Speter if unknown) to an svn_string_t-ized version of VALUE (whose size is 82251881Speter VALUE_LEN, or APR_HASH_KEY_STRING if unknown) in HASH. The value 83251881Speter will be allocated in POOL; KEY will not be duped. If either KEY or VALUE 84251881Speter is NULL, this function will do nothing. */ 85251881Speterstatic void 86251881Speterhash_store(apr_hash_t *hash, 87251881Speter const char *key, 88251881Speter apr_ssize_t key_len, 89251881Speter const char *value, 90251881Speter apr_ssize_t value_len, 91251881Speter apr_pool_t *pool) 92251881Speter{ 93251881Speter if (! (key && value)) 94251881Speter return; 95251881Speter if (value_len == APR_HASH_KEY_STRING) 96251881Speter value_len = strlen(value); 97251881Speter apr_hash_set(hash, key, key_len, 98251881Speter svn_string_ncreate(value, value_len, pool)); 99251881Speter} 100251881Speter 101251881Speter 102251881Speter/* Fetch the value of KEY from HASH, returning only the cstring data 103251881Speter of that value (if it exists). */ 104251881Speterstatic const char * 105251881Speterhash_fetch(apr_hash_t *hash, 106289180Speter const char *key) 107251881Speter{ 108251881Speter svn_string_t *str = svn_hash_gets(hash, key); 109251881Speter return str ? str->data : NULL; 110251881Speter} 111251881Speter 112251881Speter 113251881Speter/* SVN_ERR_FS_CORRUPT: the lockfile for PATH in FS is corrupt. */ 114251881Speterstatic svn_error_t * 115251881Spetererr_corrupt_lockfile(const char *fs_path, const char *path) 116251881Speter{ 117251881Speter return 118251881Speter svn_error_createf( 119251881Speter SVN_ERR_FS_CORRUPT, 0, 120251881Speter _("Corrupt lockfile for path '%s' in filesystem '%s'"), 121251881Speter path, fs_path); 122251881Speter} 123251881Speter 124251881Speter 125251881Speter/*** Digest file handling functions. ***/ 126251881Speter 127251881Speter/* Return the path of the lock/entries file for which DIGEST is the 128251881Speter hashed repository relative path. */ 129251881Speterstatic const char * 130251881Speterdigest_path_from_digest(const char *fs_path, 131251881Speter const char *digest, 132251881Speter apr_pool_t *pool) 133251881Speter{ 134251881Speter return svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR, 135251881Speter apr_pstrmemdup(pool, digest, DIGEST_SUBDIR_LEN), 136289180Speter digest, SVN_VA_NULL); 137251881Speter} 138251881Speter 139251881Speter 140251881Speter/* Set *DIGEST_PATH to the path to the lock/entries digest file associate 141251881Speter with PATH, where PATH is the path to the lock file or lock entries file 142251881Speter in FS. */ 143251881Speterstatic svn_error_t * 144251881Speterdigest_path_from_path(const char **digest_path, 145251881Speter const char *fs_path, 146251881Speter const char *path, 147251881Speter apr_pool_t *pool) 148251881Speter{ 149251881Speter const char *digest; 150251881Speter SVN_ERR(make_digest(&digest, path, pool)); 151251881Speter *digest_path = svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR, 152251881Speter apr_pstrmemdup(pool, digest, 153251881Speter DIGEST_SUBDIR_LEN), 154289180Speter digest, SVN_VA_NULL); 155251881Speter return SVN_NO_ERROR; 156251881Speter} 157251881Speter 158251881Speter 159251881Speter/* Write to DIGEST_PATH a representation of CHILDREN (which may be 160251881Speter empty, if the versioned path in FS represented by DIGEST_PATH has 161251881Speter no children) and LOCK (which may be NULL if that versioned path is 162251881Speter lock itself locked). Set the permissions of DIGEST_PATH to those of 163251881Speter PERMS_REFERENCE. Use POOL for all allocations. 164251881Speter */ 165251881Speterstatic svn_error_t * 166251881Speterwrite_digest_file(apr_hash_t *children, 167251881Speter svn_lock_t *lock, 168251881Speter const char *fs_path, 169251881Speter const char *digest_path, 170251881Speter const char *perms_reference, 171251881Speter apr_pool_t *pool) 172251881Speter{ 173251881Speter svn_error_t *err = SVN_NO_ERROR; 174251881Speter svn_stream_t *stream; 175251881Speter apr_hash_index_t *hi; 176251881Speter apr_hash_t *hash = apr_hash_make(pool); 177251881Speter const char *tmp_path; 178251881Speter 179251881Speter SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs_path, PATH_LOCKS_DIR, 180251881Speter pool), fs_path, pool)); 181251881Speter SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_dirname(digest_path, pool), 182251881Speter fs_path, pool)); 183251881Speter 184251881Speter if (lock) 185251881Speter { 186251881Speter const char *creation_date = NULL, *expiration_date = NULL; 187251881Speter if (lock->creation_date) 188251881Speter creation_date = svn_time_to_cstring(lock->creation_date, pool); 189251881Speter if (lock->expiration_date) 190251881Speter expiration_date = svn_time_to_cstring(lock->expiration_date, pool); 191251881Speter hash_store(hash, PATH_KEY, sizeof(PATH_KEY)-1, 192251881Speter lock->path, APR_HASH_KEY_STRING, pool); 193251881Speter hash_store(hash, TOKEN_KEY, sizeof(TOKEN_KEY)-1, 194251881Speter lock->token, APR_HASH_KEY_STRING, pool); 195251881Speter hash_store(hash, OWNER_KEY, sizeof(OWNER_KEY)-1, 196251881Speter lock->owner, APR_HASH_KEY_STRING, pool); 197251881Speter hash_store(hash, COMMENT_KEY, sizeof(COMMENT_KEY)-1, 198251881Speter lock->comment, APR_HASH_KEY_STRING, pool); 199251881Speter hash_store(hash, IS_DAV_COMMENT_KEY, sizeof(IS_DAV_COMMENT_KEY)-1, 200251881Speter lock->is_dav_comment ? "1" : "0", 1, pool); 201251881Speter hash_store(hash, CREATION_DATE_KEY, sizeof(CREATION_DATE_KEY)-1, 202251881Speter creation_date, APR_HASH_KEY_STRING, pool); 203251881Speter hash_store(hash, EXPIRATION_DATE_KEY, sizeof(EXPIRATION_DATE_KEY)-1, 204251881Speter expiration_date, APR_HASH_KEY_STRING, pool); 205251881Speter } 206251881Speter if (apr_hash_count(children)) 207251881Speter { 208251881Speter svn_stringbuf_t *children_list = svn_stringbuf_create_empty(pool); 209251881Speter for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) 210251881Speter { 211251881Speter svn_stringbuf_appendbytes(children_list, 212289180Speter apr_hash_this_key(hi), 213289180Speter apr_hash_this_key_len(hi)); 214251881Speter svn_stringbuf_appendbyte(children_list, '\n'); 215251881Speter } 216251881Speter hash_store(hash, CHILDREN_KEY, sizeof(CHILDREN_KEY)-1, 217251881Speter children_list->data, children_list->len, pool); 218251881Speter } 219251881Speter 220251881Speter SVN_ERR(svn_stream_open_unique(&stream, &tmp_path, 221251881Speter svn_dirent_dirname(digest_path, pool), 222251881Speter svn_io_file_del_none, pool, pool)); 223251881Speter if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool))) 224251881Speter { 225362181Sdim err = svn_error_compose_create(err, svn_stream_close(stream)); 226251881Speter return svn_error_createf(err->apr_err, 227251881Speter err, 228251881Speter _("Cannot write lock/entries hashfile '%s'"), 229251881Speter svn_dirent_local_style(tmp_path, pool)); 230251881Speter } 231251881Speter 232251881Speter SVN_ERR(svn_stream_close(stream)); 233362181Sdim SVN_ERR(svn_io_file_rename2(tmp_path, digest_path, FALSE, pool)); 234251881Speter SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, pool)); 235251881Speter return SVN_NO_ERROR; 236251881Speter} 237251881Speter 238251881Speter 239251881Speter/* Parse the file at DIGEST_PATH, populating the lock LOCK_P in that 240251881Speter file (if it exists, and if *LOCK_P is non-NULL) and the hash of 241251881Speter CHILDREN_P (if any exist, and if *CHILDREN_P is non-NULL). Use POOL 242251881Speter for all allocations. */ 243251881Speterstatic svn_error_t * 244251881Speterread_digest_file(apr_hash_t **children_p, 245251881Speter svn_lock_t **lock_p, 246251881Speter const char *fs_path, 247251881Speter const char *digest_path, 248251881Speter apr_pool_t *pool) 249251881Speter{ 250251881Speter svn_error_t *err = SVN_NO_ERROR; 251251881Speter svn_lock_t *lock; 252251881Speter apr_hash_t *hash; 253251881Speter svn_stream_t *stream; 254251881Speter const char *val; 255289180Speter svn_node_kind_t kind; 256251881Speter 257251881Speter if (lock_p) 258251881Speter *lock_p = NULL; 259251881Speter if (children_p) 260251881Speter *children_p = apr_hash_make(pool); 261251881Speter 262289180Speter SVN_ERR(svn_io_check_path(digest_path, &kind, pool)); 263289180Speter if (kind == svn_node_none) 264289180Speter return SVN_NO_ERROR; 265251881Speter 266251881Speter /* If our caller doesn't care about anything but the presence of the 267251881Speter file... whatever. */ 268289180Speter if (kind == svn_node_file && !lock_p && !children_p) 269289180Speter return SVN_NO_ERROR; 270251881Speter 271289180Speter SVN_ERR(svn_stream_open_readonly(&stream, digest_path, pool, pool)); 272289180Speter 273251881Speter hash = apr_hash_make(pool); 274251881Speter if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) 275251881Speter { 276362181Sdim err = svn_error_compose_create(err, svn_stream_close(stream)); 277251881Speter return svn_error_createf(err->apr_err, 278251881Speter err, 279251881Speter _("Can't parse lock/entries hashfile '%s'"), 280251881Speter svn_dirent_local_style(digest_path, pool)); 281251881Speter } 282251881Speter SVN_ERR(svn_stream_close(stream)); 283251881Speter 284251881Speter /* If our caller cares, see if we have a lock path in our hash. If 285251881Speter so, we'll assume we have a lock here. */ 286289180Speter val = hash_fetch(hash, PATH_KEY); 287251881Speter if (val && lock_p) 288251881Speter { 289251881Speter const char *path = val; 290251881Speter 291251881Speter /* Create our lock and load it up. */ 292251881Speter lock = svn_lock_create(pool); 293251881Speter lock->path = path; 294251881Speter 295289180Speter if (! ((lock->token = hash_fetch(hash, TOKEN_KEY)))) 296251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 297251881Speter 298289180Speter if (! ((lock->owner = hash_fetch(hash, OWNER_KEY)))) 299251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 300251881Speter 301289180Speter if (! ((val = hash_fetch(hash, IS_DAV_COMMENT_KEY)))) 302251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 303251881Speter lock->is_dav_comment = (val[0] == '1'); 304251881Speter 305289180Speter if (! ((val = hash_fetch(hash, CREATION_DATE_KEY)))) 306251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 307251881Speter SVN_ERR(svn_time_from_cstring(&(lock->creation_date), val, pool)); 308251881Speter 309289180Speter if ((val = hash_fetch(hash, EXPIRATION_DATE_KEY))) 310251881Speter SVN_ERR(svn_time_from_cstring(&(lock->expiration_date), val, pool)); 311251881Speter 312289180Speter lock->comment = hash_fetch(hash, COMMENT_KEY); 313251881Speter 314251881Speter *lock_p = lock; 315251881Speter } 316251881Speter 317251881Speter /* If our caller cares, see if we have any children for this path. */ 318289180Speter val = hash_fetch(hash, CHILDREN_KEY); 319251881Speter if (val && children_p) 320251881Speter { 321251881Speter apr_array_header_t *kiddos = svn_cstring_split(val, "\n", FALSE, pool); 322251881Speter int i; 323251881Speter 324251881Speter for (i = 0; i < kiddos->nelts; i++) 325251881Speter { 326251881Speter svn_hash_sets(*children_p, APR_ARRAY_IDX(kiddos, i, const char *), 327251881Speter (void *)1); 328251881Speter } 329251881Speter } 330251881Speter return SVN_NO_ERROR; 331251881Speter} 332251881Speter 333251881Speter 334251881Speter 335251881Speter/*** Lock helper functions (path here are still FS paths, not on-disk 336251881Speter schema-supporting paths) ***/ 337251881Speter 338251881Speter 339251881Speter/* Write LOCK in FS to the actual OS filesystem. 340251881Speter 341251881Speter Use PERMS_REFERENCE for the permissions of any digest files. 342251881Speter */ 343251881Speterstatic svn_error_t * 344251881Speterset_lock(const char *fs_path, 345251881Speter svn_lock_t *lock, 346251881Speter const char *perms_reference, 347251881Speter apr_pool_t *pool) 348251881Speter{ 349289180Speter const char *digest_path; 350289180Speter apr_hash_t *children; 351251881Speter 352289180Speter SVN_ERR(digest_path_from_path(&digest_path, fs_path, lock->path, pool)); 353251881Speter 354289180Speter /* We could get away without reading the file as children should 355289180Speter always come back empty. */ 356289180Speter SVN_ERR(read_digest_file(&children, NULL, fs_path, digest_path, pool)); 357251881Speter 358289180Speter SVN_ERR(write_digest_file(children, lock, fs_path, digest_path, 359289180Speter perms_reference, pool)); 360251881Speter 361289180Speter return SVN_NO_ERROR; 362289180Speter} 363251881Speter 364289180Speterstatic svn_error_t * 365289180Speterdelete_lock(const char *fs_path, 366289180Speter const char *path, 367289180Speter apr_pool_t *pool) 368289180Speter{ 369289180Speter const char *digest_path; 370251881Speter 371289180Speter SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool)); 372251881Speter 373289180Speter SVN_ERR(svn_io_remove_file2(digest_path, TRUE, pool)); 374251881Speter 375251881Speter return SVN_NO_ERROR; 376251881Speter} 377251881Speter 378251881Speterstatic svn_error_t * 379289180Speteradd_to_digest(const char *fs_path, 380289180Speter apr_array_header_t *paths, 381289180Speter const char *index_path, 382289180Speter const char *perms_reference, 383289180Speter apr_pool_t *pool) 384251881Speter{ 385289180Speter const char *index_digest_path; 386289180Speter apr_hash_t *children; 387289180Speter svn_lock_t *lock; 388289180Speter int i; 389289180Speter unsigned int original_count; 390251881Speter 391289180Speter SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool)); 392251881Speter 393289180Speter SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, pool)); 394289180Speter 395289180Speter original_count = apr_hash_count(children); 396289180Speter 397289180Speter for (i = 0; i < paths->nelts; ++i) 398251881Speter { 399289180Speter const char *path = APR_ARRAY_IDX(paths, i, const char *); 400251881Speter const char *digest_path, *digest_file; 401251881Speter 402289180Speter SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool)); 403289180Speter digest_file = svn_dirent_basename(digest_path, NULL); 404289180Speter svn_hash_sets(children, digest_file, (void *)1); 405289180Speter } 406251881Speter 407289180Speter if (apr_hash_count(children) != original_count) 408289180Speter SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, 409289180Speter perms_reference, pool)); 410251881Speter 411289180Speter return SVN_NO_ERROR; 412289180Speter} 413251881Speter 414289180Speterstatic svn_error_t * 415289180Speterdelete_from_digest(const char *fs_path, 416289180Speter apr_array_header_t *paths, 417289180Speter const char *index_path, 418289180Speter const char *perms_reference, 419289180Speter apr_pool_t *pool) 420289180Speter{ 421289180Speter const char *index_digest_path; 422289180Speter apr_hash_t *children; 423289180Speter svn_lock_t *lock; 424289180Speter int i; 425251881Speter 426289180Speter SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool)); 427251881Speter 428289180Speter SVN_ERR(read_digest_file(&children, &lock, fs_path, index_digest_path, pool)); 429251881Speter 430289180Speter for (i = 0; i < paths->nelts; ++i) 431289180Speter { 432289180Speter const char *path = APR_ARRAY_IDX(paths, i, const char *); 433289180Speter const char *digest_path, *digest_file; 434289180Speter 435289180Speter SVN_ERR(digest_path_from_path(&digest_path, fs_path, path, pool)); 436289180Speter digest_file = svn_dirent_basename(digest_path, NULL); 437289180Speter svn_hash_sets(children, digest_file, NULL); 438251881Speter } 439251881Speter 440289180Speter if (apr_hash_count(children) || lock) 441289180Speter SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, 442289180Speter perms_reference, pool)); 443289180Speter else 444289180Speter SVN_ERR(svn_io_remove_file2(index_digest_path, TRUE, pool)); 445289180Speter 446251881Speter return SVN_NO_ERROR; 447251881Speter} 448251881Speter 449289180Speterstatic svn_error_t * 450289180Speterunlock_single(svn_fs_t *fs, 451289180Speter svn_lock_t *lock, 452289180Speter apr_pool_t *pool); 453289180Speter 454289180Speter/* Check if LOCK has been already expired. */ 455289180Speterstatic svn_boolean_t lock_expired(const svn_lock_t *lock) 456289180Speter{ 457289180Speter return lock->expiration_date && (apr_time_now() > lock->expiration_date); 458289180Speter} 459289180Speter 460251881Speter/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be 461251881Speter TRUE if the caller (or one of its callers) has taken out the 462251881Speter repository-wide write lock, FALSE otherwise. If MUST_EXIST is 463251881Speter not set, the function will simply return NULL in *LOCK_P instead 464251881Speter of creating an SVN_FS__ERR_NO_SUCH_LOCK error in case the lock 465251881Speter was not found (much faster). Use POOL for allocations. */ 466251881Speterstatic svn_error_t * 467251881Speterget_lock(svn_lock_t **lock_p, 468251881Speter svn_fs_t *fs, 469251881Speter const char *path, 470251881Speter svn_boolean_t have_write_lock, 471251881Speter svn_boolean_t must_exist, 472251881Speter apr_pool_t *pool) 473251881Speter{ 474251881Speter svn_lock_t *lock = NULL; 475251881Speter const char *digest_path; 476251881Speter svn_node_kind_t kind; 477251881Speter 478251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); 479251881Speter SVN_ERR(svn_io_check_path(digest_path, &kind, pool)); 480251881Speter 481251881Speter *lock_p = NULL; 482251881Speter if (kind != svn_node_none) 483251881Speter SVN_ERR(read_digest_file(NULL, &lock, fs->path, digest_path, pool)); 484251881Speter 485251881Speter if (! lock) 486251881Speter return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR; 487251881Speter 488251881Speter /* Don't return an expired lock. */ 489289180Speter if (lock_expired(lock)) 490251881Speter { 491251881Speter /* Only remove the lock if we have the write lock. 492251881Speter Read operations shouldn't change the filesystem. */ 493251881Speter if (have_write_lock) 494289180Speter SVN_ERR(unlock_single(fs, lock, pool)); 495251881Speter return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token); 496251881Speter } 497251881Speter 498251881Speter *lock_p = lock; 499251881Speter return SVN_NO_ERROR; 500251881Speter} 501251881Speter 502251881Speter 503251881Speter/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be 504251881Speter TRUE if the caller (or one of its callers) has taken out the 505251881Speter repository-wide write lock, FALSE otherwise. Use POOL for 506251881Speter allocations. */ 507251881Speterstatic svn_error_t * 508251881Speterget_lock_helper(svn_fs_t *fs, 509251881Speter svn_lock_t **lock_p, 510251881Speter const char *path, 511251881Speter svn_boolean_t have_write_lock, 512251881Speter apr_pool_t *pool) 513251881Speter{ 514251881Speter svn_lock_t *lock; 515251881Speter svn_error_t *err; 516251881Speter 517251881Speter err = get_lock(&lock, fs, path, have_write_lock, FALSE, pool); 518251881Speter 519251881Speter /* We've deliberately decided that this function doesn't tell the 520251881Speter caller *why* the lock is unavailable. */ 521251881Speter if (err && ((err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK) 522251881Speter || (err->apr_err == SVN_ERR_FS_LOCK_EXPIRED))) 523251881Speter { 524251881Speter svn_error_clear(err); 525251881Speter *lock_p = NULL; 526251881Speter return SVN_NO_ERROR; 527251881Speter } 528251881Speter else 529251881Speter SVN_ERR(err); 530251881Speter 531251881Speter *lock_p = lock; 532251881Speter return SVN_NO_ERROR; 533251881Speter} 534251881Speter 535251881Speter 536289180Speter/* A function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for 537289180Speter all locks in and under PATH in FS. 538251881Speter HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) 539251881Speter has the FS write lock. */ 540251881Speterstatic svn_error_t * 541289180Speterwalk_locks(svn_fs_t *fs, 542289180Speter const char *digest_path, 543289180Speter svn_fs_get_locks_callback_t get_locks_func, 544289180Speter void *get_locks_baton, 545289180Speter svn_boolean_t have_write_lock, 546289180Speter apr_pool_t *pool) 547251881Speter{ 548251881Speter apr_hash_index_t *hi; 549251881Speter apr_hash_t *children; 550251881Speter apr_pool_t *subpool; 551251881Speter svn_lock_t *lock; 552251881Speter 553251881Speter /* First, send up any locks in the current digest file. */ 554289180Speter SVN_ERR(read_digest_file(&children, &lock, fs->path, digest_path, pool)); 555251881Speter 556289180Speter if (lock && lock_expired(lock)) 557289180Speter { 558289180Speter /* Only remove the lock if we have the write lock. 559289180Speter Read operations shouldn't change the filesystem. */ 560289180Speter if (have_write_lock) 561289180Speter SVN_ERR(unlock_single(fs, lock, pool)); 562289180Speter } 563289180Speter else if (lock) 564289180Speter { 565289180Speter SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); 566289180Speter } 567251881Speter 568289180Speter /* Now, report all the child entries (if any; bail otherwise). */ 569251881Speter if (! apr_hash_count(children)) 570251881Speter return SVN_NO_ERROR; 571251881Speter subpool = svn_pool_create(pool); 572251881Speter for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) 573251881Speter { 574289180Speter const char *digest = apr_hash_this_key(hi); 575251881Speter svn_pool_clear(subpool); 576289180Speter 577289180Speter SVN_ERR(read_digest_file 578289180Speter (NULL, &lock, fs->path, 579289180Speter digest_path_from_digest(fs->path, digest, subpool), subpool)); 580289180Speter 581289180Speter if (lock && lock_expired(lock)) 582289180Speter { 583289180Speter /* Only remove the lock if we have the write lock. 584289180Speter Read operations shouldn't change the filesystem. */ 585289180Speter if (have_write_lock) 586289180Speter SVN_ERR(unlock_single(fs, lock, pool)); 587289180Speter } 588289180Speter else if (lock) 589289180Speter { 590289180Speter SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); 591289180Speter } 592251881Speter } 593251881Speter svn_pool_destroy(subpool); 594251881Speter return SVN_NO_ERROR; 595251881Speter} 596251881Speter 597251881Speter 598251881Speter/* Utility function: verify that a lock can be used. Interesting 599251881Speter errors returned from this function: 600251881Speter 601251881Speter SVN_ERR_FS_NO_USER: No username attached to FS. 602251881Speter SVN_ERR_FS_LOCK_OWNER_MISMATCH: FS's username doesn't match LOCK's owner. 603251881Speter SVN_ERR_FS_BAD_LOCK_TOKEN: FS doesn't hold matching lock-token for LOCK. 604251881Speter */ 605251881Speterstatic svn_error_t * 606251881Speterverify_lock(svn_fs_t *fs, 607251881Speter svn_lock_t *lock, 608251881Speter apr_pool_t *pool) 609251881Speter{ 610251881Speter if ((! fs->access_ctx) || (! fs->access_ctx->username)) 611251881Speter return svn_error_createf 612251881Speter (SVN_ERR_FS_NO_USER, NULL, 613251881Speter _("Cannot verify lock on path '%s'; no username available"), 614251881Speter lock->path); 615251881Speter 616251881Speter else if (strcmp(fs->access_ctx->username, lock->owner) != 0) 617251881Speter return svn_error_createf 618251881Speter (SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL, 619251881Speter _("User '%s' does not own lock on path '%s' (currently locked by '%s')"), 620251881Speter fs->access_ctx->username, lock->path, lock->owner); 621251881Speter 622251881Speter else if (svn_hash_gets(fs->access_ctx->lock_tokens, lock->token) == NULL) 623251881Speter return svn_error_createf 624251881Speter (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, 625251881Speter _("Cannot verify lock on path '%s'; no matching lock-token available"), 626251881Speter lock->path); 627251881Speter 628251881Speter return SVN_NO_ERROR; 629251881Speter} 630251881Speter 631251881Speter 632251881Speter/* This implements the svn_fs_get_locks_callback_t interface, where 633251881Speter BATON is just an svn_fs_t object. */ 634251881Speterstatic svn_error_t * 635251881Speterget_locks_callback(void *baton, 636251881Speter svn_lock_t *lock, 637251881Speter apr_pool_t *pool) 638251881Speter{ 639251881Speter return verify_lock(baton, lock, pool); 640251881Speter} 641251881Speter 642251881Speter 643251881Speter/* The main routine for lock enforcement, used throughout libsvn_fs_fs. */ 644251881Spetersvn_error_t * 645251881Spetersvn_fs_fs__allow_locked_operation(const char *path, 646251881Speter svn_fs_t *fs, 647251881Speter svn_boolean_t recurse, 648251881Speter svn_boolean_t have_write_lock, 649251881Speter apr_pool_t *pool) 650251881Speter{ 651251881Speter path = svn_fs__canonicalize_abspath(path, pool); 652251881Speter if (recurse) 653251881Speter { 654251881Speter /* Discover all locks at or below the path. */ 655251881Speter const char *digest_path; 656251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); 657251881Speter SVN_ERR(walk_locks(fs, digest_path, get_locks_callback, 658251881Speter fs, have_write_lock, pool)); 659251881Speter } 660251881Speter else 661251881Speter { 662251881Speter /* Discover and verify any lock attached to the path. */ 663251881Speter svn_lock_t *lock; 664251881Speter SVN_ERR(get_lock_helper(fs, &lock, path, have_write_lock, pool)); 665251881Speter if (lock) 666251881Speter SVN_ERR(verify_lock(fs, lock, pool)); 667251881Speter } 668251881Speter return SVN_NO_ERROR; 669251881Speter} 670251881Speter 671289180Speter/* Helper function called from the lock and unlock code. 672289180Speter UPDATES is a map from "const char *" parent paths to "apr_array_header_t *" 673289180Speter arrays of child paths. For all of the parent paths of PATH this function 674289180Speter adds PATH to the corresponding array of child paths. */ 675289180Speterstatic void 676289180Speterschedule_index_update(apr_hash_t *updates, 677289180Speter const char *path, 678289180Speter apr_pool_t *scratch_pool) 679289180Speter{ 680289180Speter apr_pool_t *hashpool = apr_hash_pool_get(updates); 681289180Speter const char *parent_path = path; 682289180Speter 683289180Speter while (! svn_fspath__is_root(parent_path, strlen(parent_path))) 684289180Speter { 685289180Speter apr_array_header_t *children; 686289180Speter 687289180Speter parent_path = svn_fspath__dirname(parent_path, scratch_pool); 688289180Speter children = svn_hash_gets(updates, parent_path); 689289180Speter 690289180Speter if (! children) 691289180Speter { 692289180Speter children = apr_array_make(hashpool, 8, sizeof(const char *)); 693289180Speter svn_hash_sets(updates, apr_pstrdup(hashpool, parent_path), children); 694289180Speter } 695289180Speter 696289180Speter APR_ARRAY_PUSH(children, const char *) = path; 697289180Speter } 698289180Speter} 699289180Speter 700289180Speter/* The effective arguments for lock_body() below. */ 701251881Speterstruct lock_baton { 702251881Speter svn_fs_t *fs; 703289180Speter apr_array_header_t *targets; 704289180Speter apr_array_header_t *infos; 705251881Speter const char *comment; 706251881Speter svn_boolean_t is_dav_comment; 707251881Speter apr_time_t expiration_date; 708251881Speter svn_boolean_t steal_lock; 709289180Speter apr_pool_t *result_pool; 710251881Speter}; 711251881Speter 712251881Speterstatic svn_error_t * 713289180Spetercheck_lock(svn_error_t **fs_err, 714289180Speter const char *path, 715289180Speter const svn_fs_lock_target_t *target, 716289180Speter struct lock_baton *lb, 717289180Speter svn_fs_root_t *root, 718289180Speter svn_revnum_t youngest_rev, 719289180Speter apr_pool_t *pool) 720251881Speter{ 721251881Speter svn_node_kind_t kind; 722251881Speter svn_lock_t *existing_lock; 723251881Speter 724289180Speter *fs_err = SVN_NO_ERROR; 725289180Speter 726289180Speter SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool)); 727251881Speter if (kind == svn_node_dir) 728289180Speter { 729289180Speter *fs_err = SVN_FS__ERR_NOT_FILE(lb->fs, path); 730289180Speter return SVN_NO_ERROR; 731289180Speter } 732251881Speter 733251881Speter /* While our locking implementation easily supports the locking of 734251881Speter nonexistent paths, we deliberately choose not to allow such madness. */ 735251881Speter if (kind == svn_node_none) 736251881Speter { 737289180Speter if (SVN_IS_VALID_REVNUM(target->current_rev)) 738289180Speter *fs_err = svn_error_createf( 739251881Speter SVN_ERR_FS_OUT_OF_DATE, NULL, 740251881Speter _("Path '%s' doesn't exist in HEAD revision"), 741289180Speter path); 742251881Speter else 743289180Speter *fs_err = svn_error_createf( 744251881Speter SVN_ERR_FS_NOT_FOUND, NULL, 745251881Speter _("Path '%s' doesn't exist in HEAD revision"), 746289180Speter path); 747289180Speter 748289180Speter return SVN_NO_ERROR; 749251881Speter } 750251881Speter 751251881Speter /* Is the caller attempting to lock an out-of-date working file? */ 752289180Speter if (SVN_IS_VALID_REVNUM(target->current_rev)) 753251881Speter { 754251881Speter svn_revnum_t created_rev; 755289180Speter 756289180Speter if (target->current_rev > youngest_rev) 757289180Speter { 758289180Speter *fs_err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 759289180Speter _("No such revision %ld"), 760289180Speter target->current_rev); 761289180Speter return SVN_NO_ERROR; 762289180Speter } 763289180Speter 764289180Speter SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, path, 765251881Speter pool)); 766251881Speter 767251881Speter /* SVN_INVALID_REVNUM means the path doesn't exist. So 768251881Speter apparently somebody is trying to lock something in their 769251881Speter working copy, but somebody else has deleted the thing 770251881Speter from HEAD. That counts as being 'out of date'. */ 771251881Speter if (! SVN_IS_VALID_REVNUM(created_rev)) 772289180Speter { 773289180Speter *fs_err = svn_error_createf 774289180Speter (SVN_ERR_FS_OUT_OF_DATE, NULL, 775289180Speter _("Path '%s' doesn't exist in HEAD revision"), path); 776251881Speter 777289180Speter return SVN_NO_ERROR; 778289180Speter } 779289180Speter 780289180Speter if (target->current_rev < created_rev) 781289180Speter { 782289180Speter *fs_err = svn_error_createf 783289180Speter (SVN_ERR_FS_OUT_OF_DATE, NULL, 784289180Speter _("Lock failed: newer version of '%s' exists"), path); 785289180Speter 786289180Speter return SVN_NO_ERROR; 787289180Speter } 788251881Speter } 789251881Speter 790251881Speter /* If the caller provided a TOKEN, we *really* need to see 791251881Speter if a lock already exists with that token, and if so, verify that 792251881Speter the lock's path matches PATH. Otherwise we run the risk of 793251881Speter breaking the 1-to-1 mapping of lock tokens to locked paths. */ 794251881Speter /* ### TODO: actually do this check. This is tough, because the 795251881Speter schema doesn't supply a lookup-by-token mechanism. */ 796251881Speter 797251881Speter /* Is the path already locked? 798251881Speter 799251881Speter Note that this next function call will automatically ignore any 800251881Speter errors about {the path not existing as a key, the path's token 801251881Speter not existing as a key, the lock just having been expired}. And 802251881Speter that's totally fine. Any of these three errors are perfectly 803251881Speter acceptable to ignore; it means that the path is now free and 804251881Speter clear for locking, because the fsfs funcs just cleared out both 805251881Speter of the tables for us. */ 806289180Speter SVN_ERR(get_lock_helper(lb->fs, &existing_lock, path, TRUE, pool)); 807251881Speter if (existing_lock) 808251881Speter { 809251881Speter if (! lb->steal_lock) 810251881Speter { 811251881Speter /* Sorry, the path is already locked. */ 812289180Speter *fs_err = SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock); 813289180Speter return SVN_NO_ERROR; 814251881Speter } 815289180Speter } 816289180Speter 817289180Speter return SVN_NO_ERROR; 818289180Speter} 819289180Speter 820289180Speterstruct lock_info_t { 821289180Speter const char *path; 822289180Speter svn_lock_t *lock; 823289180Speter svn_error_t *fs_err; 824289180Speter}; 825289180Speter 826289180Speter/* The body of svn_fs_fs__lock(), which see. 827289180Speter 828289180Speter BATON is a 'struct lock_baton *' holding the effective arguments. 829289180Speter BATON->targets is an array of 'svn_sort__item_t' targets, sorted by 830289180Speter path, mapping canonical path to 'svn_fs_lock_target_t'. Set 831289180Speter BATON->infos to an array of 'lock_info_t' holding the results. For 832289180Speter the other arguments, see svn_fs_lock_many(). 833289180Speter 834289180Speter This implements the svn_fs_fs__with_write_lock() 'body' callback 835289180Speter type, and assumes that the write lock is held. 836289180Speter */ 837289180Speterstatic svn_error_t * 838289180Speterlock_body(void *baton, apr_pool_t *pool) 839289180Speter{ 840289180Speter struct lock_baton *lb = baton; 841289180Speter svn_fs_root_t *root; 842289180Speter svn_revnum_t youngest; 843289180Speter const char *rev_0_path; 844289180Speter int i; 845289180Speter apr_hash_t *index_updates = apr_hash_make(pool); 846289180Speter apr_hash_index_t *hi; 847289180Speter apr_pool_t *iterpool = svn_pool_create(pool); 848289180Speter 849289180Speter /* Until we implement directory locks someday, we only allow locks 850362181Sdim on files. */ 851289180Speter /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular 852289180Speter library dependencies, which are not portable. */ 853289180Speter SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); 854289180Speter SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); 855289180Speter 856289180Speter for (i = 0; i < lb->targets->nelts; ++i) 857289180Speter { 858289180Speter const svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, 859289180Speter svn_sort__item_t); 860289180Speter struct lock_info_t info; 861289180Speter 862289180Speter svn_pool_clear(iterpool); 863289180Speter 864289180Speter info.path = item->key; 865289180Speter info.lock = NULL; 866289180Speter info.fs_err = SVN_NO_ERROR; 867289180Speter 868289180Speter SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root, 869289180Speter youngest, iterpool)); 870289180Speter 871289180Speter /* If no error occurred while pre-checking, schedule the index updates for 872289180Speter this path. */ 873289180Speter if (!info.fs_err) 874289180Speter schedule_index_update(index_updates, info.path, iterpool); 875289180Speter 876289180Speter APR_ARRAY_PUSH(lb->infos, struct lock_info_t) = info; 877289180Speter } 878289180Speter 879289180Speter rev_0_path = svn_fs_fs__path_rev_absolute(lb->fs, 0, pool); 880289180Speter 881289180Speter /* We apply the scheduled index updates before writing the actual locks. 882289180Speter 883289180Speter Writing indices before locks is correct: if interrupted it leaves 884289180Speter indices without locks rather than locks without indices. An 885289180Speter index without a lock is consistent in that it always shows up as 886289180Speter unlocked in svn_fs_fs__allow_locked_operation. A lock without an 887289180Speter index is inconsistent, svn_fs_fs__allow_locked_operation will 888289180Speter show locked on the file but unlocked on the parent. */ 889289180Speter 890289180Speter for (hi = apr_hash_first(pool, index_updates); hi; hi = apr_hash_next(hi)) 891289180Speter { 892289180Speter const char *path = apr_hash_this_key(hi); 893289180Speter apr_array_header_t *children = apr_hash_this_val(hi); 894289180Speter 895289180Speter svn_pool_clear(iterpool); 896289180Speter SVN_ERR(add_to_digest(lb->fs->path, children, path, rev_0_path, 897289180Speter iterpool)); 898289180Speter } 899289180Speter 900289180Speter for (i = 0; i < lb->infos->nelts; ++i) 901289180Speter { 902289180Speter struct lock_info_t *info = &APR_ARRAY_IDX(lb->infos, i, 903289180Speter struct lock_info_t); 904289180Speter svn_sort__item_t *item = &APR_ARRAY_IDX(lb->targets, i, svn_sort__item_t); 905289180Speter svn_fs_lock_target_t *target = item->value; 906289180Speter 907289180Speter svn_pool_clear(iterpool); 908289180Speter 909289180Speter if (! info->fs_err) 910251881Speter { 911289180Speter info->lock = svn_lock_create(lb->result_pool); 912289180Speter if (target->token) 913289180Speter info->lock->token = apr_pstrdup(lb->result_pool, target->token); 914289180Speter else 915289180Speter SVN_ERR(svn_fs_fs__generate_lock_token(&(info->lock->token), lb->fs, 916289180Speter lb->result_pool)); 917289180Speter 918289180Speter /* The INFO->PATH is already allocated in LB->RESULT_POOL as a result 919289180Speter of svn_fspath__canonicalize() (see svn_fs_fs__lock()). */ 920289180Speter info->lock->path = info->path; 921289180Speter info->lock->owner = apr_pstrdup(lb->result_pool, 922289180Speter lb->fs->access_ctx->username); 923289180Speter info->lock->comment = apr_pstrdup(lb->result_pool, lb->comment); 924289180Speter info->lock->is_dav_comment = lb->is_dav_comment; 925289180Speter info->lock->creation_date = apr_time_now(); 926289180Speter info->lock->expiration_date = lb->expiration_date; 927289180Speter 928289180Speter info->fs_err = set_lock(lb->fs->path, info->lock, rev_0_path, 929289180Speter iterpool); 930251881Speter } 931251881Speter } 932251881Speter 933289180Speter svn_pool_destroy(iterpool); 934251881Speter return SVN_NO_ERROR; 935251881Speter} 936251881Speter 937289180Speter/* The effective arguments for unlock_body() below. */ 938251881Speterstruct unlock_baton { 939251881Speter svn_fs_t *fs; 940289180Speter apr_array_header_t *targets; 941289180Speter apr_array_header_t *infos; 942289180Speter /* Set skip_check TRUE to prevent the checks that set infos[].fs_err. */ 943289180Speter svn_boolean_t skip_check; 944251881Speter svn_boolean_t break_lock; 945289180Speter apr_pool_t *result_pool; 946251881Speter}; 947251881Speter 948289180Speterstatic svn_error_t * 949289180Spetercheck_unlock(svn_error_t **fs_err, 950289180Speter const char *path, 951289180Speter const char *token, 952289180Speter struct unlock_baton *ub, 953289180Speter svn_fs_root_t *root, 954289180Speter apr_pool_t *pool) 955289180Speter{ 956289180Speter svn_lock_t *lock; 957289180Speter 958289180Speter *fs_err = get_lock(&lock, ub->fs, path, TRUE, TRUE, pool); 959289180Speter if (!*fs_err && !ub->break_lock) 960289180Speter { 961289180Speter if (strcmp(token, lock->token) != 0) 962289180Speter *fs_err = SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, path); 963289180Speter else if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0) 964289180Speter *fs_err = SVN_FS__ERR_LOCK_OWNER_MISMATCH(ub->fs, 965289180Speter ub->fs->access_ctx->username, 966289180Speter lock->owner); 967289180Speter } 968289180Speter 969289180Speter return SVN_NO_ERROR; 970289180Speter} 971289180Speter 972289180Speterstruct unlock_info_t { 973289180Speter const char *path; 974289180Speter svn_error_t *fs_err; 975289180Speter svn_boolean_t done; 976289180Speter}; 977289180Speter 978289180Speter/* The body of svn_fs_fs__unlock(), which see. 979289180Speter 980289180Speter BATON is a 'struct unlock_baton *' holding the effective arguments. 981289180Speter BATON->targets is an array of 'svn_sort__item_t' targets, sorted by 982289180Speter path, mapping canonical path to (const char *) token. Set 983289180Speter BATON->infos to an array of 'unlock_info_t' results. For the other 984289180Speter arguments, see svn_fs_unlock_many(). 985289180Speter 986289180Speter This implements the svn_fs_fs__with_write_lock() 'body' callback 987251881Speter type, and assumes that the write lock is held. 988289180Speter */ 989251881Speterstatic svn_error_t * 990251881Speterunlock_body(void *baton, apr_pool_t *pool) 991251881Speter{ 992251881Speter struct unlock_baton *ub = baton; 993289180Speter svn_fs_root_t *root; 994289180Speter svn_revnum_t youngest; 995289180Speter const char *rev_0_path; 996289180Speter int i; 997289180Speter apr_hash_t *indices_updates = apr_hash_make(pool); 998289180Speter apr_hash_index_t *hi; 999289180Speter apr_pool_t *iterpool = svn_pool_create(pool); 1000251881Speter 1001289180Speter SVN_ERR(ub->fs->vtable->youngest_rev(&youngest, ub->fs, pool)); 1002289180Speter SVN_ERR(ub->fs->vtable->revision_root(&root, ub->fs, youngest, pool)); 1003251881Speter 1004289180Speter for (i = 0; i < ub->targets->nelts; ++i) 1005251881Speter { 1006289180Speter const svn_sort__item_t *item = &APR_ARRAY_IDX(ub->targets, i, 1007289180Speter svn_sort__item_t); 1008289180Speter const char *token = item->value; 1009289180Speter struct unlock_info_t info; 1010251881Speter 1011289180Speter svn_pool_clear(iterpool); 1012251881Speter 1013289180Speter info.path = item->key; 1014289180Speter info.fs_err = SVN_NO_ERROR; 1015289180Speter info.done = FALSE; 1016289180Speter 1017289180Speter if (!ub->skip_check) 1018289180Speter SVN_ERR(check_unlock(&info.fs_err, info.path, token, ub, root, 1019289180Speter iterpool)); 1020289180Speter 1021289180Speter /* If no error occurred while pre-checking, schedule the index updates for 1022289180Speter this path. */ 1023289180Speter if (!info.fs_err) 1024289180Speter schedule_index_update(indices_updates, info.path, iterpool); 1025289180Speter 1026289180Speter APR_ARRAY_PUSH(ub->infos, struct unlock_info_t) = info; 1027251881Speter } 1028251881Speter 1029289180Speter rev_0_path = svn_fs_fs__path_rev_absolute(ub->fs, 0, pool); 1030289180Speter 1031289180Speter /* Unlike the lock_body(), we need to delete locks *before* we start to 1032289180Speter update indices. */ 1033289180Speter 1034289180Speter for (i = 0; i < ub->infos->nelts; ++i) 1035289180Speter { 1036289180Speter struct unlock_info_t *info = &APR_ARRAY_IDX(ub->infos, i, 1037289180Speter struct unlock_info_t); 1038289180Speter 1039289180Speter svn_pool_clear(iterpool); 1040289180Speter 1041289180Speter if (! info->fs_err) 1042289180Speter { 1043289180Speter SVN_ERR(delete_lock(ub->fs->path, info->path, iterpool)); 1044289180Speter info->done = TRUE; 1045289180Speter } 1046289180Speter } 1047289180Speter 1048289180Speter for (hi = apr_hash_first(pool, indices_updates); hi; hi = apr_hash_next(hi)) 1049289180Speter { 1050289180Speter const char *path = apr_hash_this_key(hi); 1051289180Speter apr_array_header_t *children = apr_hash_this_val(hi); 1052289180Speter 1053289180Speter svn_pool_clear(iterpool); 1054289180Speter SVN_ERR(delete_from_digest(ub->fs->path, children, path, rev_0_path, 1055289180Speter iterpool)); 1056289180Speter } 1057289180Speter 1058289180Speter svn_pool_destroy(iterpool); 1059289180Speter return SVN_NO_ERROR; 1060251881Speter} 1061251881Speter 1062289180Speter/* Unlock the lock described by LOCK->path and LOCK->token in FS. 1063289180Speter 1064289180Speter This assumes that the write lock is held. 1065289180Speter */ 1066289180Speterstatic svn_error_t * 1067289180Speterunlock_single(svn_fs_t *fs, 1068289180Speter svn_lock_t *lock, 1069289180Speter apr_pool_t *pool) 1070289180Speter{ 1071289180Speter struct unlock_baton ub; 1072289180Speter svn_sort__item_t item; 1073289180Speter apr_array_header_t *targets = apr_array_make(pool, 1, 1074289180Speter sizeof(svn_sort__item_t)); 1075289180Speter item.key = lock->path; 1076289180Speter item.klen = strlen(item.key); 1077289180Speter item.value = (char*)lock->token; 1078289180Speter APR_ARRAY_PUSH(targets, svn_sort__item_t) = item; 1079289180Speter 1080289180Speter ub.fs = fs; 1081289180Speter ub.targets = targets; 1082289180Speter ub.infos = apr_array_make(pool, targets->nelts, 1083289180Speter sizeof(struct unlock_info_t)); 1084289180Speter ub.skip_check = TRUE; 1085289180Speter ub.result_pool = pool; 1086289180Speter 1087289180Speter /* No ub.infos[].fs_err error because skip_check is TRUE. */ 1088289180Speter SVN_ERR(unlock_body(&ub, pool)); 1089289180Speter 1090289180Speter return SVN_NO_ERROR; 1091289180Speter} 1092289180Speter 1093251881Speter 1094251881Speter/*** Public API implementations ***/ 1095251881Speter 1096251881Spetersvn_error_t * 1097289180Spetersvn_fs_fs__lock(svn_fs_t *fs, 1098289180Speter apr_hash_t *targets, 1099251881Speter const char *comment, 1100251881Speter svn_boolean_t is_dav_comment, 1101251881Speter apr_time_t expiration_date, 1102251881Speter svn_boolean_t steal_lock, 1103289180Speter svn_fs_lock_callback_t lock_callback, 1104289180Speter void *lock_baton, 1105289180Speter apr_pool_t *result_pool, 1106289180Speter apr_pool_t *scratch_pool) 1107251881Speter{ 1108251881Speter struct lock_baton lb; 1109289180Speter apr_array_header_t *sorted_targets; 1110289180Speter apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); 1111289180Speter apr_hash_index_t *hi; 1112289180Speter apr_pool_t *iterpool; 1113289180Speter svn_error_t *err, *cb_err = SVN_NO_ERROR; 1114289180Speter int i; 1115251881Speter 1116251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1117251881Speter 1118289180Speter /* We need to have a username attached to the fs. */ 1119289180Speter if (!fs->access_ctx || !fs->access_ctx->username) 1120289180Speter return SVN_FS__ERR_NO_USER(fs); 1121289180Speter 1122289180Speter /* The FS locking API allows both canonical and non-canonical 1123289180Speter paths which means that the same canonical path could be 1124289180Speter represented more than once in the TARGETS hash. We just keep 1125289180Speter one, choosing one with a token if possible. */ 1126289180Speter for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) 1127289180Speter { 1128289180Speter const char *path = apr_hash_this_key(hi); 1129289180Speter const svn_fs_lock_target_t *target = apr_hash_this_val(hi); 1130289180Speter const svn_fs_lock_target_t *other; 1131289180Speter 1132289180Speter path = svn_fspath__canonicalize(path, result_pool); 1133289180Speter other = svn_hash_gets(canonical_targets, path); 1134289180Speter 1135289180Speter if (!other || (!other->token && target->token)) 1136289180Speter svn_hash_sets(canonical_targets, path, target); 1137289180Speter } 1138289180Speter 1139289180Speter sorted_targets = svn_sort__hash(canonical_targets, 1140289180Speter svn_sort_compare_items_as_paths, 1141289180Speter scratch_pool); 1142289180Speter 1143251881Speter lb.fs = fs; 1144289180Speter lb.targets = sorted_targets; 1145289180Speter lb.infos = apr_array_make(result_pool, sorted_targets->nelts, 1146289180Speter sizeof(struct lock_info_t)); 1147251881Speter lb.comment = comment; 1148251881Speter lb.is_dav_comment = is_dav_comment; 1149251881Speter lb.expiration_date = expiration_date; 1150251881Speter lb.steal_lock = steal_lock; 1151289180Speter lb.result_pool = result_pool; 1152251881Speter 1153289180Speter iterpool = svn_pool_create(scratch_pool); 1154289180Speter err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, iterpool); 1155289180Speter for (i = 0; i < lb.infos->nelts; ++i) 1156289180Speter { 1157289180Speter struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i, 1158289180Speter struct lock_info_t); 1159289180Speter svn_pool_clear(iterpool); 1160289180Speter if (!cb_err && lock_callback) 1161289180Speter { 1162289180Speter if (!info->lock && !info->fs_err) 1163289180Speter info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 1164289180Speter 0, _("Failed to lock '%s'"), 1165289180Speter info->path); 1166289180Speter 1167289180Speter cb_err = lock_callback(lock_baton, info->path, info->lock, 1168289180Speter info->fs_err, iterpool); 1169289180Speter } 1170289180Speter svn_error_clear(info->fs_err); 1171289180Speter } 1172289180Speter svn_pool_destroy(iterpool); 1173289180Speter 1174289180Speter if (err && cb_err) 1175289180Speter svn_error_compose(err, cb_err); 1176289180Speter else if (!err) 1177289180Speter err = cb_err; 1178289180Speter 1179289180Speter return svn_error_trace(err); 1180251881Speter} 1181251881Speter 1182251881Speter 1183251881Spetersvn_error_t * 1184251881Spetersvn_fs_fs__generate_lock_token(const char **token, 1185251881Speter svn_fs_t *fs, 1186251881Speter apr_pool_t *pool) 1187251881Speter{ 1188251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1189251881Speter 1190251881Speter /* Notice that 'fs' is currently unused. But perhaps someday, we'll 1191251881Speter want to use the fs UUID + some incremented number? For now, we 1192251881Speter generate a URI that matches the DAV RFC. We could change this to 1193251881Speter some other URI scheme someday, if we wish. */ 1194251881Speter *token = apr_pstrcat(pool, "opaquelocktoken:", 1195289180Speter svn_uuid_generate(pool), SVN_VA_NULL); 1196251881Speter return SVN_NO_ERROR; 1197251881Speter} 1198251881Speter 1199251881Spetersvn_error_t * 1200251881Spetersvn_fs_fs__unlock(svn_fs_t *fs, 1201289180Speter apr_hash_t *targets, 1202251881Speter svn_boolean_t break_lock, 1203289180Speter svn_fs_lock_callback_t lock_callback, 1204289180Speter void *lock_baton, 1205289180Speter apr_pool_t *result_pool, 1206289180Speter apr_pool_t *scratch_pool) 1207251881Speter{ 1208251881Speter struct unlock_baton ub; 1209289180Speter apr_array_header_t *sorted_targets; 1210289180Speter apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); 1211289180Speter apr_hash_index_t *hi; 1212289180Speter apr_pool_t *iterpool; 1213289180Speter svn_error_t *err, *cb_err = SVN_NO_ERROR; 1214289180Speter int i; 1215251881Speter 1216251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1217251881Speter 1218289180Speter /* We need to have a username attached to the fs. */ 1219289180Speter if (!fs->access_ctx || !fs->access_ctx->username) 1220289180Speter return SVN_FS__ERR_NO_USER(fs); 1221289180Speter 1222289180Speter for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) 1223289180Speter { 1224289180Speter const char *path = apr_hash_this_key(hi); 1225289180Speter const char *token = apr_hash_this_val(hi); 1226289180Speter const char *other; 1227289180Speter 1228289180Speter path = svn_fspath__canonicalize(path, result_pool); 1229289180Speter other = svn_hash_gets(canonical_targets, path); 1230289180Speter 1231289180Speter if (!other) 1232289180Speter svn_hash_sets(canonical_targets, path, token); 1233289180Speter } 1234289180Speter 1235289180Speter sorted_targets = svn_sort__hash(canonical_targets, 1236289180Speter svn_sort_compare_items_as_paths, 1237289180Speter scratch_pool); 1238289180Speter 1239251881Speter ub.fs = fs; 1240289180Speter ub.targets = sorted_targets; 1241289180Speter ub.infos = apr_array_make(result_pool, sorted_targets->nelts, 1242289180Speter sizeof(struct unlock_info_t)); 1243289180Speter ub.skip_check = FALSE; 1244251881Speter ub.break_lock = break_lock; 1245289180Speter ub.result_pool = result_pool; 1246251881Speter 1247289180Speter iterpool = svn_pool_create(scratch_pool); 1248289180Speter err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, iterpool); 1249289180Speter for (i = 0; i < ub.infos->nelts; ++i) 1250289180Speter { 1251289180Speter struct unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i, 1252289180Speter struct unlock_info_t); 1253289180Speter svn_pool_clear(iterpool); 1254289180Speter if (!cb_err && lock_callback) 1255289180Speter { 1256289180Speter if (!info->done && !info->fs_err) 1257289180Speter info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 1258289180Speter 0, _("Failed to unlock '%s'"), 1259289180Speter info->path); 1260289180Speter cb_err = lock_callback(lock_baton, info->path, NULL, info->fs_err, 1261289180Speter iterpool); 1262289180Speter } 1263289180Speter svn_error_clear(info->fs_err); 1264289180Speter } 1265289180Speter svn_pool_destroy(iterpool); 1266289180Speter 1267289180Speter if (err && cb_err) 1268289180Speter svn_error_compose(err, cb_err); 1269289180Speter else if (!err) 1270289180Speter err = cb_err; 1271289180Speter 1272289180Speter return svn_error_trace(err); 1273251881Speter} 1274251881Speter 1275251881Speter 1276251881Spetersvn_error_t * 1277251881Spetersvn_fs_fs__get_lock(svn_lock_t **lock_p, 1278251881Speter svn_fs_t *fs, 1279251881Speter const char *path, 1280251881Speter apr_pool_t *pool) 1281251881Speter{ 1282251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1283251881Speter path = svn_fs__canonicalize_abspath(path, pool); 1284251881Speter return get_lock_helper(fs, lock_p, path, FALSE, pool); 1285251881Speter} 1286251881Speter 1287251881Speter 1288251881Speter/* Baton for get_locks_filter_func(). */ 1289251881Spetertypedef struct get_locks_filter_baton_t 1290251881Speter{ 1291251881Speter const char *path; 1292251881Speter svn_depth_t requested_depth; 1293251881Speter svn_fs_get_locks_callback_t get_locks_func; 1294251881Speter void *get_locks_baton; 1295251881Speter 1296251881Speter} get_locks_filter_baton_t; 1297251881Speter 1298251881Speter 1299251881Speter/* A wrapper for the GET_LOCKS_FUNC passed to svn_fs_fs__get_locks() 1300251881Speter which filters out locks on paths that aren't within 1301251881Speter BATON->requested_depth of BATON->path before called 1302251881Speter BATON->get_locks_func() with BATON->get_locks_baton. 1303251881Speter 1304251881Speter NOTE: See issue #3660 for details about how the FSFS lock 1305251881Speter management code is inconsistent. Until that inconsistency is 1306251881Speter resolved, we take this filtering approach rather than honoring 1307251881Speter depth requests closer to the crawling code. In other words, once 1308251881Speter we decide how to resolve issue #3660, there might be a more 1309251881Speter performant way to honor the depth passed to svn_fs_fs__get_locks(). */ 1310251881Speterstatic svn_error_t * 1311251881Speterget_locks_filter_func(void *baton, 1312251881Speter svn_lock_t *lock, 1313251881Speter apr_pool_t *pool) 1314251881Speter{ 1315251881Speter get_locks_filter_baton_t *b = baton; 1316251881Speter 1317251881Speter /* Filter out unwanted paths. Since Subversion only allows 1318251881Speter locks on files, we can treat depth=immediates the same as 1319251881Speter depth=files for filtering purposes. Meaning, we'll keep 1320251881Speter this lock if: 1321251881Speter 1322251881Speter a) its path is the very path we queried, or 1323251881Speter b) we've asked for a fully recursive answer, or 1324251881Speter c) we've asked for depth=files or depth=immediates, and this 1325251881Speter lock is on an immediate child of our query path. 1326251881Speter */ 1327251881Speter if ((strcmp(b->path, lock->path) == 0) 1328251881Speter || (b->requested_depth == svn_depth_infinity)) 1329251881Speter { 1330251881Speter SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool)); 1331251881Speter } 1332251881Speter else if ((b->requested_depth == svn_depth_files) || 1333251881Speter (b->requested_depth == svn_depth_immediates)) 1334251881Speter { 1335251881Speter const char *rel_uri = svn_fspath__skip_ancestor(b->path, lock->path); 1336251881Speter if (rel_uri && (svn_path_component_count(rel_uri) == 1)) 1337251881Speter SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool)); 1338251881Speter } 1339251881Speter 1340251881Speter return SVN_NO_ERROR; 1341251881Speter} 1342251881Speter 1343251881Spetersvn_error_t * 1344251881Spetersvn_fs_fs__get_locks(svn_fs_t *fs, 1345251881Speter const char *path, 1346251881Speter svn_depth_t depth, 1347251881Speter svn_fs_get_locks_callback_t get_locks_func, 1348251881Speter void *get_locks_baton, 1349251881Speter apr_pool_t *pool) 1350251881Speter{ 1351251881Speter const char *digest_path; 1352251881Speter get_locks_filter_baton_t glfb; 1353251881Speter 1354251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1355251881Speter path = svn_fs__canonicalize_abspath(path, pool); 1356251881Speter 1357251881Speter glfb.path = path; 1358251881Speter glfb.requested_depth = depth; 1359251881Speter glfb.get_locks_func = get_locks_func; 1360251881Speter glfb.get_locks_baton = get_locks_baton; 1361251881Speter 1362251881Speter /* Get the top digest path in our tree of interest, and then walk it. */ 1363251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); 1364251881Speter SVN_ERR(walk_locks(fs, digest_path, get_locks_filter_func, &glfb, 1365251881Speter FALSE, pool)); 1366251881Speter return SVN_NO_ERROR; 1367251881Speter} 1368