1289177Speter/* util.c --- utility functions for FSFS repo access
2289177Speter *
3289177Speter * ====================================================================
4289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
5289177Speter *    or more contributor license agreements.  See the NOTICE file
6289177Speter *    distributed with this work for additional information
7289177Speter *    regarding copyright ownership.  The ASF licenses this file
8289177Speter *    to you under the Apache License, Version 2.0 (the
9289177Speter *    "License"); you may not use this file except in compliance
10289177Speter *    with the License.  You may obtain a copy of the License at
11289177Speter *
12289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
13289177Speter *
14289177Speter *    Unless required by applicable law or agreed to in writing,
15289177Speter *    software distributed under the License is distributed on an
16289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17289177Speter *    KIND, either express or implied.  See the License for the
18289177Speter *    specific language governing permissions and limitations
19289177Speter *    under the License.
20289177Speter * ====================================================================
21289177Speter */
22289177Speter
23289177Speter#include <assert.h>
24289177Speter
25289177Speter#include "svn_ctype.h"
26289177Speter#include "svn_dirent_uri.h"
27289177Speter#include "private/svn_string_private.h"
28289177Speter
29289177Speter#include "fs_fs.h"
30289177Speter#include "pack.h"
31289177Speter#include "util.h"
32289177Speter
33289177Speter#include "../libsvn_fs/fs-loader.h"
34289177Speter
35289177Speter#include "svn_private_config.h"
36289177Speter
37289177Spetersvn_boolean_t
38289177Spetersvn_fs_fs__is_packed_rev(svn_fs_t *fs,
39289177Speter                         svn_revnum_t rev)
40289177Speter{
41289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
42289177Speter
43289177Speter  return (rev < ffd->min_unpacked_rev);
44289177Speter}
45289177Speter
46289177Spetersvn_boolean_t
47289177Spetersvn_fs_fs__is_packed_revprop(svn_fs_t *fs,
48289177Speter                             svn_revnum_t rev)
49289177Speter{
50289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
51289177Speter
52289177Speter  /* rev 0 will not be packed */
53289177Speter  return (rev < ffd->min_unpacked_rev)
54289177Speter      && (rev != 0)
55289177Speter      && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
56289177Speter}
57289177Speter
58289177Spetersvn_revnum_t
59289177Spetersvn_fs_fs__packed_base_rev(svn_fs_t *fs,
60289177Speter                           svn_revnum_t revision)
61289177Speter{
62289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
63289177Speter  return (revision < ffd->min_unpacked_rev)
64289177Speter       ? (revision - (revision % ffd->max_files_per_dir))
65289177Speter       : revision;
66289177Speter}
67289177Speter
68289177Speterconst char *
69289177Spetersvn_fs_fs__path_txn_current(svn_fs_t *fs,
70289177Speter                            apr_pool_t *pool)
71289177Speter{
72289177Speter  return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
73289177Speter}
74289177Speter
75289177Speterconst char *
76289177Spetersvn_fs_fs__path_txn_current_lock(svn_fs_t *fs,
77289177Speter                                 apr_pool_t *pool)
78289177Speter{
79289177Speter  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
80289177Speter}
81289177Speter
82289177Speterconst char *
83289177Spetersvn_fs_fs__path_lock(svn_fs_t *fs,
84289177Speter                     apr_pool_t *pool)
85289177Speter{
86289177Speter  return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
87289177Speter}
88289177Speter
89289177Speterconst char *
90289177Spetersvn_fs_fs__path_pack_lock(svn_fs_t *fs,
91289177Speter                          apr_pool_t *pool)
92289177Speter{
93289177Speter  return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool);
94289177Speter}
95289177Speter
96289177Speterconst char *
97289177Spetersvn_fs_fs__path_revprop_generation(svn_fs_t *fs,
98289177Speter                                   apr_pool_t *pool)
99289177Speter{
100289177Speter  return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool);
101289177Speter}
102289177Speter
103289177Speterconst char *
104289177Spetersvn_fs_fs__path_rev_packed(svn_fs_t *fs,
105289177Speter                           svn_revnum_t rev,
106289177Speter                           const char *kind,
107289177Speter                           apr_pool_t *pool)
108289177Speter{
109289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
110289177Speter
111289177Speter  assert(ffd->max_files_per_dir);
112289177Speter  assert(svn_fs_fs__is_packed_rev(fs, rev));
113289177Speter
114289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
115289177Speter                              apr_psprintf(pool,
116289177Speter                                           "%ld" PATH_EXT_PACKED_SHARD,
117289177Speter                                           rev / ffd->max_files_per_dir),
118289177Speter                              kind, SVN_VA_NULL);
119289177Speter}
120289177Speter
121289177Speterconst char *
122289177Spetersvn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
123289177Speter{
124289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
125289177Speter
126289177Speter  assert(ffd->max_files_per_dir);
127289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
128289177Speter                              apr_psprintf(pool, "%ld",
129289177Speter                                                 rev / ffd->max_files_per_dir),
130289177Speter                              SVN_VA_NULL);
131289177Speter}
132289177Speter
133289177Speterconst char *
134289177Spetersvn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
135289177Speter{
136289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
137289177Speter
138289177Speter  assert(! svn_fs_fs__is_packed_rev(fs, rev));
139289177Speter
140289177Speter  if (ffd->max_files_per_dir)
141289177Speter    {
142289177Speter      return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool),
143289177Speter                             apr_psprintf(pool, "%ld", rev),
144289177Speter                             pool);
145289177Speter    }
146289177Speter
147289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
148289177Speter                              apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
149289177Speter}
150289177Speter
151289177Speter/* Set *PATH to the path of REV in FS with PACKED selecting whether the
152289177Speter   (potential) pack file or single revision file name is returned.
153289177Speter   Allocate *PATH in POOL.
154289177Speter*/
155289177Speterstatic const char *
156289177Speterpath_rev_absolute_internal(svn_fs_t *fs,
157289177Speter                           svn_revnum_t rev,
158289177Speter                           svn_boolean_t packed,
159289177Speter                           apr_pool_t *pool)
160289177Speter{
161289177Speter  return packed
162289177Speter       ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool)
163289177Speter       : svn_fs_fs__path_rev(fs, rev, pool);
164289177Speter}
165289177Speter
166289177Speterconst char *
167289177Spetersvn_fs_fs__path_rev_absolute(svn_fs_t *fs,
168289177Speter                             svn_revnum_t rev,
169289177Speter                             apr_pool_t *pool)
170289177Speter{
171289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
172289177Speter  svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT
173289177Speter                         && svn_fs_fs__is_packed_rev(fs, rev);
174289177Speter
175289177Speter  return path_rev_absolute_internal(fs, rev, is_packed, pool);
176289177Speter}
177289177Speter
178289177Speterconst char *
179289177Spetersvn_fs_fs__path_revprops_shard(svn_fs_t *fs,
180289177Speter                               svn_revnum_t rev,
181289177Speter                               apr_pool_t *pool)
182289177Speter{
183289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
184289177Speter
185289177Speter  assert(ffd->max_files_per_dir);
186289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
187289177Speter                              apr_psprintf(pool, "%ld",
188289177Speter                                           rev / ffd->max_files_per_dir),
189289177Speter                              SVN_VA_NULL);
190289177Speter}
191289177Speter
192289177Speterconst char *
193289177Spetersvn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs,
194289177Speter                                    svn_revnum_t rev,
195289177Speter                                    apr_pool_t *pool)
196289177Speter{
197289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
198289177Speter
199289177Speter  assert(ffd->max_files_per_dir);
200289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
201289177Speter                              apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD,
202289177Speter                                           rev / ffd->max_files_per_dir),
203289177Speter                              SVN_VA_NULL);
204289177Speter}
205289177Speter
206289177Speterconst char *
207289177Spetersvn_fs_fs__path_revprops(svn_fs_t *fs,
208289177Speter                         svn_revnum_t rev,
209289177Speter                         apr_pool_t *pool)
210289177Speter{
211289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
212289177Speter
213289177Speter  if (ffd->max_files_per_dir)
214289177Speter    {
215289177Speter      return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool),
216289177Speter                             apr_psprintf(pool, "%ld", rev),
217289177Speter                             pool);
218289177Speter    }
219289177Speter
220289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
221289177Speter                              apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
222289177Speter}
223289177Speter
224289177Speter/* Return TO_ADD appended to the C string representation of TXN_ID.
225289177Speter * Allocate the result in POOL.
226289177Speter */
227289177Speterstatic const char *
228289177Spetercombine_txn_id_string(const svn_fs_fs__id_part_t *txn_id,
229289177Speter                      const char *to_add,
230289177Speter                      apr_pool_t *pool)
231289177Speter{
232289177Speter  return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool),
233289177Speter                     to_add, SVN_VA_NULL);
234289177Speter}
235289177Speter
236289177Speterconst char *
237289177Spetersvn_fs_fs__path_txns_dir(svn_fs_t *fs,
238289177Speter                         apr_pool_t *pool)
239289177Speter{
240289177Speter  return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
241289177Speter}
242289177Speter
243289177Speterconst char *
244289177Spetersvn_fs_fs__path_txn_dir(svn_fs_t *fs,
245289177Speter                        const svn_fs_fs__id_part_t *txn_id,
246289177Speter                        apr_pool_t *pool)
247289177Speter{
248289177Speter  SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
249289177Speter  return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
250289177Speter                         combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
251289177Speter                         pool);
252289177Speter}
253289177Speter
254289177Speterconst char*
255289177Spetersvn_fs_fs__path_l2p_proto_index(svn_fs_t *fs,
256289177Speter                                const svn_fs_fs__id_part_t *txn_id,
257289177Speter                                apr_pool_t *pool)
258289177Speter{
259289177Speter  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
260289177Speter                         PATH_INDEX PATH_EXT_L2P_INDEX, pool);
261289177Speter}
262289177Speter
263289177Speterconst char*
264289177Spetersvn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
265289177Speter                                const svn_fs_fs__id_part_t *txn_id,
266289177Speter                                apr_pool_t *pool)
267289177Speter{
268289177Speter  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
269289177Speter                         PATH_INDEX PATH_EXT_P2L_INDEX, pool);
270289177Speter}
271289177Speter
272289177Speterconst char *
273289177Spetersvn_fs_fs__path_txn_item_index(svn_fs_t *fs,
274289177Speter                               const svn_fs_fs__id_part_t *txn_id,
275289177Speter                               apr_pool_t *pool)
276289177Speter{
277289177Speter  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
278289177Speter                         PATH_TXN_ITEM_INDEX, pool);
279289177Speter}
280289177Speter
281289177Speterconst char *
282289177Spetersvn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
283289177Speter                               apr_pool_t *pool)
284289177Speter{
285289177Speter  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
286289177Speter}
287289177Speter
288289177Speterconst char *
289289177Spetersvn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
290289177Speter                              const svn_fs_fs__id_part_t *txn_id,
291289177Speter                              apr_pool_t *pool)
292289177Speter{
293289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
294289177Speter  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
295289177Speter    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
296289177Speter                           combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
297289177Speter                           pool);
298289177Speter  else
299289177Speter    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
300289177Speter                           PATH_REV, pool);
301289177Speter}
302289177Speter
303289177Speter
304289177Speterconst char *
305289177Spetersvn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs,
306289177Speter                                   const svn_fs_fs__id_part_t *txn_id,
307289177Speter                                   apr_pool_t *pool)
308289177Speter{
309289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
310289177Speter  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
311289177Speter    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
312289177Speter                           combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
313289177Speter                                                 pool),
314289177Speter                           pool);
315289177Speter  else
316289177Speter    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
317289177Speter                           PATH_REV_LOCK, pool);
318289177Speter}
319289177Speter
320289177Speterconst char *
321289177Spetersvn_fs_fs__path_txn_node_rev(svn_fs_t *fs,
322289177Speter                             const svn_fs_id_t *id,
323289177Speter                             apr_pool_t *pool)
324289177Speter{
325289177Speter  char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data;
326289177Speter  *strrchr(filename, '.') = '\0';
327289177Speter
328289177Speter  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id),
329289177Speter                                                 pool),
330289177Speter                         apr_psprintf(pool, PATH_PREFIX_NODE "%s",
331289177Speter                                      filename),
332289177Speter                         pool);
333289177Speter}
334289177Speter
335289177Speterconst char *
336289177Spetersvn_fs_fs__path_txn_node_props(svn_fs_t *fs,
337289177Speter                               const svn_fs_id_t *id,
338289177Speter                               apr_pool_t *pool)
339289177Speter{
340289177Speter  return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
341289177Speter                     PATH_EXT_PROPS, SVN_VA_NULL);
342289177Speter}
343289177Speter
344289177Speterconst char *
345289177Spetersvn_fs_fs__path_txn_node_children(svn_fs_t *fs,
346289177Speter                                  const svn_fs_id_t *id,
347289177Speter                                  apr_pool_t *pool)
348289177Speter{
349289177Speter  return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool),
350289177Speter                     PATH_EXT_CHILDREN, SVN_VA_NULL);
351289177Speter}
352289177Speter
353289177Speterconst char *
354289177Spetersvn_fs_fs__path_node_origin(svn_fs_t *fs,
355289177Speter                            const svn_fs_fs__id_part_t *node_id,
356289177Speter                            apr_pool_t *pool)
357289177Speter{
358289177Speter  char buffer[SVN_INT64_BUFFER_SIZE];
359289177Speter  apr_size_t len = svn__ui64tobase36(buffer, node_id->number);
360289177Speter
361289177Speter  if (len > 1)
362289177Speter    buffer[len - 1] = '\0';
363289177Speter
364289177Speter  return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
365289177Speter                              buffer, SVN_VA_NULL);
366289177Speter}
367289177Speter
368289177Speterconst char *
369289177Spetersvn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
370289177Speter                                 apr_pool_t *pool)
371289177Speter{
372289177Speter  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
373289177Speter}
374289177Speter
375289177Spetersvn_error_t *
376289177Spetersvn_fs_fs__check_file_buffer_numeric(const char *buf,
377289177Speter                                     apr_off_t offset,
378289177Speter                                     const char *path,
379289177Speter                                     const char *title,
380289177Speter                                     apr_pool_t *pool)
381289177Speter{
382289177Speter  const char *p;
383289177Speter
384289177Speter  for (p = buf + offset; *p; p++)
385289177Speter    if (!svn_ctype_isdigit(*p))
386289177Speter      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
387289177Speter        _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
388289177Speter        title, svn_dirent_local_style(path, pool), *p, buf);
389289177Speter
390289177Speter  return SVN_NO_ERROR;
391289177Speter}
392289177Speter
393289177Spetersvn_error_t *
394289177Spetersvn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
395289177Speter                                 svn_fs_t *fs,
396289177Speter                                 apr_pool_t *pool)
397289177Speter{
398289177Speter  char buf[80];
399289177Speter  apr_file_t *file;
400289177Speter  apr_size_t len;
401289177Speter
402289177Speter  SVN_ERR(svn_io_file_open(&file,
403289177Speter                           svn_fs_fs__path_min_unpacked_rev(fs, pool),
404289177Speter                           APR_READ | APR_BUFFERED,
405289177Speter                           APR_OS_DEFAULT,
406289177Speter                           pool));
407289177Speter  len = sizeof(buf);
408289177Speter  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
409289177Speter  SVN_ERR(svn_io_file_close(file, pool));
410289177Speter
411289177Speter  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
412289177Speter  return SVN_NO_ERROR;
413289177Speter}
414289177Speter
415289177Spetersvn_error_t *
416289177Spetersvn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs,
417289177Speter                                   apr_pool_t *pool)
418289177Speter{
419289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
420289177Speter
421289177Speter  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
422289177Speter
423289177Speter  return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool);
424289177Speter}
425289177Speter
426289177Spetersvn_error_t *
427289177Spetersvn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs,
428289177Speter                                  svn_revnum_t revnum,
429289177Speter                                  apr_pool_t *scratch_pool)
430289177Speter{
431362181Sdim  fs_fs_data_t *ffd = fs->fsap_data;
432289177Speter  const char *final_path;
433289177Speter  char buf[SVN_INT64_BUFFER_SIZE];
434289177Speter  apr_size_t len = svn__i64toa(buf, revnum);
435289177Speter  buf[len] = '\n';
436289177Speter
437289177Speter  final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
438289177Speter
439362181Sdim  SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1,
440362181Sdim                               final_path /* copy_perms */,
441362181Sdim                               ffd->flush_to_disk, scratch_pool));
442289177Speter
443289177Speter  return SVN_NO_ERROR;
444289177Speter}
445289177Speter
446289177Spetersvn_error_t *
447289177Spetersvn_fs_fs__read_current(svn_revnum_t *rev,
448289177Speter                        apr_uint64_t *next_node_id,
449289177Speter                        apr_uint64_t *next_copy_id,
450289177Speter                        svn_fs_t *fs,
451289177Speter                        apr_pool_t *pool)
452289177Speter{
453289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
454289177Speter  svn_stringbuf_t *content;
455289177Speter
456289177Speter  SVN_ERR(svn_fs_fs__read_content(&content,
457289177Speter                                  svn_fs_fs__path_current(fs, pool),
458289177Speter                                  pool));
459289177Speter
460289177Speter  if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
461289177Speter    {
462289177Speter      /* When format 1 and 2 filesystems are upgraded, the 'current' file is
463289177Speter         left intact.  As a consequence, there is a window when a filesystem
464289177Speter         has a new format, but this file still contains the IDs left from an
465289177Speter         old format, i.e. looks like "359 j5 v\n".  Do not be too strict here
466289177Speter         and only expect a parseable revision number. */
467289177Speter      SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
468289177Speter
469289177Speter      *next_node_id = 0;
470289177Speter      *next_copy_id = 0;
471289177Speter    }
472289177Speter  else
473289177Speter    {
474289177Speter      const char *str;
475289177Speter
476289177Speter      SVN_ERR(svn_revnum_parse(rev, content->data, &str));
477289177Speter      if (*str != ' ')
478289177Speter        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
479289177Speter                                _("Corrupt 'current' file"));
480289177Speter
481289177Speter      *next_node_id = svn__base36toui64(&str, str + 1);
482289177Speter      if (*str != ' ')
483289177Speter        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
484289177Speter                                _("Corrupt 'current' file"));
485289177Speter
486289177Speter      *next_copy_id = svn__base36toui64(&str, str + 1);
487289177Speter      if (*str != '\n')
488289177Speter        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
489289177Speter                                _("Corrupt 'current' file"));
490289177Speter    }
491289177Speter
492289177Speter  return SVN_NO_ERROR;
493289177Speter}
494289177Speter
495289177Spetersvn_error_t *
496289177Spetersvn_fs_fs__write_current(svn_fs_t *fs,
497289177Speter                         svn_revnum_t rev,
498289177Speter                         apr_uint64_t next_node_id,
499289177Speter                         apr_uint64_t next_copy_id,
500289177Speter                         apr_pool_t *pool)
501289177Speter{
502289177Speter  char *buf;
503289177Speter  const char *name;
504289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
505289177Speter
506289177Speter  /* Now we can just write out this line. */
507289177Speter  if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
508289177Speter    {
509289177Speter      buf = apr_psprintf(pool, "%ld\n", rev);
510289177Speter    }
511289177Speter  else
512289177Speter    {
513289177Speter      char node_id_str[SVN_INT64_BUFFER_SIZE];
514289177Speter      char copy_id_str[SVN_INT64_BUFFER_SIZE];
515289177Speter      svn__ui64tobase36(node_id_str, next_node_id);
516289177Speter      svn__ui64tobase36(copy_id_str, next_copy_id);
517289177Speter
518289177Speter      buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str);
519289177Speter    }
520289177Speter
521289177Speter  name = svn_fs_fs__path_current(fs, pool);
522362181Sdim  SVN_ERR(svn_io_write_atomic2(name, buf, strlen(buf),
523362181Sdim                               name /* copy_perms_path */,
524362181Sdim                               ffd->flush_to_disk, pool));
525289177Speter
526289177Speter  return SVN_NO_ERROR;
527289177Speter}
528289177Speter
529289177Spetersvn_error_t *
530289177Spetersvn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content,
531289177Speter                                   svn_boolean_t *missing,
532289177Speter                                   const char *path,
533289177Speter                                   svn_boolean_t last_attempt,
534289177Speter                                   apr_pool_t *pool)
535289177Speter{
536289177Speter  svn_error_t *err = svn_stringbuf_from_file2(content, path, pool);
537289177Speter  if (missing)
538289177Speter    *missing = FALSE;
539289177Speter
540289177Speter  if (err)
541289177Speter    {
542289177Speter      *content = NULL;
543289177Speter
544289177Speter      if (APR_STATUS_IS_ENOENT(err->apr_err))
545289177Speter        {
546289177Speter          if (!last_attempt)
547289177Speter            {
548289177Speter              svn_error_clear(err);
549289177Speter              if (missing)
550289177Speter                *missing = TRUE;
551289177Speter              return SVN_NO_ERROR;
552289177Speter            }
553289177Speter        }
554289177Speter#ifdef ESTALE
555289177Speter      else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
556289177Speter                || APR_TO_OS_ERROR(err->apr_err) == EIO)
557289177Speter        {
558289177Speter          if (!last_attempt)
559289177Speter            {
560289177Speter              svn_error_clear(err);
561289177Speter              return SVN_NO_ERROR;
562289177Speter            }
563289177Speter        }
564289177Speter#endif
565289177Speter    }
566289177Speter
567289177Speter  return svn_error_trace(err);
568289177Speter}
569289177Speter
570289177Spetersvn_error_t *
571289177Spetersvn_fs_fs__read_content(svn_stringbuf_t **content,
572289177Speter                        const char *fname,
573289177Speter                        apr_pool_t *pool)
574289177Speter{
575289177Speter  int i;
576289177Speter  *content = NULL;
577289177Speter
578289177Speter  for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i)
579289177Speter    SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL,
580289177Speter                        fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT,
581289177Speter                        pool));
582289177Speter
583289177Speter  if (!*content)
584289177Speter    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
585289177Speter                             _("Can't read '%s'"),
586289177Speter                             svn_dirent_local_style(fname, pool));
587289177Speter
588289177Speter  return SVN_NO_ERROR;
589289177Speter}
590289177Speter
591289177Spetersvn_error_t *
592289177Spetersvn_fs_fs__read_number_from_stream(apr_int64_t *result,
593289177Speter                                   svn_boolean_t *hit_eof,
594289177Speter                                   svn_stream_t *stream,
595289177Speter                                   apr_pool_t *scratch_pool)
596289177Speter{
597289177Speter  svn_stringbuf_t *sb;
598289177Speter  svn_boolean_t eof;
599289177Speter  svn_error_t *err;
600289177Speter
601289177Speter  SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
602289177Speter  if (hit_eof)
603289177Speter    *hit_eof = eof;
604289177Speter  else
605289177Speter    if (eof)
606289177Speter      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
607289177Speter
608289177Speter  if (!eof)
609289177Speter    {
610289177Speter      err = svn_cstring_atoi64(result, sb->data);
611289177Speter      if (err)
612289177Speter        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
613289177Speter                                 _("Number '%s' invalid or too large"),
614289177Speter                                 sb->data);
615289177Speter    }
616289177Speter
617289177Speter  return SVN_NO_ERROR;
618289177Speter}
619289177Speter
620289177Spetersvn_error_t *
621289177Spetersvn_fs_fs__move_into_place(const char *old_filename,
622289177Speter                           const char *new_filename,
623289177Speter                           const char *perms_reference,
624362181Sdim                           svn_boolean_t flush_to_disk,
625289177Speter                           apr_pool_t *pool)
626289177Speter{
627289177Speter  svn_error_t *err;
628362181Sdim  apr_file_t *file;
629289177Speter
630362181Sdim  /* Copying permissions is a no-op on WIN32. */
631289177Speter  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
632289177Speter
633289177Speter  /* Move the file into place. */
634362181Sdim  err = svn_io_file_rename2(old_filename, new_filename, flush_to_disk, pool);
635289177Speter  if (err && APR_STATUS_IS_EXDEV(err->apr_err))
636289177Speter    {
637289177Speter      /* Can't rename across devices; fall back to copying. */
638289177Speter      svn_error_clear(err);
639289177Speter      SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
640289177Speter
641362181Sdim      /* Flush the target of the copy to disk.
642362181Sdim         ### The code below is duplicates svn_io_file_rename2(), because
643362181Sdim             currently we don't have the svn_io_copy_file2() function with
644362181Sdim             a flush_to_disk argument. */
645362181Sdim      if (flush_to_disk)
646362181Sdim        {
647362181Sdim          SVN_ERR(svn_io_file_open(&file, new_filename, APR_WRITE,
648362181Sdim                                   APR_OS_DEFAULT, pool));
649362181Sdim          SVN_ERR(svn_io_file_flush_to_disk(file, pool));
650362181Sdim          SVN_ERR(svn_io_file_close(file, pool));
651362181Sdim        }
652289177Speter
653362181Sdim#ifdef SVN_ON_POSIX
654362181Sdim      if (flush_to_disk)
655362181Sdim        {
656362181Sdim          /* On POSIX, the file name is stored in the file's directory entry.
657362181Sdim             Hence, we need to fsync() that directory as well.
658362181Sdim             On other operating systems, we'd only be asking for trouble
659362181Sdim             by trying to open and fsync a directory. */
660362181Sdim          const char *dirname;
661289177Speter
662362181Sdim          dirname = svn_dirent_dirname(new_filename, pool);
663362181Sdim          SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
664362181Sdim                                   pool));
665362181Sdim          SVN_ERR(svn_io_file_flush_to_disk(file, pool));
666362181Sdim          SVN_ERR(svn_io_file_close(file, pool));
667362181Sdim        }
668289177Speter#endif
669362181Sdim    }
670362181Sdim  else if (err)
671362181Sdim    return svn_error_trace(err);
672289177Speter
673289177Speter  return SVN_NO_ERROR;
674289177Speter}
675289177Speter
676289177Spetersvn_boolean_t
677289177Spetersvn_fs_fs__use_log_addressing(svn_fs_t *fs)
678289177Speter{
679289177Speter  fs_fs_data_t *ffd = fs->fsap_data;
680289177Speter  return ffd->use_log_addressing;
681289177Speter}
682