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_pools.h" 25251881Speter#include "svn_error.h" 26251881Speter#include "svn_dirent_uri.h" 27251881Speter#include "svn_path.h" 28251881Speter#include "svn_fs.h" 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_time.h" 31251881Speter#include "svn_utf.h" 32251881Speter 33251881Speter#include <apr_uuid.h> 34251881Speter#include <apr_file_io.h> 35251881Speter#include <apr_file_info.h> 36251881Speter 37251881Speter#include "lock.h" 38251881Speter#include "tree.h" 39251881Speter#include "fs_fs.h" 40251881Speter#include "../libsvn_fs/fs-loader.h" 41251881Speter 42251881Speter#include "private/svn_fs_util.h" 43251881Speter#include "private/svn_fspath.h" 44251881Speter#include "svn_private_config.h" 45251881Speter 46251881Speter/* Names of hash keys used to store a lock for writing to disk. */ 47251881Speter#define PATH_KEY "path" 48251881Speter#define TOKEN_KEY "token" 49251881Speter#define OWNER_KEY "owner" 50251881Speter#define CREATION_DATE_KEY "creation_date" 51251881Speter#define EXPIRATION_DATE_KEY "expiration_date" 52251881Speter#define COMMENT_KEY "comment" 53251881Speter#define IS_DAV_COMMENT_KEY "is_dav_comment" 54251881Speter#define CHILDREN_KEY "children" 55251881Speter 56251881Speter/* Number of characters from the head of a digest file name used to 57251881Speter calculate a subdirectory in which to drop that file. */ 58251881Speter#define DIGEST_SUBDIR_LEN 3 59251881Speter 60251881Speter 61251881Speter 62251881Speter/*** Generic helper functions. ***/ 63251881Speter 64251881Speter/* Set *DIGEST to the MD5 hash of STR. */ 65251881Speterstatic svn_error_t * 66251881Spetermake_digest(const char **digest, 67251881Speter const char *str, 68251881Speter apr_pool_t *pool) 69251881Speter{ 70251881Speter svn_checksum_t *checksum; 71251881Speter 72251881Speter SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, strlen(str), pool)); 73251881Speter 74251881Speter *digest = svn_checksum_to_cstring_display(checksum, pool); 75251881Speter return SVN_NO_ERROR; 76251881Speter} 77251881Speter 78251881Speter 79251881Speter/* Set the value of KEY (whose size is KEY_LEN, or APR_HASH_KEY_STRING 80251881Speter if unknown) to an svn_string_t-ized version of VALUE (whose size is 81251881Speter VALUE_LEN, or APR_HASH_KEY_STRING if unknown) in HASH. The value 82251881Speter will be allocated in POOL; KEY will not be duped. If either KEY or VALUE 83251881Speter is NULL, this function will do nothing. */ 84251881Speterstatic void 85251881Speterhash_store(apr_hash_t *hash, 86251881Speter const char *key, 87251881Speter apr_ssize_t key_len, 88251881Speter const char *value, 89251881Speter apr_ssize_t value_len, 90251881Speter apr_pool_t *pool) 91251881Speter{ 92251881Speter if (! (key && value)) 93251881Speter return; 94251881Speter if (value_len == APR_HASH_KEY_STRING) 95251881Speter value_len = strlen(value); 96251881Speter apr_hash_set(hash, key, key_len, 97251881Speter svn_string_ncreate(value, value_len, pool)); 98251881Speter} 99251881Speter 100251881Speter 101251881Speter/* Fetch the value of KEY from HASH, returning only the cstring data 102251881Speter of that value (if it exists). */ 103251881Speterstatic const char * 104251881Speterhash_fetch(apr_hash_t *hash, 105251881Speter const char *key, 106251881Speter apr_pool_t *pool) 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), 136251881Speter digest, 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), 154251881Speter digest, 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, 212251881Speter svn__apr_hash_index_key(hi), 213251881Speter svn__apr_hash_index_klen(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 { 225251881Speter svn_error_clear(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)); 233251881Speter SVN_ERR(svn_io_file_rename(tmp_path, digest_path, 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; 255251881Speter 256251881Speter if (lock_p) 257251881Speter *lock_p = NULL; 258251881Speter if (children_p) 259251881Speter *children_p = apr_hash_make(pool); 260251881Speter 261251881Speter err = svn_stream_open_readonly(&stream, digest_path, pool, pool); 262251881Speter if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 263251881Speter { 264251881Speter svn_error_clear(err); 265251881Speter return SVN_NO_ERROR; 266251881Speter } 267251881Speter SVN_ERR(err); 268251881Speter 269251881Speter /* If our caller doesn't care about anything but the presence of the 270251881Speter file... whatever. */ 271251881Speter if (! (lock_p || children_p)) 272251881Speter return svn_stream_close(stream); 273251881Speter 274251881Speter hash = apr_hash_make(pool); 275251881Speter if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) 276251881Speter { 277251881Speter svn_error_clear(svn_stream_close(stream)); 278251881Speter return svn_error_createf(err->apr_err, 279251881Speter err, 280251881Speter _("Can't parse lock/entries hashfile '%s'"), 281251881Speter svn_dirent_local_style(digest_path, pool)); 282251881Speter } 283251881Speter SVN_ERR(svn_stream_close(stream)); 284251881Speter 285251881Speter /* If our caller cares, see if we have a lock path in our hash. If 286251881Speter so, we'll assume we have a lock here. */ 287251881Speter val = hash_fetch(hash, PATH_KEY, pool); 288251881Speter if (val && lock_p) 289251881Speter { 290251881Speter const char *path = val; 291251881Speter 292251881Speter /* Create our lock and load it up. */ 293251881Speter lock = svn_lock_create(pool); 294251881Speter lock->path = path; 295251881Speter 296251881Speter if (! ((lock->token = hash_fetch(hash, TOKEN_KEY, pool)))) 297251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 298251881Speter 299251881Speter if (! ((lock->owner = hash_fetch(hash, OWNER_KEY, pool)))) 300251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 301251881Speter 302251881Speter if (! ((val = hash_fetch(hash, IS_DAV_COMMENT_KEY, pool)))) 303251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 304251881Speter lock->is_dav_comment = (val[0] == '1'); 305251881Speter 306251881Speter if (! ((val = hash_fetch(hash, CREATION_DATE_KEY, pool)))) 307251881Speter return svn_error_trace(err_corrupt_lockfile(fs_path, path)); 308251881Speter SVN_ERR(svn_time_from_cstring(&(lock->creation_date), val, pool)); 309251881Speter 310251881Speter if ((val = hash_fetch(hash, EXPIRATION_DATE_KEY, pool))) 311251881Speter SVN_ERR(svn_time_from_cstring(&(lock->expiration_date), val, pool)); 312251881Speter 313251881Speter lock->comment = hash_fetch(hash, COMMENT_KEY, pool); 314251881Speter 315251881Speter *lock_p = lock; 316251881Speter } 317251881Speter 318251881Speter /* If our caller cares, see if we have any children for this path. */ 319251881Speter val = hash_fetch(hash, CHILDREN_KEY, pool); 320251881Speter if (val && children_p) 321251881Speter { 322251881Speter apr_array_header_t *kiddos = svn_cstring_split(val, "\n", FALSE, pool); 323251881Speter int i; 324251881Speter 325251881Speter for (i = 0; i < kiddos->nelts; i++) 326251881Speter { 327251881Speter svn_hash_sets(*children_p, APR_ARRAY_IDX(kiddos, i, const char *), 328251881Speter (void *)1); 329251881Speter } 330251881Speter } 331251881Speter return SVN_NO_ERROR; 332251881Speter} 333251881Speter 334251881Speter 335251881Speter 336251881Speter/*** Lock helper functions (path here are still FS paths, not on-disk 337251881Speter schema-supporting paths) ***/ 338251881Speter 339251881Speter 340251881Speter/* Write LOCK in FS to the actual OS filesystem. 341251881Speter 342251881Speter Use PERMS_REFERENCE for the permissions of any digest files. 343251881Speter 344251881Speter Note: this takes an FS_PATH because it's called from the hotcopy logic. 345251881Speter */ 346251881Speterstatic svn_error_t * 347251881Speterset_lock(const char *fs_path, 348251881Speter svn_lock_t *lock, 349251881Speter const char *perms_reference, 350251881Speter apr_pool_t *pool) 351251881Speter{ 352251881Speter svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool); 353251881Speter const char *lock_digest_path = NULL; 354251881Speter apr_pool_t *subpool; 355251881Speter 356251881Speter SVN_ERR_ASSERT(lock); 357251881Speter 358251881Speter /* Iterate in reverse, creating the lock for LOCK->path, and then 359251881Speter just adding entries for its parent, until we reach a parent 360251881Speter that's already listed in *its* parent. */ 361251881Speter subpool = svn_pool_create(pool); 362251881Speter while (1729) 363251881Speter { 364251881Speter const char *digest_path, *digest_file; 365251881Speter apr_hash_t *this_children; 366251881Speter svn_lock_t *this_lock; 367251881Speter 368251881Speter svn_pool_clear(subpool); 369251881Speter 370251881Speter /* Calculate the DIGEST_PATH for the currently FS path, and then 371251881Speter get its DIGEST_FILE basename. */ 372251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs_path, this_path->data, 373251881Speter subpool)); 374251881Speter digest_file = svn_dirent_basename(digest_path, subpool); 375251881Speter 376251881Speter SVN_ERR(read_digest_file(&this_children, &this_lock, fs_path, 377251881Speter digest_path, subpool)); 378251881Speter 379251881Speter /* We're either writing a new lock (first time through only) or 380251881Speter a new entry (every time but the first). */ 381251881Speter if (lock) 382251881Speter { 383251881Speter this_lock = lock; 384251881Speter lock = NULL; 385251881Speter lock_digest_path = apr_pstrdup(pool, digest_file); 386251881Speter } 387251881Speter else 388251881Speter { 389251881Speter /* If we already have an entry for this path, we're done. */ 390251881Speter if (svn_hash_gets(this_children, lock_digest_path)) 391251881Speter break; 392251881Speter svn_hash_sets(this_children, lock_digest_path, (void *)1); 393251881Speter } 394251881Speter SVN_ERR(write_digest_file(this_children, this_lock, fs_path, 395251881Speter digest_path, perms_reference, subpool)); 396251881Speter 397251881Speter /* Prep for next iteration, or bail if we're done. */ 398251881Speter if (svn_fspath__is_root(this_path->data, this_path->len)) 399251881Speter break; 400251881Speter svn_stringbuf_set(this_path, 401251881Speter svn_fspath__dirname(this_path->data, subpool)); 402251881Speter } 403251881Speter 404251881Speter svn_pool_destroy(subpool); 405251881Speter return SVN_NO_ERROR; 406251881Speter} 407251881Speter 408251881Speter/* Delete LOCK from FS in the actual OS filesystem. */ 409251881Speterstatic svn_error_t * 410251881Speterdelete_lock(svn_fs_t *fs, 411251881Speter svn_lock_t *lock, 412251881Speter apr_pool_t *pool) 413251881Speter{ 414251881Speter svn_stringbuf_t *this_path = svn_stringbuf_create(lock->path, pool); 415251881Speter const char *child_to_kill = NULL; 416251881Speter apr_pool_t *subpool; 417251881Speter 418251881Speter SVN_ERR_ASSERT(lock); 419251881Speter 420251881Speter /* Iterate in reverse, deleting the lock for LOCK->path, and then 421251881Speter deleting its entry as it appears in each of its parents. */ 422251881Speter subpool = svn_pool_create(pool); 423251881Speter while (1729) 424251881Speter { 425251881Speter const char *digest_path, *digest_file; 426251881Speter apr_hash_t *this_children; 427251881Speter svn_lock_t *this_lock; 428251881Speter 429251881Speter svn_pool_clear(subpool); 430251881Speter 431251881Speter /* Calculate the DIGEST_PATH for the currently FS path, and then 432251881Speter get its DIGEST_FILE basename. */ 433251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, this_path->data, 434251881Speter subpool)); 435251881Speter digest_file = svn_dirent_basename(digest_path, subpool); 436251881Speter 437251881Speter SVN_ERR(read_digest_file(&this_children, &this_lock, fs->path, 438251881Speter digest_path, subpool)); 439251881Speter 440251881Speter /* Delete the lock (first time through only). */ 441251881Speter if (lock) 442251881Speter { 443251881Speter this_lock = NULL; 444251881Speter lock = NULL; 445251881Speter child_to_kill = apr_pstrdup(pool, digest_file); 446251881Speter } 447251881Speter 448251881Speter if (child_to_kill) 449251881Speter svn_hash_sets(this_children, child_to_kill, NULL); 450251881Speter 451251881Speter if (! (this_lock || apr_hash_count(this_children) != 0)) 452251881Speter { 453251881Speter /* Special case: no goodz, no file. And remember to nix 454251881Speter the entry for it in its parent. */ 455251881Speter SVN_ERR(svn_io_remove_file2(digest_path, FALSE, subpool)); 456251881Speter } 457251881Speter else 458251881Speter { 459251881Speter const char *rev_0_path; 460251881Speter SVN_ERR(svn_fs_fs__path_rev_absolute(&rev_0_path, fs, 0, pool)); 461251881Speter SVN_ERR(write_digest_file(this_children, this_lock, fs->path, 462251881Speter digest_path, rev_0_path, subpool)); 463251881Speter } 464251881Speter 465251881Speter /* Prep for next iteration, or bail if we're done. */ 466251881Speter if (svn_fspath__is_root(this_path->data, this_path->len)) 467251881Speter break; 468251881Speter svn_stringbuf_set(this_path, 469251881Speter svn_fspath__dirname(this_path->data, subpool)); 470251881Speter } 471251881Speter 472251881Speter svn_pool_destroy(subpool); 473251881Speter return SVN_NO_ERROR; 474251881Speter} 475251881Speter 476251881Speter/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be 477251881Speter TRUE if the caller (or one of its callers) has taken out the 478251881Speter repository-wide write lock, FALSE otherwise. If MUST_EXIST is 479251881Speter not set, the function will simply return NULL in *LOCK_P instead 480251881Speter of creating an SVN_FS__ERR_NO_SUCH_LOCK error in case the lock 481251881Speter was not found (much faster). Use POOL for allocations. */ 482251881Speterstatic svn_error_t * 483251881Speterget_lock(svn_lock_t **lock_p, 484251881Speter svn_fs_t *fs, 485251881Speter const char *path, 486251881Speter svn_boolean_t have_write_lock, 487251881Speter svn_boolean_t must_exist, 488251881Speter apr_pool_t *pool) 489251881Speter{ 490251881Speter svn_lock_t *lock = NULL; 491251881Speter const char *digest_path; 492251881Speter svn_node_kind_t kind; 493251881Speter 494251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); 495251881Speter SVN_ERR(svn_io_check_path(digest_path, &kind, pool)); 496251881Speter 497251881Speter *lock_p = NULL; 498251881Speter if (kind != svn_node_none) 499251881Speter SVN_ERR(read_digest_file(NULL, &lock, fs->path, digest_path, pool)); 500251881Speter 501251881Speter if (! lock) 502251881Speter return must_exist ? SVN_FS__ERR_NO_SUCH_LOCK(fs, path) : SVN_NO_ERROR; 503251881Speter 504251881Speter /* Don't return an expired lock. */ 505251881Speter if (lock->expiration_date && (apr_time_now() > lock->expiration_date)) 506251881Speter { 507251881Speter /* Only remove the lock if we have the write lock. 508251881Speter Read operations shouldn't change the filesystem. */ 509251881Speter if (have_write_lock) 510251881Speter SVN_ERR(delete_lock(fs, lock, pool)); 511251881Speter return SVN_FS__ERR_LOCK_EXPIRED(fs, lock->token); 512251881Speter } 513251881Speter 514251881Speter *lock_p = lock; 515251881Speter return SVN_NO_ERROR; 516251881Speter} 517251881Speter 518251881Speter 519251881Speter/* Set *LOCK_P to the lock for PATH in FS. HAVE_WRITE_LOCK should be 520251881Speter TRUE if the caller (or one of its callers) has taken out the 521251881Speter repository-wide write lock, FALSE otherwise. Use POOL for 522251881Speter allocations. */ 523251881Speterstatic svn_error_t * 524251881Speterget_lock_helper(svn_fs_t *fs, 525251881Speter svn_lock_t **lock_p, 526251881Speter const char *path, 527251881Speter svn_boolean_t have_write_lock, 528251881Speter apr_pool_t *pool) 529251881Speter{ 530251881Speter svn_lock_t *lock; 531251881Speter svn_error_t *err; 532251881Speter 533251881Speter err = get_lock(&lock, fs, path, have_write_lock, FALSE, pool); 534251881Speter 535251881Speter /* We've deliberately decided that this function doesn't tell the 536251881Speter caller *why* the lock is unavailable. */ 537251881Speter if (err && ((err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK) 538251881Speter || (err->apr_err == SVN_ERR_FS_LOCK_EXPIRED))) 539251881Speter { 540251881Speter svn_error_clear(err); 541251881Speter *lock_p = NULL; 542251881Speter return SVN_NO_ERROR; 543251881Speter } 544251881Speter else 545251881Speter SVN_ERR(err); 546251881Speter 547251881Speter *lock_p = lock; 548251881Speter return SVN_NO_ERROR; 549251881Speter} 550251881Speter 551251881Speter 552251881Speter/* Baton for locks_walker(). */ 553251881Speterstruct walk_locks_baton { 554251881Speter svn_fs_get_locks_callback_t get_locks_func; 555251881Speter void *get_locks_baton; 556251881Speter svn_fs_t *fs; 557251881Speter}; 558251881Speter 559251881Speter/* Implements walk_digests_callback_t. */ 560251881Speterstatic svn_error_t * 561251881Speterlocks_walker(void *baton, 562251881Speter const char *fs_path, 563251881Speter const char *digest_path, 564251881Speter apr_hash_t *children, 565251881Speter svn_lock_t *lock, 566251881Speter svn_boolean_t have_write_lock, 567251881Speter apr_pool_t *pool) 568251881Speter{ 569251881Speter struct walk_locks_baton *wlb = baton; 570251881Speter 571251881Speter if (lock) 572251881Speter { 573251881Speter /* Don't report an expired lock. */ 574251881Speter if (lock->expiration_date == 0 575251881Speter || (apr_time_now() <= lock->expiration_date)) 576251881Speter { 577251881Speter if (wlb->get_locks_func) 578251881Speter SVN_ERR(wlb->get_locks_func(wlb->get_locks_baton, lock, pool)); 579251881Speter } 580251881Speter else 581251881Speter { 582251881Speter /* Only remove the lock if we have the write lock. 583251881Speter Read operations shouldn't change the filesystem. */ 584251881Speter if (have_write_lock) 585251881Speter SVN_ERR(delete_lock(wlb->fs, lock, pool)); 586251881Speter } 587251881Speter } 588251881Speter 589251881Speter return SVN_NO_ERROR; 590251881Speter} 591251881Speter 592251881Speter/* Callback type for walk_digest_files(). 593251881Speter * 594251881Speter * CHILDREN and LOCK come from a read_digest_file(digest_path) call. 595251881Speter */ 596251881Spetertypedef svn_error_t *(*walk_digests_callback_t)(void *baton, 597251881Speter const char *fs_path, 598251881Speter const char *digest_path, 599251881Speter apr_hash_t *children, 600251881Speter svn_lock_t *lock, 601251881Speter svn_boolean_t have_write_lock, 602251881Speter apr_pool_t *pool); 603251881Speter 604251881Speter/* A recursive function that calls WALK_DIGESTS_FUNC/WALK_DIGESTS_BATON for 605251881Speter all lock digest files in and under PATH in FS. 606251881Speter HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) 607251881Speter has the FS write lock. */ 608251881Speterstatic svn_error_t * 609251881Speterwalk_digest_files(const char *fs_path, 610251881Speter const char *digest_path, 611251881Speter walk_digests_callback_t walk_digests_func, 612251881Speter void *walk_digests_baton, 613251881Speter svn_boolean_t have_write_lock, 614251881Speter apr_pool_t *pool) 615251881Speter{ 616251881Speter apr_hash_index_t *hi; 617251881Speter apr_hash_t *children; 618251881Speter apr_pool_t *subpool; 619251881Speter svn_lock_t *lock; 620251881Speter 621251881Speter /* First, send up any locks in the current digest file. */ 622251881Speter SVN_ERR(read_digest_file(&children, &lock, fs_path, digest_path, pool)); 623251881Speter 624251881Speter SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path, 625251881Speter children, lock, 626251881Speter have_write_lock, pool)); 627251881Speter 628251881Speter /* Now, recurse on this thing's child entries (if any; bail otherwise). */ 629251881Speter if (! apr_hash_count(children)) 630251881Speter return SVN_NO_ERROR; 631251881Speter subpool = svn_pool_create(pool); 632251881Speter for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi)) 633251881Speter { 634251881Speter const char *digest = svn__apr_hash_index_key(hi); 635251881Speter svn_pool_clear(subpool); 636251881Speter SVN_ERR(walk_digest_files 637251881Speter (fs_path, digest_path_from_digest(fs_path, digest, subpool), 638251881Speter walk_digests_func, walk_digests_baton, have_write_lock, subpool)); 639251881Speter } 640251881Speter svn_pool_destroy(subpool); 641251881Speter return SVN_NO_ERROR; 642251881Speter} 643251881Speter 644251881Speter/* A recursive function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for 645251881Speter all locks in and under PATH in FS. 646251881Speter HAVE_WRITE_LOCK should be true if the caller (directly or indirectly) 647251881Speter has the FS write lock. */ 648251881Speterstatic svn_error_t * 649251881Speterwalk_locks(svn_fs_t *fs, 650251881Speter const char *digest_path, 651251881Speter svn_fs_get_locks_callback_t get_locks_func, 652251881Speter void *get_locks_baton, 653251881Speter svn_boolean_t have_write_lock, 654251881Speter apr_pool_t *pool) 655251881Speter{ 656251881Speter struct walk_locks_baton wlb; 657251881Speter 658251881Speter wlb.get_locks_func = get_locks_func; 659251881Speter wlb.get_locks_baton = get_locks_baton; 660251881Speter wlb.fs = fs; 661251881Speter SVN_ERR(walk_digest_files(fs->path, digest_path, locks_walker, &wlb, 662251881Speter have_write_lock, pool)); 663251881Speter return SVN_NO_ERROR; 664251881Speter} 665251881Speter 666251881Speter 667251881Speter/* Utility function: verify that a lock can be used. Interesting 668251881Speter errors returned from this function: 669251881Speter 670251881Speter SVN_ERR_FS_NO_USER: No username attached to FS. 671251881Speter SVN_ERR_FS_LOCK_OWNER_MISMATCH: FS's username doesn't match LOCK's owner. 672251881Speter SVN_ERR_FS_BAD_LOCK_TOKEN: FS doesn't hold matching lock-token for LOCK. 673251881Speter */ 674251881Speterstatic svn_error_t * 675251881Speterverify_lock(svn_fs_t *fs, 676251881Speter svn_lock_t *lock, 677251881Speter apr_pool_t *pool) 678251881Speter{ 679251881Speter if ((! fs->access_ctx) || (! fs->access_ctx->username)) 680251881Speter return svn_error_createf 681251881Speter (SVN_ERR_FS_NO_USER, NULL, 682251881Speter _("Cannot verify lock on path '%s'; no username available"), 683251881Speter lock->path); 684251881Speter 685251881Speter else if (strcmp(fs->access_ctx->username, lock->owner) != 0) 686251881Speter return svn_error_createf 687251881Speter (SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL, 688251881Speter _("User '%s' does not own lock on path '%s' (currently locked by '%s')"), 689251881Speter fs->access_ctx->username, lock->path, lock->owner); 690251881Speter 691251881Speter else if (svn_hash_gets(fs->access_ctx->lock_tokens, lock->token) == NULL) 692251881Speter return svn_error_createf 693251881Speter (SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, 694251881Speter _("Cannot verify lock on path '%s'; no matching lock-token available"), 695251881Speter lock->path); 696251881Speter 697251881Speter return SVN_NO_ERROR; 698251881Speter} 699251881Speter 700251881Speter 701251881Speter/* This implements the svn_fs_get_locks_callback_t interface, where 702251881Speter BATON is just an svn_fs_t object. */ 703251881Speterstatic svn_error_t * 704251881Speterget_locks_callback(void *baton, 705251881Speter svn_lock_t *lock, 706251881Speter apr_pool_t *pool) 707251881Speter{ 708251881Speter return verify_lock(baton, lock, pool); 709251881Speter} 710251881Speter 711251881Speter 712251881Speter/* The main routine for lock enforcement, used throughout libsvn_fs_fs. */ 713251881Spetersvn_error_t * 714251881Spetersvn_fs_fs__allow_locked_operation(const char *path, 715251881Speter svn_fs_t *fs, 716251881Speter svn_boolean_t recurse, 717251881Speter svn_boolean_t have_write_lock, 718251881Speter apr_pool_t *pool) 719251881Speter{ 720251881Speter path = svn_fs__canonicalize_abspath(path, pool); 721251881Speter if (recurse) 722251881Speter { 723251881Speter /* Discover all locks at or below the path. */ 724251881Speter const char *digest_path; 725251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); 726251881Speter SVN_ERR(walk_locks(fs, digest_path, get_locks_callback, 727251881Speter fs, have_write_lock, pool)); 728251881Speter } 729251881Speter else 730251881Speter { 731251881Speter /* Discover and verify any lock attached to the path. */ 732251881Speter svn_lock_t *lock; 733251881Speter SVN_ERR(get_lock_helper(fs, &lock, path, have_write_lock, pool)); 734251881Speter if (lock) 735251881Speter SVN_ERR(verify_lock(fs, lock, pool)); 736251881Speter } 737251881Speter return SVN_NO_ERROR; 738251881Speter} 739251881Speter 740251881Speter/* Baton used for lock_body below. */ 741251881Speterstruct lock_baton { 742251881Speter svn_lock_t **lock_p; 743251881Speter svn_fs_t *fs; 744251881Speter const char *path; 745251881Speter const char *token; 746251881Speter const char *comment; 747251881Speter svn_boolean_t is_dav_comment; 748251881Speter apr_time_t expiration_date; 749251881Speter svn_revnum_t current_rev; 750251881Speter svn_boolean_t steal_lock; 751251881Speter apr_pool_t *pool; 752251881Speter}; 753251881Speter 754251881Speter 755251881Speter/* This implements the svn_fs_fs__with_write_lock() 'body' callback 756251881Speter type, and assumes that the write lock is held. 757251881Speter BATON is a 'struct lock_baton *'. */ 758251881Speterstatic svn_error_t * 759251881Speterlock_body(void *baton, apr_pool_t *pool) 760251881Speter{ 761251881Speter struct lock_baton *lb = baton; 762251881Speter svn_node_kind_t kind; 763251881Speter svn_lock_t *existing_lock; 764251881Speter svn_lock_t *lock; 765251881Speter svn_fs_root_t *root; 766251881Speter svn_revnum_t youngest; 767251881Speter const char *rev_0_path; 768251881Speter 769251881Speter /* Until we implement directory locks someday, we only allow locks 770251881Speter on files or non-existent paths. */ 771251881Speter /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular 772251881Speter library dependencies, which are not portable. */ 773251881Speter SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool)); 774251881Speter SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool)); 775251881Speter SVN_ERR(svn_fs_fs__check_path(&kind, root, lb->path, pool)); 776251881Speter if (kind == svn_node_dir) 777251881Speter return SVN_FS__ERR_NOT_FILE(lb->fs, lb->path); 778251881Speter 779251881Speter /* While our locking implementation easily supports the locking of 780251881Speter nonexistent paths, we deliberately choose not to allow such madness. */ 781251881Speter if (kind == svn_node_none) 782251881Speter { 783251881Speter if (SVN_IS_VALID_REVNUM(lb->current_rev)) 784251881Speter return svn_error_createf( 785251881Speter SVN_ERR_FS_OUT_OF_DATE, NULL, 786251881Speter _("Path '%s' doesn't exist in HEAD revision"), 787251881Speter lb->path); 788251881Speter else 789251881Speter return svn_error_createf( 790251881Speter SVN_ERR_FS_NOT_FOUND, NULL, 791251881Speter _("Path '%s' doesn't exist in HEAD revision"), 792251881Speter lb->path); 793251881Speter } 794251881Speter 795251881Speter /* We need to have a username attached to the fs. */ 796251881Speter if (!lb->fs->access_ctx || !lb->fs->access_ctx->username) 797251881Speter return SVN_FS__ERR_NO_USER(lb->fs); 798251881Speter 799251881Speter /* Is the caller attempting to lock an out-of-date working file? */ 800251881Speter if (SVN_IS_VALID_REVNUM(lb->current_rev)) 801251881Speter { 802251881Speter svn_revnum_t created_rev; 803251881Speter SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, lb->path, 804251881Speter pool)); 805251881Speter 806251881Speter /* SVN_INVALID_REVNUM means the path doesn't exist. So 807251881Speter apparently somebody is trying to lock something in their 808251881Speter working copy, but somebody else has deleted the thing 809251881Speter from HEAD. That counts as being 'out of date'. */ 810251881Speter if (! SVN_IS_VALID_REVNUM(created_rev)) 811251881Speter return svn_error_createf 812251881Speter (SVN_ERR_FS_OUT_OF_DATE, NULL, 813251881Speter _("Path '%s' doesn't exist in HEAD revision"), lb->path); 814251881Speter 815251881Speter if (lb->current_rev < created_rev) 816251881Speter return svn_error_createf 817251881Speter (SVN_ERR_FS_OUT_OF_DATE, NULL, 818251881Speter _("Lock failed: newer version of '%s' exists"), lb->path); 819251881Speter } 820251881Speter 821251881Speter /* If the caller provided a TOKEN, we *really* need to see 822251881Speter if a lock already exists with that token, and if so, verify that 823251881Speter the lock's path matches PATH. Otherwise we run the risk of 824251881Speter breaking the 1-to-1 mapping of lock tokens to locked paths. */ 825251881Speter /* ### TODO: actually do this check. This is tough, because the 826251881Speter schema doesn't supply a lookup-by-token mechanism. */ 827251881Speter 828251881Speter /* Is the path already locked? 829251881Speter 830251881Speter Note that this next function call will automatically ignore any 831251881Speter errors about {the path not existing as a key, the path's token 832251881Speter not existing as a key, the lock just having been expired}. And 833251881Speter that's totally fine. Any of these three errors are perfectly 834251881Speter acceptable to ignore; it means that the path is now free and 835251881Speter clear for locking, because the fsfs funcs just cleared out both 836251881Speter of the tables for us. */ 837251881Speter SVN_ERR(get_lock_helper(lb->fs, &existing_lock, lb->path, TRUE, pool)); 838251881Speter if (existing_lock) 839251881Speter { 840251881Speter if (! lb->steal_lock) 841251881Speter { 842251881Speter /* Sorry, the path is already locked. */ 843251881Speter return SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock); 844251881Speter } 845251881Speter else 846251881Speter { 847251881Speter /* STEAL_LOCK was passed, so fs_username is "stealing" the 848251881Speter lock from lock->owner. Destroy the existing lock. */ 849251881Speter SVN_ERR(delete_lock(lb->fs, existing_lock, pool)); 850251881Speter } 851251881Speter } 852251881Speter 853251881Speter /* Create our new lock, and add it to the tables. 854251881Speter Ensure that the lock is created in the correct pool. */ 855251881Speter lock = svn_lock_create(lb->pool); 856251881Speter if (lb->token) 857251881Speter lock->token = apr_pstrdup(lb->pool, lb->token); 858251881Speter else 859251881Speter SVN_ERR(svn_fs_fs__generate_lock_token(&(lock->token), lb->fs, 860251881Speter lb->pool)); 861251881Speter lock->path = apr_pstrdup(lb->pool, lb->path); 862251881Speter lock->owner = apr_pstrdup(lb->pool, lb->fs->access_ctx->username); 863251881Speter lock->comment = apr_pstrdup(lb->pool, lb->comment); 864251881Speter lock->is_dav_comment = lb->is_dav_comment; 865251881Speter lock->creation_date = apr_time_now(); 866251881Speter lock->expiration_date = lb->expiration_date; 867251881Speter SVN_ERR(svn_fs_fs__path_rev_absolute(&rev_0_path, lb->fs, 0, pool)); 868251881Speter SVN_ERR(set_lock(lb->fs->path, lock, rev_0_path, pool)); 869251881Speter *lb->lock_p = lock; 870251881Speter 871251881Speter return SVN_NO_ERROR; 872251881Speter} 873251881Speter 874251881Speter/* Baton used for unlock_body below. */ 875251881Speterstruct unlock_baton { 876251881Speter svn_fs_t *fs; 877251881Speter const char *path; 878251881Speter const char *token; 879251881Speter svn_boolean_t break_lock; 880251881Speter}; 881251881Speter 882251881Speter/* This implements the svn_fs_fs__with_write_lock() 'body' callback 883251881Speter type, and assumes that the write lock is held. 884251881Speter BATON is a 'struct unlock_baton *'. */ 885251881Speterstatic svn_error_t * 886251881Speterunlock_body(void *baton, apr_pool_t *pool) 887251881Speter{ 888251881Speter struct unlock_baton *ub = baton; 889251881Speter svn_lock_t *lock; 890251881Speter 891251881Speter /* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */ 892251881Speter SVN_ERR(get_lock(&lock, ub->fs, ub->path, TRUE, TRUE, pool)); 893251881Speter 894251881Speter /* Unless breaking the lock, we do some checks. */ 895251881Speter if (! ub->break_lock) 896251881Speter { 897251881Speter /* Sanity check: the incoming token should match lock->token. */ 898251881Speter if (strcmp(ub->token, lock->token) != 0) 899251881Speter return SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, lock->path); 900251881Speter 901251881Speter /* There better be a username attached to the fs. */ 902251881Speter if (! (ub->fs->access_ctx && ub->fs->access_ctx->username)) 903251881Speter return SVN_FS__ERR_NO_USER(ub->fs); 904251881Speter 905251881Speter /* And that username better be the same as the lock's owner. */ 906251881Speter if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0) 907251881Speter return SVN_FS__ERR_LOCK_OWNER_MISMATCH( 908251881Speter ub->fs, ub->fs->access_ctx->username, lock->owner); 909251881Speter } 910251881Speter 911251881Speter /* Remove lock and lock token files. */ 912251881Speter return delete_lock(ub->fs, lock, pool); 913251881Speter} 914251881Speter 915251881Speter 916251881Speter/*** Public API implementations ***/ 917251881Speter 918251881Spetersvn_error_t * 919251881Spetersvn_fs_fs__lock(svn_lock_t **lock_p, 920251881Speter svn_fs_t *fs, 921251881Speter const char *path, 922251881Speter const char *token, 923251881Speter const char *comment, 924251881Speter svn_boolean_t is_dav_comment, 925251881Speter apr_time_t expiration_date, 926251881Speter svn_revnum_t current_rev, 927251881Speter svn_boolean_t steal_lock, 928251881Speter apr_pool_t *pool) 929251881Speter{ 930251881Speter struct lock_baton lb; 931251881Speter 932251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 933251881Speter path = svn_fs__canonicalize_abspath(path, pool); 934251881Speter 935251881Speter lb.lock_p = lock_p; 936251881Speter lb.fs = fs; 937251881Speter lb.path = path; 938251881Speter lb.token = token; 939251881Speter lb.comment = comment; 940251881Speter lb.is_dav_comment = is_dav_comment; 941251881Speter lb.expiration_date = expiration_date; 942251881Speter lb.current_rev = current_rev; 943251881Speter lb.steal_lock = steal_lock; 944251881Speter lb.pool = pool; 945251881Speter 946251881Speter return svn_fs_fs__with_write_lock(fs, lock_body, &lb, pool); 947251881Speter} 948251881Speter 949251881Speter 950251881Spetersvn_error_t * 951251881Spetersvn_fs_fs__generate_lock_token(const char **token, 952251881Speter svn_fs_t *fs, 953251881Speter apr_pool_t *pool) 954251881Speter{ 955251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 956251881Speter 957251881Speter /* Notice that 'fs' is currently unused. But perhaps someday, we'll 958251881Speter want to use the fs UUID + some incremented number? For now, we 959251881Speter generate a URI that matches the DAV RFC. We could change this to 960251881Speter some other URI scheme someday, if we wish. */ 961251881Speter *token = apr_pstrcat(pool, "opaquelocktoken:", 962251881Speter svn_uuid_generate(pool), (char *)NULL); 963251881Speter return SVN_NO_ERROR; 964251881Speter} 965251881Speter 966251881Speter 967251881Spetersvn_error_t * 968251881Spetersvn_fs_fs__unlock(svn_fs_t *fs, 969251881Speter const char *path, 970251881Speter const char *token, 971251881Speter svn_boolean_t break_lock, 972251881Speter apr_pool_t *pool) 973251881Speter{ 974251881Speter struct unlock_baton ub; 975251881Speter 976251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 977251881Speter path = svn_fs__canonicalize_abspath(path, pool); 978251881Speter 979251881Speter ub.fs = fs; 980251881Speter ub.path = path; 981251881Speter ub.token = token; 982251881Speter ub.break_lock = break_lock; 983251881Speter 984251881Speter return svn_fs_fs__with_write_lock(fs, unlock_body, &ub, pool); 985251881Speter} 986251881Speter 987251881Speter 988251881Spetersvn_error_t * 989251881Spetersvn_fs_fs__get_lock(svn_lock_t **lock_p, 990251881Speter svn_fs_t *fs, 991251881Speter const char *path, 992251881Speter apr_pool_t *pool) 993251881Speter{ 994251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 995251881Speter path = svn_fs__canonicalize_abspath(path, pool); 996251881Speter return get_lock_helper(fs, lock_p, path, FALSE, pool); 997251881Speter} 998251881Speter 999251881Speter 1000251881Speter/* Baton for get_locks_filter_func(). */ 1001251881Spetertypedef struct get_locks_filter_baton_t 1002251881Speter{ 1003251881Speter const char *path; 1004251881Speter svn_depth_t requested_depth; 1005251881Speter svn_fs_get_locks_callback_t get_locks_func; 1006251881Speter void *get_locks_baton; 1007251881Speter 1008251881Speter} get_locks_filter_baton_t; 1009251881Speter 1010251881Speter 1011251881Speter/* A wrapper for the GET_LOCKS_FUNC passed to svn_fs_fs__get_locks() 1012251881Speter which filters out locks on paths that aren't within 1013251881Speter BATON->requested_depth of BATON->path before called 1014251881Speter BATON->get_locks_func() with BATON->get_locks_baton. 1015251881Speter 1016251881Speter NOTE: See issue #3660 for details about how the FSFS lock 1017251881Speter management code is inconsistent. Until that inconsistency is 1018251881Speter resolved, we take this filtering approach rather than honoring 1019251881Speter depth requests closer to the crawling code. In other words, once 1020251881Speter we decide how to resolve issue #3660, there might be a more 1021251881Speter performant way to honor the depth passed to svn_fs_fs__get_locks(). */ 1022251881Speterstatic svn_error_t * 1023251881Speterget_locks_filter_func(void *baton, 1024251881Speter svn_lock_t *lock, 1025251881Speter apr_pool_t *pool) 1026251881Speter{ 1027251881Speter get_locks_filter_baton_t *b = baton; 1028251881Speter 1029251881Speter /* Filter out unwanted paths. Since Subversion only allows 1030251881Speter locks on files, we can treat depth=immediates the same as 1031251881Speter depth=files for filtering purposes. Meaning, we'll keep 1032251881Speter this lock if: 1033251881Speter 1034251881Speter a) its path is the very path we queried, or 1035251881Speter b) we've asked for a fully recursive answer, or 1036251881Speter c) we've asked for depth=files or depth=immediates, and this 1037251881Speter lock is on an immediate child of our query path. 1038251881Speter */ 1039251881Speter if ((strcmp(b->path, lock->path) == 0) 1040251881Speter || (b->requested_depth == svn_depth_infinity)) 1041251881Speter { 1042251881Speter SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool)); 1043251881Speter } 1044251881Speter else if ((b->requested_depth == svn_depth_files) || 1045251881Speter (b->requested_depth == svn_depth_immediates)) 1046251881Speter { 1047251881Speter const char *rel_uri = svn_fspath__skip_ancestor(b->path, lock->path); 1048251881Speter if (rel_uri && (svn_path_component_count(rel_uri) == 1)) 1049251881Speter SVN_ERR(b->get_locks_func(b->get_locks_baton, lock, pool)); 1050251881Speter } 1051251881Speter 1052251881Speter return SVN_NO_ERROR; 1053251881Speter} 1054251881Speter 1055251881Spetersvn_error_t * 1056251881Spetersvn_fs_fs__get_locks(svn_fs_t *fs, 1057251881Speter const char *path, 1058251881Speter svn_depth_t depth, 1059251881Speter svn_fs_get_locks_callback_t get_locks_func, 1060251881Speter void *get_locks_baton, 1061251881Speter apr_pool_t *pool) 1062251881Speter{ 1063251881Speter const char *digest_path; 1064251881Speter get_locks_filter_baton_t glfb; 1065251881Speter 1066251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1067251881Speter path = svn_fs__canonicalize_abspath(path, pool); 1068251881Speter 1069251881Speter glfb.path = path; 1070251881Speter glfb.requested_depth = depth; 1071251881Speter glfb.get_locks_func = get_locks_func; 1072251881Speter glfb.get_locks_baton = get_locks_baton; 1073251881Speter 1074251881Speter /* Get the top digest path in our tree of interest, and then walk it. */ 1075251881Speter SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool)); 1076251881Speter SVN_ERR(walk_locks(fs, digest_path, get_locks_filter_func, &glfb, 1077251881Speter FALSE, pool)); 1078251881Speter return SVN_NO_ERROR; 1079251881Speter} 1080