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