1/* util.c --- utility functions for FSFS repo access
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 <assert.h>
24
25#include "svn_ctype.h"
26#include "svn_dirent_uri.h"
27#include "private/svn_string_private.h"
28
29#include "fs_fs.h"
30#include "pack.h"
31#include "util.h"
32
33#include "../libsvn_fs/fs-loader.h"
34
35#include "svn_private_config.h"
36
37svn_boolean_t
38svn_fs_fs__is_packed_rev(svn_fs_t *fs,
39                         svn_revnum_t rev)
40{
41  fs_fs_data_t *ffd = fs->fsap_data;
42
43  return (rev < ffd->min_unpacked_rev);
44}
45
46svn_boolean_t
47svn_fs_fs__is_packed_revprop(svn_fs_t *fs,
48                             svn_revnum_t rev)
49{
50  fs_fs_data_t *ffd = fs->fsap_data;
51
52  /* rev 0 will not be packed */
53  return (rev < ffd->min_unpacked_rev)
54      && (rev != 0)
55      && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
56}
57
58svn_revnum_t
59svn_fs_fs__packed_base_rev(svn_fs_t *fs,
60                           svn_revnum_t revision)
61{
62  fs_fs_data_t *ffd = fs->fsap_data;
63  return (revision < ffd->min_unpacked_rev)
64       ? (revision - (revision % ffd->max_files_per_dir))
65       : revision;
66}
67
68const char *
69svn_fs_fs__path_txn_current(svn_fs_t *fs,
70                            apr_pool_t *pool)
71{
72  return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
73}
74
75const char *
76svn_fs_fs__path_txn_current_lock(svn_fs_t *fs,
77                                 apr_pool_t *pool)
78{
79  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
80}
81
82const char *
83svn_fs_fs__path_lock(svn_fs_t *fs,
84                     apr_pool_t *pool)
85{
86  return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
87}
88
89const char *
90svn_fs_fs__path_pack_lock(svn_fs_t *fs,
91                          apr_pool_t *pool)
92{
93  return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool);
94}
95
96const char *
97svn_fs_fs__path_revprop_generation(svn_fs_t *fs,
98                                   apr_pool_t *pool)
99{
100  return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
101}
102
103const char *
104svn_fs_fs__path_rev_packed(svn_fs_t *fs,
105                           svn_revnum_t rev,
106                           const char *kind,
107                           apr_pool_t *pool)
108{
109  fs_fs_data_t *ffd = fs->fsap_data;
110
111  assert(ffd->max_files_per_dir);
112  assert(svn_fs_fs__is_packed_rev(fs, rev));
113
114  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
115                              apr_psprintf(pool,
116                                           "%ld" PATH_EXT_PACKED_SHARD,
117                                           rev / ffd->max_files_per_dir),
118                              kind, SVN_VA_NULL);
119}
120
121const char *
122svn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
123{
124  fs_fs_data_t *ffd = fs->fsap_data;
125
126  assert(ffd->max_files_per_dir);
127  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
128                              apr_psprintf(pool, "%ld",
129                                                 rev / ffd->max_files_per_dir),
130                              SVN_VA_NULL);
131}
132
133const char *
134svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
135{
136  fs_fs_data_t *ffd = fs->fsap_data;
137
138  assert(! svn_fs_fs__is_packed_rev(fs, rev));
139
140  if (ffd->max_files_per_dir)
141    {
142      return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool),
143                             apr_psprintf(pool, "%ld", rev),
144                             pool);
145    }
146
147  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
148                              apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
149}
150
151/* Set *PATH to the path of REV in FS with PACKED selecting whether the
152   (potential) pack file or single revision file name is returned.
153   Allocate *PATH in POOL.
154*/
155static const char *
156path_rev_absolute_internal(svn_fs_t *fs,
157                           svn_revnum_t rev,
158                           svn_boolean_t packed,
159                           apr_pool_t *pool)
160{
161  return packed
162       ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool)
163       : svn_fs_fs__path_rev(fs, rev, pool);
164}
165
166const char *
167svn_fs_fs__path_rev_absolute(svn_fs_t *fs,
168                             svn_revnum_t rev,
169                             apr_pool_t *pool)
170{
171  fs_fs_data_t *ffd = fs->fsap_data;
172  svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT
173                         && svn_fs_fs__is_packed_rev(fs, rev);
174
175  return path_rev_absolute_internal(fs, rev, is_packed, pool);
176}
177
178const char *
179svn_fs_fs__path_revprops_shard(svn_fs_t *fs,
180                               svn_revnum_t rev,
181                               apr_pool_t *pool)
182{
183  fs_fs_data_t *ffd = fs->fsap_data;
184
185  assert(ffd->max_files_per_dir);
186  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
187                              apr_psprintf(pool, "%ld",
188                                           rev / ffd->max_files_per_dir),
189                              SVN_VA_NULL);
190}
191
192const char *
193svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs,
194                                    svn_revnum_t rev,
195                                    apr_pool_t *pool)
196{
197  fs_fs_data_t *ffd = fs->fsap_data;
198
199  assert(ffd->max_files_per_dir);
200  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
201                              apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD,
202                                           rev / ffd->max_files_per_dir),
203                              SVN_VA_NULL);
204}
205
206const char *
207svn_fs_fs__path_revprops(svn_fs_t *fs,
208                         svn_revnum_t rev,
209                         apr_pool_t *pool)
210{
211  fs_fs_data_t *ffd = fs->fsap_data;
212
213  if (ffd->max_files_per_dir)
214    {
215      return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool),
216                             apr_psprintf(pool, "%ld", rev),
217                             pool);
218    }
219
220  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
221                              apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
222}
223
224/* Return TO_ADD appended to the C string representation of TXN_ID.
225 * Allocate the result in POOL.
226 */
227static const char *
228combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id,
229                      const char *to_add,
230                      apr_pool_t *pool)
231{
232  return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool),
233                     to_add, SVN_VA_NULL);
234}
235
236const char *
237svn_fs_fs__path_txns_dir(svn_fs_t *fs,
238                         apr_pool_t *pool)
239{
240  return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
241}
242
243const char *
244svn_fs_fs__path_txn_dir(svn_fs_t *fs,
245                        const svn_fs_fs__id_part_t *txn_id,
246                        apr_pool_t *pool)
247{
248  SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
249  return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
250                         combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
251                         pool);
252}
253
254const char*
255svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs,
256                                const svn_fs_fs__id_part_t *txn_id,
257                                apr_pool_t *pool)
258{
259  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
260                         PATH_INDEX PATH_EXT_L2P_INDEX, pool);
261}
262
263const char*
264svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
265                                const svn_fs_fs__id_part_t *txn_id,
266                                apr_pool_t *pool)
267{
268  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
269                         PATH_INDEX PATH_EXT_P2L_INDEX, pool);
270}
271
272const char *
273svn_fs_fs__path_txn_item_index(svn_fs_t *fs,
274                               const svn_fs_fs__id_part_t *txn_id,
275                               apr_pool_t *pool)
276{
277  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
278                         PATH_TXN_ITEM_INDEX, pool);
279}
280
281const char *
282svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
283                               apr_pool_t *pool)
284{
285  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
286}
287
288const char *
289svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
290                              const svn_fs_fs__id_part_t *txn_id,
291                              apr_pool_t *pool)
292{
293  fs_fs_data_t *ffd = fs->fsap_data;
294  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
295    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
296                           combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
297                           pool);
298  else
299    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
300                           PATH_REV, pool);
301}
302
303
304const char *
305svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs,
306                                   const svn_fs_fs__id_part_t *txn_id,
307                                   apr_pool_t *pool)
308{
309  fs_fs_data_t *ffd = fs->fsap_data;
310  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
311    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
312                           combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
313                                                 pool),
314                           pool);
315  else
316    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
317                           PATH_REV_LOCK, pool);
318}
319
320const char *
321svn_fs_fs__path_txn_node_rev(svn_fs_t *fs,
322                             const svn_fs_id_t *id,
323                             apr_pool_t *pool)
324{
325  char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data;
326  *strrchr(filename, '.') = '\0';
327
328  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id),
329                                                 pool),
330                         apr_psprintf(pool, PATH_PREFIX_NODE "%s",
331                                      filename),
332                         pool);
333}
334
335const char *
336svn_fs_fs__path_txn_node_props(svn_fs_t *fs,
337                               const svn_fs_id_t *id,
338                               apr_pool_t *pool)
339{
340  return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
341                     PATH_EXT_PROPS, SVN_VA_NULL);
342}
343
344const char *
345svn_fs_fs__path_txn_node_children(svn_fs_t *fs,
346                                  const svn_fs_id_t *id,
347                                  apr_pool_t *pool)
348{
349  return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
350                     PATH_EXT_CHILDREN, SVN_VA_NULL);
351}
352
353const char *
354svn_fs_fs__path_node_origin(svn_fs_t *fs,
355                            const svn_fs_fs__id_part_t *node_id,
356                            apr_pool_t *pool)
357{
358  char buffer[SVN_INT64_BUFFER_SIZE];
359  apr_size_t len = svn__ui64tobase36(buffer, node_id->number);
360
361  if (len > 1)
362    buffer[len - 1] = '\0';
363
364  return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
365                              buffer, SVN_VA_NULL);
366}
367
368const char *
369svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
370                                 apr_pool_t *pool)
371{
372  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
373}
374
375svn_error_t *
376svn_fs_fs__check_file_buffer_numeric(const char *buf,
377                                     apr_off_t offset,
378                                     const char *path,
379                                     const char *title,
380                                     apr_pool_t *pool)
381{
382  const char *p;
383
384  for (p = buf + offset; *p; p++)
385    if (!svn_ctype_isdigit(*p))
386      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
387        _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
388        title, svn_dirent_local_style(path, pool), *p, buf);
389
390  return SVN_NO_ERROR;
391}
392
393svn_error_t *
394svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
395                                 svn_fs_t *fs,
396                                 apr_pool_t *pool)
397{
398  char buf[80];
399  apr_file_t *file;
400  apr_size_t len;
401
402  SVN_ERR(svn_io_file_open(&file,
403                           svn_fs_fs__path_min_unpacked_rev(fs, pool),
404                           APR_READ | APR_BUFFERED,
405                           APR_OS_DEFAULT,
406                           pool));
407  len = sizeof(buf);
408  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
409  SVN_ERR(svn_io_file_close(file, pool));
410
411  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
412  return SVN_NO_ERROR;
413}
414
415svn_error_t *
416svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs,
417                                   apr_pool_t *pool)
418{
419  fs_fs_data_t *ffd = fs->fsap_data;
420
421  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
422
423  return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool);
424}
425
426svn_error_t *
427svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs,
428                                  svn_revnum_t revnum,
429                                  apr_pool_t *scratch_pool)
430{
431  fs_fs_data_t *ffd = fs->fsap_data;
432  const char *final_path;
433  char buf[SVN_INT64_BUFFER_SIZE];
434  apr_size_t len = svn__i64toa(buf, revnum);
435  buf[len] = '\n';
436
437  final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
438
439  SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1,
440                               final_path /* copy_perms */,
441                               ffd->flush_to_disk, scratch_pool));
442
443  return SVN_NO_ERROR;
444}
445
446svn_error_t *
447svn_fs_fs__read_current(svn_revnum_t *rev,
448                        apr_uint64_t *next_node_id,
449                        apr_uint64_t *next_copy_id,
450                        svn_fs_t *fs,
451                        apr_pool_t *pool)
452{
453  fs_fs_data_t *ffd = fs->fsap_data;
454  svn_stringbuf_t *content;
455
456  SVN_ERR(svn_fs_fs__read_content(&content,
457                                  svn_fs_fs__path_current(fs, pool),
458                                  pool));
459
460  if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
461    {
462      /* When format 1 and 2 filesystems are upgraded, the 'current' file is
463         left intact.  As a consequence, there is a window when a filesystem
464         has a new format, but this file still contains the IDs left from an
465         old format, i.e. looks like "359 j5 v\n".  Do not be too strict here
466         and only expect a parseable revision number. */
467      SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
468
469      *next_node_id = 0;
470      *next_copy_id = 0;
471    }
472  else
473    {
474      const char *str;
475
476      SVN_ERR(svn_revnum_parse(rev, content->data, &str));
477      if (*str != ' ')
478        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
479                                _("Corrupt 'current' file"));
480
481      *next_node_id = svn__base36toui64(&str, str + 1);
482      if (*str != ' ')
483        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
484                                _("Corrupt 'current' file"));
485
486      *next_copy_id = svn__base36toui64(&str, str + 1);
487      if (*str != '\n')
488        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
489                                _("Corrupt 'current' file"));
490    }
491
492  return SVN_NO_ERROR;
493}
494
495svn_error_t *
496svn_fs_fs__write_current(svn_fs_t *fs,
497                         svn_revnum_t rev,
498                         apr_uint64_t next_node_id,
499                         apr_uint64_t next_copy_id,
500                         apr_pool_t *pool)
501{
502  char *buf;
503  const char *name;
504  fs_fs_data_t *ffd = fs->fsap_data;
505
506  /* Now we can just write out this line. */
507  if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
508    {
509      buf = apr_psprintf(pool, "%ld\n", rev);
510    }
511  else
512    {
513      char node_id_str[SVN_INT64_BUFFER_SIZE];
514      char copy_id_str[SVN_INT64_BUFFER_SIZE];
515      svn__ui64tobase36(node_id_str, next_node_id);
516      svn__ui64tobase36(copy_id_str, next_copy_id);
517
518      buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
519    }
520
521  name = svn_fs_fs__path_current(fs, pool);
522  SVN_ERR(svn_io_write_atomic2(name, buf, strlen(buf),
523                               name /* copy_perms_path */,
524                               ffd->flush_to_disk, pool));
525
526  return SVN_NO_ERROR;
527}
528
529svn_error_t *
530svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
531                                   svn_boolean_t *missing,
532                                   const char *path,
533                                   svn_boolean_t last_attempt,
534                                   apr_pool_t *pool)
535{
536  svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
537  if (missing)
538    *missing = FALSE;
539
540  if (err)
541    {
542      *content = NULL;
543
544      if (APR_STATUS_IS_ENOENT(err->apr_err))
545        {
546          if (!last_attempt)
547            {
548              svn_error_clear(err);
549              if (missing)
550                *missing = TRUE;
551              return SVN_NO_ERROR;
552            }
553        }
554#ifdef ESTALE
555      else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
556                || APR_TO_OS_ERROR(err->apr_err) == EIO)
557        {
558          if (!last_attempt)
559            {
560              svn_error_clear(err);
561              return SVN_NO_ERROR;
562            }
563        }
564#endif
565    }
566
567  return svn_error_trace(err);
568}
569
570svn_error_t *
571svn_fs_fs__read_content(svn_stringbuf_t **content,
572                        const char *fname,
573                        apr_pool_t *pool)
574{
575  int i;
576  *content = NULL;
577
578  for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i)
579    SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL,
580                        fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT,
581                        pool));
582
583  if (!*content)
584    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
585                             _("Can't read '%s'"),
586                             svn_dirent_local_style(fname, pool));
587
588  return SVN_NO_ERROR;
589}
590
591svn_error_t *
592svn_fs_fs__read_number_from_stream(apr_int64_t *result,
593                                   svn_boolean_t *hit_eof,
594                                   svn_stream_t *stream,
595                                   apr_pool_t *scratch_pool)
596{
597  svn_stringbuf_t *sb;
598  svn_boolean_t eof;
599  svn_error_t *err;
600
601  SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
602  if (hit_eof)
603    *hit_eof = eof;
604  else
605    if (eof)
606      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
607
608  if (!eof)
609    {
610      err = svn_cstring_atoi64(result, sb->data);
611      if (err)
612        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
613                                 _("Number '%s' invalid or too large"),
614                                 sb->data);
615    }
616
617  return SVN_NO_ERROR;
618}
619
620svn_error_t *
621svn_fs_fs__move_into_place(const char *old_filename,
622                           const char *new_filename,
623                           const char *perms_reference,
624                           svn_boolean_t flush_to_disk,
625                           apr_pool_t *pool)
626{
627  svn_error_t *err;
628  apr_file_t *file;
629
630  /* Copying permissions is a no-op on WIN32. */
631  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
632
633  /* Move the file into place. */
634  err = svn_io_file_rename2(old_filename, new_filename, flush_to_disk, pool);
635  if (err && APR_STATUS_IS_EXDEV(err->apr_err))
636    {
637      /* Can't rename across devices; fall back to copying. */
638      svn_error_clear(err);
639      SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
640
641      /* Flush the target of the copy to disk.
642         ### The code below is duplicates svn_io_file_rename2(), because
643             currently we don't have the svn_io_copy_file2() function with
644             a flush_to_disk argument. */
645      if (flush_to_disk)
646        {
647          SVN_ERR(svn_io_file_open(&file, new_filename, APR_WRITE,
648                                   APR_OS_DEFAULT, pool));
649          SVN_ERR(svn_io_file_flush_to_disk(file, pool));
650          SVN_ERR(svn_io_file_close(file, pool));
651        }
652
653#ifdef SVN_ON_POSIX
654      if (flush_to_disk)
655        {
656          /* On POSIX, the file name is stored in the file's directory entry.
657             Hence, we need to fsync() that directory as well.
658             On other operating systems, we'd only be asking for trouble
659             by trying to open and fsync a directory. */
660          const char *dirname;
661
662          dirname = svn_dirent_dirname(new_filename, pool);
663          SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
664                                   pool));
665          SVN_ERR(svn_io_file_flush_to_disk(file, pool));
666          SVN_ERR(svn_io_file_close(file, pool));
667        }
668#endif
669    }
670  else if (err)
671    return svn_error_trace(err);
672
673  return SVN_NO_ERROR;
674}
675
676svn_boolean_t
677svn_fs_fs__use_log_addressing(svn_fs_t *fs)
678{
679  fs_fs_data_t *ffd = fs->fsap_data;
680  return ffd->use_log_addressing;
681}
682