util.c revision 299742
140496Sbde/* util.c --- utility functions for FSFS repo access
250473Speter *
31539Srgrimes * ====================================================================
4114731Sbde *    Licensed to the Apache Software Foundation (ASF) under one
51539Srgrimes *    or more contributor license agreements.  See the NOTICE file
6156813Sru *    distributed with this work for additional information
7156813Sru *    regarding copyright ownership.  The ASF licenses this file
818420Sbde *    to you under the Apache License, Version 2.0 (the
9232498Stheraven *    "License"); you may not use this file except in compliance
10133567Stjr *    with the License.  You may obtain a copy of the License at
11133559Stjr *
12107046Smarcel *      http://www.apache.org/licenses/LICENSE-2.0
13153838Sdfr *
14220370Sobrien *    Unless required by applicable law or agreed to in writing,
15107046Smarcel *    software distributed under the License is distributed on an
16157236Sjasone *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17152994Sru *    KIND, either express or implied.  See the License for the
18166243Speter *    specific language governing permissions and limitations
19153486Sphk *    under the License.
20205146Sed * ====================================================================
21201546Sdavidxu */
22251230Sed
23228879Sed#include <assert.h>
24199898Sed
25133333Sstefanf#include "svn_ctype.h"
26250883Sed#include "svn_dirent_uri.h"
27232498Stheraven#include "private/svn_string_private.h"
281539Srgrimes
29244401Sbrooks#include "fs_fs.h"
30244401Sbrooks#include "pack.h"
31244401Sbrooks#include "util.h"
32119630Skan
3334030Sdufault#include "../libsvn_fs/fs-loader.h"
34201546Sdavidxu
3534030Sdufault#include "svn_private_config.h"
36251230Sed
37251230Sedsvn_boolean_t
381539Srgrimessvn_fs_fs__is_packed_rev(svn_fs_t *fs,
39171453Srwatson                         svn_revnum_t rev)
40252356Sdavide{
41156905Sru  fs_fs_data_t *ffd = fs->fsap_data;
42188642Snyan
4317900Speter  return (rev < ffd->min_unpacked_rev);
44195534Sscottl}
45246367Sjhb
46246367Sjhbsvn_boolean_t
47240621Sjimharrissvn_fs_fs__is_packed_revprop(svn_fs_t *fs,
48246367Sjhb                             svn_revnum_t rev)
49172397Sru{
50241636Sattilio  fs_fs_data_t *ffd = fs->fsap_data;
51252356Sdavide
52163851Spjd  /* rev 0 will not be packed */
53202437Strasz  return (rev < ffd->min_unpacked_rev)
54219974Smav      && (rev != 0)
55135339Sglebius      && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
56156885Srwatson}
57135339Sglebius
58135339Sglebiussvn_revnum_t
59137556Smarkmsvn_fs_fs__packed_base_rev(svn_fs_t *fs,
6077031Sru                           svn_revnum_t revision)
61192901Sthompsa{
62178818Sjhb  fs_fs_data_t *ffd = fs->fsap_data;
63210024Snwhitehorn  return (revision < ffd->min_unpacked_rev)
64160892Ssobomax       ? (revision - (revision % ffd->max_files_per_dir))
65160892Ssobomax       : revision;
66160892Ssobomax}
67156813Sru
68141397Sphkconst char *
69141397Sphksvn_fs_fs__path_txn_current(svn_fs_t *fs,
70141397Sphk                            apr_pool_t *pool)
71156813Sru{
72148796Sphk  return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
73148796Sphk}
74148796Sphk
75156813Sruconst char *
76178818Sjhbsvn_fs_fs__path_txn_current_lock(svn_fs_t *fs,
77125123Semax                                 apr_pool_t *pool)
78107139Sjulian{
79252356Sdavide  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
80156905Sru}
81156905Sru
82156905Sruconst char *
83156905Srusvn_fs_fs__path_lock(svn_fs_t *fs,
84254273Speter                     apr_pool_t *pool)
85254273Speter{
86254273Speter  return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
87254273Speter}
88254273Speter
89254273Speterconst char *
90254273Spetersvn_fs_fs__path_pack_lock(svn_fs_t *fs,
91254273Speter                          apr_pool_t *pool)
92254273Speter{
93254273Speter  return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool);
94254273Speter}
95254273Speter
96254273Speterconst char *
97254273Spetersvn_fs_fs__path_revprop_generation(svn_fs_t *fs,
9854351Smarcel                                   apr_pool_t *pool)
9954351Smarcel{
10054351Smarcel  return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
10154351Smarcel}
10254351Smarcel
10354351Smarcelconst char *
10454351Smarcelsvn_fs_fs__path_rev_packed(svn_fs_t *fs,
10596462Sru                           svn_revnum_t rev,
10625734Speter                           const char *kind,
107255775Sian                           apr_pool_t *pool)
108255775Sian{
109255775Sian  fs_fs_data_t *ffd = fs->fsap_data;
11025734Speter
111255775Sian  assert(ffd->max_files_per_dir);
112255775Sian  assert(svn_fs_fs__is_packed_rev(fs, rev));
113255775Sian
114255775Sian  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
115255775Sian                              apr_psprintf(pool,
116255807Sian                                           "%ld" PATH_EXT_PACKED_SHARD,
117255775Sian                                           rev / ffd->max_files_per_dir),
11888055Sru                              kind, SVN_VA_NULL);
11996462Sru}
12017900Speter
12188055Sruconst char *
12296462Srusvn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
12317900Speter{
12488055Sru  fs_fs_data_t *ffd = fs->fsap_data;
125164184Strhodes
12634030Sdufault  assert(ffd->max_files_per_dir);
12754351Smarcel  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
128211725Simp                              apr_psprintf(pool, "%ld",
129214629Sjhb                                                 rev / ffd->max_files_per_dir),
130144514Simp                              SVN_VA_NULL);
131214629Sjhb}
132214629Sjhb
133214629Sjhbconst char *
134144514Simpsvn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
135114731Sbde{
136114731Sbde  fs_fs_data_t *ffd = fs->fsap_data;
137114731Sbde
138114731Sbde  assert(! svn_fs_fs__is_packed_rev(fs, rev));
139114731Sbde
140114731Sbde  if (ffd->max_files_per_dir)
141114731Sbde    {
142214629Sjhb      return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool),
143114731Sbde                             apr_psprintf(pool, "%ld", rev),
144114731Sbde                             pool);
14554351Smarcel    }
14654351Smarcel
147114731Sbde  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
148114731Sbde                              apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
149114731Sbde}
150156813Sru
151135851Sdougb/* Set *PATH to the path of REV in FS with PACKED selecting whether the
152135851Sdougb   (potential) pack file or single revision file name is returned.
153135851Sdougb   Allocate *PATH in POOL.
154135851Sdougb*/
155114731Sbdestatic const char *
156114731Sbdepath_rev_absolute_internal(svn_fs_t *fs,
157144514Simp                           svn_revnum_t rev,
158214629Sjhb                           svn_boolean_t packed,
159114731Sbde                           apr_pool_t *pool)
160114731Sbde{
161114731Sbde  return packed
162114731Sbde       ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool)
163114731Sbde       : svn_fs_fs__path_rev(fs, rev, pool);
164114731Sbde}
165114731Sbde
166246367Sjhbconst char *
16754351Smarcelsvn_fs_fs__path_rev_absolute(svn_fs_t *fs,
168114731Sbde                             svn_revnum_t rev,
169114731Sbde                             apr_pool_t *pool)
17054351Smarcel{
171143013Snjl  fs_fs_data_t *ffd = fs->fsap_data;
172143013Snjl  svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT
173143013Snjl                         && svn_fs_fs__is_packed_rev(fs, rev);
174246367Sjhb
175246367Sjhb  return path_rev_absolute_internal(fs, rev, is_packed, pool);
176246367Sjhb}
177123288Sobrien
178123288Sobrienconst char *
179123288Sobriensvn_fs_fs__path_revprops_shard(svn_fs_t *fs,
180235537Sgber                               svn_revnum_t rev,
181235537Sgber                               apr_pool_t *pool)
182235537Sgber{
183235537Sgber  fs_fs_data_t *ffd = fs->fsap_data;
184235537Sgber
185235537Sgber  assert(ffd->max_files_per_dir);
186235537Sgber  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
187246367Sjhb                              apr_psprintf(pool, "%ld",
188246367Sjhb                                           rev / ffd->max_files_per_dir),
189246367Sjhb                              SVN_VA_NULL);
190130416Smlaier}
191130416Smlaier
192130416Smlaierconst char *
193166640Srodrigcsvn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs,
194166640Srodrigc                                    svn_revnum_t rev,
195166640Srodrigc                                    apr_pool_t *pool)
196156813Sru{
197116734Sru  fs_fs_data_t *ffd = fs->fsap_data;
198116734Sru
199116734Sru  assert(ffd->max_files_per_dir);
200145539Sscottl  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
201143423Sume                              apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD,
202143423Sume                                           rev / ffd->max_files_per_dir),
203143423Sume                              SVN_VA_NULL);
204116734Sru}
205116734Sru
206114731Sbdeconst char *
207144514Simpsvn_fs_fs__path_revprops(svn_fs_t *fs,
208114731Sbde                         svn_revnum_t rev,
209114731Sbde                         apr_pool_t *pool)
210144514Simp{
211144514Simp  fs_fs_data_t *ffd = fs->fsap_data;
212114731Sbde
213114731Sbde  if (ffd->max_files_per_dir)
21477857Sjlemon    {
215214629Sjhb      return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool),
216214629Sjhb                             apr_psprintf(pool, "%ld", rev),
217144561Simp                             pool);
218144561Simp    }
219144514Simp
220144514Simp  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
221144514Simp                              apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
222144514Simp}
223144561Simp
224144561Simp/* Return TO_ADD appended to the C string representation of TXN_ID.
225144514Simp * Allocate the result in POOL.
226144514Simp */
227144514Simpstatic const char *
228144514Simpcombine_txn_id_string(const svn_fs_fs__id_part_t *txn_id,
229144514Simp                      const char *to_add,
230214629Sjhb                      apr_pool_t *pool)
231168677Spjd{
232168677Spjd  return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool),
233168677Spjd                     to_add, SVN_VA_NULL);
2341539Srgrimes}
23554351Smarcel
23654351Smarcelconst char *
23756645Spetersvn_fs_fs__path_txns_dir(svn_fs_t *fs,
238114731Sbde                         apr_pool_t *pool)
239114731Sbde{
240114731Sbde  return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
241114731Sbde}
24254351Smarcel
243246367Sjhbconst char *
244114731Sbdesvn_fs_fs__path_txn_dir(svn_fs_t *fs,
245114731Sbde                        const svn_fs_fs__id_part_t *txn_id,
246114731Sbde                        apr_pool_t *pool)
247114731Sbde{
24877046Sru  SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
249143013Snjl  return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
250143013Snjl                         combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
251143013Snjl                         pool);
252143013Snjl}
253143013Snjl
254246367Sjhbconst char*
255246367Sjhbsvn_fs_fs__path_l2p_proto_index(svn_fs_t *fs,
256246367Sjhb                                const svn_fs_fs__id_part_t *txn_id,
257246367Sjhb                                apr_pool_t *pool)
258246367Sjhb{
259142992Sru  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
260142992Sru                         PATH_INDEX PATH_EXT_L2P_INDEX, pool);
261142992Sru}
262142992Sru
263142992Sruconst char*
264235537Sgbersvn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
265235537Sgber                                const svn_fs_fs__id_part_t *txn_id,
266235537Sgber                                apr_pool_t *pool)
267235537Sgber{
268235537Sgber  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
269235537Sgber                         PATH_INDEX PATH_EXT_P2L_INDEX, pool);
270235537Sgber}
271246367Sjhb
272246367Sjhbconst char *
273246367Sjhbsvn_fs_fs__path_txn_item_index(svn_fs_t *fs,
274246367Sjhb                               const svn_fs_fs__id_part_t *txn_id,
275246367Sjhb                               apr_pool_t *pool)
276114731Sbde{
277114731Sbde  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
278114731Sbde                         PATH_TXN_ITEM_INDEX, pool);
279114731Sbde}
280114731Sbde
281114731Sbdeconst char *
282130416Smlaiersvn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
283130416Smlaier                               apr_pool_t *pool)
284130416Smlaier{
285130416Smlaier  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
286130416Smlaier}
287156813Sru
288116734Sruconst char *
289116734Srusvn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
290116734Sru                              const svn_fs_fs__id_part_t *txn_id,
291116734Sru                              apr_pool_t *pool)
292116734Sru{
293145539Sscottl  fs_fs_data_t *ffd = fs->fsap_data;
294143423Sume  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
295143423Sume    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
296143423Sume                           combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
297143423Sume                           pool);
298143423Sume  else
299114731Sbde    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
300114731Sbde                           PATH_REV, pool);
301114731Sbde}
302114731Sbde
303114731Sbde
304144514Simpconst char *
305114731Sbdesvn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs,
306144514Simp                                   const svn_fs_fs__id_part_t *txn_id,
307114731Sbde                                   apr_pool_t *pool)
308114731Sbde{
309144514Simp  fs_fs_data_t *ffd = fs->fsap_data;
310144514Simp  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
311114731Sbde    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
312144514Simp                           combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
313114731Sbde                                                 pool),
314114731Sbde                           pool);
315114731Sbde  else
316214629Sjhb    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
317214629Sjhb                           PATH_REV_LOCK, pool);
318144561Simp}
319144561Simp
320144514Simpconst char *
321144514Simpsvn_fs_fs__path_txn_node_rev(svn_fs_t *fs,
322144514Simp                             const svn_fs_id_t *id,
323144514Simp                             apr_pool_t *pool)
324144514Simp{
325144514Simp  char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data;
326144561Simp  *strrchr(filename, '.') = '\0';
327144561Simp
328144514Simp  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id),
329144514Simp                                                 pool),
330144514Simp                         apr_psprintf(pool, PATH_PREFIX_NODE "%s",
331144514Simp                                      filename),
332144514Simp                         pool);
333144514Simp}
334144514Simp
335214629Sjhbconst char *
336166640Srodrigcsvn_fs_fs__path_txn_node_props(svn_fs_t *fs,
337166640Srodrigc                               const svn_fs_id_t *id,
338166640Srodrigc                               apr_pool_t *pool)
339166640Srodrigc{
340166640Srodrigc  return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
341168677Spjd                     PATH_EXT_PROPS, SVN_VA_NULL);
342168677Spjd}
343168677Spjd
344168677Spjdconst char *
345168677Spjdsvn_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  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
444svn_error_t *
445svn_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
493svn_error_t *
494svn_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
526svn_error_t *
527svn_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
567svn_error_t *
568svn_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
583svn_error_t *
584svn_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
604svn_error_t *
605svn_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
633svn_error_t *
634svn_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
689svn_boolean_t
690svn_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}
695