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
svn_fs_fs__is_packed_rev(svn_fs_t * fs,svn_revnum_t rev)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
svn_fs_fs__is_packed_revprop(svn_fs_t * fs,svn_revnum_t rev)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
svn_fs_fs__packed_base_rev(svn_fs_t * fs,svn_revnum_t revision)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 *
svn_fs_fs__path_txn_current(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_current_lock(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_lock(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_pack_lock(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_revprop_generation(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_rev_packed(svn_fs_t * fs,svn_revnum_t rev,const char * kind,apr_pool_t * pool)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 *
svn_fs_fs__path_rev_shard(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)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 *
svn_fs_fs__path_rev(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)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 *
path_rev_absolute_internal(svn_fs_t * fs,svn_revnum_t rev,svn_boolean_t packed,apr_pool_t * pool)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 *
svn_fs_fs__path_rev_absolute(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)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 *
svn_fs_fs__path_revprops_shard(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)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 *
svn_fs_fs__path_revprops_pack_shard(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)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 *
svn_fs_fs__path_revprops(svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)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 *
combine_txn_id_string(const svn_fs_fs__id_part_t * txn_id,const char * to_add,apr_pool_t * pool)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 *
svn_fs_fs__path_txns_dir(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_dir(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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*
svn_fs_fs__path_l2p_proto_index(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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*
svn_fs_fs__path_p2l_proto_index(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_item_index(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_proto_revs(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_proto_rev(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_node_rev(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_node_props(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)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 *
svn_fs_fs__path_txn_node_children(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)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 *
svn_fs_fs__path_node_origin(svn_fs_t * fs,const svn_fs_fs__id_part_t * node_id,apr_pool_t * pool)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 *
svn_fs_fs__path_min_unpacked_rev(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__check_file_buffer_numeric(const char * buf,apr_off_t offset,const char * path,const char * title,apr_pool_t * pool)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 *
svn_fs_fs__read_min_unpacked_rev(svn_revnum_t * min_unpacked_rev,svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__update_min_unpacked_rev(svn_fs_t * fs,apr_pool_t * pool)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 *
svn_fs_fs__write_min_unpacked_rev(svn_fs_t * fs,svn_revnum_t revnum,apr_pool_t * scratch_pool)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   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 
446 svn_error_t *
svn_fs_fs__read_current(svn_revnum_t * rev,apr_uint64_t * next_node_id,apr_uint64_t * next_copy_id,svn_fs_t * fs,apr_pool_t * pool)447 svn_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 
495 svn_error_t *
svn_fs_fs__write_current(svn_fs_t * fs,svn_revnum_t rev,apr_uint64_t next_node_id,apr_uint64_t next_copy_id,apr_pool_t * pool)496 svn_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 
529 svn_error_t *
svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t ** content,svn_boolean_t * missing,const char * path,svn_boolean_t last_attempt,apr_pool_t * pool)530 svn_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 
570 svn_error_t *
svn_fs_fs__read_content(svn_stringbuf_t ** content,const char * fname,apr_pool_t * pool)571 svn_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 
591 svn_error_t *
svn_fs_fs__read_number_from_stream(apr_int64_t * result,svn_boolean_t * hit_eof,svn_stream_t * stream,apr_pool_t * scratch_pool)592 svn_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 
620 svn_error_t *
svn_fs_fs__move_into_place(const char * old_filename,const char * new_filename,const char * perms_reference,svn_boolean_t flush_to_disk,apr_pool_t * pool)621 svn_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 
676 svn_boolean_t
svn_fs_fs__use_log_addressing(svn_fs_t * fs)677 svn_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