summaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_fs/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_fs/util.c')
-rw-r--r--subversion/libsvn_fs_fs/util.c694
1 files changed, 694 insertions, 0 deletions
diff --git a/subversion/libsvn_fs_fs/util.c b/subversion/libsvn_fs_fs/util.c
new file mode 100644
index 0000000..faa1e3d
--- /dev/null
+++ b/subversion/libsvn_fs_fs/util.c
@@ -0,0 +1,694 @@
+/* util.c --- utility functions for FSFS repo access
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <assert.h>
+
+#include "svn_ctype.h"
+#include "svn_dirent_uri.h"
+#include "private/svn_string_private.h"
+
+#include "fs_fs.h"
+#include "pack.h"
+#include "util.h"
+
+#include "../libsvn_fs/fs-loader.h"
+
+#include "svn_private_config.h"
+
+svn_boolean_t
+svn_fs_fs__is_packed_rev(svn_fs_t *fs,
+ svn_revnum_t rev)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ return (rev < ffd->min_unpacked_rev);
+}
+
+svn_boolean_t
+svn_fs_fs__is_packed_revprop(svn_fs_t *fs,
+ svn_revnum_t rev)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ /* rev 0 will not be packed */
+ return (rev < ffd->min_unpacked_rev)
+ && (rev != 0)
+ && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
+}
+
+svn_revnum_t
+svn_fs_fs__packed_base_rev(svn_fs_t *fs,
+ svn_revnum_t revision)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ return (revision < ffd->min_unpacked_rev)
+ ? (revision - (revision % ffd->max_files_per_dir))
+ : revision;
+}
+
+const char *
+svn_fs_fs__path_txn_current(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
+}
+
+const char *
+svn_fs_fs__path_txn_current_lock(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
+}
+
+const char *
+svn_fs_fs__path_lock(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
+}
+
+const char *
+svn_fs_fs__path_pack_lock(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool);
+}
+
+const char *
+svn_fs_fs__path_revprop_generation(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
+}
+
+const char *
+svn_fs_fs__path_rev_packed(svn_fs_t *fs,
+ svn_revnum_t rev,
+ const char *kind,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ assert(ffd->max_files_per_dir);
+ assert(svn_fs_fs__is_packed_rev(fs, rev));
+
+ return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
+ apr_psprintf(pool,
+ "%ld" PATH_EXT_PACKED_SHARD,
+ rev / ffd->max_files_per_dir),
+ kind, SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ assert(ffd->max_files_per_dir);
+ return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
+ apr_psprintf(pool, "%ld",
+ rev / ffd->max_files_per_dir),
+ SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ assert(! svn_fs_fs__is_packed_rev(fs, rev));
+
+ if (ffd->max_files_per_dir)
+ {
+ return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool),
+ apr_psprintf(pool, "%ld", rev),
+ pool);
+ }
+
+ return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
+ apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
+}
+
+/* Set *PATH to the path of REV in FS with PACKED selecting whether the
+ (potential) pack file or single revision file name is returned.
+ Allocate *PATH in POOL.
+*/
+static const char *
+path_rev_absolute_internal(svn_fs_t *fs,
+ svn_revnum_t rev,
+ svn_boolean_t packed,
+ apr_pool_t *pool)
+{
+ return packed
+ ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool)
+ : svn_fs_fs__path_rev(fs, rev, pool);
+}
+
+const char *
+svn_fs_fs__path_rev_absolute(svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT
+ && svn_fs_fs__is_packed_rev(fs, rev);
+
+ return path_rev_absolute_internal(fs, rev, is_packed, pool);
+}
+
+const char *
+svn_fs_fs__path_revprops_shard(svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ assert(ffd->max_files_per_dir);
+ return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
+ apr_psprintf(pool, "%ld",
+ rev / ffd->max_files_per_dir),
+ SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ assert(ffd->max_files_per_dir);
+ return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
+ apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD,
+ rev / ffd->max_files_per_dir),
+ SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_revprops(svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ if (ffd->max_files_per_dir)
+ {
+ return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool),
+ apr_psprintf(pool, "%ld", rev),
+ pool);
+ }
+
+ return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
+ apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
+}
+
+/* Return TO_ADD appended to the C string representation of TXN_ID.
+ * Allocate the result in POOL.
+ */
+static const char *
+combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id,
+ const char *to_add,
+ apr_pool_t *pool)
+{
+ return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool),
+ to_add, SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_txns_dir(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
+}
+
+const char *
+svn_fs_fs__path_txn_dir(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *txn_id,
+ apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
+ return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
+ combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
+ pool);
+}
+
+const char*
+svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *txn_id,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
+ PATH_INDEX PATH_EXT_L2P_INDEX, pool);
+}
+
+const char*
+svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *txn_id,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
+ PATH_INDEX PATH_EXT_P2L_INDEX, pool);
+}
+
+const char *
+svn_fs_fs__path_txn_item_index(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *txn_id,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
+ PATH_TXN_ITEM_INDEX, pool);
+}
+
+const char *
+svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
+}
+
+const char *
+svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *txn_id,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
+ return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
+ combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
+ pool);
+ else
+ return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
+ PATH_REV, pool);
+}
+
+
+const char *
+svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *txn_id,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
+ return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
+ combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
+ pool),
+ pool);
+ else
+ return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
+ PATH_REV_LOCK, pool);
+}
+
+const char *
+svn_fs_fs__path_txn_node_rev(svn_fs_t *fs,
+ const svn_fs_id_t *id,
+ apr_pool_t *pool)
+{
+ char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data;
+ *strrchr(filename, '.') = '\0';
+
+ return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id),
+ pool),
+ apr_psprintf(pool, PATH_PREFIX_NODE "%s",
+ filename),
+ pool);
+}
+
+const char *
+svn_fs_fs__path_txn_node_props(svn_fs_t *fs,
+ const svn_fs_id_t *id,
+ apr_pool_t *pool)
+{
+ return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
+ PATH_EXT_PROPS, SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_txn_node_children(svn_fs_t *fs,
+ const svn_fs_id_t *id,
+ apr_pool_t *pool)
+{
+ return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
+ PATH_EXT_CHILDREN, SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_node_origin(svn_fs_t *fs,
+ const svn_fs_fs__id_part_t *node_id,
+ apr_pool_t *pool)
+{
+ char buffer[SVN_INT64_BUFFER_SIZE];
+ apr_size_t len = svn__ui64tobase36(buffer, node_id->number);
+
+ if (len > 1)
+ buffer[len - 1] = '\0';
+
+ return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
+ buffer, SVN_VA_NULL);
+}
+
+const char *
+svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
+}
+
+svn_error_t *
+svn_fs_fs__check_file_buffer_numeric(const char *buf,
+ apr_off_t offset,
+ const char *path,
+ const char *title,
+ apr_pool_t *pool)
+{
+ const char *p;
+
+ for (p = buf + offset; *p; p++)
+ if (!svn_ctype_isdigit(*p))
+ return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
+ _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
+ title, svn_dirent_local_style(path, pool), *p, buf);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
+ svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ char buf[80];
+ apr_file_t *file;
+ apr_size_t len;
+
+ SVN_ERR(svn_io_file_open(&file,
+ svn_fs_fs__path_min_unpacked_rev(fs, pool),
+ APR_READ | APR_BUFFERED,
+ APR_OS_DEFAULT,
+ pool));
+ len = sizeof(buf);
+ SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
+
+ SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
+
+ return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool);
+}
+
+svn_error_t *
+svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs,
+ svn_revnum_t revnum,
+ apr_pool_t *scratch_pool)
+{
+ const char *final_path;
+ char buf[SVN_INT64_BUFFER_SIZE];
+ apr_size_t len = svn__i64toa(buf, revnum);
+ buf[len] = '\n';
+
+ final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
+
+ SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1,
+ final_path /* copy_perms */, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__read_current(svn_revnum_t *rev,
+ apr_uint64_t *next_node_id,
+ apr_uint64_t *next_copy_id,
+ svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ svn_stringbuf_t *content;
+
+ SVN_ERR(svn_fs_fs__read_content(&content,
+ svn_fs_fs__path_current(fs, pool),
+ pool));
+
+ if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
+ {
+ /* When format 1 and 2 filesystems are upgraded, the 'current' file is
+ left intact. As a consequence, there is a window when a filesystem
+ has a new format, but this file still contains the IDs left from an
+ old format, i.e. looks like "359 j5 v\n". Do not be too strict here
+ and only expect a parseable revision number. */
+ SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
+
+ *next_node_id = 0;
+ *next_copy_id = 0;
+ }
+ else
+ {
+ const char *str;
+
+ SVN_ERR(svn_revnum_parse(rev, content->data, &str));
+ if (*str != ' ')
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Corrupt 'current' file"));
+
+ *next_node_id = svn__base36toui64(&str, str + 1);
+ if (*str != ' ')
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Corrupt 'current' file"));
+
+ *next_copy_id = svn__base36toui64(&str, str + 1);
+ if (*str != '\n')
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Corrupt 'current' file"));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__write_current(svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_uint64_t next_node_id,
+ apr_uint64_t next_copy_id,
+ apr_pool_t *pool)
+{
+ char *buf;
+ const char *name;
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ /* Now we can just write out this line. */
+ if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
+ {
+ buf = apr_psprintf(pool, "%ld\n", rev);
+ }
+ else
+ {
+ char node_id_str[SVN_INT64_BUFFER_SIZE];
+ char copy_id_str[SVN_INT64_BUFFER_SIZE];
+ svn__ui64tobase36(node_id_str, next_node_id);
+ svn__ui64tobase36(copy_id_str, next_copy_id);
+
+ buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
+ }
+
+ name = svn_fs_fs__path_current(fs, pool);
+ SVN_ERR(svn_io_write_atomic(name, buf, strlen(buf),
+ name /* copy_perms_path */, pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
+ svn_boolean_t *missing,
+ const char *path,
+ svn_boolean_t last_attempt,
+ apr_pool_t *pool)
+{
+ svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
+ if (missing)
+ *missing = FALSE;
+
+ if (err)
+ {
+ *content = NULL;
+
+ if (APR_STATUS_IS_ENOENT(err->apr_err))
+ {
+ if (!last_attempt)
+ {
+ svn_error_clear(err);
+ if (missing)
+ *missing = TRUE;
+ return SVN_NO_ERROR;
+ }
+ }
+#ifdef ESTALE
+ else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
+ || APR_TO_OS_ERROR(err->apr_err) == EIO)
+ {
+ if (!last_attempt)
+ {
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ }
+#endif
+ }
+
+ return svn_error_trace(err);
+}
+
+svn_error_t *
+svn_fs_fs__get_file_offset(apr_off_t *offset_p,
+ apr_file_t *file,
+ apr_pool_t *pool)
+{
+ apr_off_t offset;
+
+ /* Note that, for buffered files, one (possibly surprising) side-effect
+ of this call is to flush any unwritten data to disk. */
+ offset = 0;
+ SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
+ *offset_p = offset;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__read_content(svn_stringbuf_t **content,
+ const char *fname,
+ apr_pool_t *pool)
+{
+ int i;
+ *content = NULL;
+
+ for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i)
+ SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL,
+ fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT,
+ pool));
+
+ if (!*content)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Can't read '%s'"),
+ svn_dirent_local_style(fname, pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__read_number_from_stream(apr_int64_t *result,
+ svn_boolean_t *hit_eof,
+ svn_stream_t *stream,
+ apr_pool_t *scratch_pool)
+{
+ svn_stringbuf_t *sb;
+ svn_boolean_t eof;
+ svn_error_t *err;
+
+ SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
+ if (hit_eof)
+ *hit_eof = eof;
+ else
+ if (eof)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
+
+ if (!eof)
+ {
+ err = svn_cstring_atoi64(result, sb->data);
+ if (err)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
+ _("Number '%s' invalid or too large"),
+ sb->data);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__move_into_place(const char *old_filename,
+ const char *new_filename,
+ const char *perms_reference,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+
+ SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
+
+ /* Move the file into place. */
+ err = svn_io_file_rename(old_filename, new_filename, pool);
+ if (err && APR_STATUS_IS_EXDEV(err->apr_err))
+ {
+ apr_file_t *file;
+
+ /* Can't rename across devices; fall back to copying. */
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+ SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
+
+ /* Flush the target of the copy to disk. */
+ SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
+ APR_OS_DEFAULT, pool));
+ /* ### BH: Does this really guarantee a flush of the data written
+ ### via a completely different handle on all operating systems?
+ ###
+ ### Maybe we should perform the copy ourselves instead of making
+ ### apr do that and flush the real handle? */
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
+ }
+ if (err)
+ return svn_error_trace(err);
+
+#ifdef __linux__
+ {
+ /* Linux has the unusual feature that fsync() on a file is not
+ enough to ensure that a file's directory entries have been
+ flushed to disk; you have to fsync the directory as well.
+ On other operating systems, we'd only be asking for trouble
+ by trying to open and fsync a directory. */
+ const char *dirname;
+ apr_file_t *file;
+
+ dirname = svn_dirent_dirname(new_filename, pool);
+ SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
+ pool));
+ SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
+ }
+#endif
+
+ return SVN_NO_ERROR;
+}
+
+svn_boolean_t
+svn_fs_fs__use_log_addressing(svn_fs_t *fs)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ return ffd->use_log_addressing;
+}