1251881Speter/* fs.c --- creating, opening and closing filesystems 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 <stdlib.h> 24251881Speter#include <stdio.h> 25251881Speter#include <string.h> 26251881Speter 27251881Speter#include <apr_general.h> 28251881Speter#include <apr_pools.h> 29251881Speter#include <apr_file_io.h> 30251881Speter#include <apr_thread_mutex.h> 31251881Speter 32251881Speter#include "svn_fs.h" 33251881Speter#include "svn_delta.h" 34251881Speter#include "svn_version.h" 35251881Speter#include "svn_pools.h" 36251881Speter#include "fs.h" 37251881Speter#include "fs_fs.h" 38251881Speter#include "tree.h" 39251881Speter#include "lock.h" 40251881Speter#include "id.h" 41251881Speter#include "rep-cache.h" 42251881Speter#include "svn_private_config.h" 43251881Speter#include "private/svn_fs_util.h" 44262253Speter#include "private/svn_subr_private.h" 45251881Speter 46251881Speter#include "../libsvn_fs/fs-loader.h" 47251881Speter 48251881Speter/* A prefix for the pool userdata variables used to hold 49251881Speter per-filesystem shared data. See fs_serialized_init. */ 50251881Speter#define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-" 51251881Speter 52251881Speter 53251881Speter 54251881Speterstatic svn_error_t * 55251881Speterfs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) 56251881Speter{ 57251881Speter fs_fs_data_t *ffd = fs->fsap_data; 58251881Speter const char *key; 59251881Speter void *val; 60251881Speter fs_fs_shared_data_t *ffsd; 61251881Speter apr_status_t status; 62251881Speter 63251881Speter /* Note that we are allocating a small amount of long-lived data for 64251881Speter each separate repository opened during the lifetime of the 65251881Speter svn_fs_initialize pool. It's unlikely that anyone will notice 66251881Speter the modest expenditure; the alternative is to allocate each structure 67251881Speter in a subpool, add a reference-count, and add a serialized deconstructor 68251881Speter to the FS vtable. That's more machinery than it's worth. 69251881Speter 70251881Speter Using the uuid to obtain the lock creates a corner case if a 71251881Speter caller uses svn_fs_set_uuid on the repository in a process where 72251881Speter other threads might be using the same repository through another 73251881Speter FS object. The only real-world consumer of svn_fs_set_uuid is 74251881Speter "svnadmin load", so this is a low-priority problem, and we don't 75251881Speter know of a better way of associating such data with the 76251881Speter repository. */ 77251881Speter 78251881Speter SVN_ERR_ASSERT(fs->uuid); 79251881Speter key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid, 80251881Speter (char *) NULL); 81251881Speter status = apr_pool_userdata_get(&val, key, common_pool); 82251881Speter if (status) 83251881Speter return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data")); 84251881Speter ffsd = val; 85251881Speter 86251881Speter if (!ffsd) 87251881Speter { 88251881Speter ffsd = apr_pcalloc(common_pool, sizeof(*ffsd)); 89251881Speter ffsd->common_pool = common_pool; 90251881Speter 91251881Speter /* POSIX fcntl locks are per-process, so we need a mutex for 92251881Speter intra-process synchronization when grabbing the repository write 93251881Speter lock. */ 94251881Speter SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, 95251881Speter SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 96251881Speter 97251881Speter /* ... not to mention locking the txn-current file. */ 98251881Speter SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, 99251881Speter SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 100251881Speter 101269847Speter /* We also need a mutex for synchronizing access to the active 102269847Speter transaction list and free transaction pointer. This one is 103269847Speter enabled unconditionally. */ 104251881Speter SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, 105269847Speter TRUE, common_pool)); 106251881Speter 107251881Speter key = apr_pstrdup(common_pool, key); 108251881Speter status = apr_pool_userdata_set(ffsd, key, NULL, common_pool); 109251881Speter if (status) 110251881Speter return svn_error_wrap_apr(status, _("Can't store FSFS shared data")); 111251881Speter } 112251881Speter 113251881Speter ffd->shared = ffsd; 114251881Speter 115251881Speter return SVN_NO_ERROR; 116251881Speter} 117251881Speter 118251881Speter 119251881Speter 120251881Speter/* This function is provided for Subversion 1.0.x compatibility. It 121251881Speter has no effect for fsfs backed Subversion filesystems. It conforms 122251881Speter to the fs_library_vtable_t.bdb_set_errcall() API. */ 123251881Speterstatic svn_error_t * 124251881Speterfs_set_errcall(svn_fs_t *fs, 125251881Speter void (*db_errcall_fcn)(const char *errpfx, char *msg)) 126251881Speter{ 127251881Speter 128251881Speter return SVN_NO_ERROR; 129251881Speter} 130251881Speter 131251881Speterstruct fs_freeze_baton_t { 132251881Speter svn_fs_t *fs; 133251881Speter svn_fs_freeze_func_t freeze_func; 134251881Speter void *freeze_baton; 135251881Speter}; 136251881Speter 137251881Speterstatic svn_error_t * 138251881Speterfs_freeze_body(void *baton, 139251881Speter apr_pool_t *pool) 140251881Speter{ 141251881Speter struct fs_freeze_baton_t *b = baton; 142251881Speter svn_boolean_t exists; 143251881Speter 144251881Speter SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool)); 145251881Speter if (exists) 146251881Speter SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool)); 147251881Speter 148251881Speter SVN_ERR(b->freeze_func(b->freeze_baton, pool)); 149251881Speter 150251881Speter return SVN_NO_ERROR; 151251881Speter} 152251881Speter 153251881Speterstatic svn_error_t * 154251881Speterfs_freeze(svn_fs_t *fs, 155251881Speter svn_fs_freeze_func_t freeze_func, 156251881Speter void *freeze_baton, 157251881Speter apr_pool_t *pool) 158251881Speter{ 159251881Speter struct fs_freeze_baton_t b; 160251881Speter 161251881Speter b.fs = fs; 162251881Speter b.freeze_func = freeze_func; 163251881Speter b.freeze_baton = freeze_baton; 164251881Speter 165251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 166251881Speter SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool)); 167251881Speter 168251881Speter return SVN_NO_ERROR; 169251881Speter} 170251881Speter 171251881Speter 172251881Speter 173251881Speter/* The vtable associated with a specific open filesystem. */ 174251881Speterstatic fs_vtable_t fs_vtable = { 175251881Speter svn_fs_fs__youngest_rev, 176251881Speter svn_fs_fs__revision_prop, 177251881Speter svn_fs_fs__revision_proplist, 178251881Speter svn_fs_fs__change_rev_prop, 179251881Speter svn_fs_fs__set_uuid, 180251881Speter svn_fs_fs__revision_root, 181251881Speter svn_fs_fs__begin_txn, 182251881Speter svn_fs_fs__open_txn, 183251881Speter svn_fs_fs__purge_txn, 184251881Speter svn_fs_fs__list_transactions, 185251881Speter svn_fs_fs__deltify, 186251881Speter svn_fs_fs__lock, 187251881Speter svn_fs_fs__generate_lock_token, 188251881Speter svn_fs_fs__unlock, 189251881Speter svn_fs_fs__get_lock, 190251881Speter svn_fs_fs__get_locks, 191251881Speter svn_fs_fs__verify_root, 192251881Speter fs_freeze, 193251881Speter fs_set_errcall 194251881Speter}; 195251881Speter 196251881Speter 197251881Speter/* Creating a new filesystem. */ 198251881Speter 199251881Speter/* Set up vtable and fsap_data fields in FS. */ 200251881Speterstatic svn_error_t * 201251881Speterinitialize_fs_struct(svn_fs_t *fs) 202251881Speter{ 203251881Speter fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd)); 204251881Speter fs->vtable = &fs_vtable; 205251881Speter fs->fsap_data = ffd; 206251881Speter return SVN_NO_ERROR; 207251881Speter} 208251881Speter 209251881Speter/* This implements the fs_library_vtable_t.create() API. Create a new 210251881Speter fsfs-backed Subversion filesystem at path PATH and link it into 211251881Speter *FS. Perform temporary allocations in POOL, and fs-global allocations 212251881Speter in COMMON_POOL. */ 213251881Speterstatic svn_error_t * 214251881Speterfs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, 215251881Speter apr_pool_t *common_pool) 216251881Speter{ 217251881Speter SVN_ERR(svn_fs__check_fs(fs, FALSE)); 218251881Speter 219251881Speter SVN_ERR(initialize_fs_struct(fs)); 220251881Speter 221251881Speter SVN_ERR(svn_fs_fs__create(fs, path, pool)); 222251881Speter 223251881Speter SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 224251881Speter return fs_serialized_init(fs, common_pool, pool); 225251881Speter} 226251881Speter 227251881Speter 228251881Speter 229251881Speter/* Gaining access to an existing filesystem. */ 230251881Speter 231251881Speter/* This implements the fs_library_vtable_t.open() API. Open an FSFS 232251881Speter Subversion filesystem located at PATH, set *FS to point to the 233251881Speter correct vtable for the filesystem. Use POOL for any temporary 234251881Speter allocations, and COMMON_POOL for fs-global allocations. */ 235251881Speterstatic svn_error_t * 236251881Speterfs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, 237251881Speter apr_pool_t *common_pool) 238251881Speter{ 239251881Speter SVN_ERR(initialize_fs_struct(fs)); 240251881Speter 241251881Speter SVN_ERR(svn_fs_fs__open(fs, path, pool)); 242251881Speter 243251881Speter SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 244251881Speter return fs_serialized_init(fs, common_pool, pool); 245251881Speter} 246251881Speter 247251881Speter 248251881Speter 249251881Speter/* This implements the fs_library_vtable_t.open_for_recovery() API. */ 250251881Speterstatic svn_error_t * 251251881Speterfs_open_for_recovery(svn_fs_t *fs, 252251881Speter const char *path, 253251881Speter apr_pool_t *pool, apr_pool_t *common_pool) 254251881Speter{ 255251881Speter /* Recovery for FSFS is currently limited to recreating the 'current' 256251881Speter file from the latest revision. */ 257251881Speter 258251881Speter /* The only thing we have to watch out for is that the 'current' file 259251881Speter might not exist. So we'll try to create it here unconditionally, 260251881Speter and just ignore any errors that might indicate that it's already 261251881Speter present. (We'll need it to exist later anyway as a source for the 262251881Speter new file's permissions). */ 263251881Speter 264251881Speter /* Use a partly-filled fs pointer first to create 'current'. This will fail 265251881Speter if 'current' already exists, but we don't care about that. */ 266251881Speter fs->path = apr_pstrdup(fs->pool, path); 267251881Speter svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool), 268251881Speter "0 1 1\n", pool)); 269251881Speter 270251881Speter /* Now open the filesystem properly by calling the vtable method directly. */ 271251881Speter return fs_open(fs, path, pool, common_pool); 272251881Speter} 273251881Speter 274251881Speter 275251881Speter 276251881Speter/* This implements the fs_library_vtable_t.upgrade_fs() API. */ 277251881Speterstatic svn_error_t * 278251881Speterfs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, 279251881Speter apr_pool_t *common_pool) 280251881Speter{ 281251881Speter SVN_ERR(svn_fs__check_fs(fs, FALSE)); 282251881Speter SVN_ERR(initialize_fs_struct(fs)); 283251881Speter SVN_ERR(svn_fs_fs__open(fs, path, pool)); 284251881Speter SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 285251881Speter SVN_ERR(fs_serialized_init(fs, common_pool, pool)); 286251881Speter return svn_fs_fs__upgrade(fs, pool); 287251881Speter} 288251881Speter 289251881Speterstatic svn_error_t * 290251881Speterfs_verify(svn_fs_t *fs, const char *path, 291251881Speter svn_revnum_t start, 292251881Speter svn_revnum_t end, 293251881Speter svn_fs_progress_notify_func_t notify_func, 294251881Speter void *notify_baton, 295251881Speter svn_cancel_func_t cancel_func, 296251881Speter void *cancel_baton, 297251881Speter apr_pool_t *pool, 298251881Speter apr_pool_t *common_pool) 299251881Speter{ 300251881Speter SVN_ERR(svn_fs__check_fs(fs, FALSE)); 301251881Speter SVN_ERR(initialize_fs_struct(fs)); 302251881Speter SVN_ERR(svn_fs_fs__open(fs, path, pool)); 303251881Speter SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 304251881Speter SVN_ERR(fs_serialized_init(fs, common_pool, pool)); 305251881Speter return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton, 306251881Speter cancel_func, cancel_baton, pool); 307251881Speter} 308251881Speter 309251881Speterstatic svn_error_t * 310251881Speterfs_pack(svn_fs_t *fs, 311251881Speter const char *path, 312251881Speter svn_fs_pack_notify_t notify_func, 313251881Speter void *notify_baton, 314251881Speter svn_cancel_func_t cancel_func, 315251881Speter void *cancel_baton, 316251881Speter apr_pool_t *pool, 317251881Speter apr_pool_t *common_pool) 318251881Speter{ 319251881Speter SVN_ERR(svn_fs__check_fs(fs, FALSE)); 320251881Speter SVN_ERR(initialize_fs_struct(fs)); 321251881Speter SVN_ERR(svn_fs_fs__open(fs, path, pool)); 322251881Speter SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 323251881Speter SVN_ERR(fs_serialized_init(fs, common_pool, pool)); 324251881Speter return svn_fs_fs__pack(fs, notify_func, notify_baton, 325251881Speter cancel_func, cancel_baton, pool); 326251881Speter} 327251881Speter 328251881Speter 329251881Speter 330251881Speter 331251881Speter/* This implements the fs_library_vtable_t.hotcopy() API. Copy a 332251881Speter possibly live Subversion filesystem SRC_FS from SRC_PATH to a 333251881Speter DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to 334251881Speter re-copy data which already exists in DST_FS. 335251881Speter The CLEAN_LOGS argument is ignored and included for Subversion 336251881Speter 1.0.x compatibility. Perform all temporary allocations in POOL. */ 337251881Speterstatic svn_error_t * 338251881Speterfs_hotcopy(svn_fs_t *src_fs, 339251881Speter svn_fs_t *dst_fs, 340251881Speter const char *src_path, 341251881Speter const char *dst_path, 342251881Speter svn_boolean_t clean_logs, 343251881Speter svn_boolean_t incremental, 344251881Speter svn_cancel_func_t cancel_func, 345251881Speter void *cancel_baton, 346251881Speter apr_pool_t *pool) 347251881Speter{ 348251881Speter SVN_ERR(svn_fs__check_fs(src_fs, FALSE)); 349251881Speter SVN_ERR(initialize_fs_struct(src_fs)); 350251881Speter SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool)); 351251881Speter SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool)); 352251881Speter SVN_ERR(fs_serialized_init(src_fs, pool, pool)); 353251881Speter 354251881Speter SVN_ERR(svn_fs__check_fs(dst_fs, FALSE)); 355251881Speter SVN_ERR(initialize_fs_struct(dst_fs)); 356251881Speter /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS. 357251881Speter Otherwise, it's not an FS yet --- possibly just an empty dir --- so 358251881Speter can't be opened. 359251881Speter */ 360251881Speter return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path, 361251881Speter incremental, cancel_func, cancel_baton, pool); 362251881Speter} 363251881Speter 364251881Speter 365251881Speter 366251881Speter/* This function is included for Subversion 1.0.x compatibility. It 367251881Speter has no effect for fsfs backed Subversion filesystems. It conforms 368251881Speter to the fs_library_vtable_t.bdb_logfiles() API. */ 369251881Speterstatic svn_error_t * 370251881Speterfs_logfiles(apr_array_header_t **logfiles, 371251881Speter const char *path, 372251881Speter svn_boolean_t only_unused, 373251881Speter apr_pool_t *pool) 374251881Speter{ 375251881Speter /* A no-op for FSFS. */ 376251881Speter *logfiles = apr_array_make(pool, 0, sizeof(const char *)); 377251881Speter 378251881Speter return SVN_NO_ERROR; 379251881Speter} 380251881Speter 381251881Speter 382251881Speter 383251881Speter 384251881Speter 385251881Speter/* Delete the filesystem located at path PATH. Perform any temporary 386251881Speter allocations in POOL. */ 387251881Speterstatic svn_error_t * 388251881Speterfs_delete_fs(const char *path, 389251881Speter apr_pool_t *pool) 390251881Speter{ 391251881Speter /* Remove everything. */ 392251881Speter return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 393251881Speter} 394251881Speter 395251881Speterstatic const svn_version_t * 396251881Speterfs_version(void) 397251881Speter{ 398251881Speter SVN_VERSION_BODY; 399251881Speter} 400251881Speter 401251881Speterstatic const char * 402251881Speterfs_get_description(void) 403251881Speter{ 404251881Speter return _("Module for working with a plain file (FSFS) repository."); 405251881Speter} 406251881Speter 407251881Speterstatic svn_error_t * 408251881Speterfs_set_svn_fs_open(svn_fs_t *fs, 409251881Speter svn_error_t *(*svn_fs_open_)(svn_fs_t **, 410251881Speter const char *, 411251881Speter apr_hash_t *, 412251881Speter apr_pool_t *)) 413251881Speter{ 414251881Speter fs_fs_data_t *ffd = fs->fsap_data; 415251881Speter ffd->svn_fs_open_ = svn_fs_open_; 416251881Speter return SVN_NO_ERROR; 417251881Speter} 418251881Speter 419251881Speter 420251881Speter/* Base FS library vtable, used by the FS loader library. */ 421251881Speter 422251881Speterstatic fs_library_vtable_t library_vtable = { 423251881Speter fs_version, 424251881Speter fs_create, 425251881Speter fs_open, 426251881Speter fs_open_for_recovery, 427251881Speter fs_upgrade, 428251881Speter fs_verify, 429251881Speter fs_delete_fs, 430251881Speter fs_hotcopy, 431251881Speter fs_get_description, 432251881Speter svn_fs_fs__recover, 433251881Speter fs_pack, 434251881Speter fs_logfiles, 435251881Speter NULL /* parse_id */, 436251881Speter fs_set_svn_fs_open 437251881Speter}; 438251881Speter 439251881Spetersvn_error_t * 440251881Spetersvn_fs_fs__init(const svn_version_t *loader_version, 441251881Speter fs_library_vtable_t **vtable, apr_pool_t* common_pool) 442251881Speter{ 443251881Speter static const svn_version_checklist_t checklist[] = 444251881Speter { 445251881Speter { "svn_subr", svn_subr_version }, 446251881Speter { "svn_delta", svn_delta_version }, 447251881Speter { NULL, NULL } 448251881Speter }; 449251881Speter 450251881Speter /* Simplified version check to make sure we can safely use the 451251881Speter VTABLE parameter. The FS loader does a more exhaustive check. */ 452251881Speter if (loader_version->major != SVN_VER_MAJOR) 453251881Speter return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 454251881Speter _("Unsupported FS loader version (%d) for fsfs"), 455251881Speter loader_version->major); 456262253Speter SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal)); 457251881Speter 458251881Speter *vtable = &library_vtable; 459251881Speter return SVN_NO_ERROR; 460251881Speter} 461