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