]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_fs/util.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_fs / util.c
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
37 svn_boolean_t
38 svn_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
46 svn_boolean_t
47 svn_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
58 svn_revnum_t
59 svn_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
68 const char *
69 svn_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
75 const char *
76 svn_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
82 const char *
83 svn_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
89 const char *
90 svn_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
96 const char *
97 svn_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
103 const char *
104 svn_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
121 const char *
122 svn_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
133 const char *
134 svn_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 */
155 static const char *
156 path_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
166 const char *
167 svn_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
178 const char *
179 svn_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
192 const char *
193 svn_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
206 const char *
207 svn_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  */
227 static const char *
228 combine_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
236 const char *
237 svn_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
243 const char *
244 svn_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
254 const char*
255 svn_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
263 const char*
264 svn_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
272 const char *
273 svn_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
281 const char *
282 svn_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
288 const char *
289 svn_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
304 const char *
305 svn_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
320 const char *
321 svn_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
335 const char *
336 svn_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
344 const char *
345 svn_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
353 const char *
354 svn_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
368 const char *
369 svn_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
375 svn_error_t *
376 svn_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
393 svn_error_t *
394 svn_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
415 svn_error_t *
416 svn_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
426 svn_error_t *
427 svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs,
428                                   svn_revnum_t revnum,
429                                   apr_pool_t *scratch_pool)
430 {
431   const char *final_path;
432   char buf[SVN_INT64_BUFFER_SIZE];
433   apr_size_t len = svn__i64toa(buf, revnum);
434   buf[len] = '\n';
435
436   final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
437
438   SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1,
439                               final_path /* copy_perms */, scratch_pool));
440
441   return SVN_NO_ERROR;
442 }
443
444 svn_error_t *
445 svn_fs_fs__read_current(svn_revnum_t *rev,
446                         apr_uint64_t *next_node_id,
447                         apr_uint64_t *next_copy_id,
448                         svn_fs_t *fs,
449                         apr_pool_t *pool)
450 {
451   fs_fs_data_t *ffd = fs->fsap_data;
452   svn_stringbuf_t *content;
453
454   SVN_ERR(svn_fs_fs__read_content(&content,
455                                   svn_fs_fs__path_current(fs, pool),
456                                   pool));
457
458   if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
459     {
460       /* When format 1 and 2 filesystems are upgraded, the 'current' file is
461          left intact.  As a consequence, there is a window when a filesystem
462          has a new format, but this file still contains the IDs left from an
463          old format, i.e. looks like "359 j5 v\n".  Do not be too strict here
464          and only expect a parseable revision number. */
465       SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
466
467       *next_node_id = 0;
468       *next_copy_id = 0;
469     }
470   else
471     {
472       const char *str;
473
474       SVN_ERR(svn_revnum_parse(rev, content->data, &str));
475       if (*str != ' ')
476         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
477                                 _("Corrupt 'current' file"));
478
479       *next_node_id = svn__base36toui64(&str, str + 1);
480       if (*str != ' ')
481         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
482                                 _("Corrupt 'current' file"));
483
484       *next_copy_id = svn__base36toui64(&str, str + 1);
485       if (*str != '\n')
486         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
487                                 _("Corrupt 'current' file"));
488     }
489
490   return SVN_NO_ERROR;
491 }
492
493 svn_error_t *
494 svn_fs_fs__write_current(svn_fs_t *fs,
495                          svn_revnum_t rev,
496                          apr_uint64_t next_node_id,
497                          apr_uint64_t next_copy_id,
498                          apr_pool_t *pool)
499 {
500   char *buf;
501   const char *name;
502   fs_fs_data_t *ffd = fs->fsap_data;
503
504   /* Now we can just write out this line. */
505   if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
506     {
507       buf = apr_psprintf(pool, "%ld\n", rev);
508     }
509   else
510     {
511       char node_id_str[SVN_INT64_BUFFER_SIZE];
512       char copy_id_str[SVN_INT64_BUFFER_SIZE];
513       svn__ui64tobase36(node_id_str, next_node_id);
514       svn__ui64tobase36(copy_id_str, next_copy_id);
515
516       buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
517     }
518
519   name = svn_fs_fs__path_current(fs, pool);
520   SVN_ERR(svn_io_write_atomic(name, buf, strlen(buf),
521                               name /* copy_perms_path */, pool));
522
523   return SVN_NO_ERROR;
524 }
525
526 svn_error_t *
527 svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
528                                    svn_boolean_t *missing,
529                                    const char *path,
530                                    svn_boolean_t last_attempt,
531                                    apr_pool_t *pool)
532 {
533   svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
534   if (missing)
535     *missing = FALSE;
536
537   if (err)
538     {
539       *content = NULL;
540
541       if (APR_STATUS_IS_ENOENT(err->apr_err))
542         {
543           if (!last_attempt)
544             {
545               svn_error_clear(err);
546               if (missing)
547                 *missing = TRUE;
548               return SVN_NO_ERROR;
549             }
550         }
551 #ifdef ESTALE
552       else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
553                 || APR_TO_OS_ERROR(err->apr_err) == EIO)
554         {
555           if (!last_attempt)
556             {
557               svn_error_clear(err);
558               return SVN_NO_ERROR;
559             }
560         }
561 #endif
562     }
563
564   return svn_error_trace(err);
565 }
566
567 svn_error_t *
568 svn_fs_fs__get_file_offset(apr_off_t *offset_p,
569                            apr_file_t *file,
570                            apr_pool_t *pool)
571 {
572   apr_off_t offset;
573
574   /* Note that, for buffered files, one (possibly surprising) side-effect
575      of this call is to flush any unwritten data to disk. */
576   offset = 0;
577   SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
578   *offset_p = offset;
579
580   return SVN_NO_ERROR;
581 }
582
583 svn_error_t *
584 svn_fs_fs__read_content(svn_stringbuf_t **content,
585                         const char *fname,
586                         apr_pool_t *pool)
587 {
588   int i;
589   *content = NULL;
590
591   for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i)
592     SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL,
593                         fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT,
594                         pool));
595
596   if (!*content)
597     return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
598                              _("Can't read '%s'"),
599                              svn_dirent_local_style(fname, pool));
600
601   return SVN_NO_ERROR;
602 }
603
604 svn_error_t *
605 svn_fs_fs__read_number_from_stream(apr_int64_t *result,
606                                    svn_boolean_t *hit_eof,
607                                    svn_stream_t *stream,
608                                    apr_pool_t *scratch_pool)
609 {
610   svn_stringbuf_t *sb;
611   svn_boolean_t eof;
612   svn_error_t *err;
613
614   SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
615   if (hit_eof)
616     *hit_eof = eof;
617   else
618     if (eof)
619       return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
620
621   if (!eof)
622     {
623       err = svn_cstring_atoi64(result, sb->data);
624       if (err)
625         return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
626                                  _("Number '%s' invalid or too large"),
627                                  sb->data);
628     }
629
630   return SVN_NO_ERROR;
631 }
632
633 svn_error_t *
634 svn_fs_fs__move_into_place(const char *old_filename,
635                            const char *new_filename,
636                            const char *perms_reference,
637                            apr_pool_t *pool)
638 {
639   svn_error_t *err;
640
641   SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
642
643   /* Move the file into place. */
644   err = svn_io_file_rename(old_filename, new_filename, pool);
645   if (err && APR_STATUS_IS_EXDEV(err->apr_err))
646     {
647       apr_file_t *file;
648
649       /* Can't rename across devices; fall back to copying. */
650       svn_error_clear(err);
651       err = SVN_NO_ERROR;
652       SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
653
654       /* Flush the target of the copy to disk. */
655       SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
656                                APR_OS_DEFAULT, pool));
657       /* ### BH: Does this really guarantee a flush of the data written
658          ### via a completely different handle on all operating systems?
659          ###
660          ### Maybe we should perform the copy ourselves instead of making
661          ### apr do that and flush the real handle? */
662       SVN_ERR(svn_io_file_flush_to_disk(file, pool));
663       SVN_ERR(svn_io_file_close(file, pool));
664     }
665   if (err)
666     return svn_error_trace(err);
667
668 #ifdef __linux__
669   {
670     /* Linux has the unusual feature that fsync() on a file is not
671        enough to ensure that a file's directory entries have been
672        flushed to disk; you have to fsync the directory as well.
673        On other operating systems, we'd only be asking for trouble
674        by trying to open and fsync a directory. */
675     const char *dirname;
676     apr_file_t *file;
677
678     dirname = svn_dirent_dirname(new_filename, pool);
679     SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
680                              pool));
681     SVN_ERR(svn_io_file_flush_to_disk(file, pool));
682     SVN_ERR(svn_io_file_close(file, pool));
683   }
684 #endif
685
686   return SVN_NO_ERROR;
687 }
688
689 svn_boolean_t
690 svn_fs_fs__use_log_addressing(svn_fs_t *fs)
691 {
692   fs_fs_data_t *ffd = fs->fsap_data;
693   return ffd->use_log_addressing;
694 }