1289177Speter/* util.c --- utility functions for FSX 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_x.h"
30289177Speter#include "id.h"
31289177Speter#include "util.h"
32289177Speter
33289177Speter#include "../libsvn_fs/fs-loader.h"
34289177Speter
35289177Speter#include "svn_private_config.h"
36289177Speter
37289177Speter/* Following are defines that specify the textual elements of the
38289177Speter   native filesystem directories and revision files. */
39289177Speter
40289177Speter/* Notes:
41289177Speter
42289177SpeterTo avoid opening and closing the rev-files all the time, it would
43289177Speterprobably be advantageous to keep each rev-file open for the
44289177Speterlifetime of the transaction object.  I'll leave that as a later
45289177Speteroptimization for now.
46289177Speter
47289177SpeterI didn't keep track of pool lifetimes at all in this code.  There
48289177Speterare likely some errors because of that.
49289177Speter
50289177Speter*/
51289177Speter
52289177Speter/* Pathname helper functions */
53289177Speter
54289177Speter/* Return TRUE is REV is packed in FS, FALSE otherwise. */
55289177Spetersvn_boolean_t
56289177Spetersvn_fs_x__is_packed_rev(svn_fs_t *fs, svn_revnum_t rev)
57289177Speter{
58289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
59289177Speter
60289177Speter  return (rev < ffd->min_unpacked_rev);
61289177Speter}
62289177Speter
63289177Speter/* Return TRUE is REV is packed in FS, FALSE otherwise. */
64289177Spetersvn_boolean_t
65289177Spetersvn_fs_x__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev)
66289177Speter{
67289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
68289177Speter
69289177Speter  /* rev 0 will not be packed */
70289177Speter  return (rev < ffd->min_unpacked_rev) && (rev != 0);
71289177Speter}
72289177Speter
73289177Spetersvn_revnum_t
74289177Spetersvn_fs_x__packed_base_rev(svn_fs_t *fs, svn_revnum_t rev)
75289177Speter{
76289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
77289177Speter
78289177Speter  return rev < ffd->min_unpacked_rev
79289177Speter       ? rev - (rev % ffd->max_files_per_dir)
80289177Speter       : rev;
81289177Speter}
82289177Speter
83289177Spetersvn_revnum_t
84289177Spetersvn_fs_x__pack_size(svn_fs_t *fs, svn_revnum_t rev)
85289177Speter{
86289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
87289177Speter
88289177Speter  return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1;
89289177Speter}
90289177Speter
91289177Speterconst char *
92289177Spetersvn_fs_x__path_format(svn_fs_t *fs,
93289177Speter                      apr_pool_t *result_pool)
94289177Speter{
95289177Speter  return svn_dirent_join(fs->path, PATH_FORMAT, result_pool);
96289177Speter}
97289177Speter
98289177Speterconst char *
99289177Spetersvn_fs_x__path_uuid(svn_fs_t *fs,
100289177Speter                    apr_pool_t *result_pool)
101289177Speter{
102289177Speter  return svn_dirent_join(fs->path, PATH_UUID, result_pool);
103289177Speter}
104289177Speter
105289177Speterconst char *
106289177Spetersvn_fs_x__path_current(svn_fs_t *fs,
107289177Speter                       apr_pool_t *result_pool)
108289177Speter{
109289177Speter  return svn_dirent_join(fs->path, PATH_CURRENT, result_pool);
110289177Speter}
111289177Speter
112289177Speterconst char *
113289177Spetersvn_fs_x__path_txn_current(svn_fs_t *fs,
114289177Speter                           apr_pool_t *result_pool)
115289177Speter{
116289177Speter  return svn_dirent_join(fs->path, PATH_TXN_CURRENT,
117289177Speter                         result_pool);
118289177Speter}
119289177Speter
120289177Speterconst char *
121289177Spetersvn_fs_x__path_txn_current_lock(svn_fs_t *fs,
122289177Speter                                apr_pool_t *result_pool)
123289177Speter{
124289177Speter  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, result_pool);
125289177Speter}
126289177Speter
127289177Speterconst char *
128289177Spetersvn_fs_x__path_lock(svn_fs_t *fs,
129289177Speter                    apr_pool_t *result_pool)
130289177Speter{
131289177Speter  return svn_dirent_join(fs->path, PATH_LOCK_FILE, result_pool);
132289177Speter}
133289177Speter
134289177Speterconst char *
135289177Spetersvn_fs_x__path_pack_lock(svn_fs_t *fs,
136289177Speter                         apr_pool_t *result_pool)
137289177Speter{
138289177Speter  return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, result_pool);
139289177Speter}
140289177Speter
141289177Speterconst char *
142289177Spetersvn_fs_x__path_revprop_generation(svn_fs_t *fs,
143289177Speter                                  apr_pool_t *result_pool)
144289177Speter{
145289177Speter  return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, result_pool);
146289177Speter}
147289177Speter
148289177Speter/* Return the full path of the file FILENAME within revision REV's shard in
149289177Speter * FS.  If FILENAME is NULL, return the shard directory directory itself.
150289177Speter * REVPROPS indicates the parent of the shard parent folder ("revprops" or
151289177Speter * "revs").  PACKED says whether we want the packed shard's name.
152289177Speter *
153289177Speter * Allocate the result in RESULT_POOL.
154289177Speter */static const char*
155289177Speterconstruct_shard_sub_path(svn_fs_t *fs,
156289177Speter                         svn_revnum_t rev,
157289177Speter                         svn_boolean_t revprops,
158289177Speter                         svn_boolean_t packed,
159289177Speter                         const char *filename,
160289177Speter                         apr_pool_t *result_pool)
161289177Speter{
162289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
163289177Speter  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_PACKED_SHARD)] = { 0 };
164289177Speter
165289177Speter  /* Select the appropriate parent path constant. */
166289177Speter  const char *parent = revprops ? PATH_REVPROPS_DIR : PATH_REVS_DIR;
167289177Speter
168289177Speter  /* String containing the shard number. */
169289177Speter  apr_size_t len = svn__i64toa(buffer, rev / ffd->max_files_per_dir);
170289177Speter
171289177Speter  /* Append the suffix.  Limit it to the buffer size (should never hit it). */
172289177Speter  if (packed)
173289177Speter    strncpy(buffer + len, PATH_EXT_PACKED_SHARD, sizeof(buffer) - len - 1);
174289177Speter
175289177Speter  /* This will also work for NULL FILENAME as well. */
176289177Speter  return svn_dirent_join_many(result_pool, fs->path, parent, buffer,
177289177Speter                              filename, SVN_VA_NULL);
178289177Speter}
179289177Speter
180289177Speterconst char *
181289177Spetersvn_fs_x__path_rev_packed(svn_fs_t *fs,
182289177Speter                          svn_revnum_t rev,
183289177Speter                          const char *kind,
184289177Speter                          apr_pool_t *result_pool)
185289177Speter{
186289177Speter  assert(svn_fs_x__is_packed_rev(fs, rev));
187289177Speter  return construct_shard_sub_path(fs, rev, FALSE, TRUE, kind, result_pool);
188289177Speter}
189289177Speter
190289177Speterconst char *
191289177Spetersvn_fs_x__path_rev_shard(svn_fs_t *fs,
192289177Speter                         svn_revnum_t rev,
193289177Speter                         apr_pool_t *result_pool)
194289177Speter{
195289177Speter  return construct_shard_sub_path(fs, rev, FALSE, FALSE, NULL, result_pool);
196289177Speter}
197289177Speter
198289177Speterconst char *
199289177Spetersvn_fs_x__path_rev(svn_fs_t *fs,
200289177Speter                   svn_revnum_t rev,
201289177Speter                   apr_pool_t *result_pool)
202289177Speter{
203289177Speter  char buffer[SVN_INT64_BUFFER_SIZE];
204289177Speter  svn__i64toa(buffer, rev);
205289177Speter
206289177Speter  assert(! svn_fs_x__is_packed_rev(fs, rev));
207289177Speter  return construct_shard_sub_path(fs, rev, FALSE, FALSE, buffer, result_pool);
208289177Speter}
209289177Speter
210289177Speterconst char *
211289177Spetersvn_fs_x__path_rev_absolute(svn_fs_t *fs,
212289177Speter                            svn_revnum_t rev,
213289177Speter                            apr_pool_t *result_pool)
214289177Speter{
215289177Speter  return svn_fs_x__is_packed_rev(fs, rev)
216289177Speter       ? svn_fs_x__path_rev_packed(fs, rev, PATH_PACKED, result_pool)
217289177Speter       : svn_fs_x__path_rev(fs, rev, result_pool);
218289177Speter}
219289177Speter
220289177Speterconst char *
221289177Spetersvn_fs_x__path_revprops_shard(svn_fs_t *fs,
222289177Speter                              svn_revnum_t rev,
223289177Speter                              apr_pool_t *result_pool)
224289177Speter{
225289177Speter  return construct_shard_sub_path(fs, rev, TRUE, FALSE, NULL, result_pool);
226289177Speter}
227289177Speter
228289177Speterconst char *
229289177Spetersvn_fs_x__path_revprops_pack_shard(svn_fs_t *fs,
230289177Speter                                   svn_revnum_t rev,
231289177Speter                                   apr_pool_t *result_pool)
232289177Speter{
233289177Speter  return construct_shard_sub_path(fs, rev, TRUE, TRUE, NULL, result_pool);
234289177Speter}
235289177Speter
236289177Speterconst char *
237289177Spetersvn_fs_x__path_revprops(svn_fs_t *fs,
238289177Speter                        svn_revnum_t rev,
239289177Speter                        apr_pool_t *result_pool)
240289177Speter{
241289177Speter  char buffer[SVN_INT64_BUFFER_SIZE];
242289177Speter  svn__i64toa(buffer, rev);
243289177Speter
244289177Speter  assert(! svn_fs_x__is_packed_revprop(fs, rev));
245289177Speter  return construct_shard_sub_path(fs, rev, TRUE, FALSE, buffer, result_pool);
246289177Speter}
247289177Speter
248289177Speterconst char *
249289177Spetersvn_fs_x__txn_name(svn_fs_x__txn_id_t txn_id,
250289177Speter                   apr_pool_t *result_pool)
251289177Speter{
252289177Speter  char *p = apr_palloc(result_pool, SVN_INT64_BUFFER_SIZE);
253289177Speter  svn__ui64tobase36(p, txn_id);
254289177Speter  return p;
255289177Speter}
256289177Speter
257289177Spetersvn_error_t *
258289177Spetersvn_fs_x__txn_by_name(svn_fs_x__txn_id_t *txn_id,
259289177Speter                      const char *txn_name)
260289177Speter{
261289177Speter  const char *next;
262289177Speter  apr_uint64_t id = svn__base36toui64(&next, txn_name);
263289177Speter  if (next == NULL || *next != 0 || *txn_name == 0)
264289177Speter    return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
265289177Speter                             "Malformed TXN name '%s'", txn_name);
266289177Speter
267289177Speter  *txn_id = id;
268289177Speter  return SVN_NO_ERROR;
269289177Speter}
270289177Speter
271289177Speterconst char *
272289177Spetersvn_fs_x__path_txns_dir(svn_fs_t *fs,
273289177Speter                        apr_pool_t *result_pool)
274289177Speter{
275289177Speter  return svn_dirent_join(fs->path, PATH_TXNS_DIR, result_pool);
276289177Speter}
277289177Speter
278289177Speter/* Return the full path of the file FILENAME within transaction TXN_ID's
279289177Speter * transaction directory in FS.  If FILENAME is NULL, return the transaction
280289177Speter * directory itself.
281289177Speter *
282289177Speter * Allocate the result in RESULT_POOL.
283289177Speter */
284289177Speterstatic const char *
285289177Speterconstruct_txn_path(svn_fs_t *fs,
286289177Speter                   svn_fs_x__txn_id_t txn_id,
287289177Speter                   const char *filename,
288289177Speter                   apr_pool_t *result_pool)
289289177Speter{
290289177Speter  /* Construct the transaction directory name without temp. allocations. */
291289177Speter  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_TXN)] = { 0 };
292289177Speter  apr_size_t len = svn__ui64tobase36(buffer, txn_id);
293289177Speter  strncpy(buffer + len, PATH_EXT_TXN, sizeof(buffer) - len - 1);
294289177Speter
295289177Speter  /* If FILENAME is NULL, it will terminate the list of segments
296289177Speter     to concatenate. */
297289177Speter  return svn_dirent_join_many(result_pool, fs->path, PATH_TXNS_DIR,
298289177Speter                              buffer, filename, SVN_VA_NULL);
299289177Speter}
300289177Speter
301289177Speterconst char *
302289177Spetersvn_fs_x__path_txn_dir(svn_fs_t *fs,
303289177Speter                       svn_fs_x__txn_id_t txn_id,
304289177Speter                       apr_pool_t *result_pool)
305289177Speter{
306289177Speter  return construct_txn_path(fs, txn_id, NULL, result_pool);
307289177Speter}
308289177Speter
309289177Speter/* Return the name of the sha1->rep mapping file in transaction TXN_ID
310289177Speter * within FS for the given SHA1 checksum.  Use POOL for allocations.
311289177Speter */
312289177Speterconst char *
313289177Spetersvn_fs_x__path_txn_sha1(svn_fs_t *fs,
314289177Speter                        svn_fs_x__txn_id_t txn_id,
315289177Speter                        const unsigned char *sha1,
316289177Speter                        apr_pool_t *pool)
317289177Speter{
318289177Speter  svn_checksum_t checksum;
319289177Speter  checksum.digest = sha1;
320289177Speter  checksum.kind = svn_checksum_sha1;
321289177Speter
322289177Speter  return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, pool),
323289177Speter                         svn_checksum_to_cstring(&checksum, pool),
324289177Speter                         pool);
325289177Speter}
326289177Speter
327289177Speterconst char *
328289177Spetersvn_fs_x__path_txn_changes(svn_fs_t *fs,
329289177Speter                           svn_fs_x__txn_id_t txn_id,
330289177Speter                           apr_pool_t *result_pool)
331289177Speter{
332289177Speter  return construct_txn_path(fs, txn_id, PATH_CHANGES, result_pool);
333289177Speter}
334289177Speter
335289177Speterconst char *
336289177Spetersvn_fs_x__path_txn_props(svn_fs_t *fs,
337289177Speter                         svn_fs_x__txn_id_t txn_id,
338289177Speter                         apr_pool_t *result_pool)
339289177Speter{
340289177Speter  return construct_txn_path(fs, txn_id, PATH_TXN_PROPS, result_pool);
341289177Speter}
342289177Speter
343289177Speterconst char *
344289177Spetersvn_fs_x__path_txn_props_final(svn_fs_t *fs,
345289177Speter                               svn_fs_x__txn_id_t txn_id,
346289177Speter                               apr_pool_t *result_pool)
347289177Speter{
348289177Speter  return construct_txn_path(fs, txn_id, PATH_TXN_PROPS_FINAL, result_pool);
349289177Speter}
350289177Speter
351289177Speterconst char*
352289177Spetersvn_fs_x__path_l2p_proto_index(svn_fs_t *fs,
353289177Speter                               svn_fs_x__txn_id_t txn_id,
354289177Speter                               apr_pool_t *result_pool)
355289177Speter{
356289177Speter  return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_L2P_INDEX,
357289177Speter                            result_pool);
358289177Speter}
359289177Speter
360289177Speterconst char*
361289177Spetersvn_fs_x__path_p2l_proto_index(svn_fs_t *fs,
362289177Speter                               svn_fs_x__txn_id_t txn_id,
363289177Speter                               apr_pool_t *result_pool)
364289177Speter{
365289177Speter  return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_P2L_INDEX,
366289177Speter                            result_pool);
367289177Speter}
368289177Speter
369289177Speterconst char *
370289177Spetersvn_fs_x__path_txn_next_ids(svn_fs_t *fs,
371289177Speter                            svn_fs_x__txn_id_t txn_id,
372289177Speter                            apr_pool_t *result_pool)
373289177Speter{
374289177Speter  return construct_txn_path(fs, txn_id, PATH_NEXT_IDS, result_pool);
375289177Speter}
376289177Speter
377289177Speterconst char *
378289177Spetersvn_fs_x__path_min_unpacked_rev(svn_fs_t *fs,
379289177Speter                                apr_pool_t *result_pool)
380289177Speter{
381289177Speter  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, result_pool);
382289177Speter}
383289177Speter
384289177Speterconst char *
385289177Spetersvn_fs_x__path_txn_proto_revs(svn_fs_t *fs,
386289177Speter                              apr_pool_t *result_pool)
387289177Speter{
388289177Speter  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, result_pool);
389289177Speter}
390289177Speter
391289177Speterconst char *
392289177Spetersvn_fs_x__path_txn_item_index(svn_fs_t *fs,
393289177Speter                              svn_fs_x__txn_id_t txn_id,
394289177Speter                              apr_pool_t *result_pool)
395289177Speter{
396289177Speter  return construct_txn_path(fs, txn_id, PATH_TXN_ITEM_INDEX, result_pool);
397289177Speter}
398289177Speter
399289177Speter/* Return the full path of the proto-rev file / lock file for transaction
400289177Speter * TXN_ID in FS.  The SUFFIX determines what file (rev / lock) it will be.
401289177Speter *
402289177Speter * Allocate the result in RESULT_POOL.
403289177Speter */
404289177Speterstatic const char *
405289177Speterconstruct_proto_rev_path(svn_fs_t *fs,
406289177Speter                         svn_fs_x__txn_id_t txn_id,
407289177Speter                         const char *suffix,
408289177Speter                         apr_pool_t *result_pool)
409289177Speter{
410289177Speter  /* Construct the file name without temp. allocations. */
411289177Speter  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_REV_LOCK)] = { 0 };
412289177Speter  apr_size_t len = svn__ui64tobase36(buffer, txn_id);
413289177Speter  strncpy(buffer + len, suffix, sizeof(buffer) - len - 1);
414289177Speter
415289177Speter  /* If FILENAME is NULL, it will terminate the list of segments
416289177Speter     to concatenate. */
417289177Speter  return svn_dirent_join_many(result_pool, fs->path, PATH_TXN_PROTOS_DIR,
418289177Speter                              buffer, SVN_VA_NULL);
419289177Speter}
420289177Speter
421289177Speterconst char *
422289177Spetersvn_fs_x__path_txn_proto_rev(svn_fs_t *fs,
423289177Speter                             svn_fs_x__txn_id_t txn_id,
424289177Speter                             apr_pool_t *result_pool)
425289177Speter{
426289177Speter  return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV, result_pool);
427289177Speter}
428289177Speter
429289177Speterconst char *
430289177Spetersvn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs,
431289177Speter                                  svn_fs_x__txn_id_t txn_id,
432289177Speter                                  apr_pool_t *result_pool)
433289177Speter{
434289177Speter  return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV_LOCK, result_pool);
435289177Speter}
436289177Speter
437289177Speter/* Return the full path of the noderev-related file with the extension SUFFIX
438289177Speter * for noderev *ID in transaction TXN_ID in FS.
439289177Speter *
440289177Speter * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL.
441289177Speter */
442289177Speterstatic const char *
443289177Speterconstruct_txn_node_path(svn_fs_t *fs,
444289177Speter                        const svn_fs_x__id_t *id,
445289177Speter                        const char *suffix,
446289177Speter                        apr_pool_t *result_pool,
447289177Speter                        apr_pool_t *scratch_pool)
448289177Speter{
449289177Speter  const char *filename = svn_fs_x__id_unparse(id, result_pool)->data;
450289177Speter  apr_int64_t txn_id = svn_fs_x__get_txn_id(id->change_set);
451289177Speter
452289177Speter  return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool),
453289177Speter                         apr_psprintf(scratch_pool, PATH_PREFIX_NODE "%s%s",
454289177Speter                                      filename, suffix),
455289177Speter                         result_pool);
456289177Speter}
457289177Speter
458289177Speterconst char *
459289177Spetersvn_fs_x__path_txn_node_rev(svn_fs_t *fs,
460289177Speter                            const svn_fs_x__id_t *id,
461289177Speter                            apr_pool_t *result_pool,
462289177Speter                            apr_pool_t *scratch_pool)
463289177Speter{
464289177Speter  return construct_txn_node_path(fs, id, "", result_pool, scratch_pool);
465289177Speter}
466289177Speter
467289177Speterconst char *
468289177Spetersvn_fs_x__path_txn_node_props(svn_fs_t *fs,
469289177Speter                              const svn_fs_x__id_t *id,
470289177Speter                              apr_pool_t *result_pool,
471289177Speter                              apr_pool_t *scratch_pool)
472289177Speter{
473289177Speter  return construct_txn_node_path(fs, id, PATH_EXT_PROPS, result_pool,
474289177Speter                                 scratch_pool);
475289177Speter}
476289177Speter
477289177Speterconst char *
478289177Spetersvn_fs_x__path_txn_node_children(svn_fs_t *fs,
479289177Speter                                 const svn_fs_x__id_t *id,
480289177Speter                                 apr_pool_t *result_pool,
481289177Speter                                 apr_pool_t *scratch_pool)
482289177Speter{
483289177Speter  return construct_txn_node_path(fs, id, PATH_EXT_CHILDREN, result_pool,
484289177Speter                                 scratch_pool);
485289177Speter}
486289177Speter
487289177Spetersvn_error_t *
488289177Spetersvn_fs_x__check_file_buffer_numeric(const char *buf,
489289177Speter                                    apr_off_t offset,
490289177Speter                                    const char *path,
491289177Speter                                    const char *title,
492289177Speter                                    apr_pool_t *scratch_pool)
493289177Speter{
494289177Speter  const char *p;
495289177Speter
496289177Speter  for (p = buf + offset; *p; p++)
497289177Speter    if (!svn_ctype_isdigit(*p))
498289177Speter      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
499289177Speter        _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
500289177Speter        title, svn_dirent_local_style(path, scratch_pool), *p, buf);
501289177Speter
502289177Speter  return SVN_NO_ERROR;
503289177Speter}
504289177Speter
505289177Spetersvn_error_t *
506289177Spetersvn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
507289177Speter                                svn_fs_t *fs,
508289177Speter                                apr_pool_t *scratch_pool)
509289177Speter{
510289177Speter  char buf[80];
511289177Speter  apr_file_t *file;
512289177Speter  apr_size_t len;
513289177Speter
514289177Speter  SVN_ERR(svn_io_file_open(&file,
515289177Speter                           svn_fs_x__path_min_unpacked_rev(fs, scratch_pool),
516289177Speter                           APR_READ | APR_BUFFERED,
517289177Speter                           APR_OS_DEFAULT,
518289177Speter                           scratch_pool));
519289177Speter  len = sizeof(buf);
520289177Speter  SVN_ERR(svn_io_read_length_line(file, buf, &len, scratch_pool));
521289177Speter  SVN_ERR(svn_io_file_close(file, scratch_pool));
522289177Speter
523289177Speter  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
524289177Speter  return SVN_NO_ERROR;
525289177Speter}
526289177Speter
527289177Spetersvn_error_t *
528289177Spetersvn_fs_x__update_min_unpacked_rev(svn_fs_t *fs,
529289177Speter                                  apr_pool_t *scratch_pool)
530289177Speter{
531289177Speter  svn_fs_x__data_t *ffd = fs->fsap_data;
532289177Speter  return svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs,
533289177Speter                                         scratch_pool);
534289177Speter}
535289177Speter
536289177Speter/* Write a file FILENAME in directory FS_PATH, containing a single line
537289177Speter * with the number REVNUM in ASCII decimal.  Move the file into place
538289177Speter * atomically, overwriting any existing file.
539289177Speter *
540289177Speter * Similar to write_current(). */
541289177Spetersvn_error_t *
542289177Spetersvn_fs_x__write_min_unpacked_rev(svn_fs_t *fs,
543289177Speter                                 svn_revnum_t revnum,
544289177Speter                                 apr_pool_t *scratch_pool)
545289177Speter{
546289177Speter  const char *final_path;
547289177Speter  char buf[SVN_INT64_BUFFER_SIZE];
548289177Speter  apr_size_t len = svn__i64toa(buf, revnum);
549289177Speter  buf[len] = '\n';
550289177Speter
551289177Speter  final_path = svn_fs_x__path_min_unpacked_rev(fs, scratch_pool);
552289177Speter
553289177Speter  SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1,
554289177Speter                              final_path /* copy_perms */, scratch_pool));
555289177Speter
556289177Speter  return SVN_NO_ERROR;
557289177Speter}
558289177Speter
559289177Spetersvn_error_t *
560289177Spetersvn_fs_x__read_current(svn_revnum_t *rev,
561289177Speter                       svn_fs_t *fs,
562289177Speter                       apr_pool_t *scratch_pool)
563289177Speter{
564289177Speter  const char *str;
565289177Speter  svn_stringbuf_t *content;
566289177Speter  SVN_ERR(svn_fs_x__read_content(&content,
567289177Speter                                 svn_fs_x__path_current(fs, scratch_pool),
568289177Speter                                 scratch_pool));
569289177Speter  SVN_ERR(svn_revnum_parse(rev, content->data, &str));
570289177Speter  if (*str != '\n')
571289177Speter    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
572289177Speter                            _("Corrupt 'current' file"));
573289177Speter
574289177Speter  return SVN_NO_ERROR;
575289177Speter}
576289177Speter
577289177Speter/* Atomically update the 'current' file to hold the specifed REV.
578289177Speter   Perform temporary allocations in SCRATCH_POOL. */
579289177Spetersvn_error_t *
580289177Spetersvn_fs_x__write_current(svn_fs_t *fs,
581289177Speter                        svn_revnum_t rev,
582289177Speter                        apr_pool_t *scratch_pool)
583289177Speter{
584289177Speter  char *buf;
585289177Speter  const char *tmp_name, *name;
586289177Speter
587289177Speter  /* Now we can just write out this line. */
588289177Speter  buf = apr_psprintf(scratch_pool, "%ld\n", rev);
589289177Speter
590289177Speter  name = svn_fs_x__path_current(fs, scratch_pool);
591289177Speter  SVN_ERR(svn_io_write_unique(&tmp_name,
592289177Speter                              svn_dirent_dirname(name, scratch_pool),
593289177Speter                              buf, strlen(buf),
594289177Speter                              svn_io_file_del_none, scratch_pool));
595289177Speter
596289177Speter  return svn_fs_x__move_into_place(tmp_name, name, name, scratch_pool);
597289177Speter}
598289177Speter
599289177Speter
600289177Spetersvn_error_t *
601289177Spetersvn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content,
602289177Speter                                  svn_boolean_t *missing,
603289177Speter                                  const char *path,
604289177Speter                                  svn_boolean_t last_attempt,
605289177Speter                                  apr_pool_t *result_pool)
606289177Speter{
607289177Speter  svn_error_t *err = svn_stringbuf_from_file2(content, path, result_pool);
608289177Speter  if (missing)
609289177Speter    *missing = FALSE;
610289177Speter
611289177Speter  if (err)
612289177Speter    {
613289177Speter      *content = NULL;
614289177Speter
615289177Speter      if (APR_STATUS_IS_ENOENT(err->apr_err))
616289177Speter        {
617289177Speter          if (!last_attempt)
618289177Speter            {
619289177Speter              svn_error_clear(err);
620289177Speter              if (missing)
621289177Speter                *missing = TRUE;
622289177Speter              return SVN_NO_ERROR;
623289177Speter            }
624289177Speter        }
625289177Speter#ifdef ESTALE
626289177Speter      else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
627289177Speter                || APR_TO_OS_ERROR(err->apr_err) == EIO)
628289177Speter        {
629289177Speter          if (!last_attempt)
630289177Speter            {
631289177Speter              svn_error_clear(err);
632289177Speter              return SVN_NO_ERROR;
633289177Speter            }
634289177Speter        }
635289177Speter#endif
636289177Speter    }
637289177Speter
638289177Speter  return svn_error_trace(err);
639289177Speter}
640289177Speter
641289177Speter/* Fetch the current offset of FILE into *OFFSET_P. */
642289177Spetersvn_error_t *
643289177Spetersvn_fs_x__get_file_offset(apr_off_t *offset_p,
644289177Speter                          apr_file_t *file,
645289177Speter                          apr_pool_t *scratch_pool)
646289177Speter{
647289177Speter  apr_off_t offset;
648289177Speter
649289177Speter  /* Note that, for buffered files, one (possibly surprising) side-effect
650289177Speter     of this call is to flush any unwritten data to disk. */
651289177Speter  offset = 0;
652289177Speter  SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, scratch_pool));
653289177Speter  *offset_p = offset;
654289177Speter
655289177Speter  return SVN_NO_ERROR;
656289177Speter}
657289177Speter
658289177Spetersvn_error_t *
659289177Spetersvn_fs_x__read_content(svn_stringbuf_t **content,
660289177Speter                       const char *fname,
661289177Speter                       apr_pool_t *result_pool)
662289177Speter{
663289177Speter  int i;
664289177Speter  *content = NULL;
665289177Speter
666289177Speter  for (i = 0; !*content && (i < SVN_FS_X__RECOVERABLE_RETRY_COUNT); ++i)
667289177Speter    SVN_ERR(svn_fs_x__try_stringbuf_from_file(content, NULL,
668289177Speter                           fname, i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT,
669289177Speter                           result_pool));
670289177Speter
671289177Speter  if (!*content)
672289177Speter    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
673289177Speter                             _("Can't read '%s'"),
674289177Speter                             svn_dirent_local_style(fname, result_pool));
675289177Speter
676289177Speter  return SVN_NO_ERROR;
677289177Speter}
678289177Speter
679289177Speter/* Reads a line from STREAM and converts it to a 64 bit integer to be
680289177Speter * returned in *RESULT.  If we encounter eof, set *HIT_EOF and leave
681289177Speter * *RESULT unchanged.  If HIT_EOF is NULL, EOF causes an "corrupt FS"
682289177Speter * error return.
683289177Speter * SCRATCH_POOL is used for temporary allocations.
684289177Speter */
685289177Spetersvn_error_t *
686289177Spetersvn_fs_x__read_number_from_stream(apr_int64_t *result,
687289177Speter                                  svn_boolean_t *hit_eof,
688289177Speter                                  svn_stream_t *stream,
689289177Speter                                  apr_pool_t *scratch_pool)
690289177Speter{
691289177Speter  svn_stringbuf_t *sb;
692289177Speter  svn_boolean_t eof;
693289177Speter  svn_error_t *err;
694289177Speter
695289177Speter  SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
696289177Speter  if (hit_eof)
697289177Speter    *hit_eof = eof;
698289177Speter  else
699289177Speter    if (eof)
700289177Speter      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
701289177Speter
702289177Speter  if (!eof)
703289177Speter    {
704289177Speter      err = svn_cstring_atoi64(result, sb->data);
705289177Speter      if (err)
706289177Speter        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
707289177Speter                                 _("Number '%s' invalid or too large"),
708289177Speter                                 sb->data);
709289177Speter    }
710289177Speter
711289177Speter  return SVN_NO_ERROR;
712289177Speter}
713289177Speter
714289177Speter
715289177Speter/* Move a file into place from OLD_FILENAME in the transactions
716289177Speter   directory to its final location NEW_FILENAME in the repository.  On
717289177Speter   Unix, match the permissions of the new file to the permissions of
718289177Speter   PERMS_REFERENCE.  Temporary allocations are from SCRATCH_POOL.
719289177Speter
720289177Speter   This function almost duplicates svn_io_file_move(), but it tries to
721289177Speter   guarantee a flush. */
722289177Spetersvn_error_t *
723289177Spetersvn_fs_x__move_into_place(const char *old_filename,
724289177Speter                          const char *new_filename,
725289177Speter                          const char *perms_reference,
726289177Speter                          apr_pool_t *scratch_pool)
727289177Speter{
728289177Speter  svn_error_t *err;
729289177Speter
730289177Speter  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, scratch_pool));
731289177Speter
732289177Speter  /* Move the file into place. */
733289177Speter  err = svn_io_file_rename(old_filename, new_filename, scratch_pool);
734289177Speter  if (err && APR_STATUS_IS_EXDEV(err->apr_err))
735289177Speter    {
736289177Speter      apr_file_t *file;
737289177Speter
738289177Speter      /* Can't rename across devices; fall back to copying. */
739289177Speter      svn_error_clear(err);
740289177Speter      err = SVN_NO_ERROR;
741289177Speter      SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE,
742289177Speter                               scratch_pool));
743289177Speter
744289177Speter      /* Flush the target of the copy to disk. */
745289177Speter      SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
746289177Speter                               APR_OS_DEFAULT, scratch_pool));
747289177Speter      /* ### BH: Does this really guarantee a flush of the data written
748289177Speter         ### via a completely different handle on all operating systems?
749289177Speter         ###
750289177Speter         ### Maybe we should perform the copy ourselves instead of making
751289177Speter         ### apr do that and flush the real handle? */
752289177Speter      SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
753289177Speter      SVN_ERR(svn_io_file_close(file, scratch_pool));
754289177Speter    }
755289177Speter  if (err)
756289177Speter    return svn_error_trace(err);
757289177Speter
758289177Speter#ifdef __linux__
759289177Speter  {
760289177Speter    /* Linux has the unusual feature that fsync() on a file is not
761289177Speter       enough to ensure that a file's directory entries have been
762289177Speter       flushed to disk; you have to fsync the directory as well.
763289177Speter       On other operating systems, we'd only be asking for trouble
764289177Speter       by trying to open and fsync a directory. */
765289177Speter    const char *dirname;
766289177Speter    apr_file_t *file;
767289177Speter
768289177Speter    dirname = svn_dirent_dirname(new_filename, scratch_pool);
769289177Speter    SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
770289177Speter                             scratch_pool));
771289177Speter    SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
772289177Speter    SVN_ERR(svn_io_file_close(file, scratch_pool));
773289177Speter  }
774289177Speter#endif
775289177Speter
776289177Speter  return SVN_NO_ERROR;
777289177Speter}
778