~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_x/util.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
42
To avoid opening and closing the rev-files all the time, it would
 
43
probably be advantageous to keep each rev-file open for the
 
44
lifetime of the transaction object.  I'll leave that as a later
 
45
optimization for now.
 
46
 
 
47
I didn't keep track of pool lifetimes at all in this code.  There
 
48
are 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. */
 
55
svn_boolean_t
 
56
svn_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. */
 
64
svn_boolean_t
 
65
svn_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
 
 
73
svn_revnum_t
 
74
svn_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
 
 
83
svn_revnum_t
 
84
svn_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
 
 
91
const char *
 
92
svn_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
 
 
98
const char *
 
99
svn_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
 
 
105
const char *
 
106
svn_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
 
 
112
const char *
 
113
svn_fs_x__path_txn_current(svn_fs_t *fs,
 
114
                           apr_pool_t *result_pool)
 
115
{
 
116
  return svn_dirent_join(fs->path, PATH_TXN_CURRENT,
 
117
                         result_pool);
 
118
}
 
119
 
 
120
const char *
 
121
svn_fs_x__path_txn_current_lock(svn_fs_t *fs,
 
122
                                apr_pool_t *result_pool)
 
123
{
 
124
  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, result_pool);
 
125
}
 
126
 
 
127
const char *
 
128
svn_fs_x__path_lock(svn_fs_t *fs,
 
129
                    apr_pool_t *result_pool)
 
130
{
 
131
  return svn_dirent_join(fs->path, PATH_LOCK_FILE, result_pool);
 
132
}
 
133
 
 
134
const char *
 
135
svn_fs_x__path_pack_lock(svn_fs_t *fs,
 
136
                         apr_pool_t *result_pool)
 
137
{
 
138
  return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, result_pool);
 
139
}
 
140
 
 
141
const char *
 
142
svn_fs_x__path_revprop_generation(svn_fs_t *fs,
 
143
                                  apr_pool_t *result_pool)
 
144
{
 
145
  return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, result_pool);
 
146
}
 
147
 
 
148
/* Return the full path of the file FILENAME within revision REV's shard in
 
149
 * FS.  If FILENAME is NULL, return the shard directory directory itself.
 
150
 * REVPROPS indicates the parent of the shard parent folder ("revprops" or
 
151
 * "revs").  PACKED says whether we want the packed shard's name.
 
152
 *
 
153
 * Allocate the result in RESULT_POOL.
 
154
 */static const char*
 
155
construct_shard_sub_path(svn_fs_t *fs,
 
156
                         svn_revnum_t rev,
 
157
                         svn_boolean_t revprops,
 
158
                         svn_boolean_t packed,
 
159
                         const char *filename,
 
160
                         apr_pool_t *result_pool)
 
161
{
 
162
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
163
  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_PACKED_SHARD)] = { 0 };
 
164
 
 
165
  /* Select the appropriate parent path constant. */
 
166
  const char *parent = revprops ? PATH_REVPROPS_DIR : PATH_REVS_DIR;
 
167
 
 
168
  /* String containing the shard number. */
 
169
  apr_size_t len = svn__i64toa(buffer, rev / ffd->max_files_per_dir);
 
170
 
 
171
  /* Append the suffix.  Limit it to the buffer size (should never hit it). */
 
172
  if (packed)
 
173
    strncpy(buffer + len, PATH_EXT_PACKED_SHARD, sizeof(buffer) - len - 1);
 
174
 
 
175
  /* This will also work for NULL FILENAME as well. */
 
176
  return svn_dirent_join_many(result_pool, fs->path, parent, buffer,
 
177
                              filename, SVN_VA_NULL);
 
178
}
 
179
 
 
180
const char *
 
181
svn_fs_x__path_rev_packed(svn_fs_t *fs,
 
182
                          svn_revnum_t rev,
 
183
                          const char *kind,
 
184
                          apr_pool_t *result_pool)
 
185
{
 
186
  assert(svn_fs_x__is_packed_rev(fs, rev));
 
187
  return construct_shard_sub_path(fs, rev, FALSE, TRUE, kind, result_pool);
 
188
}
 
189
 
 
190
const char *
 
191
svn_fs_x__path_rev_shard(svn_fs_t *fs,
 
192
                         svn_revnum_t rev,
 
193
                         apr_pool_t *result_pool)
 
194
{
 
195
  return construct_shard_sub_path(fs, rev, FALSE, FALSE, NULL, result_pool);
 
196
}
 
197
 
 
198
const char *
 
199
svn_fs_x__path_rev(svn_fs_t *fs,
 
200
                   svn_revnum_t rev,
 
201
                   apr_pool_t *result_pool)
 
202
{
 
203
  char buffer[SVN_INT64_BUFFER_SIZE];
 
204
  svn__i64toa(buffer, rev);
 
205
 
 
206
  assert(! svn_fs_x__is_packed_rev(fs, rev));
 
207
  return construct_shard_sub_path(fs, rev, FALSE, FALSE, buffer, result_pool);
 
208
}
 
209
 
 
210
const char *
 
211
svn_fs_x__path_rev_absolute(svn_fs_t *fs,
 
212
                            svn_revnum_t rev,
 
213
                            apr_pool_t *result_pool)
 
214
{
 
215
  return svn_fs_x__is_packed_rev(fs, rev)
 
216
       ? svn_fs_x__path_rev_packed(fs, rev, PATH_PACKED, result_pool)
 
217
       : svn_fs_x__path_rev(fs, rev, result_pool);
 
218
}
 
219
 
 
220
const char *
 
221
svn_fs_x__path_revprops_shard(svn_fs_t *fs,
 
222
                              svn_revnum_t rev,
 
223
                              apr_pool_t *result_pool)
 
224
{
 
225
  return construct_shard_sub_path(fs, rev, TRUE, FALSE, NULL, result_pool);
 
226
}
 
227
 
 
228
const char *
 
229
svn_fs_x__path_revprops_pack_shard(svn_fs_t *fs,
 
230
                                   svn_revnum_t rev,
 
231
                                   apr_pool_t *result_pool)
 
232
{
 
233
  return construct_shard_sub_path(fs, rev, TRUE, TRUE, NULL, result_pool);
 
234
}
 
235
 
 
236
const char *
 
237
svn_fs_x__path_revprops(svn_fs_t *fs,
 
238
                        svn_revnum_t rev,
 
239
                        apr_pool_t *result_pool)
 
240
{
 
241
  char buffer[SVN_INT64_BUFFER_SIZE];
 
242
  svn__i64toa(buffer, rev);
 
243
 
 
244
  assert(! svn_fs_x__is_packed_revprop(fs, rev));
 
245
  return construct_shard_sub_path(fs, rev, TRUE, FALSE, buffer, result_pool);
 
246
}
 
247
 
 
248
const char *
 
249
svn_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
 
 
257
svn_error_t *
 
258
svn_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
 
 
271
const char *
 
272
svn_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
 */
 
284
static const char *
 
285
construct_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
 
 
301
const char *
 
302
svn_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
 */
 
312
const char *
 
313
svn_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
 
 
327
const char *
 
328
svn_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
 
 
335
const char *
 
336
svn_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
 
 
343
const char *
 
344
svn_fs_x__path_txn_props_final(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_TXN_PROPS_FINAL, result_pool);
 
349
}
 
350
 
 
351
const char*
 
352
svn_fs_x__path_l2p_proto_index(svn_fs_t *fs,
 
353
                               svn_fs_x__txn_id_t txn_id,
 
354
                               apr_pool_t *result_pool)
 
355
{
 
356
  return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_L2P_INDEX,
 
357
                            result_pool);
 
358
}
 
359
 
 
360
const char*
 
361
svn_fs_x__path_p2l_proto_index(svn_fs_t *fs,
 
362
                               svn_fs_x__txn_id_t txn_id,
 
363
                               apr_pool_t *result_pool)
 
364
{
 
365
  return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_P2L_INDEX,
 
366
                            result_pool);
 
367
}
 
368
 
 
369
const char *
 
370
svn_fs_x__path_txn_next_ids(svn_fs_t *fs,
 
371
                            svn_fs_x__txn_id_t txn_id,
 
372
                            apr_pool_t *result_pool)
 
373
{
 
374
  return construct_txn_path(fs, txn_id, PATH_NEXT_IDS, result_pool);
 
375
}
 
376
 
 
377
const char *
 
378
svn_fs_x__path_min_unpacked_rev(svn_fs_t *fs,
 
379
                                apr_pool_t *result_pool)
 
380
{
 
381
  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, result_pool);
 
382
}
 
383
 
 
384
const char *
 
385
svn_fs_x__path_txn_proto_revs(svn_fs_t *fs,
 
386
                              apr_pool_t *result_pool)
 
387
{
 
388
  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, result_pool);
 
389
}
 
390
 
 
391
const char *
 
392
svn_fs_x__path_txn_item_index(svn_fs_t *fs,
 
393
                              svn_fs_x__txn_id_t txn_id,
 
394
                              apr_pool_t *result_pool)
 
395
{
 
396
  return construct_txn_path(fs, txn_id, PATH_TXN_ITEM_INDEX, result_pool);
 
397
}
 
398
 
 
399
/* Return the full path of the proto-rev file / lock file for transaction
 
400
 * TXN_ID in FS.  The SUFFIX determines what file (rev / lock) it will be.
 
401
 *
 
402
 * Allocate the result in RESULT_POOL.
 
403
 */
 
404
static const char *
 
405
construct_proto_rev_path(svn_fs_t *fs,
 
406
                         svn_fs_x__txn_id_t txn_id,
 
407
                         const char *suffix,
 
408
                         apr_pool_t *result_pool)
 
409
{
 
410
  /* Construct the file name without temp. allocations. */
 
411
  char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_REV_LOCK)] = { 0 };
 
412
  apr_size_t len = svn__ui64tobase36(buffer, txn_id);
 
413
  strncpy(buffer + len, suffix, sizeof(buffer) - len - 1);
 
414
 
 
415
  /* If FILENAME is NULL, it will terminate the list of segments
 
416
     to concatenate. */
 
417
  return svn_dirent_join_many(result_pool, fs->path, PATH_TXN_PROTOS_DIR,
 
418
                              buffer, SVN_VA_NULL);
 
419
}
 
420
 
 
421
const char *
 
422
svn_fs_x__path_txn_proto_rev(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, result_pool);
 
427
}
 
428
 
 
429
const char *
 
430
svn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs,
 
431
                                  svn_fs_x__txn_id_t txn_id,
 
432
                                  apr_pool_t *result_pool)
 
433
{
 
434
  return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV_LOCK, result_pool);
 
435
}
 
436
 
 
437
/* Return the full path of the noderev-related file with the extension SUFFIX
 
438
 * for noderev *ID in transaction TXN_ID in FS.
 
439
 *
 
440
 * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL.
 
441
 */
 
442
static const char *
 
443
construct_txn_node_path(svn_fs_t *fs,
 
444
                        const svn_fs_x__id_t *id,
 
445
                        const char *suffix,
 
446
                        apr_pool_t *result_pool,
 
447
                        apr_pool_t *scratch_pool)
 
448
{
 
449
  const char *filename = svn_fs_x__id_unparse(id, result_pool)->data;
 
450
  apr_int64_t txn_id = svn_fs_x__get_txn_id(id->change_set);
 
451
 
 
452
  return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool),
 
453
                         apr_psprintf(scratch_pool, PATH_PREFIX_NODE "%s%s",
 
454
                                      filename, suffix),
 
455
                         result_pool);
 
456
}
 
457
 
 
458
const char *
 
459
svn_fs_x__path_txn_node_rev(svn_fs_t *fs,
 
460
                            const svn_fs_x__id_t *id,
 
461
                            apr_pool_t *result_pool,
 
462
                            apr_pool_t *scratch_pool)
 
463
{
 
464
  return construct_txn_node_path(fs, id, "", result_pool, scratch_pool);
 
465
}
 
466
 
 
467
const char *
 
468
svn_fs_x__path_txn_node_props(svn_fs_t *fs,
 
469
                              const svn_fs_x__id_t *id,
 
470
                              apr_pool_t *result_pool,
 
471
                              apr_pool_t *scratch_pool)
 
472
{
 
473
  return construct_txn_node_path(fs, id, PATH_EXT_PROPS, result_pool,
 
474
                                 scratch_pool);
 
475
}
 
476
 
 
477
const char *
 
478
svn_fs_x__path_txn_node_children(svn_fs_t *fs,
 
479
                                 const svn_fs_x__id_t *id,
 
480
                                 apr_pool_t *result_pool,
 
481
                                 apr_pool_t *scratch_pool)
 
482
{
 
483
  return construct_txn_node_path(fs, id, PATH_EXT_CHILDREN, result_pool,
 
484
                                 scratch_pool);
 
485
}
 
486
 
 
487
svn_error_t *
 
488
svn_fs_x__check_file_buffer_numeric(const char *buf,
 
489
                                    apr_off_t offset,
 
490
                                    const char *path,
 
491
                                    const char *title,
 
492
                                    apr_pool_t *scratch_pool)
 
493
{
 
494
  const char *p;
 
495
 
 
496
  for (p = buf + offset; *p; p++)
 
497
    if (!svn_ctype_isdigit(*p))
 
498
      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
 
499
        _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
 
500
        title, svn_dirent_local_style(path, scratch_pool), *p, buf);
 
501
 
 
502
  return SVN_NO_ERROR;
 
503
}
 
504
 
 
505
svn_error_t *
 
506
svn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
 
507
                                svn_fs_t *fs,
 
508
                                apr_pool_t *scratch_pool)
 
509
{
 
510
  char buf[80];
 
511
  apr_file_t *file;
 
512
  apr_size_t len;
 
513
 
 
514
  SVN_ERR(svn_io_file_open(&file,
 
515
                           svn_fs_x__path_min_unpacked_rev(fs, scratch_pool),
 
516
                           APR_READ | APR_BUFFERED,
 
517
                           APR_OS_DEFAULT,
 
518
                           scratch_pool));
 
519
  len = sizeof(buf);
 
520
  SVN_ERR(svn_io_read_length_line(file, buf, &len, scratch_pool));
 
521
  SVN_ERR(svn_io_file_close(file, scratch_pool));
 
522
 
 
523
  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
 
524
  return SVN_NO_ERROR;
 
525
}
 
526
 
 
527
svn_error_t *
 
528
svn_fs_x__update_min_unpacked_rev(svn_fs_t *fs,
 
529
                                  apr_pool_t *scratch_pool)
 
530
{
 
531
  svn_fs_x__data_t *ffd = fs->fsap_data;
 
532
  return svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs,
 
533
                                         scratch_pool);
 
534
}
 
535
 
 
536
/* Write a file FILENAME in directory FS_PATH, containing a single line
 
537
 * with the number REVNUM in ASCII decimal.  Move the file into place
 
538
 * atomically, overwriting any existing file.
 
539
 *
 
540
 * Similar to write_current(). */
 
541
svn_error_t *
 
542
svn_fs_x__write_min_unpacked_rev(svn_fs_t *fs,
 
543
                                 svn_revnum_t revnum,
 
544
                                 apr_pool_t *scratch_pool)
 
545
{
 
546
  const char *final_path;
 
547
  char buf[SVN_INT64_BUFFER_SIZE];
 
548
  apr_size_t len = svn__i64toa(buf, revnum);
 
549
  buf[len] = '\n';
 
550
 
 
551
  final_path = svn_fs_x__path_min_unpacked_rev(fs, scratch_pool);
 
552
 
 
553
  SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1,
 
554
                              final_path /* copy_perms */, scratch_pool));
 
555
 
 
556
  return SVN_NO_ERROR;
 
557
}
 
558
 
 
559
svn_error_t *
 
560
svn_fs_x__read_current(svn_revnum_t *rev,
 
561
                       svn_fs_t *fs,
 
562
                       apr_pool_t *scratch_pool)
 
563
{
 
564
  const char *str;
 
565
  svn_stringbuf_t *content;
 
566
  SVN_ERR(svn_fs_x__read_content(&content,
 
567
                                 svn_fs_x__path_current(fs, scratch_pool),
 
568
                                 scratch_pool));
 
569
  SVN_ERR(svn_revnum_parse(rev, content->data, &str));
 
570
  if (*str != '\n')
 
571
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
 
572
                            _("Corrupt 'current' file"));
 
573
 
 
574
  return SVN_NO_ERROR;
 
575
}
 
576
 
 
577
/* Atomically update the 'current' file to hold the specifed REV.
 
578
   Perform temporary allocations in SCRATCH_POOL. */
 
579
svn_error_t *
 
580
svn_fs_x__write_current(svn_fs_t *fs,
 
581
                        svn_revnum_t rev,
 
582
                        apr_pool_t *scratch_pool)
 
583
{
 
584
  char *buf;
 
585
  const char *tmp_name, *name;
 
586
 
 
587
  /* Now we can just write out this line. */
 
588
  buf = apr_psprintf(scratch_pool, "%ld\n", rev);
 
589
 
 
590
  name = svn_fs_x__path_current(fs, scratch_pool);
 
591
  SVN_ERR(svn_io_write_unique(&tmp_name,
 
592
                              svn_dirent_dirname(name, scratch_pool),
 
593
                              buf, strlen(buf),
 
594
                              svn_io_file_del_none, scratch_pool));
 
595
 
 
596
  return svn_fs_x__move_into_place(tmp_name, name, name, scratch_pool);
 
597
}
 
598
 
 
599
 
 
600
svn_error_t *
 
601
svn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content,
 
602
                                  svn_boolean_t *missing,
 
603
                                  const char *path,
 
604
                                  svn_boolean_t last_attempt,
 
605
                                  apr_pool_t *result_pool)
 
606
{
 
607
  svn_error_t *err = svn_stringbuf_from_file2(content, path, result_pool);
 
608
  if (missing)
 
609
    *missing = FALSE;
 
610
 
 
611
  if (err)
 
612
    {
 
613
      *content = NULL;
 
614
 
 
615
      if (APR_STATUS_IS_ENOENT(err->apr_err))
 
616
        {
 
617
          if (!last_attempt)
 
618
            {
 
619
              svn_error_clear(err);
 
620
              if (missing)
 
621
                *missing = TRUE;
 
622
              return SVN_NO_ERROR;
 
623
            }
 
624
        }
 
625
#ifdef ESTALE
 
626
      else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
 
627
                || APR_TO_OS_ERROR(err->apr_err) == EIO)
 
628
        {
 
629
          if (!last_attempt)
 
630
            {
 
631
              svn_error_clear(err);
 
632
              return SVN_NO_ERROR;
 
633
            }
 
634
        }
 
635
#endif
 
636
    }
 
637
 
 
638
  return svn_error_trace(err);
 
639
}
 
640
 
 
641
/* Fetch the current offset of FILE into *OFFSET_P. */
 
642
svn_error_t *
 
643
svn_fs_x__get_file_offset(apr_off_t *offset_p,
 
644
                          apr_file_t *file,
 
645
                          apr_pool_t *scratch_pool)
 
646
{
 
647
  apr_off_t offset;
 
648
 
 
649
  /* Note that, for buffered files, one (possibly surprising) side-effect
 
650
     of this call is to flush any unwritten data to disk. */
 
651
  offset = 0;
 
652
  SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, scratch_pool));
 
653
  *offset_p = offset;
 
654
 
 
655
  return SVN_NO_ERROR;
 
656
}
 
657
 
 
658
svn_error_t *
 
659
svn_fs_x__read_content(svn_stringbuf_t **content,
 
660
                       const char *fname,
 
661
                       apr_pool_t *result_pool)
 
662
{
 
663
  int i;
 
664
  *content = NULL;
 
665
 
 
666
  for (i = 0; !*content && (i < SVN_FS_X__RECOVERABLE_RETRY_COUNT); ++i)
 
667
    SVN_ERR(svn_fs_x__try_stringbuf_from_file(content, NULL,
 
668
                           fname, i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT,
 
669
                           result_pool));
 
670
 
 
671
  if (!*content)
 
672
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
673
                             _("Can't read '%s'"),
 
674
                             svn_dirent_local_style(fname, result_pool));
 
675
 
 
676
  return SVN_NO_ERROR;
 
677
}
 
678
 
 
679
/* Reads a line from STREAM and converts it to a 64 bit integer to be
 
680
 * returned in *RESULT.  If we encounter eof, set *HIT_EOF and leave
 
681
 * *RESULT unchanged.  If HIT_EOF is NULL, EOF causes an "corrupt FS"
 
682
 * error return.
 
683
 * SCRATCH_POOL is used for temporary allocations.
 
684
 */
 
685
svn_error_t *
 
686
svn_fs_x__read_number_from_stream(apr_int64_t *result,
 
687
                                  svn_boolean_t *hit_eof,
 
688
                                  svn_stream_t *stream,
 
689
                                  apr_pool_t *scratch_pool)
 
690
{
 
691
  svn_stringbuf_t *sb;
 
692
  svn_boolean_t eof;
 
693
  svn_error_t *err;
 
694
 
 
695
  SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool));
 
696
  if (hit_eof)
 
697
    *hit_eof = eof;
 
698
  else
 
699
    if (eof)
 
700
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF"));
 
701
 
 
702
  if (!eof)
 
703
    {
 
704
      err = svn_cstring_atoi64(result, sb->data);
 
705
      if (err)
 
706
        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
 
707
                                 _("Number '%s' invalid or too large"),
 
708
                                 sb->data);
 
709
    }
 
710
 
 
711
  return SVN_NO_ERROR;
 
712
}
 
713
 
 
714
 
 
715
/* Move a file into place from OLD_FILENAME in the transactions
 
716
   directory to its final location NEW_FILENAME in the repository.  On
 
717
   Unix, match the permissions of the new file to the permissions of
 
718
   PERMS_REFERENCE.  Temporary allocations are from SCRATCH_POOL.
 
719
 
 
720
   This function almost duplicates svn_io_file_move(), but it tries to
 
721
   guarantee a flush. */
 
722
svn_error_t *
 
723
svn_fs_x__move_into_place(const char *old_filename,
 
724
                          const char *new_filename,
 
725
                          const char *perms_reference,
 
726
                          apr_pool_t *scratch_pool)
 
727
{
 
728
  svn_error_t *err;
 
729
 
 
730
  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, scratch_pool));
 
731
 
 
732
  /* Move the file into place. */
 
733
  err = svn_io_file_rename(old_filename, new_filename, scratch_pool);
 
734
  if (err && APR_STATUS_IS_EXDEV(err->apr_err))
 
735
    {
 
736
      apr_file_t *file;
 
737
 
 
738
      /* Can't rename across devices; fall back to copying. */
 
739
      svn_error_clear(err);
 
740
      err = SVN_NO_ERROR;
 
741
      SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE,
 
742
                               scratch_pool));
 
743
 
 
744
      /* Flush the target of the copy to disk. */
 
745
      SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
 
746
                               APR_OS_DEFAULT, scratch_pool));
 
747
      /* ### BH: Does this really guarantee a flush of the data written
 
748
         ### via a completely different handle on all operating systems?
 
749
         ###
 
750
         ### Maybe we should perform the copy ourselves instead of making
 
751
         ### apr do that and flush the real handle? */
 
752
      SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
 
753
      SVN_ERR(svn_io_file_close(file, scratch_pool));
 
754
    }
 
755
  if (err)
 
756
    return svn_error_trace(err);
 
757
 
 
758
#ifdef __linux__
 
759
  {
 
760
    /* Linux has the unusual feature that fsync() on a file is not
 
761
       enough to ensure that a file's directory entries have been
 
762
       flushed to disk; you have to fsync the directory as well.
 
763
       On other operating systems, we'd only be asking for trouble
 
764
       by trying to open and fsync a directory. */
 
765
    const char *dirname;
 
766
    apr_file_t *file;
 
767
 
 
768
    dirname = svn_dirent_dirname(new_filename, scratch_pool);
 
769
    SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
 
770
                             scratch_pool));
 
771
    SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool));
 
772
    SVN_ERR(svn_io_file_close(file, scratch_pool));
 
773
  }
 
774
#endif
 
775
 
 
776
  return SVN_NO_ERROR;
 
777
}