1/* fs.c --- creating, opening and closing filesystems 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include <stdlib.h> 24#include <stdio.h> 25#include <string.h> 26 27#include <apr_general.h> 28#include <apr_pools.h> 29#include <apr_file_io.h> 30#include <apr_thread_mutex.h> 31 32#include "svn_fs.h" 33#include "svn_delta.h" 34#include "svn_version.h" 35#include "svn_pools.h" 36#include "fs.h" 37#include "fs_fs.h" 38#include "tree.h" 39#include "lock.h" 40#include "id.h" 41#include "rep-cache.h" 42#include "svn_private_config.h" 43#include "private/svn_fs_util.h" 44 45#include "../libsvn_fs/fs-loader.h" 46 47/* A prefix for the pool userdata variables used to hold 48 per-filesystem shared data. See fs_serialized_init. */ 49#define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-" 50 51 52 53static svn_error_t * 54fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) 55{ 56 fs_fs_data_t *ffd = fs->fsap_data; 57 const char *key; 58 void *val; 59 fs_fs_shared_data_t *ffsd; 60 apr_status_t status; 61 62 /* Note that we are allocating a small amount of long-lived data for 63 each separate repository opened during the lifetime of the 64 svn_fs_initialize pool. It's unlikely that anyone will notice 65 the modest expenditure; the alternative is to allocate each structure 66 in a subpool, add a reference-count, and add a serialized deconstructor 67 to the FS vtable. That's more machinery than it's worth. 68 69 Using the uuid to obtain the lock creates a corner case if a 70 caller uses svn_fs_set_uuid on the repository in a process where 71 other threads might be using the same repository through another 72 FS object. The only real-world consumer of svn_fs_set_uuid is 73 "svnadmin load", so this is a low-priority problem, and we don't 74 know of a better way of associating such data with the 75 repository. */ 76 77 SVN_ERR_ASSERT(fs->uuid); 78 key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid, 79 (char *) NULL); 80 status = apr_pool_userdata_get(&val, key, common_pool); 81 if (status) 82 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data")); 83 ffsd = val; 84 85 if (!ffsd) 86 { 87 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd)); 88 ffsd->common_pool = common_pool; 89 90 /* POSIX fcntl locks are per-process, so we need a mutex for 91 intra-process synchronization when grabbing the repository write 92 lock. */ 93 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, 94 SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 95 96 /* ... not to mention locking the txn-current file. */ 97 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, 98 SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 99 100 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, 101 SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 102 103 key = apr_pstrdup(common_pool, key); 104 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool); 105 if (status) 106 return svn_error_wrap_apr(status, _("Can't store FSFS shared data")); 107 } 108 109 ffd->shared = ffsd; 110 111 return SVN_NO_ERROR; 112} 113 114 115 116/* This function is provided for Subversion 1.0.x compatibility. It 117 has no effect for fsfs backed Subversion filesystems. It conforms 118 to the fs_library_vtable_t.bdb_set_errcall() API. */ 119static svn_error_t * 120fs_set_errcall(svn_fs_t *fs, 121 void (*db_errcall_fcn)(const char *errpfx, char *msg)) 122{ 123 124 return SVN_NO_ERROR; 125} 126 127struct fs_freeze_baton_t { 128 svn_fs_t *fs; 129 svn_fs_freeze_func_t freeze_func; 130 void *freeze_baton; 131}; 132 133static svn_error_t * 134fs_freeze_body(void *baton, 135 apr_pool_t *pool) 136{ 137 struct fs_freeze_baton_t *b = baton; 138 svn_boolean_t exists; 139 140 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool)); 141 if (exists) 142 SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool)); 143 144 SVN_ERR(b->freeze_func(b->freeze_baton, pool)); 145 146 return SVN_NO_ERROR; 147} 148 149static svn_error_t * 150fs_freeze(svn_fs_t *fs, 151 svn_fs_freeze_func_t freeze_func, 152 void *freeze_baton, 153 apr_pool_t *pool) 154{ 155 struct fs_freeze_baton_t b; 156 157 b.fs = fs; 158 b.freeze_func = freeze_func; 159 b.freeze_baton = freeze_baton; 160 161 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 162 SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool)); 163 164 return SVN_NO_ERROR; 165} 166 167 168 169/* The vtable associated with a specific open filesystem. */ 170static fs_vtable_t fs_vtable = { 171 svn_fs_fs__youngest_rev, 172 svn_fs_fs__revision_prop, 173 svn_fs_fs__revision_proplist, 174 svn_fs_fs__change_rev_prop, 175 svn_fs_fs__set_uuid, 176 svn_fs_fs__revision_root, 177 svn_fs_fs__begin_txn, 178 svn_fs_fs__open_txn, 179 svn_fs_fs__purge_txn, 180 svn_fs_fs__list_transactions, 181 svn_fs_fs__deltify, 182 svn_fs_fs__lock, 183 svn_fs_fs__generate_lock_token, 184 svn_fs_fs__unlock, 185 svn_fs_fs__get_lock, 186 svn_fs_fs__get_locks, 187 svn_fs_fs__verify_root, 188 fs_freeze, 189 fs_set_errcall 190}; 191 192 193/* Creating a new filesystem. */ 194 195/* Set up vtable and fsap_data fields in FS. */ 196static svn_error_t * 197initialize_fs_struct(svn_fs_t *fs) 198{ 199 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd)); 200 fs->vtable = &fs_vtable; 201 fs->fsap_data = ffd; 202 return SVN_NO_ERROR; 203} 204 205/* This implements the fs_library_vtable_t.create() API. Create a new 206 fsfs-backed Subversion filesystem at path PATH and link it into 207 *FS. Perform temporary allocations in POOL, and fs-global allocations 208 in COMMON_POOL. */ 209static svn_error_t * 210fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, 211 apr_pool_t *common_pool) 212{ 213 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 214 215 SVN_ERR(initialize_fs_struct(fs)); 216 217 SVN_ERR(svn_fs_fs__create(fs, path, pool)); 218 219 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 220 return fs_serialized_init(fs, common_pool, pool); 221} 222 223 224 225/* Gaining access to an existing filesystem. */ 226 227/* This implements the fs_library_vtable_t.open() API. Open an FSFS 228 Subversion filesystem located at PATH, set *FS to point to the 229 correct vtable for the filesystem. Use POOL for any temporary 230 allocations, and COMMON_POOL for fs-global allocations. */ 231static svn_error_t * 232fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, 233 apr_pool_t *common_pool) 234{ 235 SVN_ERR(initialize_fs_struct(fs)); 236 237 SVN_ERR(svn_fs_fs__open(fs, path, pool)); 238 239 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 240 return fs_serialized_init(fs, common_pool, pool); 241} 242 243 244 245/* This implements the fs_library_vtable_t.open_for_recovery() API. */ 246static svn_error_t * 247fs_open_for_recovery(svn_fs_t *fs, 248 const char *path, 249 apr_pool_t *pool, apr_pool_t *common_pool) 250{ 251 /* Recovery for FSFS is currently limited to recreating the 'current' 252 file from the latest revision. */ 253 254 /* The only thing we have to watch out for is that the 'current' file 255 might not exist. So we'll try to create it here unconditionally, 256 and just ignore any errors that might indicate that it's already 257 present. (We'll need it to exist later anyway as a source for the 258 new file's permissions). */ 259 260 /* Use a partly-filled fs pointer first to create 'current'. This will fail 261 if 'current' already exists, but we don't care about that. */ 262 fs->path = apr_pstrdup(fs->pool, path); 263 svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool), 264 "0 1 1\n", pool)); 265 266 /* Now open the filesystem properly by calling the vtable method directly. */ 267 return fs_open(fs, path, pool, common_pool); 268} 269 270 271 272/* This implements the fs_library_vtable_t.upgrade_fs() API. */ 273static svn_error_t * 274fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, 275 apr_pool_t *common_pool) 276{ 277 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 278 SVN_ERR(initialize_fs_struct(fs)); 279 SVN_ERR(svn_fs_fs__open(fs, path, pool)); 280 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 281 SVN_ERR(fs_serialized_init(fs, common_pool, pool)); 282 return svn_fs_fs__upgrade(fs, pool); 283} 284 285static svn_error_t * 286fs_verify(svn_fs_t *fs, const char *path, 287 svn_revnum_t start, 288 svn_revnum_t end, 289 svn_fs_progress_notify_func_t notify_func, 290 void *notify_baton, 291 svn_cancel_func_t cancel_func, 292 void *cancel_baton, 293 apr_pool_t *pool, 294 apr_pool_t *common_pool) 295{ 296 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 297 SVN_ERR(initialize_fs_struct(fs)); 298 SVN_ERR(svn_fs_fs__open(fs, path, pool)); 299 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 300 SVN_ERR(fs_serialized_init(fs, common_pool, pool)); 301 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton, 302 cancel_func, cancel_baton, pool); 303} 304 305static svn_error_t * 306fs_pack(svn_fs_t *fs, 307 const char *path, 308 svn_fs_pack_notify_t notify_func, 309 void *notify_baton, 310 svn_cancel_func_t cancel_func, 311 void *cancel_baton, 312 apr_pool_t *pool, 313 apr_pool_t *common_pool) 314{ 315 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 316 SVN_ERR(initialize_fs_struct(fs)); 317 SVN_ERR(svn_fs_fs__open(fs, path, pool)); 318 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 319 SVN_ERR(fs_serialized_init(fs, common_pool, pool)); 320 return svn_fs_fs__pack(fs, notify_func, notify_baton, 321 cancel_func, cancel_baton, pool); 322} 323 324 325 326 327/* This implements the fs_library_vtable_t.hotcopy() API. Copy a 328 possibly live Subversion filesystem SRC_FS from SRC_PATH to a 329 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to 330 re-copy data which already exists in DST_FS. 331 The CLEAN_LOGS argument is ignored and included for Subversion 332 1.0.x compatibility. Perform all temporary allocations in POOL. */ 333static svn_error_t * 334fs_hotcopy(svn_fs_t *src_fs, 335 svn_fs_t *dst_fs, 336 const char *src_path, 337 const char *dst_path, 338 svn_boolean_t clean_logs, 339 svn_boolean_t incremental, 340 svn_cancel_func_t cancel_func, 341 void *cancel_baton, 342 apr_pool_t *pool) 343{ 344 SVN_ERR(svn_fs__check_fs(src_fs, FALSE)); 345 SVN_ERR(initialize_fs_struct(src_fs)); 346 SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool)); 347 SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool)); 348 SVN_ERR(fs_serialized_init(src_fs, pool, pool)); 349 350 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE)); 351 SVN_ERR(initialize_fs_struct(dst_fs)); 352 /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS. 353 Otherwise, it's not an FS yet --- possibly just an empty dir --- so 354 can't be opened. 355 */ 356 return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path, 357 incremental, cancel_func, cancel_baton, pool); 358} 359 360 361 362/* This function is included for Subversion 1.0.x compatibility. It 363 has no effect for fsfs backed Subversion filesystems. It conforms 364 to the fs_library_vtable_t.bdb_logfiles() API. */ 365static svn_error_t * 366fs_logfiles(apr_array_header_t **logfiles, 367 const char *path, 368 svn_boolean_t only_unused, 369 apr_pool_t *pool) 370{ 371 /* A no-op for FSFS. */ 372 *logfiles = apr_array_make(pool, 0, sizeof(const char *)); 373 374 return SVN_NO_ERROR; 375} 376 377 378 379 380 381/* Delete the filesystem located at path PATH. Perform any temporary 382 allocations in POOL. */ 383static svn_error_t * 384fs_delete_fs(const char *path, 385 apr_pool_t *pool) 386{ 387 /* Remove everything. */ 388 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 389} 390 391static const svn_version_t * 392fs_version(void) 393{ 394 SVN_VERSION_BODY; 395} 396 397static const char * 398fs_get_description(void) 399{ 400 return _("Module for working with a plain file (FSFS) repository."); 401} 402 403static svn_error_t * 404fs_set_svn_fs_open(svn_fs_t *fs, 405 svn_error_t *(*svn_fs_open_)(svn_fs_t **, 406 const char *, 407 apr_hash_t *, 408 apr_pool_t *)) 409{ 410 fs_fs_data_t *ffd = fs->fsap_data; 411 ffd->svn_fs_open_ = svn_fs_open_; 412 return SVN_NO_ERROR; 413} 414 415 416/* Base FS library vtable, used by the FS loader library. */ 417 418static fs_library_vtable_t library_vtable = { 419 fs_version, 420 fs_create, 421 fs_open, 422 fs_open_for_recovery, 423 fs_upgrade, 424 fs_verify, 425 fs_delete_fs, 426 fs_hotcopy, 427 fs_get_description, 428 svn_fs_fs__recover, 429 fs_pack, 430 fs_logfiles, 431 NULL /* parse_id */, 432 fs_set_svn_fs_open 433}; 434 435svn_error_t * 436svn_fs_fs__init(const svn_version_t *loader_version, 437 fs_library_vtable_t **vtable, apr_pool_t* common_pool) 438{ 439 static const svn_version_checklist_t checklist[] = 440 { 441 { "svn_subr", svn_subr_version }, 442 { "svn_delta", svn_delta_version }, 443 { NULL, NULL } 444 }; 445 446 /* Simplified version check to make sure we can safely use the 447 VTABLE parameter. The FS loader does a more exhaustive check. */ 448 if (loader_version->major != SVN_VER_MAJOR) 449 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 450 _("Unsupported FS loader version (%d) for fsfs"), 451 loader_version->major); 452 SVN_ERR(svn_ver_check_list(fs_version(), checklist)); 453 454 *vtable = &library_vtable; 455 return SVN_NO_ERROR; 456} 457