1/* util.c --- utility functions for FSX 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_x.h"
30#include "id.h"
31#include "util.h"
32
33#include "../libsvn_fs/fs-loader.h"
34
35#include "svn_private_config.h"
36
37/* Following are defines that specify the textual elements of the
38   native filesystem directories and revision files. */
39
40/* Notes:
41
42To avoid opening and closing the rev-files all the time, it would
43probably be advantageous to keep each rev-file open for the
44lifetime of the transaction object.  I'll leave that as a later
45optimization for now.
46
47I didn't keep track of pool lifetimes at all in this code.  There
48are likely some errors because of that.
49
50*/
51
52/* Pathname helper functions */
53
54/* Return TRUE is REV is packed in FS, FALSE otherwise. */
55svn_boolean_t
56svn_fs_x__is_packed_rev(svn_fs_t *fs, svn_revnum_t rev)
57{
58  svn_fs_x__data_t *ffd = fs->fsap_data;
59
60  return (rev < ffd->min_unpacked_rev);
61}
62
63/* Return TRUE is REV is packed in FS, FALSE otherwise. */
64svn_boolean_t
65svn_fs_x__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev)
66{
67  svn_fs_x__data_t *ffd = fs->fsap_data;
68
69  /* rev 0 will not be packed */
70  return (rev < ffd->min_unpacked_rev) && (rev != 0);
71}
72
73svn_revnum_t
74svn_fs_x__packed_base_rev(svn_fs_t *fs, svn_revnum_t rev)
75{
76  svn_fs_x__data_t *ffd = fs->fsap_data;
77
78  return rev < ffd->min_unpacked_rev
79       ? rev - (rev % ffd->max_files_per_dir)
80       : rev;
81}
82
83svn_revnum_t
84svn_fs_x__pack_size(svn_fs_t *fs, svn_revnum_t rev)
85{
86  svn_fs_x__data_t *ffd = fs->fsap_data;
87
88  return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1;
89}
90
91const char *
92svn_fs_x__path_format(svn_fs_t *fs,
93                      apr_pool_t *result_pool)
94{
95  return svn_dirent_join(fs->path, PATH_FORMAT, result_pool);
96}
97
98const char *
99svn_fs_x__path_uuid(svn_fs_t *fs,
100                    apr_pool_t *result_pool)
101{
102  return svn_dirent_join(fs->path, PATH_UUID, result_pool);
103}
104
105const char *
106svn_fs_x__path_current(svn_fs_t *fs,
107                       apr_pool_t *result_pool)
108{
109  return svn_dirent_join(fs->path, PATH_CURRENT, result_pool);
110}
111
112const char *
113svn_fs_x__path_next(svn_fs_t *fs,
114                       apr_pool_t *result_pool)
115{
116  return svn_dirent_join(fs->path, PATH_NEXT, result_pool);
117}
118
119const char *
120svn_fs_x__path_txn_current(svn_fs_t *fs,
121                           apr_pool_t *result_pool)
122{
123  return svn_dirent_join(fs->path, PATH_TXN_CURRENT, result_pool);
124}
125
126const char *
127svn_fs_x__path_txn_current_lock(svn_fs_t *fs,
128                                apr_pool_t *result_pool)
129{
130  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, result_pool);
131}
132
133const char *
134svn_fs_x__path_lock(svn_fs_t *fs,
135                    apr_pool_t *result_pool)
136{
137  return svn_dirent_join(fs->path, PATH_LOCK_FILE, result_pool);
138}
139
140const char *
141svn_fs_x__path_pack_lock(svn_fs_t *fs,
142                         apr_pool_t *result_pool)
143{
144  return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, result_pool);
145}
146
147const char *
148svn_fs_x__path_revprop_generation(svn_fs_t *fs,
149                                  apr_pool_t *result_pool)
150{
151  return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, result_pool);
152}
153
154/* Return the full path of the file FILENAME within revision REV's shard in
155 * FS.  If FILENAME is NULL, return the shard directory directory itself.
156 * PACKED says whether we want the packed shard's name.
157 *
158 * Allocate the result in RESULT_POOL.
159 */static const char*
160construct_shard_sub_path(svn_fs_t *fs,
161                         svn_revnum_t rev,
162                         svn_boolean_t packed,
163                         const char *filename,
164                         apr_pool_t *result_pool)
165{
166  svn_fs_x__data_t *ffd = fs->fsap_data;
167  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_PACKED_SHARD)] = { 0 };
168
169  /* String containing the shard number. */
170  apr_size_t len = svn__i64toa(buffer, rev / ffd->max_files_per_dir);
171
172  /* Append the suffix.  Limit it to the buffer size (should never hit it). */
173  if (packed)
174    strncpy(buffer + len, PATH_EXT_PACKED_SHARD, sizeof(buffer) - len - 1);
175
176  /* This will also work for NULL FILENAME as well. */
177  return svn_dirent_join_many(result_pool, fs->path, PATH_REVS_DIR, buffer,
178                              filename, SVN_VA_NULL);
179}
180
181const char *
182svn_fs_x__path_rev_packed(svn_fs_t *fs,
183                          svn_revnum_t rev,
184                          const char *kind,
185                          apr_pool_t *result_pool)
186{
187  assert(svn_fs_x__is_packed_rev(fs, rev));
188  return construct_shard_sub_path(fs, rev, TRUE, kind, result_pool);
189}
190
191const char *
192svn_fs_x__path_shard(svn_fs_t *fs,
193                     svn_revnum_t rev,
194                     apr_pool_t *result_pool)
195{
196  return construct_shard_sub_path(fs, rev, FALSE, NULL, result_pool);
197}
198
199const char *
200svn_fs_x__path_rev(svn_fs_t *fs,
201                   svn_revnum_t rev,
202                   apr_pool_t *result_pool)
203{
204  char buffer[SVN_INT64_BUFFER_SIZE + 1];
205  buffer[0] = 'r';
206  svn__i64toa(buffer + 1, rev);
207
208  assert(! svn_fs_x__is_packed_rev(fs, rev));
209  return construct_shard_sub_path(fs, rev, FALSE, buffer, result_pool);
210}
211
212const char *
213svn_fs_x__path_rev_absolute(svn_fs_t *fs,
214                            svn_revnum_t rev,
215                            apr_pool_t *result_pool)
216{
217  return svn_fs_x__is_packed_rev(fs, rev)
218       ? svn_fs_x__path_rev_packed(fs, rev, PATH_PACKED, result_pool)
219       : svn_fs_x__path_rev(fs, rev, result_pool);
220}
221
222const char *
223svn_fs_x__path_pack_shard(svn_fs_t *fs,
224                                   svn_revnum_t rev,
225                                   apr_pool_t *result_pool)
226{
227  return construct_shard_sub_path(fs, rev, TRUE, NULL, result_pool);
228}
229
230const char *
231svn_fs_x__path_revprops(svn_fs_t *fs,
232                        svn_revnum_t rev,
233                        apr_pool_t *result_pool)
234{
235  char buffer[SVN_INT64_BUFFER_SIZE + 1];
236  buffer[0] = 'p';
237  svn__i64toa(buffer + 1, rev);
238
239  assert(! svn_fs_x__is_packed_revprop(fs, rev));
240
241  /* Revprops for packed r0 are not packed, yet stored in the packed shard.
242     Hence, the second flag must check for packed _rev_ - not revprop. */
243  return construct_shard_sub_path(fs, rev,
244                                  svn_fs_x__is_packed_rev(fs, rev) /* sic! */,
245                                  buffer, result_pool);
246}
247
248const char *
249svn_fs_x__txn_name(svn_fs_x__txn_id_t txn_id,
250                   apr_pool_t *result_pool)
251{
252  char *p = apr_palloc(result_pool, SVN_INT64_BUFFER_SIZE);
253  svn__ui64tobase36(p, txn_id);
254  return p;
255}
256
257svn_error_t *
258svn_fs_x__txn_by_name(svn_fs_x__txn_id_t *txn_id,
259                      const char *txn_name)
260{
261  const char *next;
262  apr_uint64_t id = svn__base36toui64(&next, txn_name);
263  if (next == NULL || *next != 0 || *txn_name == 0)
264    return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
265                             "Malformed TXN name '%s'", txn_name);
266
267  *txn_id = id;
268  return SVN_NO_ERROR;
269}
270
271const char *
272svn_fs_x__path_txns_dir(svn_fs_t *fs,
273                        apr_pool_t *result_pool)
274{
275  return svn_dirent_join(fs->path, PATH_TXNS_DIR, result_pool);
276}
277
278/* Return the full path of the file FILENAME within transaction TXN_ID's
279 * transaction directory in FS.  If FILENAME is NULL, return the transaction
280 * directory itself.
281 *
282 * Allocate the result in RESULT_POOL.
283 */
284static const char *
285construct_txn_path(svn_fs_t *fs,
286                   svn_fs_x__txn_id_t txn_id,
287                   const char *filename,
288                   apr_pool_t *result_pool)
289{
290  /* Construct the transaction directory name without temp. allocations. */
291  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_TXN)] = { 0 };
292  apr_size_t len = svn__ui64tobase36(buffer, txn_id);
293  strncpy(buffer + len, PATH_EXT_TXN, sizeof(buffer) - len - 1);
294
295  /* If FILENAME is NULL, it will terminate the list of segments
296     to concatenate. */
297  return svn_dirent_join_many(result_pool, fs->path, PATH_TXNS_DIR,
298                              buffer, filename, SVN_VA_NULL);
299}
300
301const char *
302svn_fs_x__path_txn_dir(svn_fs_t *fs,
303                       svn_fs_x__txn_id_t txn_id,
304                       apr_pool_t *result_pool)
305{
306  return construct_txn_path(fs, txn_id, NULL, result_pool);
307}
308
309/* Return the name of the sha1->rep mapping file in transaction TXN_ID
310 * within FS for the given SHA1 checksum.  Use POOL for allocations.
311 */
312const char *
313svn_fs_x__path_txn_sha1(svn_fs_t *fs,
314                        svn_fs_x__txn_id_t txn_id,
315                        const unsigned char *sha1,
316                        apr_pool_t *pool)
317{
318  svn_checksum_t checksum;
319  checksum.digest = sha1;
320  checksum.kind = svn_checksum_sha1;
321
322  return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, pool),
323                         svn_checksum_to_cstring(&checksum, pool),
324                         pool);
325}
326
327const char *
328svn_fs_x__path_txn_changes(svn_fs_t *fs,
329                           svn_fs_x__txn_id_t txn_id,
330                           apr_pool_t *result_pool)
331{
332  return construct_txn_path(fs, txn_id, PATH_CHANGES, result_pool);
333}
334
335const char *
336svn_fs_x__path_txn_props(svn_fs_t *fs,
337                         svn_fs_x__txn_id_t txn_id,
338                         apr_pool_t *result_pool)
339{
340  return construct_txn_path(fs, txn_id, PATH_TXN_PROPS, result_pool);
341}
342
343const char*
344svn_fs_x__path_l2p_proto_index(svn_fs_t *fs,
345                               svn_fs_x__txn_id_t txn_id,
346                               apr_pool_t *result_pool)
347{
348  return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_L2P_INDEX,
349                            result_pool);
350}
351
352const char*
353svn_fs_x__path_p2l_proto_index(svn_fs_t *fs,
354                               svn_fs_x__txn_id_t txn_id,
355                               apr_pool_t *result_pool)
356{
357  return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_P2L_INDEX,
358                            result_pool);
359}
360
361const char *
362svn_fs_x__path_txn_next_ids(svn_fs_t *fs,
363                            svn_fs_x__txn_id_t txn_id,
364                            apr_pool_t *result_pool)
365{
366  return construct_txn_path(fs, txn_id, PATH_NEXT_IDS, result_pool);
367}
368
369const char *
370svn_fs_x__path_min_unpacked_rev(svn_fs_t *fs,
371                                apr_pool_t *result_pool)
372{
373  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, result_pool);
374}
375
376const char *
377svn_fs_x__path_txn_proto_revs(svn_fs_t *fs,
378                              apr_pool_t *result_pool)
379{
380  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, result_pool);
381}
382
383const char *
384svn_fs_x__path_txn_item_index(svn_fs_t *fs,
385                              svn_fs_x__txn_id_t txn_id,
386                              apr_pool_t *result_pool)
387{
388  return construct_txn_path(fs, txn_id, PATH_TXN_ITEM_INDEX, result_pool);
389}
390
391/* Return the full path of the proto-rev file / lock file for transaction
392 * TXN_ID in FS.  The SUFFIX determines what file (rev / lock) it will be.
393 *
394 * Allocate the result in RESULT_POOL.
395 */
396static const char *
397construct_proto_rev_path(svn_fs_t *fs,
398                         svn_fs_x__txn_id_t txn_id,
399                         const char *suffix,
400                         apr_pool_t *result_pool)
401{
402  /* Construct the file name without temp. allocations. */
403  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_REV_LOCK)] = { 0 };
404  apr_size_t len = svn__ui64tobase36(buffer, txn_id);
405  strncpy(buffer + len, suffix, sizeof(buffer) - len - 1);
406
407  /* If FILENAME is NULL, it will terminate the list of segments
408     to concatenate. */
409  return svn_dirent_join_many(result_pool, fs->path, PATH_TXN_PROTOS_DIR,
410                              buffer, SVN_VA_NULL);
411}
412
413const char *
414svn_fs_x__path_txn_proto_rev(svn_fs_t *fs,
415                             svn_fs_x__txn_id_t txn_id,
416                             apr_pool_t *result_pool)
417{
418  return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV, result_pool);
419}
420
421const char *
422svn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs,
423                                  svn_fs_x__txn_id_t txn_id,
424                                  apr_pool_t *result_pool)
425{
426  return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV_LOCK, result_pool);
427}
428
429/* Return the full path of the noderev-related file with the extension SUFFIX
430 * for noderev *ID in transaction TXN_ID in FS.
431 *
432 * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL.
433 */
434static const char *
435construct_txn_node_path(svn_fs_t *fs,
436                        const svn_fs_x__id_t *id,
437                        const char *suffix,
438                        apr_pool_t *result_pool,
439                        apr_pool_t *scratch_pool)
440{
441  const char *filename = svn_fs_x__id_unparse(id, result_pool)->data;
442  apr_int64_t txn_id = svn_fs_x__get_txn_id(id->change_set);
443
444  return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool),
445                         apr_psprintf(scratch_pool, PATH_PREFIX_NODE "%s%s",
446                                      filename, suffix),
447                         result_pool);
448}
449
450const char *
451svn_fs_x__path_txn_node_rev(svn_fs_t *fs,
452                            const svn_fs_x__id_t *id,
453                            apr_pool_t *result_pool,
454                            apr_pool_t *scratch_pool)
455{
456  return construct_txn_node_path(fs, id, "", result_pool, scratch_pool);
457}
458
459const char *
460svn_fs_x__path_txn_node_props(svn_fs_t *fs,
461                              const svn_fs_x__id_t *id,
462                              apr_pool_t *result_pool,
463                              apr_pool_t *scratch_pool)
464{
465  return construct_txn_node_path(fs, id, PATH_EXT_PROPS, result_pool,
466                                 scratch_pool);
467}
468
469const char *
470svn_fs_x__path_txn_node_children(svn_fs_t *fs,
471                                 const svn_fs_x__id_t *id,
472                                 apr_pool_t *result_pool,
473                                 apr_pool_t *scratch_pool)
474{
475  return construct_txn_node_path(fs, id, PATH_EXT_CHILDREN, result_pool,
476                                 scratch_pool);
477}
478
479svn_error_t *
480svn_fs_x__check_file_buffer_numeric(const char *buf,
481                                    apr_off_t offset,
482                                    const char *path,
483                                    const char *title,
484                                    apr_pool_t *scratch_pool)
485{
486  const char *p;
487
488  for (p = buf + offset; *p; p++)
489    if (!svn_ctype_isdigit(*p))
490      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
491        _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
492        title, svn_dirent_local_style(path, scratch_pool), *p, buf);
493
494  return SVN_NO_ERROR;
495}
496
497svn_error_t *
498svn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
499                                svn_fs_t *fs,
500                                apr_pool_t *scratch_pool)
501{
502  char buf[80];
503  apr_file_t *file;
504  apr_size_t len;
505
506  SVN_ERR(svn_io_file_open(&file,
507                           svn_fs_x__path_min_unpacked_rev(fs, scratch_pool),
508                           APR_READ | APR_BUFFERED,
509                           APR_OS_DEFAULT,
510                           scratch_pool));
511  len = sizeof(buf);
512  SVN_ERR(svn_io_read_length_line(file, buf, &len, scratch_pool));
513  SVN_ERR(svn_io_file_close(file, scratch_pool));
514
515  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
516  return SVN_NO_ERROR;
517}
518
519svn_error_t *
520svn_fs_x__update_min_unpacked_rev(svn_fs_t *fs,
521                                  apr_pool_t *scratch_pool)
522{
523  svn_fs_x__data_t *ffd = fs->fsap_data;
524  return svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs,
525                                         scratch_pool);
526}
527
528/* Write a file FILENAME in directory FS_PATH, containing a single line
529 * with the number REVNUM in ASCII decimal.  Move the file into place
530 * atomically, overwriting any existing file.
531 *
532 * Similar to write_current(). */
533svn_error_t *
534svn_fs_x__write_min_unpacked_rev(svn_fs_t *fs,
535                                 svn_revnum_t revnum,
536                                 apr_pool_t *scratch_pool)
537{
538  svn_fs_x__data_t *ffd = fs->fsap_data;
539  const char *final_path;
540  char buf[SVN_INT64_BUFFER_SIZE];
541  apr_size_t len = svn__i64toa(buf, revnum);
542  buf[len] = '\n';
543
544  final_path = svn_fs_x__path_min_unpacked_rev(fs, scratch_pool);
545
546  SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1,
547                               final_path /* copy_perms */,
548                               ffd->flush_to_disk, scratch_pool));
549
550  return SVN_NO_ERROR;
551}
552
553svn_error_t *
554svn_fs_x__read_current(svn_revnum_t *rev,
555                       svn_fs_t *fs,
556                       apr_pool_t *scratch_pool)
557{
558  const char *str;
559  svn_stringbuf_t *content;
560  SVN_ERR(svn_fs_x__read_content(&content,
561                                 svn_fs_x__path_current(fs, scratch_pool),
562                                 scratch_pool));
563  SVN_ERR(svn_revnum_parse(rev, content->data, &str));
564  if (*str != '\n')
565    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
566                            _("Corrupt 'current' file"));
567
568  return SVN_NO_ERROR;
569}
570
571/* Atomically update the 'current' file to hold the specified REV.
572   Perform temporary allocations in SCRATCH_POOL. */
573svn_error_t *
574svn_fs_x__write_current(svn_fs_t *fs,
575                        svn_revnum_t rev,
576                        apr_pool_t *scratch_pool)
577{
578  char *buf;
579  const char *tmp_name, *name;
580  apr_file_t *file;
581
582  /* Now we can just write out this line. */
583  buf = apr_psprintf(scratch_pool, "%ld\n", rev);
584
585  name = svn_fs_x__path_current(fs, scratch_pool);
586  tmp_name = svn_fs_x__path_next(fs, scratch_pool);
587
588  SVN_ERR(svn_io_file_open(&file, tmp_name,
589                           APR_WRITE | APR_CREATE | APR_BUFFERED,
590                           APR_OS_DEFAULT, scratch_pool));
591  SVN_ERR(svn_io_file_write_full(file, buf, strlen(buf), NULL,
592                                 scratch_pool));
593  SVN_ERR(svn_io_file_close(file, scratch_pool));
594
595  /* Copying permissions is a no-op on WIN32. */
596  SVN_ERR(svn_io_copy_perms(name, tmp_name, scratch_pool));
597
598  /* Move the file into place. */
599  SVN_ERR(svn_io_file_rename2(tmp_name, name, TRUE, scratch_pool));
600
601  return SVN_NO_ERROR;
602}
603
604
605svn_error_t *
606svn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content,
607                                  svn_boolean_t *missing,
608                                  const char *path,
609                                  svn_boolean_t last_attempt,
610                                  apr_pool_t *result_pool)
611{
612  svn_error_t *err = svn_stringbuf_from_file2(content, path, result_pool);
613  if (missing)
614    *missing = FALSE;
615
616  if (err)
617    {
618      *content = NULL;
619
620      if (APR_STATUS_IS_ENOENT(err->apr_err))
621        {
622          if (!last_attempt)
623            {
624              svn_error_clear(err);
625              if (missing)
626                *missing = TRUE;
627              return SVN_NO_ERROR;
628            }
629        }
630#ifdef ESTALE
631      else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
632                || APR_TO_OS_ERROR(err->apr_err) == EIO)
633        {
634          if (!last_attempt)
635            {
636              svn_error_clear(err);
637              return SVN_NO_ERROR;
638            }
639        }
640#endif
641    }
642
643  return svn_error_trace(err);
644}
645
646/* Fetch the current offset of FILE into *OFFSET_P. */
647svn_error_t *
648svn_fs_x__read_content(svn_stringbuf_t **content,
649                       const char *fname,
650                       apr_pool_t *result_pool)
651{
652  int i;
653  *content = NULL;
654
655  for (i = 0; !*content && (i < SVN_FS_X__RECOVERABLE_RETRY_COUNT); ++i)
656    SVN_ERR(svn_fs_x__try_stringbuf_from_file(content, NULL,
657                           fname, i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT,
658                           result_pool));
659
660  if (!*content)
661    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
662                             _("Can't read '%s'"),
663                             svn_dirent_local_style(fname, result_pool));
664
665  return SVN_NO_ERROR;
666}
667
668/* Reads a line from STREAM and converts it to a 64 bit integer to be
669 * returned in *RESULT.  If we encounter eof, set *HIT_EOF and leave
670 * *RESULT unchanged.  If HIT_EOF is NULL, EOF causes an "corrupt FS"
671 * error return.
672 * SCRATCH_POOL is used for temporary allocations.
673 */
674svn_error_t *
675svn_fs_x__read_number_from_stream(apr_int64_t *result,
676                                  svn_boolean_t *hit_eof,
677                                  svn_stream_t *stream,
678                                  apr_pool_t *scratch_pool)
679{
680  svn_stringbuf_t *sb;
681  svn_boolean_t eof;
682  svn_error_t *err;
683
684  SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
685  if (hit_eof)
686    *hit_eof = eof;
687  else
688    if (eof)
689      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
690
691  if (!eof)
692    {
693      err = svn_cstring_atoi64(result, sb->data);
694      if (err)
695        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
696                                 _("Number '%s' invalid or too large"),
697                                 sb->data);
698    }
699
700  return SVN_NO_ERROR;
701}
702
703svn_error_t *
704svn_fs_x__move_into_place(const char *old_filename,
705                          const char *new_filename,
706                          const char *perms_reference,
707                          svn_fs_x__batch_fsync_t *batch,
708                          apr_pool_t *scratch_pool)
709{
710  /* Copying permissions is a no-op on WIN32. */
711  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, scratch_pool));
712
713  /* We use specific 'fsyncing move' Win32 API calls on Windows while the
714   * directory update fsync is POSIX-only.  Moreover, there tend to be only
715   * a few moved files (1 or 2) per batch.
716   *
717   * Therefore, we use the platform-optimized "immediate" fsyncs on all
718   * non-POSIX platforms and the "scheduled" fsyncs on POSIX only.
719   */
720#if defined(SVN_ON_POSIX)
721  /* Move the file into place. */
722  SVN_ERR(svn_io_file_rename2(old_filename, new_filename, FALSE,
723                              scratch_pool));
724
725  /* Schedule for synchronization. */
726  SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, new_filename, scratch_pool));
727#else
728  SVN_ERR(svn_io_file_rename2(old_filename, new_filename, TRUE,
729                              scratch_pool));
730#endif
731
732  return SVN_NO_ERROR;
733}
734