📄 fs.c
字号:
}
SVN_ERR (svn_io_remove_file (live_log_path, sub_pool));
}
svn_pool_destroy (sub_pool);
}
return SVN_NO_ERROR;
}
/* ### There -must- be a more elegant way to do a compile-time check
for BDB 4.2 or later. We're doing this because apparently
env->get_flags() and DB->get_pagesize() don't exist in earlier
versions of BDB. */
#ifdef DB_LOG_AUTOREMOVE
/* Open the BDB environment at PATH and compare its configuration
flags with FLAGS. If every flag in FLAGS is set in the
environment, then set *MATCH to true. Else set *MATCH to false. */
static svn_error_t *
check_env_flags (svn_boolean_t *match,
u_int32_t flags,
const char *path,
apr_pool_t *pool)
{
DB_ENV *env;
u_int32_t envflags;
const char *path_native;
bdb_errcall_baton_t *ec_baton;
SVN_BDB_ERR (ec_baton, create_env (&env, &ec_baton, pool));
SVN_ERR (svn_utf_cstring_from_utf8 (&path_native, path, pool));
SVN_BDB_ERR (ec_baton, env->open (env, path_native,
(DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG
| DB_INIT_MPOOL | DB_INIT_TXN),
0666));
SVN_BDB_ERR (ec_baton, env->get_flags (env, &envflags));
SVN_BDB_ERR (ec_baton, env->close (env, 0));
if (flags & envflags)
*match = TRUE;
else
*match = FALSE;
return SVN_NO_ERROR;
}
/* Set *PAGESIZE to the size of pages used to hold items in the
database environment located at PATH.
*/
static svn_error_t *
get_db_pagesize (u_int32_t *pagesize,
const char *path,
apr_pool_t *pool)
{
DB_ENV *env;
DB *nodes_table;
const char *path_native;
bdb_errcall_baton_t *ec_baton;
SVN_BDB_ERR (ec_baton, create_env (&env, &ec_baton, pool));
SVN_ERR (svn_utf_cstring_from_utf8 (&path_native, path, pool));
SVN_BDB_ERR (ec_baton, env->open (env, path_native,
(DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG
| DB_INIT_MPOOL | DB_INIT_TXN),
0666));
/* ### We're only asking for the pagesize on the 'nodes' table.
Is this enough? We never call DB->set_pagesize() on any of
our tables, so presumably BDB is using the same default
pagesize for all our databases, right? */
SVN_BDB_ERR (ec_baton, svn_fs_bdb__open_nodes_table (&nodes_table, env,
FALSE));
SVN_BDB_ERR (ec_baton, nodes_table->get_pagesize (nodes_table, pagesize));
SVN_BDB_ERR (ec_baton, nodes_table->close (nodes_table, 0));
SVN_BDB_ERR (ec_baton, env->close (env, 0));
return SVN_NO_ERROR;
}
#endif /* DB_LOG_AUTOREMOVE */
/* Ensure compatibility with older APR 0.9.5 snapshots which don't
* support the APR_LARGEFILE flag. */
#ifndef APR_LARGEFILE
#define APR_LARGEFILE (0)
#endif
/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size
CHUNKSIZE. The read/write buffer of size CHUNKSIZE will be
allocated in POOL. */
static svn_error_t *
copy_db_file_safely (const char *src_dir,
const char *dst_dir,
const char *filename,
u_int32_t chunksize,
apr_pool_t *pool)
{
apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */
const char *file_src_path = svn_path_join (src_dir, filename, pool);
const char *file_dst_path = svn_path_join (dst_dir, filename, pool);
apr_status_t status;
char *buf;
/* Open source file. */
status = apr_file_open (&s, file_src_path, (APR_READ | APR_LARGEFILE),
APR_OS_DEFAULT, pool);
if (status)
return svn_error_createf (status, NULL,
"Can't open file '%s' for reading.",
file_src_path);
/* Open destination file. */
status = apr_file_open (&d, file_dst_path,
(APR_WRITE | APR_CREATE | APR_LARGEFILE),
APR_OS_DEFAULT, pool);
if (status)
return svn_error_createf (status, NULL,
"Can't open file '%s' for writing.",
file_dst_path);
/* Allocate our read/write buffer. */
buf = apr_palloc (pool, chunksize);
/* Copy bytes till the cows come home. */
while (1)
{
apr_size_t bytes_this_time = chunksize;
apr_status_t read_err;
apr_status_t write_err;
/* Read 'em. */
read_err = apr_file_read(s, buf, &bytes_this_time);
if (read_err && !APR_STATUS_IS_EOF(read_err))
{
apr_file_close(s); /* toss any error */
apr_file_close(d); /* toss any error */
return svn_error_createf (status, NULL,
"Error reading file '%s'.",
file_src_path);
}
/* Write 'em. */
write_err = apr_file_write_full(d, buf, bytes_this_time, NULL);
if (write_err)
{
apr_file_close(s); /* toss any error */
apr_file_close(d); /* toss any error */
return svn_error_createf (status, NULL,
"Error writing file '%s'.",
file_dst_path);
}
if (read_err && APR_STATUS_IS_EOF(read_err))
{
status = apr_file_close(s);
if (status)
return svn_error_createf (status, NULL, "Can't close file '%s'.",
file_src_path);
status = apr_file_close(d);
if (status)
return svn_error_createf (status, NULL, "Can't close file '%s'.",
file_dst_path);
break; /* got EOF on read, all files closed, all done. */
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
base_hotcopy (const char *src_path,
const char *dest_path,
svn_boolean_t clean_logs,
apr_pool_t *pool)
{
svn_error_t *err;
u_int32_t pagesize;
svn_boolean_t log_autoremove = FALSE;
/* If using DB 4.2 or later, note whether the DB_LOG_AUTOREMOVE
feature is on. If it is, we have a potential race condition:
another process might delete a logfile while we're in the middle
of copying all the logfiles. (This is not a huge deal; at worst,
the hotcopy fails with a file-not-found error.) */
#ifdef DB_LOG_AUTOREMOVE
SVN_ERR (check_env_flags (&log_autoremove, DB_LOG_AUTOREMOVE,
src_path, pool));
#endif
/* Copy the DB_CONFIG file. */
SVN_ERR (svn_io_dir_file_copy (src_path, dest_path, "DB_CONFIG", pool));
/* In order to copy the database files safely and atomically, we
must copy them in chunks which are multiples of the page-size
used by BDB. See sleepycat docs for details, or svn issue #1818. */
#ifdef DB_LOG_AUTOREMOVE
SVN_ERR (get_db_pagesize (&pagesize, src_path, pool));
if (pagesize < SVN_STREAM_CHUNK_SIZE)
{
/* use the largest multiple of BDB pagesize we can. */
int multiple = SVN_STREAM_CHUNK_SIZE / pagesize;
pagesize *= multiple;
}
#else
/* default to 128K chunks, which should be safe.
BDB almost certainly uses a power-of-2 pagesize. */
pagesize = (4096 * 32);
#endif
/* Copy the databases. */
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"nodes", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"transactions", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"revisions", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"copies", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"changes", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"representations", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"strings", pagesize, pool));
SVN_ERR (copy_db_file_safely (src_path, dest_path,
"uuids", pagesize, pool));
{
apr_array_header_t *logfiles;
int idx;
apr_pool_t *subpool;
SVN_ERR (base_bdb_logfiles (&logfiles,
src_path,
FALSE, /* All logs */
pool));
/* Process log files. */
subpool = svn_pool_create (pool);
for (idx = 0; idx < logfiles->nelts; idx++)
{
svn_pool_clear (subpool);
err = svn_io_dir_file_copy (src_path, dest_path,
APR_ARRAY_IDX (logfiles, idx,
const char *),
subpool);
if (err)
{
if (log_autoremove)
return
svn_error_quick_wrap
(err,
_("Error copying logfile; the DB_LOG_AUTOREMOVE feature \n"
"may be interfering with the hotcopy algorithm. If \n"
"the problem persists, try deactivating this feature \n"
"in DB_CONFIG."));
else
return err;
}
}
svn_pool_destroy (subpool);
}
/* Since this is a copy we will have exclusive access to the repository. */
err = bdb_catastrophic_recover (dest_path, pool);
if (err)
{
if (log_autoremove)
return
svn_error_quick_wrap
(err,
_("Error running catastrophic recovery on hotcopy; the \n"
"DB_LOG_AUTOREMOVE feature may be interfering with the \n"
"hotcopy algorithm. If the problem persists, try deactivating \n"
"this feature in DB_CONFIG."));
else
return err;
}
if (clean_logs == TRUE)
SVN_ERR (svn_fs_base__clean_logs (src_path, dest_path, pool));
return SVN_NO_ERROR;
}
/* Deleting a Berkeley DB-based filesystem. */
static svn_error_t *
base_delete_fs (const char *path,
apr_pool_t *pool)
{
DB_ENV *env;
const char *path_native;
bdb_errcall_baton_t *ec_baton;
/* First, use the Berkeley DB library function to remove any shared
memory segments. */
SVN_BDB_ERR (ec_baton, create_env (&env, &ec_baton, pool));
SVN_ERR (svn_utf_cstring_from_utf8 (&path_native, path, pool));
SVN_BDB_ERR (ec_baton, env->remove (env, path_native, DB_FORCE));
/* Remove the environment directory. */
SVN_ERR (svn_io_remove_dir (path, pool));
return SVN_NO_ERROR;
}
/* Miscellany */
const char *
svn_fs_base__canonicalize_abspath (const char *path, apr_pool_t *pool)
{
char *newpath;
int path_len;
int path_i = 0, newpath_i = 0;
svn_boolean_t eating_slashes = FALSE;
/* No PATH? No problem. */
if (! path)
return NULL;
/* Empty PATH? That's just "/". */
if (! *path)
return apr_pstrdup (pool, "/");
/* Now, the fun begins. Alloc enough room to hold PATH with an
added leading '/'. */
path_len = strlen (path);
newpath = apr_pcalloc (pool, path_len + 2);
/* No leading slash? Fix that. */
if (*path != '/')
{
newpath[newpath_i++] = '/';
}
for (path_i = 0; path_i < path_len; path_i++)
{
if (path[path_i] == '/')
{
/* The current character is a '/'. If we are eating up
extra '/' characters, skip this character. Else, note
that we are now eating slashes. */
if (eating_slashes)
continue;
eating_slashes = TRUE;
}
else
{
/* The current character is NOT a '/'. If we were eating
slashes, we need not do that any more. */
if (eating_slashes)
eating_slashes = FALSE;
}
/* Copy the current character into our new buffer. */
newpath[newpath_i++] = path[path_i];
}
/* Did we leave a '/' attached to the end of NEWPATH (other than in
the root directory case)? */
if ((newpath[newpath_i - 1] == '/') && (newpath_i > 1))
newpath[newpath_i - 1] = '\0';
return newpath;
}
static const svn_version_t *
base_version (void)
{
SVN_VERSION_BODY;
}
/* Base FS library vtable, used by the FS loader library. */
static fs_library_vtable_t library_vtable = {
base_version,
base_create,
base_open,
base_delete_fs,
base_hotcopy,
base_bdb_set_errcall,
base_bdb_recover,
base_bdb_logfiles,
svn_fs_base__id_parse
};
svn_error_t *
svn_fs_base__init (const svn_version_t *loader_version,
fs_library_vtable_t **vtable)
{
static const svn_version_checklist_t checklist[] =
{
{ "svn_subr", svn_subr_version },
{ "svn_delta", svn_delta_version },
{ NULL, NULL }
};
/* Simplified version check to make sure we can safely use the
VTABLE parameter. The FS loader does a more exhaustive check. */
if (loader_version->major != SVN_VER_MAJOR)
return svn_error_createf (SVN_ERR_VERSION_MISMATCH, NULL,
_("Unsupported FS loader version (%d) for bdb"),
loader_version->major);
SVN_ERR (svn_ver_check_list (base_version(), checklist));
SVN_ERR (check_bdb_version());
*vtable = &library_vtable;
return SVN_NO_ERROR;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -