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