~ubuntu-branches/ubuntu/trusty/subversion/trusty-proposed

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_fs/fs_fs.c

  • Committer: Package Import Robot
  • Author(s): Andy Whitcroft
  • Date: 2012-06-21 15:36:36 UTC
  • mfrom: (0.4.13 sid)
  • Revision ID: package-import@ubuntu.com-20120621153636-amqqmuidgwgxz1ly
Tags: 1.7.5-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - Create pot file on build.
  - Build a python-subversion-dbg package.
  - Build-depend on python-dbg.
  - Build-depend on default-jre-headless/-jdk.
  - Do not apply java-build patch.
  - debian/rules: Manually create the doxygen output directory, otherwise
    we get weird build failures when running parallel builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* fs_fs.c --- filesystem operations specific to fs_fs
2
2
 *
3
3
 * ====================================================================
4
 
 * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
5
 
 *
6
 
 * This software is licensed as described in the file COPYING, which
7
 
 * you should have received as part of this distribution.  The terms
8
 
 * are also available at http://subversion.tigris.org/license-1.html.
9
 
 * If newer versions of this license are posted there, you may use a
10
 
 * newer version instead, at your option.
11
 
 *
12
 
 * This software consists of voluntary contributions made by many
13
 
 * individuals.  For exact contribution history, see the revision
14
 
 * history and logs, available at http://subversion.tigris.org/.
 
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.
15
20
 * ====================================================================
16
21
 */
17
22
 
29
34
#include <apr_lib.h>
30
35
#include <apr_md5.h>
31
36
#include <apr_sha1.h>
 
37
#include <apr_strings.h>
32
38
#include <apr_thread_mutex.h>
33
39
 
34
40
#include "svn_pools.h"
38
44
#include "svn_hash.h"
39
45
#include "svn_props.h"
40
46
#include "svn_sorts.h"
 
47
#include "svn_string.h"
41
48
#include "svn_time.h"
42
49
#include "svn_mergeinfo.h"
43
50
#include "svn_config.h"
 
51
#include "svn_ctype.h"
44
52
 
45
53
#include "fs.h"
46
 
#include "err.h"
47
54
#include "tree.h"
48
55
#include "lock.h"
49
56
#include "key-gen.h"
50
57
#include "fs_fs.h"
51
58
#include "id.h"
52
59
#include "rep-cache.h"
 
60
#include "temp_serializer.h"
53
61
 
54
62
#include "private/svn_fs_util.h"
55
63
#include "../libsvn_fs/fs-loader.h"
56
64
 
57
65
#include "svn_private_config.h"
 
66
#include "temp_serializer.h"
58
67
 
59
68
/* An arbitrary maximum path length, so clients can't run us out of memory
60
69
 * by giving us arbitrarily large paths. */
149
158
  return (rev < ffd->min_unpacked_rev);
150
159
}
151
160
 
 
161
/* Return TRUE is REV is packed in FS, FALSE otherwise. */
 
162
static svn_boolean_t
 
163
is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev)
 
164
{
 
165
#if 0
 
166
  fs_fs_data_t *ffd = fs->fsap_data;
 
167
 
 
168
  return (rev < ffd->min_unpacked_revprop);
 
169
#else
 
170
  return FALSE;
 
171
#endif
 
172
}
 
173
 
152
174
static const char *
153
175
path_format(svn_fs_t *fs, apr_pool_t *pool)
154
176
{
155
 
  return svn_path_join(fs->path, PATH_FORMAT, pool);
 
177
  return svn_dirent_join(fs->path, PATH_FORMAT, pool);
156
178
}
157
179
 
158
180
static APR_INLINE const char *
159
181
path_uuid(svn_fs_t *fs, apr_pool_t *pool)
160
182
{
161
 
  return svn_path_join(fs->path, PATH_UUID, pool);
 
183
  return svn_dirent_join(fs->path, PATH_UUID, pool);
162
184
}
163
185
 
164
186
const char *
165
187
svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool)
166
188
{
167
 
  return svn_path_join(fs->path, PATH_CURRENT, pool);
 
189
  return svn_dirent_join(fs->path, PATH_CURRENT, pool);
168
190
}
169
191
 
170
192
static APR_INLINE const char *
171
193
path_txn_current(svn_fs_t *fs, apr_pool_t *pool)
172
194
{
173
 
  return svn_path_join(fs->path, PATH_TXN_CURRENT, pool);
 
195
  return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
174
196
}
175
197
 
176
198
static APR_INLINE const char *
177
199
path_txn_current_lock(svn_fs_t *fs, apr_pool_t *pool)
178
200
{
179
 
  return svn_path_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
 
201
  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
180
202
}
181
203
 
182
204
static APR_INLINE const char *
183
205
path_lock(svn_fs_t *fs, apr_pool_t *pool)
184
206
{
185
 
  return svn_path_join(fs->path, PATH_LOCK_FILE, pool);
 
207
  return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
186
208
}
187
209
 
188
210
static const char *
194
216
  assert(ffd->max_files_per_dir);
195
217
  assert(is_packed_rev(fs, rev));
196
218
 
197
 
  return svn_path_join_many(pool, fs->path, PATH_REVS_DIR,
198
 
                            apr_psprintf(pool, "%ld.pack",
199
 
                                         rev / ffd->max_files_per_dir),
200
 
                            kind, NULL);
 
219
  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
 
220
                              apr_psprintf(pool, "%ld.pack",
 
221
                                           rev / ffd->max_files_per_dir),
 
222
                              kind, NULL);
201
223
}
202
224
 
203
225
static const char *
206
228
  fs_fs_data_t *ffd = fs->fsap_data;
207
229
 
208
230
  assert(ffd->max_files_per_dir);
209
 
  return svn_path_join_many(pool, fs->path, PATH_REVS_DIR,
210
 
                            apr_psprintf(pool, "%ld",
211
 
                                               rev / ffd->max_files_per_dir),
212
 
                            NULL);
 
231
  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
 
232
                              apr_psprintf(pool, "%ld",
 
233
                                                 rev / ffd->max_files_per_dir),
 
234
                              NULL);
213
235
}
214
236
 
215
237
static const char *
221
243
 
222
244
  if (ffd->max_files_per_dir)
223
245
    {
224
 
      return svn_path_join(path_rev_shard(fs, rev, pool),
225
 
                           apr_psprintf(pool, "%ld", rev),
226
 
                           pool);
 
246
      return svn_dirent_join(path_rev_shard(fs, rev, pool),
 
247
                             apr_psprintf(pool, "%ld", rev),
 
248
                             pool);
227
249
    }
228
250
 
229
 
  return svn_path_join_many(pool, fs->path, PATH_REVS_DIR,
230
 
                            apr_psprintf(pool, "%ld", rev), NULL);
 
251
  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
 
252
                              apr_psprintf(pool, "%ld", rev), NULL);
231
253
}
232
254
 
233
 
/* Returns the path of REV in FS, whether in a pack file or not.
234
 
   Allocate in POOL. */
235
255
svn_error_t *
236
256
svn_fs_fs__path_rev_absolute(const char **path,
237
257
                             svn_fs_t *fs,
238
258
                             svn_revnum_t rev,
239
259
                             apr_pool_t *pool)
240
260
{
241
 
  if (! is_packed_rev(fs, rev))
 
261
  fs_fs_data_t *ffd = fs->fsap_data;
 
262
 
 
263
  if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT
 
264
      || ! is_packed_rev(fs, rev))
242
265
    {
243
 
      svn_node_kind_t kind;
244
 
 
245
 
      /* Initialize the return variable. */
246
266
      *path = path_rev(fs, rev, pool);
247
 
 
248
 
      SVN_ERR(svn_io_check_path(*path, &kind, pool));
249
 
      if (kind == svn_node_file)
250
 
        {
251
 
          /* *path is already set correctly. */
252
 
          return SVN_NO_ERROR;
253
 
        }
254
 
      else
255
 
        {
256
 
          /* Someone must have run 'svnadmin pack' while this fs object
257
 
           * was open. */
258
 
 
259
 
          SVN_ERR(update_min_unpacked_rev(fs, pool));
260
 
 
261
 
          /* The rev really should be present now. */
262
 
          if (! is_packed_rev(fs, rev))
263
 
            return svn_error_createf(APR_ENOENT, NULL,
264
 
                                     _("Revision file '%s' does not exist, "
265
 
                                       "and r%ld is not packed"),
266
 
                                     svn_path_local_style(*path, pool),
267
 
                                     rev);
268
 
          /* Fall through. */
269
 
        }
270
 
    }
271
 
 
272
 
  *path = path_rev_packed(fs, rev, "pack", pool);
 
267
    }
 
268
  else
 
269
    {
 
270
      *path = path_rev_packed(fs, rev, "pack", pool);
 
271
    }
273
272
 
274
273
  return SVN_NO_ERROR;
275
274
}
280
279
  fs_fs_data_t *ffd = fs->fsap_data;
281
280
 
282
281
  assert(ffd->max_files_per_dir);
283
 
  return svn_path_join_many(pool, fs->path, PATH_REVPROPS_DIR,
284
 
                            apr_psprintf(pool, "%ld",
285
 
                                               rev / ffd->max_files_per_dir),
286
 
                            NULL);
 
282
  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
 
283
                              apr_psprintf(pool, "%ld",
 
284
                                           rev / ffd->max_files_per_dir),
 
285
                              NULL);
287
286
}
288
287
 
289
288
static const char *
293
292
 
294
293
  if (ffd->max_files_per_dir)
295
294
    {
296
 
      return svn_path_join(path_revprops_shard(fs, rev, pool),
297
 
                           apr_psprintf(pool, "%ld", rev),
298
 
                           pool);
 
295
      return svn_dirent_join(path_revprops_shard(fs, rev, pool),
 
296
                             apr_psprintf(pool, "%ld", rev),
 
297
                             pool);
299
298
    }
300
299
 
301
 
  return svn_path_join_many(pool, fs->path, PATH_REVPROPS_DIR,
302
 
                            apr_psprintf(pool, "%ld", rev), NULL);
 
300
  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
 
301
                              apr_psprintf(pool, "%ld", rev), NULL);
303
302
}
304
303
 
305
304
static APR_INLINE const char *
306
305
path_txn_dir(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
307
306
{
308
 
  return svn_path_join_many(pool, fs->path, PATH_TXNS_DIR,
309
 
                            apr_pstrcat(pool, txn_id, PATH_EXT_TXN, NULL),
310
 
                            NULL);
 
307
  SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
 
308
  return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
 
309
                              apr_pstrcat(pool, txn_id, PATH_EXT_TXN,
 
310
                                          (char *)NULL),
 
311
                              NULL);
311
312
}
312
313
 
313
314
static APR_INLINE const char *
314
315
path_txn_changes(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
315
316
{
316
 
  return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);
 
317
  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);
317
318
}
318
319
 
319
320
static APR_INLINE const char *
320
321
path_txn_props(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
321
322
{
322
 
  return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);
 
323
  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);
323
324
}
324
325
 
325
326
static APR_INLINE const char *
326
327
path_txn_next_ids(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
327
328
{
328
 
  return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);
 
329
  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);
329
330
}
330
331
 
331
332
static APR_INLINE const char *
332
333
path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool)
333
334
{
334
 
  return svn_path_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
 
335
  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
335
336
}
336
337
 
 
338
 
337
339
static APR_INLINE const char *
338
340
path_txn_proto_rev(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
339
341
{
340
342
  fs_fs_data_t *ffd = fs->fsap_data;
341
343
  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
342
 
    return svn_path_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
343
 
                              apr_pstrcat(pool, txn_id, PATH_EXT_REV, NULL),
344
 
                              NULL);
 
344
    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
 
345
                                apr_pstrcat(pool, txn_id, PATH_EXT_REV,
 
346
                                            (char *)NULL),
 
347
                                NULL);
345
348
  else
346
 
    return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);
 
349
    return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);
347
350
}
348
351
 
349
352
static APR_INLINE const char *
351
354
{
352
355
  fs_fs_data_t *ffd = fs->fsap_data;
353
356
  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
354
 
    return svn_path_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
355
 
                              apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
356
 
                                          NULL),
357
 
                              NULL);
 
357
    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
 
358
                                apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
 
359
                                            (char *)NULL),
 
360
                                NULL);
358
361
  else
359
 
    return svn_path_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK, pool);
 
362
    return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK,
 
363
                           pool);
360
364
}
361
365
 
362
366
static const char *
368
372
  const char *name = apr_psprintf(pool, PATH_PREFIX_NODE "%s.%s",
369
373
                                  node_id, copy_id);
370
374
 
371
 
  return svn_path_join(path_txn_dir(fs, txn_id, pool), name, pool);
 
375
  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), name, pool);
372
376
}
373
377
 
374
378
static APR_INLINE const char *
375
379
path_txn_node_props(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
376
380
{
377
381
  return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), PATH_EXT_PROPS,
378
 
                     NULL);
 
382
                     (char *)NULL);
379
383
}
380
384
 
381
385
static APR_INLINE const char *
382
386
path_txn_node_children(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
383
387
{
384
388
  return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool),
385
 
                     PATH_EXT_CHILDREN, NULL);
 
389
                     PATH_EXT_CHILDREN, (char *)NULL);
386
390
}
387
391
 
388
392
static APR_INLINE const char *
389
393
path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool)
390
394
{
391
 
  int len = strlen(node_id);
 
395
  size_t len = strlen(node_id);
392
396
  const char *node_id_minus_last_char =
393
397
    (len == 1) ? "0" : apr_pstrmemdup(pool, node_id, len - 1);
394
 
  return svn_path_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
395
 
                            node_id_minus_last_char, NULL);
396
 
}
 
398
  return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
 
399
                              node_id_minus_last_char, NULL);
 
400
}
 
401
 
 
402
static APR_INLINE const char *
 
403
path_and_offset_of(apr_file_t *file, apr_pool_t *pool)
 
404
{
 
405
  const char *path;
 
406
  apr_off_t offset = 0;
 
407
 
 
408
  if (apr_file_name_get(&path, file) != APR_SUCCESS)
 
409
    path = "(unknown)";
 
410
 
 
411
  if (apr_file_seek(file, APR_CUR, &offset) != APR_SUCCESS)
 
412
    offset = -1;
 
413
 
 
414
  return apr_psprintf(pool, "%s:%" APR_OFF_T_FMT, path, offset);
 
415
}
 
416
 
397
417
 
398
418
 
399
419
/* Functions for working with shared transaction data. */
432
452
    }
433
453
 
434
454
  assert(strlen(txn_id) < sizeof(txn->txn_id));
435
 
  strcpy(txn->txn_id, txn_id);
 
455
  apr_cpystrn(txn->txn_id, txn_id, sizeof(txn->txn_id));
436
456
  txn->being_written = FALSE;
437
457
 
438
458
  /* Link this transaction into the head of the list.  We will typically
486
506
                  svn_error_t *(*body)(svn_fs_t *fs,
487
507
                                       const void *baton,
488
508
                                       apr_pool_t *pool),
489
 
                  void *baton,
 
509
                  const void *baton,
490
510
                  apr_pool_t *pool)
491
511
{
492
512
  svn_error_t *err;
508
528
    return svn_error_wrap_apr(apr_err, _("Can't ungrab FSFS txn list mutex"));
509
529
#endif
510
530
 
511
 
  return err;
 
531
  return svn_error_trace(err);
512
532
}
513
533
 
514
534
 
530
550
      SVN_ERR(svn_io_file_lock2(lock_filename, TRUE, FALSE, pool));
531
551
    }
532
552
 
533
 
  return err;
 
553
  return svn_error_trace(err);
534
554
}
535
555
 
536
556
/* Obtain a write lock on the file LOCK_FILENAME (protecting with
538
558
   BATON and that subpool, destroy the subpool (releasing the write
539
559
   lock) and return what BODY returned. */
540
560
static svn_error_t *
541
 
with_some_lock(svn_error_t *(*body)(void *baton,
 
561
with_some_lock(svn_fs_t *fs,
 
562
               svn_error_t *(*body)(void *baton,
542
563
                                    apr_pool_t *pool),
543
564
               void *baton,
544
565
               const char *lock_filename,
565
586
  err = get_lock_on_filesystem(lock_filename, subpool);
566
587
 
567
588
  if (!err)
568
 
    err = body(baton, subpool);
 
589
    {
 
590
      fs_fs_data_t *ffd = fs->fsap_data;
 
591
      if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
 
592
        SVN_ERR(update_min_unpacked_rev(fs, pool));
 
593
#if 0 /* Might be a good idea? */
 
594
      SVN_ERR(get_youngest(&ffd->youngest_rev_cache, fs->path,
 
595
                           pool));
 
596
#endif
 
597
      err = body(baton, subpool);
 
598
    }
569
599
 
570
600
  svn_pool_destroy(subpool);
571
601
 
577
607
                              lock_filename);
578
608
#endif
579
609
 
580
 
  return err;
 
610
  return svn_error_trace(err);
581
611
}
582
612
 
583
613
svn_error_t *
593
623
  apr_thread_mutex_t *mutex = ffsd->fs_write_lock;
594
624
#endif
595
625
 
596
 
  return with_some_lock(body, baton,
 
626
  return with_some_lock(fs, body, baton,
597
627
                        path_lock(fs, pool),
598
628
#if SVN_FS_FS__USE_LOCK_MUTEX
599
629
                        mutex,
616
646
  apr_thread_mutex_t *mutex = ffsd->txn_current_lock;
617
647
#endif
618
648
 
619
 
  return with_some_lock(body, baton,
 
649
  return with_some_lock(fs, body, baton,
620
650
                        path_txn_current_lock(fs, pool),
621
651
#if SVN_FS_FS__USE_LOCK_MUTEX
622
652
                        mutex,
767
797
 
768
798
        return svn_error_wrap_apr(apr_err,
769
799
                                  _("Can't get exclusive lock on file '%s'"),
770
 
                                  svn_path_local_style(lockfile_path, pool));
 
800
                                  svn_dirent_local_style(lockfile_path, pool));
771
801
      }
772
802
 
773
803
    *lockcookie = lockfile;
796
826
 
797
827
  if (err)
798
828
    {
799
 
      svn_error_clear(unlock_proto_rev_list_locked(fs, txn_id, *lockcookie,
800
 
                                                   pool));
 
829
      err = svn_error_compose_create(
 
830
              err,
 
831
              unlock_proto_rev_list_locked(fs, txn_id, *lockcookie, pool));
 
832
      
801
833
      *lockcookie = NULL;
802
834
    }
803
835
 
804
 
  return err;
 
836
  return svn_error_trace(err);
805
837
}
806
838
 
807
839
/* Get a handle to the prototype revision file for transaction TXN_ID in
833
865
static svn_error_t *
834
866
purge_shared_txn_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
835
867
{
836
 
  const char *txn_id = *(const char **)baton;
 
868
  const char *txn_id = baton;
837
869
 
838
870
  free_shared_txn(fs, txn_id);
 
871
  svn_fs_fs__reset_txn_caches(fs);
 
872
 
839
873
  return SVN_NO_ERROR;
840
874
}
841
875
 
844
878
static svn_error_t *
845
879
purge_shared_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
846
880
{
847
 
  return with_txnlist_lock(fs, purge_shared_txn_body, (char **) &txn_id, pool);
 
881
  return with_txnlist_lock(fs, purge_shared_txn_body, txn_id, pool);
848
882
}
849
883
 
850
884
 
865
899
}
866
900
 
867
901
 
868
 
/* Check that BUF, a buffer of text from format file PATH, contains
869
 
   only digits, raising error SVN_ERR_BAD_VERSION_FILE_FORMAT if not.
 
902
/* Check that BUF, a nul-terminated buffer of text from format file PATH,
 
903
   contains only digits at OFFSET and beyond, raising an error if not.
870
904
 
871
905
   Uses POOL for temporary allocation. */
872
906
static svn_error_t *
873
 
check_format_file_buffer_numeric(const char *buf, const char *path,
874
 
                                 apr_pool_t *pool)
 
907
check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
 
908
                                 const char *path, apr_pool_t *pool)
875
909
{
876
910
  const char *p;
877
911
 
878
 
  for (p = buf; *p; p++)
879
 
    if (!apr_isdigit(*p))
 
912
  for (p = buf + offset; *p; p++)
 
913
    if (!svn_ctype_isdigit(*p))
880
914
      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
881
 
        _("Format file '%s' contains an unexpected non-digit"),
882
 
        svn_path_local_style(path, pool));
 
915
        _("Format file '%s' contains unexpected non-digit '%c' within '%s'"),
 
916
        svn_dirent_local_style(path, pool), *p, buf);
883
917
 
884
918
  return SVN_NO_ERROR;
885
919
}
928
962
      svn_error_clear(err);
929
963
      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
930
964
                               _("Can't read first line of format file '%s'"),
931
 
                               svn_path_local_style(path, pool));
 
965
                               svn_dirent_local_style(path, pool));
932
966
    }
933
967
  SVN_ERR(err);
934
968
 
935
969
  /* Check that the first line contains only digits. */
936
 
  SVN_ERR(check_format_file_buffer_numeric(buf, path, pool));
937
 
  *pformat = atoi(buf);
 
970
  SVN_ERR(check_format_file_buffer_numeric(buf, 0, path, pool));
 
971
  SVN_ERR(svn_cstring_atoi(pformat, buf));
938
972
 
939
973
  /* Set the default values for anything that can be set via an option. */
940
974
  *max_files_per_dir = 0;
964
998
          if (strncmp(buf+7, "sharded ", 8) == 0)
965
999
            {
966
1000
              /* Check that the argument is numeric. */
967
 
              SVN_ERR(check_format_file_buffer_numeric(buf+15, path, pool));
968
 
              *max_files_per_dir = atoi(buf+15);
 
1001
              SVN_ERR(check_format_file_buffer_numeric(buf, 15, path, pool));
 
1002
              SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf + 15));
969
1003
              continue;
970
1004
            }
971
1005
        }
972
1006
 
973
1007
      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
974
1008
         _("'%s' contains invalid filesystem format option '%s'"),
975
 
         svn_path_local_style(path, pool), buf);
 
1009
         svn_dirent_local_style(path, pool), buf);
976
1010
    }
977
1011
 
978
1012
  return svn_io_file_close(file, pool);
988
1022
             svn_boolean_t overwrite, apr_pool_t *pool)
989
1023
{
990
1024
  svn_stringbuf_t *sb;
991
 
  svn_string_t *contents;
992
1025
 
993
1026
  SVN_ERR_ASSERT(1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER);
994
1027
 
1003
1036
        svn_stringbuf_appendcstr(sb, "layout linear\n");
1004
1037
    }
1005
1038
 
1006
 
  contents = svn_string_create_from_buf(sb, pool);
1007
 
 
1008
1039
  /* svn_io_write_version_file() does a load of magic to allow it to
1009
1040
     replace version files that already exist.  We only need to do
1010
1041
     that when we're allowed to overwrite an existing file. */
1011
1042
  if (! overwrite)
1012
1043
    {
1013
1044
      /* Create the file */
1014
 
      SVN_ERR(svn_io_file_create(path, contents->data, pool));
 
1045
      SVN_ERR(svn_io_file_create(path, sb->data, pool));
1015
1046
    }
1016
1047
  else
1017
1048
    {
1018
1049
      const char *path_tmp;
1019
1050
 
1020
1051
      SVN_ERR(svn_io_write_unique(&path_tmp,
1021
 
                                  svn_path_dirname(path, pool),
1022
 
                                  contents->data, contents->len,
 
1052
                                  svn_dirent_dirname(path, pool),
 
1053
                                  sb->data, sb->len,
1023
1054
                                  svn_io_file_del_none, pool));
1024
1055
 
1025
 
#ifdef WIN32
1026
 
      /* make the destination writable, but only on Windows, because
1027
 
         Windows does not let us replace read-only files. */
1028
 
      SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
1029
 
#endif /* WIN32 */
1030
 
 
1031
1056
      /* rename the temp file as the real destination */
1032
1057
      SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
1033
1058
    }
1042
1067
static svn_error_t *
1043
1068
check_format(int format)
1044
1069
{
 
1070
  /* Blacklist.  These formats may be either younger or older than
 
1071
     SVN_FS_FS__FORMAT_NUMBER, but we don't support them. */
 
1072
  if (format == SVN_FS_FS__PACKED_REVPROP_SQLITE_DEV_FORMAT)
 
1073
    return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
 
1074
                             _("Found format '%d', only created by "
 
1075
                               "unreleased dev builds; see "
 
1076
                               "http://subversion.apache.org"
 
1077
                               "/docs/release-notes/1.7#revprop-packing"),
 
1078
                             format);
 
1079
 
1045
1080
  /* We support all formats from 1-current simultaneously */
1046
1081
  if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
1047
1082
    return SVN_NO_ERROR;
1064
1099
{
1065
1100
  fs_fs_data_t *ffd = fs->fsap_data;
1066
1101
 
1067
 
  SVN_ERR(svn_config_read(&ffd->config,
1068
 
                          svn_path_join(fs->path, PATH_CONFIG, pool),
1069
 
                          FALSE, fs->pool));
 
1102
  SVN_ERR(svn_config_read2(&ffd->config,
 
1103
                           svn_dirent_join(fs->path, PATH_CONFIG, pool),
 
1104
                           FALSE, FALSE, fs->pool));
1070
1105
 
1071
1106
  /* Initialize ffd->rep_sharing_allowed. */
1072
1107
  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
1111
1146
""                                                                           NL
1112
1147
"[" CONFIG_SECTION_REP_SHARING "]"                                           NL
1113
1148
"### To conserve space, the filesystem can optionally avoid storing"         NL
1114
 
"### duplicate representations.  This comes at a slight cost in performace," NL
1115
 
"### as maintaining a database of shared representations can increase"       NL
1116
 
"### commit times.  The space savings are dependent upon the size of the"    NL
1117
 
"### repository, the number of objects it contains and the amount of"        NL
 
1149
"### duplicate representations.  This comes at a slight cost in"             NL
 
1150
"### performance, as maintaining a database of shared representations can"   NL
 
1151
"### increase commit times.  The space savings are dependent upon the size"  NL
 
1152
"### of the repository, the number of objects it contains and the amount of" NL
1118
1153
"### duplication between them, usually a function of the branching and"      NL
1119
1154
"### merging process."                                                       NL
1120
1155
"###"                                                                        NL
1121
1156
"### The following parameter enables rep-sharing in the repository.  It can" NL
1122
1157
"### be switched on and off at will, but for best space-saving results"      NL
1123
1158
"### should be enabled consistently over the life of the repository."        NL
1124
 
"# " CONFIG_OPTION_ENABLE_REP_SHARING " = false"                             NL
 
1159
"### rep-sharing is enabled by default."                                     NL
 
1160
"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true"                              NL
1125
1161
 
1126
1162
;
1127
1163
#undef NL
1128
 
  return svn_io_file_create(svn_path_join(fs->path, PATH_CONFIG, pool),
 
1164
  return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1129
1165
                            fsfs_conf_contents, pool);
1130
1166
}
1131
1167
 
1153
1189
{
1154
1190
  fs_fs_data_t *ffd = fs->fsap_data;
1155
1191
 
 
1192
  SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT);
 
1193
 
1156
1194
  return read_min_unpacked_rev(&ffd->min_unpacked_rev,
1157
1195
                               path_min_unpacked_rev(fs, pool),
1158
1196
                               pool);
1175
1213
  /* Read the FS format number. */
1176
1214
  SVN_ERR(read_format(&format, &max_files_per_dir,
1177
1215
                      path_format(fs, pool), pool));
 
1216
  SVN_ERR(check_format(format));
1178
1217
 
1179
1218
  /* Now we've got a format number no matter what. */
1180
1219
  ffd->format = format;
1181
1220
  ffd->max_files_per_dir = max_files_per_dir;
1182
 
  SVN_ERR(check_format(format));
1183
1221
 
1184
1222
  /* Read in and cache the repository uuid. */
1185
1223
  SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, pool),
1213
1251
      svn_error_clear(err);
1214
1252
      err = SVN_NO_ERROR;
1215
1253
    }
1216
 
  return err;
 
1254
  return svn_error_trace(err);
1217
1255
}
1218
1256
 
1219
1257
static svn_error_t *
1226
1264
 
1227
1265
  /* Read the FS format number and max-files-per-dir setting. */
1228
1266
  SVN_ERR(read_format(&format, &max_files_per_dir, format_path, pool));
 
1267
  SVN_ERR(check_format(format));
1229
1268
 
1230
1269
  /* If the config file does not exist, create one. */
1231
1270
  SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1235
1274
    case svn_node_none:
1236
1275
      SVN_ERR(write_config(fs, pool));
1237
1276
      break;
1238
 
    case svn_node_dir:
 
1277
    case svn_node_file:
 
1278
      break;
 
1279
    default:
1239
1280
      return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
1240
 
                               _("'%s' is a directory. Please move it out of "
 
1281
                               _("'%s' is not a regular file."
 
1282
                                 " Please move it out of "
1241
1283
                                 "the way and try again"),
1242
1284
                               svn_dirent_join(fs->path, PATH_CONFIG, pool));
1243
 
    default:
1244
 
      break;
1245
1285
    }
1246
1286
 
1247
1287
  /* If we're already up-to-date, there's nothing else to be done here. */
1264
1304
    {
1265
1305
      /* We don't use path_txn_proto_rev() here because it expects
1266
1306
         we've already bumped our format. */
1267
 
      SVN_ERR(svn_io_make_dir_recursively
1268
 
              (svn_path_join(fs->path, PATH_TXN_PROTOS_DIR, pool), pool));
 
1307
      SVN_ERR(svn_io_make_dir_recursively(
 
1308
          svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool), pool));
1269
1309
    }
1270
1310
 
1271
1311
  /* If our filesystem is new enough, write the min unpacked rev file. */
1332
1372
#define RECOVERABLE_RETRY_COUNT 10
1333
1373
 
1334
1374
#ifdef ESTALE
 
1375
/* Do not use do-while due to the embedded 'continue'.  */
1335
1376
#define RETRY_RECOVERABLE(err, filehandle, expr)                \
1336
 
  {                                                             \
 
1377
  if (1) {                                                      \
1337
1378
    svn_error_clear(err);                                       \
1338
1379
    err = (expr);                                               \
1339
1380
    if (err)                                                    \
1344
1385
            (void)apr_file_close(filehandle);                   \
1345
1386
          continue;                                             \
1346
1387
        }                                                       \
1347
 
        return err;                                             \
 
1388
        return svn_error_trace(err);                           \
1348
1389
      }                                                         \
1349
 
  }
 
1390
  } else
1350
1391
#define IGNORE_RECOVERABLE(err, expr)                           \
1351
 
  {                                                             \
 
1392
  if (1) {                                                      \
1352
1393
    svn_error_clear(err);                                       \
1353
1394
    err = (expr);                                               \
1354
1395
    if (err)                                                    \
1355
1396
      {                                                         \
1356
1397
        apr_status_t _e = APR_TO_OS_ERROR(err->apr_err);        \
1357
1398
        if ((_e != ESTALE) && (_e != EIO))                      \
1358
 
          return err;                                           \
 
1399
          return svn_error_trace(err);                         \
1359
1400
      }                                                         \
1360
 
  }
 
1401
  } else
1361
1402
#else
1362
1403
#define RETRY_RECOVERABLE(err, filehandle, expr)  SVN_ERR(expr)
1363
1404
#define IGNORE_RECOVERABLE(err, expr) SVN_ERR(expr)
1404
1445
    }
1405
1446
  svn_pool_destroy(iterpool);
1406
1447
 
1407
 
  return err;
 
1448
  return svn_error_trace(err);
1408
1449
}
1409
1450
 
1410
1451
/* Find the youngest revision in a repository at path FS_PATH and
1417
1458
{
1418
1459
  char *buf;
1419
1460
 
1420
 
  SVN_ERR(read_current(svn_path_join(fs_path, PATH_CURRENT, pool),
 
1461
  SVN_ERR(read_current(svn_dirent_join(fs_path, PATH_CURRENT, pool),
1421
1462
                       &buf, pool));
1422
1463
 
1423
1464
  *youngest_p = SVN_STR_TO_REV(buf);
1438
1479
 
1439
1480
  /* Check format to be sure we know how to hotcopy this FS. */
1440
1481
  SVN_ERR(read_format(&format, &max_files_per_dir,
1441
 
                      svn_path_join(src_path, PATH_FORMAT, pool),
 
1482
                      svn_dirent_join(src_path, PATH_FORMAT, pool),
1442
1483
                      pool));
1443
1484
  SVN_ERR(check_format(format));
1444
1485
 
1476
1517
              config_relpath = svn_dirent_join(src_path, PATH_CONFIG, pool);
1477
1518
              err2 = svn_dirent_get_absolute(&src_abspath, src_path, pool);
1478
1519
              if (err2)
1479
 
                return svn_error_compose_create(err, err2);
 
1520
                return svn_error_trace(svn_error_compose_create(err, err2));
1480
1521
              err2 = svn_dirent_get_absolute(&dst_abspath, dst_path, pool);
1481
1522
              if (err2)
1482
 
                return svn_error_compose_create(err, err2);
1483
 
              
 
1523
                return svn_error_trace(svn_error_compose_create(err, err2));
 
1524
 
1484
1525
              /* ### hack: strip off the 'db/' directory from paths so
1485
1526
               * ### they make sense to the user */
1486
1527
              src_abspath = svn_dirent_dirname(src_abspath, pool);
1495
1536
              return svn_error_quick_wrap(err, msg);
1496
1537
            }
1497
1538
          else
1498
 
            return err;
 
1539
            return svn_error_trace(err);
1499
1540
        }
1500
1541
    }
1501
1542
 
1502
 
  /* Copy the current file. */
 
1543
  /* Copy the 'current' file. */
1503
1544
  SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CURRENT, pool));
1504
1545
 
1505
1546
  /* Copy the uuid. */
1506
1547
  SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_UUID, pool));
1507
1548
 
 
1549
  /* Copy the rep cache before copying the rev files to make sure all
 
1550
     cached references will be present in the copy. */
 
1551
  src_subdir = svn_dirent_join(src_path, REP_CACHE_DB_NAME, pool);
 
1552
  dst_subdir = svn_dirent_join(dst_path, REP_CACHE_DB_NAME, pool);
 
1553
  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
 
1554
  if (kind == svn_node_file)
 
1555
    SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
 
1556
 
1508
1557
  /* Copy the min unpacked rev, and read its value. */
1509
1558
  if (format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1510
1559
    {
1511
1560
      const char *min_unpacked_rev_path;
1512
 
      min_unpacked_rev_path = svn_path_join(src_path, PATH_MIN_UNPACKED_REV,
1513
 
                                            pool);
 
1561
      min_unpacked_rev_path = svn_dirent_join(src_path, PATH_MIN_UNPACKED_REV,
 
1562
                                              pool);
1514
1563
 
1515
1564
      SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_MIN_UNPACKED_REV,
1516
1565
                                   pool));
1522
1571
      min_unpacked_rev = 0;
1523
1572
    }
1524
1573
 
1525
 
  /* Find the youngest revision from this current file. */
 
1574
  /* Find the youngest revision from this 'current' file. */
1526
1575
  SVN_ERR(get_youngest(&youngest, dst_path, pool));
1527
1576
 
1528
1577
  /* Copy the necessary rev files. */
1529
 
  src_subdir = svn_path_join(src_path, PATH_REVS_DIR, pool);
1530
 
  dst_subdir = svn_path_join(dst_path, PATH_REVS_DIR, pool);
 
1578
  src_subdir = svn_dirent_join(src_path, PATH_REVS_DIR, pool);
 
1579
  dst_subdir = svn_dirent_join(dst_path, PATH_REVS_DIR, pool);
1531
1580
 
1532
1581
  SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
1533
1582
 
1538
1587
      const char *packed_shard = apr_psprintf(iterpool, "%ld.pack",
1539
1588
                                              rev / max_files_per_dir);
1540
1589
      const char *src_subdir_packed_shard;
1541
 
      src_subdir_packed_shard = svn_path_join(src_subdir, packed_shard, pool);
 
1590
      src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
 
1591
                                                iterpool);
1542
1592
 
1543
1593
      SVN_ERR(svn_io_copy_dir_recursively(src_subdir_packed_shard,
1544
1594
                                          dst_subdir, packed_shard,
1549
1599
    }
1550
1600
 
1551
1601
  /* Then, copy non-packed shards. */
1552
 
  assert(rev == min_unpacked_rev);
 
1602
  SVN_ERR_ASSERT(rev == min_unpacked_rev);
1553
1603
  for (; rev <= youngest; rev++)
1554
1604
    {
1555
1605
      const char *src_subdir_shard = src_subdir,
1559
1609
        {
1560
1610
          const char *shard = apr_psprintf(iterpool, "%ld",
1561
1611
                                           rev / max_files_per_dir);
1562
 
          src_subdir_shard = svn_path_join(src_subdir, shard, iterpool);
1563
 
          dst_subdir_shard = svn_path_join(dst_subdir, shard, iterpool);
 
1612
          src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
 
1613
          dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
1564
1614
 
1565
1615
          if (rev % max_files_per_dir == 0)
1566
1616
            {
1567
1617
              SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
1568
1618
                                      iterpool));
1569
 
              SVN_ERR(svn_fs_fs__dup_perms(dst_subdir_shard, dst_subdir,
1570
 
                                           iterpool));
 
1619
              SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
 
1620
                                        iterpool));
1571
1621
            }
1572
1622
        }
1573
1623
 
1578
1628
    }
1579
1629
 
1580
1630
  /* Copy the necessary revprop files. */
1581
 
  src_subdir = svn_path_join(src_path, PATH_REVPROPS_DIR, pool);
1582
 
  dst_subdir = svn_path_join(dst_path, PATH_REVPROPS_DIR, pool);
 
1631
  src_subdir = svn_dirent_join(src_path, PATH_REVPROPS_DIR, pool);
 
1632
  dst_subdir = svn_dirent_join(dst_path, PATH_REVPROPS_DIR, pool);
1583
1633
 
1584
1634
  SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
1585
1635
 
1594
1644
        {
1595
1645
          const char *shard = apr_psprintf(iterpool, "%ld",
1596
1646
                                           rev / max_files_per_dir);
1597
 
          src_subdir_shard = svn_path_join(src_subdir, shard, iterpool);
1598
 
          dst_subdir_shard = svn_path_join(dst_subdir, shard, iterpool);
 
1647
          src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
 
1648
          dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
1599
1649
 
1600
1650
          if (rev % max_files_per_dir == 0)
1601
1651
            {
1602
1652
              SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
1603
1653
                                      iterpool));
1604
 
              SVN_ERR(svn_fs_fs__dup_perms(dst_subdir_shard, dst_subdir,
1605
 
                                           iterpool));
 
1654
              SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
 
1655
                                        iterpool));
1606
1656
            }
1607
1657
        }
1608
1658
 
1616
1666
  /* Make an empty transactions directory for now.  Eventually some
1617
1667
     method of copying in progress transactions will need to be
1618
1668
     developed.*/
1619
 
  dst_subdir = svn_path_join(dst_path, PATH_TXNS_DIR, pool);
 
1669
  dst_subdir = svn_dirent_join(dst_path, PATH_TXNS_DIR, pool);
1620
1670
  SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
1621
1671
  if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
1622
1672
    {
1623
 
      dst_subdir = svn_path_join(dst_path, PATH_TXN_PROTOS_DIR, pool);
 
1673
      dst_subdir = svn_dirent_join(dst_path, PATH_TXN_PROTOS_DIR, pool);
1624
1674
      SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
1625
1675
    }
1626
1676
 
1627
1677
  /* Now copy the locks tree. */
1628
 
  src_subdir = svn_path_join(src_path, PATH_LOCKS_DIR, pool);
 
1678
  src_subdir = svn_dirent_join(src_path, PATH_LOCKS_DIR, pool);
1629
1679
  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1630
1680
  if (kind == svn_node_dir)
1631
1681
    SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
1633
1683
                                        NULL, pool));
1634
1684
 
1635
1685
  /* Now copy the node-origins cache tree. */
1636
 
  src_subdir = svn_path_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
 
1686
  src_subdir = svn_dirent_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
1637
1687
  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1638
1688
  if (kind == svn_node_dir)
1639
1689
    SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
1640
1690
                                        PATH_NODE_ORIGINS_DIR, TRUE, NULL,
1641
1691
                                        NULL, pool));
1642
1692
 
1643
 
  /* Now copy the rep cache. */
1644
 
  src_subdir = svn_path_join(src_path, REP_CACHE_DB_NAME, pool);
1645
 
  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
1646
 
  if (kind == svn_node_file)
1647
 
    SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, REP_CACHE_DB_NAME, pool));
1648
 
 
1649
1693
  /* Copy the txn-current file. */
1650
1694
  if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
1651
1695
    SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_TXN_CURRENT, pool));
1652
1696
 
1653
1697
  /* Hotcopied FS is complete. Stamp it with a format file. */
1654
 
  return write_format(svn_path_join(dst_path, PATH_FORMAT, pool),
 
1698
  return write_format(svn_dirent_join(dst_path, PATH_FORMAT, pool),
1655
1699
                      format, max_files_per_dir, FALSE, pool);
1656
1700
}
1657
1701
 
1693
1737
      while (header_str->data[i] != ':')
1694
1738
        {
1695
1739
          if (header_str->data[i] == '\0')
1696
 
            return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1697
 
                                    _("Found malformed header in "
1698
 
                                      "revision file"));
 
1740
            return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
1741
                                     _("Found malformed header '%s' in "
 
1742
                                       "revision file"),
 
1743
                                     header_str->data);
1699
1744
          i++;
1700
1745
        }
1701
1746
 
1707
1752
      i += 2;
1708
1753
 
1709
1754
      if (i > header_str->len)
1710
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1711
 
                                _("Found malformed header in "
1712
 
                                  "revision file"));
 
1755
        {
 
1756
          /* Restore the original line for the error. */
 
1757
          i -= 2;
 
1758
          header_str->data[i] = ':';
 
1759
          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
1760
                                   _("Found malformed header '%s' in "
 
1761
                                     "revision file"),
 
1762
                                   header_str->data);
 
1763
        }
1713
1764
 
1714
1765
      value = header_str->data + i;
1715
1766
 
1763
1814
/* Open the correct revision file for REV.  If the filesystem FS has
1764
1815
   been packed, *FILE will be set to the packed file; otherwise, set *FILE
1765
1816
   to the revision file for REV.  Return SVN_ERR_FS_NO_SUCH_REVISION if the
1766
 
   file doesn't exist.  Use POOL for allocations. */
 
1817
   file doesn't exist.
 
1818
 
 
1819
   TODO: Consider returning an indication of whether this is a packed rev
 
1820
         file, so the caller need not rely on is_packed_rev() which in turn
 
1821
         relies on the cached FFD->min_unpacked_rev value not having changed
 
1822
         since the rev file was opened.
 
1823
 
 
1824
   Use POOL for allocations. */
1767
1825
static svn_error_t *
1768
1826
open_pack_or_rev_file(apr_file_t **file,
1769
1827
                      svn_fs_t *fs,
1770
1828
                      svn_revnum_t rev,
1771
1829
                      apr_pool_t *pool)
1772
1830
{
 
1831
  fs_fs_data_t *ffd = fs->fsap_data;
1773
1832
  svn_error_t *err;
1774
1833
  const char *path;
1775
 
 
1776
 
  err = svn_fs_fs__path_rev_absolute(&path, fs, rev, pool);
1777
 
 
1778
 
  if (! err)
1779
 
    err = svn_io_file_open(file, path,
1780
 
                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
1781
 
 
1782
 
  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
 
1834
  svn_boolean_t retry = FALSE;
 
1835
 
 
1836
  do
1783
1837
    {
1784
 
      svn_error_clear(err);
1785
 
      return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1786
 
                               _("No such revision %ld"), rev);
 
1838
      err = svn_fs_fs__path_rev_absolute(&path, fs, rev, pool);
 
1839
 
 
1840
      /* open the revision file in buffered r/o mode */
 
1841
      if (! err)
 
1842
        err = svn_io_file_open(file, path,
 
1843
                              APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
 
1844
 
 
1845
      if (err && APR_STATUS_IS_ENOENT(err->apr_err)
 
1846
          && ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
 
1847
        {
 
1848
          /* Could not open the file. This may happen if the
 
1849
           * file once existed but got packed later. */
 
1850
          svn_error_clear(err);
 
1851
 
 
1852
          /* if that was our 2nd attempt, leave it at that. */
 
1853
          if (retry)
 
1854
            return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
 
1855
                                    _("No such revision %ld"), rev);
 
1856
 
 
1857
          /* We failed for the first time. Refresh cache & retry. */
 
1858
          SVN_ERR(update_min_unpacked_rev(fs, pool));
 
1859
 
 
1860
          retry = TRUE;
 
1861
        }
 
1862
      else
 
1863
        {
 
1864
          retry = FALSE;
 
1865
        }
1787
1866
    }
 
1867
  while (retry);
1788
1868
 
1789
 
  return err;
 
1869
  return svn_error_trace(err);
1790
1870
}
1791
1871
 
1792
1872
/* Given REV in FS, set *REV_OFFSET to REV's offset in the packed file.
1801
1881
  svn_stream_t *manifest_stream;
1802
1882
  svn_boolean_t is_cached;
1803
1883
  svn_revnum_t shard;
 
1884
  apr_int64_t shard_pos;
1804
1885
  apr_array_header_t *manifest;
1805
1886
  apr_pool_t *iterpool;
1806
1887
 
1807
1888
  shard = rev / ffd->max_files_per_dir;
1808
 
  SVN_ERR(svn_cache__get((void **) &manifest, &is_cached,
1809
 
                         ffd->packed_offset_cache, &shard, pool));
 
1889
 
 
1890
  /* position of the shard within the manifest */
 
1891
  shard_pos = rev % ffd->max_files_per_dir;
 
1892
 
 
1893
  /* fetch exactly that element into *rev_offset, if the manifest is found
 
1894
     in the cache */
 
1895
  SVN_ERR(svn_cache__get_partial((void **) rev_offset, &is_cached,
 
1896
                                 ffd->packed_offset_cache, &shard,
 
1897
                                 svn_fs_fs__get_sharded_offset, &shard_pos,
 
1898
                                 pool));
1810
1899
 
1811
1900
  if (is_cached)
1812
 
    {
1813
 
      *rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir,
1814
 
                                  apr_off_t);
1815
1901
      return SVN_NO_ERROR;
1816
 
    }
1817
1902
 
1818
1903
  /* Open the manifest file. */
1819
1904
  SVN_ERR(svn_stream_open_readonly(&manifest_stream,
1828
1913
    {
1829
1914
      svn_stringbuf_t *sb;
1830
1915
      svn_boolean_t eof;
 
1916
      apr_int64_t val;
 
1917
      svn_error_t *err;
1831
1918
 
1832
1919
      svn_pool_clear(iterpool);
1833
1920
      SVN_ERR(svn_stream_readline(manifest_stream, &sb, "\n", &eof, iterpool));
1834
1921
      if (eof)
1835
1922
        break;
1836
1923
 
1837
 
      APR_ARRAY_PUSH(manifest, apr_off_t) =
1838
 
                apr_atoi64(svn_string_create_from_buf(sb, iterpool)->data);
1839
 
      if (errno == ERANGE)
1840
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1841
 
                                "Manifest offset too large");
 
1924
      err = svn_cstring_atoi64(&val, sb->data);
 
1925
      if (err)
 
1926
        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
 
1927
                                 _("Manifest offset '%s' too large"),
 
1928
                                 sb->data);
 
1929
      APR_ARRAY_PUSH(manifest, apr_off_t) = (apr_off_t)val;
1842
1930
    }
1843
1931
  svn_pool_destroy(iterpool);
1844
1932
 
1931
2019
   expected except the "-1" revision number for a mutable
1932
2020
   representation.  Allocate *REP_P in POOL. */
1933
2021
static svn_error_t *
1934
 
read_rep_offsets(representation_t **rep_p,
1935
 
                 char *string,
1936
 
                 const char *txn_id,
1937
 
                 svn_boolean_t mutable_rep_truncated,
1938
 
                 apr_pool_t *pool)
 
2022
read_rep_offsets_body(representation_t **rep_p,
 
2023
                      char *string,
 
2024
                      const char *txn_id,
 
2025
                      svn_boolean_t mutable_rep_truncated,
 
2026
                      apr_pool_t *pool)
1939
2027
{
1940
2028
  representation_t *rep;
1941
2029
  char *str, *last_str;
 
2030
  apr_int64_t val;
1942
2031
 
1943
2032
  rep = apr_pcalloc(pool, sizeof(*rep));
1944
2033
  *rep_p = rep;
1962
2051
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1963
2052
                            _("Malformed text representation offset line in node-rev"));
1964
2053
 
1965
 
  rep->offset = apr_atoi64(str);
1966
 
 
1967
 
  str = apr_strtok(NULL, " ", &last_str);
1968
 
  if (str == NULL)
1969
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1970
 
                            _("Malformed text representation offset line in node-rev"));
1971
 
 
1972
 
  rep->size = apr_atoi64(str);
1973
 
 
1974
 
  str = apr_strtok(NULL, " ", &last_str);
1975
 
  if (str == NULL)
1976
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1977
 
                            _("Malformed text representation offset line in node-rev"));
1978
 
 
1979
 
  rep->expanded_size = apr_atoi64(str);
 
2054
  SVN_ERR(svn_cstring_atoi64(&val, str));
 
2055
  rep->offset = (apr_off_t)val;
 
2056
 
 
2057
  str = apr_strtok(NULL, " ", &last_str);
 
2058
  if (str == NULL)
 
2059
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
 
2060
                            _("Malformed text representation offset line in node-rev"));
 
2061
 
 
2062
  SVN_ERR(svn_cstring_atoi64(&val, str));
 
2063
  rep->size = (svn_filesize_t)val;
 
2064
 
 
2065
  str = apr_strtok(NULL, " ", &last_str);
 
2066
  if (str == NULL)
 
2067
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
 
2068
                            _("Malformed text representation offset line in node-rev"));
 
2069
 
 
2070
  SVN_ERR(svn_cstring_atoi64(&val, str));
 
2071
  rep->expanded_size = (svn_filesize_t)val;
1980
2072
 
1981
2073
  /* Read in the MD5 hash. */
1982
2074
  str = apr_strtok(NULL, " ", &last_str);
2011
2103
  return SVN_NO_ERROR;
2012
2104
}
2013
2105
 
2014
 
/* See svn_fs_fs__get_node_revision, which wraps this and adds another
 
2106
/* Wrap read_rep_offsets_body(), extracting its TXN_ID from our NODEREV_ID,
 
2107
   and adding an error message. */
 
2108
static svn_error_t *
 
2109
read_rep_offsets(representation_t **rep_p,
 
2110
                 char *string,
 
2111
                 const svn_fs_id_t *noderev_id,
 
2112
                 svn_boolean_t mutable_rep_truncated,
 
2113
                 apr_pool_t *pool)
 
2114
{
 
2115
  svn_error_t *err;
 
2116
  const char *txn_id;
 
2117
 
 
2118
  if (noderev_id)
 
2119
    txn_id = svn_fs_fs__id_txn_id(noderev_id);
 
2120
  else
 
2121
    txn_id = NULL;
 
2122
 
 
2123
  err = read_rep_offsets_body(rep_p, string, txn_id, mutable_rep_truncated,
 
2124
                              pool);
 
2125
  if (err)
 
2126
    {
 
2127
      const svn_string_t *id_unparsed = svn_fs_fs__id_unparse(noderev_id, pool);
 
2128
      const char *where;
 
2129
      where = apr_psprintf(pool,
 
2130
                           _("While reading representation offsets "
 
2131
                             "for node-revision '%s':"),
 
2132
                           noderev_id ? id_unparsed->data : "(null)");
 
2133
 
 
2134
      return svn_error_quick_wrap(err, where);
 
2135
    }
 
2136
  else
 
2137
    return SVN_NO_ERROR;
 
2138
}
 
2139
 
 
2140
static svn_error_t *
 
2141
err_dangling_id(svn_fs_t *fs, const svn_fs_id_t *id)
 
2142
{
 
2143
  svn_string_t *id_str = svn_fs_fs__id_unparse(id, fs->pool);
 
2144
  return svn_error_createf
 
2145
    (SVN_ERR_FS_ID_NOT_FOUND, 0,
 
2146
     _("Reference to non-existent node '%s' in filesystem '%s'"),
 
2147
     id_str->data, fs->path);
 
2148
}
 
2149
 
 
2150
/* Return a string that uniquely identifies the noderev with the
 
2151
 * given ID, for use as a cache key.
 
2152
 */
 
2153
static const char *
 
2154
get_noderev_cache_key(const svn_fs_id_t *id, apr_pool_t *pool)
 
2155
{
 
2156
  const svn_string_t *id_unparsed = svn_fs_fs__id_unparse(id, pool);
 
2157
  return id_unparsed->data;
 
2158
}
 
2159
 
 
2160
/* Look up the NODEREV_P for ID in FS' node revsion cache. If noderev
 
2161
 * caching has been enabled and the data can be found, IS_CACHED will
 
2162
 * be set to TRUE. The noderev will be allocated from POOL.
 
2163
 *
 
2164
 * Non-permanent ids (e.g. ids within a TXN) will not be cached.
 
2165
 */
 
2166
static svn_error_t *
 
2167
get_cached_node_revision_body(node_revision_t **noderev_p,
 
2168
                              svn_fs_t *fs,
 
2169
                              const svn_fs_id_t *id,
 
2170
                              svn_boolean_t *is_cached,
 
2171
                              apr_pool_t *pool)
 
2172
{
 
2173
  fs_fs_data_t *ffd = fs->fsap_data;
 
2174
  if (! ffd->node_revision_cache || svn_fs_fs__id_txn_id(id))
 
2175
    *is_cached = FALSE;
 
2176
  else
 
2177
    SVN_ERR(svn_cache__get((void **) noderev_p,
 
2178
                           is_cached,
 
2179
                           ffd->node_revision_cache,
 
2180
                           get_noderev_cache_key(id, pool),
 
2181
                           pool));
 
2182
 
 
2183
  return SVN_NO_ERROR;
 
2184
}
 
2185
 
 
2186
/* If noderev caching has been enabled, store the NODEREV_P for the given ID
 
2187
 * in FS' node revsion cache. SCRATCH_POOL is used for temporary allcations.
 
2188
 *
 
2189
 * Non-permanent ids (e.g. ids within a TXN) will not be cached.
 
2190
 */
 
2191
static svn_error_t *
 
2192
set_cached_node_revision_body(node_revision_t *noderev_p,
 
2193
                              svn_fs_t *fs,
 
2194
                              const svn_fs_id_t *id,
 
2195
                              apr_pool_t *scratch_pool)
 
2196
{
 
2197
  fs_fs_data_t *ffd = fs->fsap_data;
 
2198
 
 
2199
  if (ffd->node_revision_cache && !svn_fs_fs__id_txn_id(id))
 
2200
    return svn_cache__set(ffd->node_revision_cache,
 
2201
                          get_noderev_cache_key(id, scratch_pool),
 
2202
                          noderev_p,
 
2203
                          scratch_pool);
 
2204
 
 
2205
  return SVN_NO_ERROR;
 
2206
}
 
2207
 
 
2208
/* Get the node-revision for the node ID in FS.
 
2209
   Set *NODEREV_P to the new node-revision structure, allocated in POOL.
 
2210
   See svn_fs_fs__get_node_revision, which wraps this and adds another
2015
2211
   error. */
2016
2212
static svn_error_t *
2017
2213
get_node_revision_body(node_revision_t **noderev_p,
2021
2217
{
2022
2218
  apr_file_t *revision_file;
2023
2219
  svn_error_t *err;
 
2220
  svn_boolean_t is_cached = FALSE;
 
2221
 
 
2222
  /* First, try a cache lookup. If that succeeds, we are done here. */
 
2223
  SVN_ERR(get_cached_node_revision_body(noderev_p, fs, id, &is_cached, pool));
 
2224
  if (is_cached)
 
2225
    return SVN_NO_ERROR;
2024
2226
 
2025
2227
  if (svn_fs_fs__id_txn_id(id))
2026
2228
    {
2042
2244
      if (APR_STATUS_IS_ENOENT(err->apr_err))
2043
2245
        {
2044
2246
          svn_error_clear(err);
2045
 
          return svn_fs_fs__err_dangling_id(fs, id);
 
2247
          return svn_error_trace(err_dangling_id(fs, id));
2046
2248
        }
2047
2249
 
2048
 
      return err;
 
2250
      return svn_error_trace(err);
2049
2251
    }
2050
2252
 
2051
 
  return svn_fs_fs__read_noderev(noderev_p,
2052
 
                                 svn_stream_from_aprfile2(revision_file, FALSE,
2053
 
                                                          pool),
2054
 
                                 pool);
 
2253
  SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
 
2254
                                  svn_stream_from_aprfile2(revision_file, FALSE,
 
2255
                                                           pool),
 
2256
                                  pool));
 
2257
 
 
2258
  /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
 
2259
  return set_cached_node_revision_body(*noderev_p, fs, id, pool);
2055
2260
}
2056
2261
 
2057
2262
svn_error_t *
2062
2267
  apr_hash_t *headers;
2063
2268
  node_revision_t *noderev;
2064
2269
  char *value;
 
2270
  const char *noderev_id;
2065
2271
 
2066
2272
  SVN_ERR(read_header_block(&headers, stream, pool));
2067
2273
 
2070
2276
  /* Read the node-rev id. */
2071
2277
  value = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
2072
2278
  if (value == NULL)
 
2279
      /* ### More information: filename/offset coordinates */
2073
2280
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2074
2281
                              _("Missing id field in node-rev"));
2075
2282
 
2076
2283
  SVN_ERR(svn_stream_close(stream));
2077
2284
 
2078
2285
  noderev->id = svn_fs_fs__id_parse(value, strlen(value), pool);
 
2286
  noderev_id = value; /* for error messages later */
2079
2287
 
2080
2288
  /* Read the type. */
2081
2289
  value = apr_hash_get(headers, HEADER_TYPE, APR_HASH_KEY_STRING);
2082
2290
 
2083
2291
  if ((value == NULL) ||
2084
2292
      (strcmp(value, KIND_FILE) != 0 && strcmp(value, KIND_DIR)))
2085
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2086
 
                            _("Missing kind field in node-rev"));
 
2293
    /* ### s/kind/type/ */
 
2294
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2295
                             _("Missing kind field in node-rev '%s'"),
 
2296
                             noderev_id);
2087
2297
 
2088
2298
  noderev->kind = (strcmp(value, KIND_FILE) == 0) ? svn_node_file
2089
2299
    : svn_node_dir;
2090
2300
 
2091
2301
  /* Read the 'count' field. */
2092
2302
  value = apr_hash_get(headers, HEADER_COUNT, APR_HASH_KEY_STRING);
2093
 
  noderev->predecessor_count = (value == NULL) ? 0 : atoi(value);
 
2303
  if (value)
 
2304
    SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, value));
 
2305
  else
 
2306
    noderev->predecessor_count = 0;
2094
2307
 
2095
2308
  /* Get the properties location. */
2096
2309
  value = apr_hash_get(headers, HEADER_PROPS, APR_HASH_KEY_STRING);
2097
2310
  if (value)
2098
2311
    {
2099
2312
      SVN_ERR(read_rep_offsets(&noderev->prop_rep, value,
2100
 
                               svn_fs_fs__id_txn_id(noderev->id), TRUE, pool));
 
2313
                               noderev->id, TRUE, pool));
2101
2314
    }
2102
2315
 
2103
2316
  /* Get the data location. */
2105
2318
  if (value)
2106
2319
    {
2107
2320
      SVN_ERR(read_rep_offsets(&noderev->data_rep, value,
2108
 
                               svn_fs_fs__id_txn_id(noderev->id),
 
2321
                               noderev->id,
2109
2322
                               (noderev->kind == svn_node_dir), pool));
2110
2323
    }
2111
2324
 
2113
2326
  value = apr_hash_get(headers, HEADER_CPATH, APR_HASH_KEY_STRING);
2114
2327
  if (value == NULL)
2115
2328
    {
2116
 
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2117
 
                              _("Missing cpath in node-rev"));
 
2329
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2330
                               _("Missing cpath field in node-rev '%s'"),
 
2331
                               noderev_id);
2118
2332
    }
2119
2333
  else
2120
2334
    {
2140
2354
 
2141
2355
      str = apr_strtok(value, " ", &last_str);
2142
2356
      if (str == NULL)
2143
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2144
 
                                _("Malformed copyroot line in node-rev"));
 
2357
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2358
                                 _("Malformed copyroot line in node-rev '%s'"),
 
2359
                                 noderev_id);
2145
2360
 
2146
 
      noderev->copyroot_rev = atoi(str);
 
2361
      noderev->copyroot_rev = SVN_STR_TO_REV(str);
2147
2362
 
2148
2363
      if (last_str == NULL)
2149
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2150
 
                                _("Malformed copyroot line in node-rev"));
 
2364
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2365
                                 _("Malformed copyroot line in node-rev '%s'"),
 
2366
                                 noderev_id);
2151
2367
      noderev->copyroot_path = apr_pstrdup(pool, last_str);
2152
2368
    }
2153
2369
 
2164
2380
 
2165
2381
      str = apr_strtok(value, " ", &last_str);
2166
2382
      if (str == NULL)
2167
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2168
 
                                _("Malformed copyfrom line in node-rev"));
 
2383
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2384
                                 _("Malformed copyfrom line in node-rev '%s'"),
 
2385
                                 noderev_id);
2169
2386
 
2170
 
      noderev->copyfrom_rev = atoi(str);
 
2387
      noderev->copyfrom_rev = SVN_STR_TO_REV(str);
2171
2388
 
2172
2389
      if (last_str == NULL)
2173
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2174
 
                                _("Malformed copyfrom line in node-rev"));
 
2390
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2391
                                 _("Malformed copyfrom line in node-rev '%s'"),
 
2392
                                 noderev_id);
2175
2393
      noderev->copyfrom_path = apr_pstrdup(pool, last_str);
2176
2394
    }
2177
2395
 
2181
2399
 
2182
2400
  /* Get the mergeinfo count. */
2183
2401
  value = apr_hash_get(headers, HEADER_MINFO_CNT, APR_HASH_KEY_STRING);
2184
 
  noderev->mergeinfo_count = (value == NULL) ? 0 : apr_atoi64(value);
 
2402
  if (value)
 
2403
    SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, value));
 
2404
  else
 
2405
    noderev->mergeinfo_count = 0;
2185
2406
 
2186
2407
  /* Get whether *this* node has mergeinfo. */
2187
2408
  value = apr_hash_get(headers, HEADER_MINFO_HERE, APR_HASH_KEY_STRING);
2206
2427
                               "Corrupt node-revision '%s'",
2207
2428
                               id_string->data);
2208
2429
    }
2209
 
  return err;
 
2430
  return svn_error_trace(err);
2210
2431
}
2211
2432
 
2212
2433
 
2327
2548
  noderev->is_fresh_txn_root = fresh_txn_root;
2328
2549
 
2329
2550
  if (! txn_id)
2330
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2331
 
                            _("Attempted to write to non-transaction"));
 
2551
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2552
                             _("Attempted to write to non-transaction '%s'"),
 
2553
                             svn_fs_fs__id_unparse(id, pool)->data);
2332
2554
 
2333
2555
  SVN_ERR(svn_io_file_open(&noderev_file, path_txn_node_rev(fs, id, pool),
2334
2556
                           APR_WRITE | APR_CREATE | APR_TRUNCATE
2353
2575
 
2354
2576
  svn_revnum_t base_revision;
2355
2577
  apr_off_t base_offset;
2356
 
  apr_size_t base_length;
 
2578
  svn_filesize_t base_length;
2357
2579
};
2358
2580
 
2359
2581
/* Read the next line from file FILE and parse it as a text
2368
2590
  apr_size_t limit;
2369
2591
  struct rep_args *rep_args;
2370
2592
  char *str, *last_str;
 
2593
  apr_int64_t val;
2371
2594
 
2372
2595
  limit = sizeof(buffer);
2373
2596
  SVN_ERR(svn_io_read_length_line(file, buffer, &limit, pool));
2395
2618
 
2396
2619
  /* We have hopefully a DELTA vs. a non-empty base revision. */
2397
2620
  str = apr_strtok(buffer, " ", &last_str);
2398
 
  if (! str || (strcmp(str, REP_DELTA) != 0)) goto err;
2399
 
 
2400
 
  str = apr_strtok(NULL, " ", &last_str);
2401
 
  if (! str) goto err;
2402
 
  rep_args->base_revision = atol(str);
2403
 
 
2404
 
  str = apr_strtok(NULL, " ", &last_str);
2405
 
  if (! str) goto err;
2406
 
  rep_args->base_offset = (apr_off_t) apr_atoi64(str);
2407
 
 
2408
 
  str = apr_strtok(NULL, " ", &last_str);
2409
 
  if (! str) goto err;
2410
 
  rep_args->base_length = (apr_size_t) apr_atoi64(str);
 
2621
  if (! str || (strcmp(str, REP_DELTA) != 0))
 
2622
    goto error;
 
2623
 
 
2624
  str = apr_strtok(NULL, " ", &last_str);
 
2625
  if (! str)
 
2626
    goto error;
 
2627
  rep_args->base_revision = SVN_STR_TO_REV(str);
 
2628
 
 
2629
  str = apr_strtok(NULL, " ", &last_str);
 
2630
  if (! str)
 
2631
    goto error;
 
2632
  SVN_ERR(svn_cstring_atoi64(&val, str));
 
2633
  rep_args->base_offset = (apr_off_t)val;
 
2634
 
 
2635
  str = apr_strtok(NULL, " ", &last_str);
 
2636
  if (! str)
 
2637
    goto error;
 
2638
  SVN_ERR(svn_cstring_atoi64(&val, str));
 
2639
  rep_args->base_length = (svn_filesize_t)val;
2411
2640
 
2412
2641
  *rep_args_p = rep_args;
2413
2642
  return SVN_NO_ERROR;
2414
2643
 
2415
 
 err:
2416
 
  return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2417
 
                          _("Malformed representation header"));
 
2644
 error:
 
2645
  return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2646
                           _("Malformed representation header at %s"),
 
2647
                           path_and_offset_of(file, pool));
2418
2648
}
2419
2649
 
2420
2650
/* Given a revision file REV_FILE, opened to REV in FS, find the Node-ID
2438
2668
                            svn_stream_from_aprfile2(rev_file, TRUE, pool),
2439
2669
                            pool));
2440
2670
 
 
2671
  /* In error messages, the offset is relative to the pack file,
 
2672
     not to the rev file. */
 
2673
 
2441
2674
  node_id_str = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
2442
2675
 
2443
2676
  if (node_id_str == NULL)
2444
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2445
 
                            _("Missing node-id in node-rev"));
 
2677
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2678
                             _("Missing node-id in node-rev at r%ld "
 
2679
                             "(offset %s)"),
 
2680
                             rev,
 
2681
                             apr_psprintf(pool, "%" APR_OFF_T_FMT, offset));
2446
2682
 
2447
2683
  id = svn_fs_fs__id_parse(node_id_str, strlen(node_id_str), pool);
2448
2684
 
2449
2685
  if (id == NULL)
2450
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2451
 
                            _("Corrupt node-id in node-rev"));
 
2686
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2687
                             _("Corrupt node-id '%s' in node-rev at r%ld "
 
2688
                               "(offset %s)"),
 
2689
                             node_id_str, rev,
 
2690
                             apr_psprintf(pool, "%" APR_OFF_T_FMT, offset));
2452
2691
 
2453
2692
  *id_p = id;
2454
2693
 
 
2694
  /* ### assert that the txn_id is REV/OFFSET ? */
 
2695
 
2455
2696
  return SVN_NO_ERROR;
2456
2697
}
2457
2698
 
2460
2701
   specifies the offset to the root node-id and to the changed path
2461
2702
   information.  Store the root node offset in *ROOT_OFFSET and the
2462
2703
   changed path offset in *CHANGES_OFFSET.  If either of these
2463
 
   pointers is NULL, do nothing with it.  If PACKED is true, REV_FILE
2464
 
   should be a packed shard file.  Allocate temporary variables from POOL. */
 
2704
   pointers is NULL, do nothing with it.
 
2705
 
 
2706
   If PACKED is true, REV_FILE should be a packed shard file.
 
2707
   ### There is currently no such parameter.  This function assumes that
 
2708
       is_packed_rev(FS, REV) will indicate whether REV_FILE is a packed
 
2709
       file.  Therefore FS->fsap_data->min_unpacked_rev must not have been
 
2710
       refreshed since REV_FILE was opened if there is a possibility that
 
2711
       revision REV may have become packed since then.
 
2712
       TODO: Take an IS_PACKED parameter instead, in order to remove this
 
2713
       requirement.
 
2714
 
 
2715
   Allocate temporary variables from POOL. */
2465
2716
static svn_error_t *
2466
2717
get_root_changes_offset(apr_off_t *root_offset,
2467
2718
                        apr_off_t *changes_offset,
2475
2726
  apr_off_t rev_offset;
2476
2727
  char buf[64];
2477
2728
  int i, num_bytes;
 
2729
  const char *str;
2478
2730
  apr_size_t len;
2479
2731
  apr_seek_where_t seek_relative;
2480
2732
 
2523
2775
  if (buf[num_bytes - 1] != '\n')
2524
2776
    {
2525
2777
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2526
 
                               _("Revision file lacks trailing newline"));
 
2778
                               _("Revision file (r%ld) lacks trailing newline"),
 
2779
                               rev);
2527
2780
    }
2528
2781
 
2529
2782
  /* Look for the next previous newline. */
2530
2783
  for (i = num_bytes - 2; i >= 0; i--)
2531
2784
    {
2532
 
      if (buf[i] == '\n') break;
 
2785
      if (buf[i] == '\n')
 
2786
        break;
2533
2787
    }
2534
2788
 
2535
2789
  if (i < 0)
2536
2790
    {
2537
2791
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2538
 
                               _("Final line in revision file longer than 64 "
2539
 
                                 "characters"));
 
2792
                               _("Final line in revision file (r%ld) longer "
 
2793
                                 "than 64 characters"),
 
2794
                               rev);
2540
2795
    }
2541
2796
 
2542
2797
  i++;
2543
 
 
2544
 
  if (root_offset)
2545
 
    *root_offset = rev_offset + apr_atoi64(&buf[i]);
 
2798
  str = &buf[i];
2546
2799
 
2547
2800
  /* find the next space */
2548
2801
  for ( ; i < (num_bytes - 2) ; i++)
2549
 
    if (buf[i] == ' ') break;
 
2802
    if (buf[i] == ' ')
 
2803
      break;
2550
2804
 
2551
2805
  if (i == (num_bytes - 2))
2552
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
2553
 
                            _("Final line in revision file missing space"));
 
2806
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
2807
                             _("Final line in revision file r%ld missing space"),
 
2808
                             rev);
 
2809
 
 
2810
  if (root_offset)
 
2811
    {
 
2812
      apr_int64_t val;
 
2813
 
 
2814
      buf[i] = '\0';
 
2815
      SVN_ERR(svn_cstring_atoi64(&val, str));
 
2816
      *root_offset = rev_offset + (apr_off_t)val;
 
2817
    }
2554
2818
 
2555
2819
  i++;
2556
 
 
2557
 
  /* note that apr_atoi64() will stop reading as soon as it encounters
2558
 
     the final newline. */
 
2820
  str = &buf[i];
 
2821
 
 
2822
  /* find the next newline */
 
2823
  for ( ; i < num_bytes; i++)
 
2824
    if (buf[i] == '\n')
 
2825
      break;
 
2826
 
2559
2827
  if (changes_offset)
2560
 
    *changes_offset = rev_offset + apr_atoi64(&buf[i]);
 
2828
    {
 
2829
      apr_int64_t val;
 
2830
 
 
2831
      buf[i] = '\0';
 
2832
      SVN_ERR(svn_cstring_atoi64(&val, str));
 
2833
      *changes_offset = rev_offset + (apr_off_t)val;
 
2834
    }
2561
2835
 
2562
2836
  return SVN_NO_ERROR;
2563
2837
}
2565
2839
/* Move a file into place from OLD_FILENAME in the transactions
2566
2840
   directory to its final location NEW_FILENAME in the repository.  On
2567
2841
   Unix, match the permissions of the new file to the permissions of
2568
 
   PERMS_REFERENCE.  Temporary allocations are from POOL. */
 
2842
   PERMS_REFERENCE.  Temporary allocations are from POOL.
 
2843
 
 
2844
   This function almost duplicates svn_io_file_move(), but it tries to
 
2845
   guarantee a flush. */
2569
2846
static svn_error_t *
2570
2847
move_into_place(const char *old_filename,
2571
2848
                const char *new_filename,
2574
2851
{
2575
2852
  svn_error_t *err;
2576
2853
 
2577
 
  SVN_ERR(svn_fs_fs__dup_perms(old_filename, perms_reference, pool));
 
2854
  SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
2578
2855
 
2579
2856
  /* Move the file into place. */
2580
2857
  err = svn_io_file_rename(old_filename, new_filename, pool);
2590
2867
      /* Flush the target of the copy to disk. */
2591
2868
      SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
2592
2869
                               APR_OS_DEFAULT, pool));
 
2870
      /* ### BH: Does this really guarantee a flush of the data written
 
2871
         ### via a completely different handle on all operating systems?
 
2872
         ###
 
2873
         ### Maybe we should perform the copy ourselves instead of making
 
2874
         ### apr do that and flush the real handle? */
2593
2875
      SVN_ERR(svn_io_file_flush_to_disk(file, pool));
2594
2876
      SVN_ERR(svn_io_file_close(file, pool));
2595
2877
    }
2596
2878
  if (err)
2597
 
    return err;
 
2879
    return svn_error_trace(err);
2598
2880
 
2599
2881
#ifdef __linux__
2600
2882
  {
2606
2888
    const char *dirname;
2607
2889
    apr_file_t *file;
2608
2890
 
2609
 
    dirname = svn_path_dirname(new_filename, pool);
 
2891
    dirname = svn_dirent_dirname(new_filename, pool);
2610
2892
    SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
2611
2893
                             pool));
2612
2894
    SVN_ERR(svn_io_file_flush_to_disk(file, pool));
2626
2908
  fs_fs_data_t *ffd = fs->fsap_data;
2627
2909
  apr_file_t *revision_file;
2628
2910
  apr_off_t root_offset;
2629
 
  svn_fs_id_t *root_id;
 
2911
  svn_fs_id_t *root_id = NULL;
2630
2912
  svn_boolean_t is_cached;
2631
2913
 
2632
2914
  SVN_ERR(ensure_revision_exists(fs, rev, pool));
2652
2934
  return SVN_NO_ERROR;
2653
2935
}
2654
2936
 
2655
 
svn_error_t *
2656
 
svn_fs_fs__set_revision_proplist(svn_fs_t *fs,
2657
 
                                 svn_revnum_t rev,
2658
 
                                 apr_hash_t *proplist,
2659
 
                                 apr_pool_t *pool)
2660
 
{
2661
 
  const char *final_path = path_revprops(fs, rev, pool);
2662
 
  const char *tmp_path;
2663
 
  const char *perms_reference;
2664
 
  svn_stream_t *stream;
2665
 
 
2666
 
  SVN_ERR(ensure_revision_exists(fs, rev, pool));
2667
 
 
2668
 
  /* ### do we have a directory sitting around already? we really shouldn't
2669
 
     ### have to get the dirname here. */
2670
 
  SVN_ERR(svn_stream_open_unique(&stream, &tmp_path,
2671
 
                                 svn_path_dirname(final_path, pool),
2672
 
                                 svn_io_file_del_none, pool, pool));
2673
 
  SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
2674
 
  SVN_ERR(svn_stream_close(stream));
2675
 
 
2676
 
  /* We use the rev file of this revision as the perms reference,
2677
 
     because when setting revprops for the first time, the revprop
2678
 
     file won't exist and therefore can't serve as its own reference.
2679
 
     (Whereas the rev file should already exist at this point.) */
2680
 
  SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
2681
 
  SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
 
2937
/* Set the revision property list of revision REV in filesystem FS to
 
2938
   PROPLIST.  Use POOL for temporary allocations. */
 
2939
static svn_error_t *
 
2940
set_revision_proplist(svn_fs_t *fs,
 
2941
                      svn_revnum_t rev,
 
2942
                      apr_hash_t *proplist,
 
2943
                      apr_pool_t *pool)
 
2944
{
 
2945
  SVN_ERR(ensure_revision_exists(fs, rev, pool));
 
2946
 
 
2947
  if (1)
 
2948
    {
 
2949
      const char *final_path = path_revprops(fs, rev, pool);
 
2950
      const char *tmp_path;
 
2951
      const char *perms_reference;
 
2952
      svn_stream_t *stream;
 
2953
 
 
2954
      /* ### do we have a directory sitting around already? we really shouldn't
 
2955
         ### have to get the dirname here. */
 
2956
      SVN_ERR(svn_stream_open_unique(&stream, &tmp_path,
 
2957
                                     svn_dirent_dirname(final_path, pool),
 
2958
                                     svn_io_file_del_none, pool, pool));
 
2959
      SVN_ERR(svn_hash_write2(proplist, stream, SVN_HASH_TERMINATOR, pool));
 
2960
      SVN_ERR(svn_stream_close(stream));
 
2961
 
 
2962
      /* We use the rev file of this revision as the perms reference,
 
2963
         because when setting revprops for the first time, the revprop
 
2964
         file won't exist and therefore can't serve as its own reference.
 
2965
         (Whereas the rev file should already exist at this point.) */
 
2966
      SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
 
2967
      SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
 
2968
 
 
2969
      return SVN_NO_ERROR;
 
2970
    }
 
2971
 
 
2972
  return SVN_NO_ERROR;
 
2973
}
 
2974
 
 
2975
static svn_error_t *
 
2976
revision_proplist(apr_hash_t **proplist_p,
 
2977
                  svn_fs_t *fs,
 
2978
                  svn_revnum_t rev,
 
2979
                  apr_pool_t *pool)
 
2980
{
 
2981
  apr_hash_t *proplist;
 
2982
 
 
2983
  SVN_ERR(ensure_revision_exists(fs, rev, pool));
 
2984
 
 
2985
  if (1)
 
2986
    {
 
2987
      apr_file_t *revprop_file = NULL;
 
2988
      svn_error_t *err = SVN_NO_ERROR;
 
2989
      int i;
 
2990
      apr_pool_t *iterpool;
 
2991
 
 
2992
      proplist = apr_hash_make(pool);
 
2993
      iterpool = svn_pool_create(pool);
 
2994
      for (i = 0; i < RECOVERABLE_RETRY_COUNT; i++)
 
2995
        {
 
2996
          svn_pool_clear(iterpool);
 
2997
 
 
2998
          /* Clear err here rather than after finding a recoverable error so
 
2999
           * we can return that error on the last iteration of the loop. */
 
3000
          svn_error_clear(err);
 
3001
          err = svn_io_file_open(&revprop_file, path_revprops(fs, rev,
 
3002
                                                              iterpool),
 
3003
                                 APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
 
3004
                                 iterpool);
 
3005
          if (err)
 
3006
            {
 
3007
              if (APR_STATUS_IS_ENOENT(err->apr_err))
 
3008
                {
 
3009
                  svn_error_clear(err);
 
3010
                  return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
 
3011
                                           _("No such revision %ld"), rev);
 
3012
                }
 
3013
#ifdef ESTALE
 
3014
              else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
 
3015
                       || APR_TO_OS_ERROR(err->apr_err) == EIO
 
3016
                       || APR_TO_OS_ERROR(err->apr_err) == ENOENT)
 
3017
                continue;
 
3018
#endif
 
3019
              return svn_error_trace(err);
 
3020
            }
 
3021
 
 
3022
          SVN_ERR(svn_hash__clear(proplist, iterpool));
 
3023
          RETRY_RECOVERABLE(err, revprop_file,
 
3024
                            svn_hash_read2(proplist,
 
3025
                                           svn_stream_from_aprfile2(
 
3026
                                                revprop_file, TRUE, iterpool),
 
3027
                                           SVN_HASH_TERMINATOR, pool));
 
3028
 
 
3029
          IGNORE_RECOVERABLE(err, svn_io_file_close(revprop_file, iterpool));
 
3030
 
 
3031
          break;
 
3032
        }
 
3033
 
 
3034
      if (err)
 
3035
        return svn_error_trace(err);
 
3036
      svn_pool_destroy(iterpool);
 
3037
    }
 
3038
 
 
3039
  *proplist_p = proplist;
2682
3040
 
2683
3041
  return SVN_NO_ERROR;
2684
3042
}
2689
3047
                             svn_revnum_t rev,
2690
3048
                             apr_pool_t *pool)
2691
3049
{
2692
 
  apr_file_t *revprop_file = NULL;
2693
 
  apr_hash_t *proplist;
2694
 
  svn_error_t *err = SVN_NO_ERROR;
2695
 
  int i;
2696
 
  apr_pool_t *iterpool;
2697
 
 
2698
 
  SVN_ERR(ensure_revision_exists(fs, rev, pool));
2699
 
 
2700
 
  proplist = apr_hash_make(pool);
2701
 
  iterpool = svn_pool_create(pool);
2702
 
  for (i = 0; i < RECOVERABLE_RETRY_COUNT; i++)
2703
 
    {
2704
 
      svn_pool_clear(iterpool);
2705
 
 
2706
 
      /* Clear err here rather than after finding a recoverable error so
2707
 
       * we can return that error on the last iteration of the loop. */
2708
 
      svn_error_clear(err);
2709
 
      err = svn_io_file_open(&revprop_file, path_revprops(fs, rev, iterpool),
2710
 
                             APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
2711
 
                             iterpool);
2712
 
      if (err)
2713
 
        {
2714
 
          if (APR_STATUS_IS_ENOENT(err->apr_err))
2715
 
            {
2716
 
              svn_error_clear(err);
2717
 
              return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
2718
 
                                       _("No such revision %ld"), rev);
2719
 
            }
2720
 
#ifdef ESTALE
2721
 
          else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE
2722
 
                   || APR_TO_OS_ERROR(err->apr_err) == EIO
2723
 
                   || APR_TO_OS_ERROR(err->apr_err) == ENOENT)
2724
 
            continue;
2725
 
#endif
2726
 
          return err;
2727
 
        }
2728
 
 
2729
 
      SVN_ERR(svn_hash__clear(proplist));
2730
 
      RETRY_RECOVERABLE(err, revprop_file,
2731
 
                        svn_hash_read2(proplist,
2732
 
                                       svn_stream_from_aprfile2(revprop_file,
2733
 
                                                                TRUE,
2734
 
                                                                iterpool),
2735
 
                                       SVN_HASH_TERMINATOR, pool));
2736
 
 
2737
 
      IGNORE_RECOVERABLE(err, svn_io_file_close(revprop_file, iterpool));
2738
 
 
2739
 
      break;
2740
 
    }
2741
 
  if (err)
2742
 
    return err;
2743
 
  svn_pool_destroy(iterpool);
2744
 
 
2745
 
  *proplist_p = proplist;
 
3050
  SVN_ERR(revision_proplist(proplist_p, fs, rev, pool));
2746
3051
 
2747
3052
  return SVN_NO_ERROR;
2748
3053
}
2752
3057
struct rep_state
2753
3058
{
2754
3059
  apr_file_t *file;
 
3060
                    /* The txdelta window cache to use or NULL. */
 
3061
  svn_cache__t *window_cache;
2755
3062
  apr_off_t start;  /* The starting offset for the raw
2756
3063
                       svndiff/plaintext data minus header. */
2757
3064
  apr_off_t off;    /* The current offset into the file. */
2768
3075
                      svn_fs_t *fs,
2769
3076
                      apr_pool_t *pool)
2770
3077
{
 
3078
  fs_fs_data_t *ffd = fs->fsap_data;
2771
3079
  struct rep_state *rs = apr_pcalloc(pool, sizeof(*rs));
2772
3080
  struct rep_args *ra;
2773
3081
  unsigned char buf[4];
2774
3082
 
2775
3083
  SVN_ERR(open_and_seek_representation(&rs->file, fs, rep, pool));
 
3084
  rs->window_cache = ffd->txdelta_window_cache;
 
3085
 
2776
3086
  SVN_ERR(read_rep_line(&ra, rs->file, pool));
2777
3087
  SVN_ERR(get_file_offset(&rs->start, rs->file, pool));
2778
3088
  rs->off = rs->start;
2785
3095
    return SVN_NO_ERROR;
2786
3096
 
2787
3097
  /* We are dealing with a delta, find out what version. */
2788
 
  SVN_ERR(svn_io_file_read_full(rs->file, buf, sizeof(buf), NULL, pool));
 
3098
  SVN_ERR(svn_io_file_read_full2(rs->file, buf, sizeof(buf),
 
3099
                                 NULL, NULL, pool));
 
3100
  /* ### Layering violation */
2789
3101
  if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N')))
2790
3102
    return svn_error_create
2791
3103
      (SVN_ERR_FS_CORRUPT, NULL,
2821
3133
         ### going to jump straight to this comment anyway! */
2822
3134
      return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
2823
3135
                               "Corrupt representation '%s'",
2824
 
                               representation_string(rep, ffd->format, TRUE,
2825
 
                                                     pool));
 
3136
                               rep 
 
3137
                               ? representation_string(rep, ffd->format, TRUE,
 
3138
                                                       pool)
 
3139
                               : "(null)");
2826
3140
    }
2827
 
  return err;
 
3141
  /* ### Call representation_string() ? */
 
3142
  return svn_error_trace(err);
2828
3143
}
2829
3144
 
2830
3145
/* Build an array of rep_state structures in *LIST giving the delta
2943
3258
  b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
2944
3259
  b->checksum_finalized = FALSE;
2945
3260
  b->md5_checksum = svn_checksum_dup(rep->md5_checksum, pool);
2946
 
  b->len = rep->expanded_size;
 
3261
  b->len = rep->expanded_size ? rep->expanded_size : rep->size;
2947
3262
  b->off = 0;
2948
3263
  b->fulltext_cache_key = fulltext_cache_key;
2949
3264
  b->pool = svn_pool_create(pool);
2950
3265
  b->filehandle_pool = svn_pool_create(pool);
2951
3266
 
2952
3267
  if (fulltext_cache_key)
2953
 
    b->current_fulltext = svn_stringbuf_create("", b->filehandle_pool);
 
3268
    b->current_fulltext = svn_stringbuf_create_ensure
 
3269
                            ((apr_size_t)b->len,
 
3270
                             b->filehandle_pool);
2954
3271
  else
2955
3272
    b->current_fulltext = NULL;
2956
3273
 
2963
3280
  return SVN_NO_ERROR;
2964
3281
}
2965
3282
 
 
3283
/* Combine the name of the rev file in RS with the given OFFSET to form
 
3284
 * a cache lookup key. Allocations will be made from POOL. */
 
3285
static const char*
 
3286
get_window_key(struct rep_state *rs, apr_off_t offset, apr_pool_t *pool)
 
3287
{
 
3288
  const char *name;
 
3289
  const char *last_part;
 
3290
  const char *name_last;
 
3291
 
 
3292
  /* the rev file name containing the txdelta window.
 
3293
   * If this fails we are in serious trouble anyways.
 
3294
   * And if nobody else detects the problems, the file content checksum
 
3295
   * comparison _will_ find them.
 
3296
   */
 
3297
  if (apr_file_name_get(&name, rs->file))
 
3298
    return "";
 
3299
 
 
3300
  /* Handle packed files as well by scanning backwards until we find the
 
3301
   * revision or pack number. */
 
3302
  name_last = name + strlen(name) - 1;
 
3303
  while (! svn_ctype_isdigit(*name_last))
 
3304
    --name_last;
 
3305
 
 
3306
  last_part = name_last;
 
3307
  while (svn_ctype_isdigit(*last_part))
 
3308
    --last_part;
 
3309
 
 
3310
  /* We must differentiate between packed files (as of today, the number
 
3311
   * is being followed by a dot) and non-packed files (followed by \0).
 
3312
   * Otherwise, there might be overlaps in the numbering range if the
 
3313
   * repo gets packed after caching the txdeltas of non-packed revs.
 
3314
   * => add the first non-digit char to the packed number. */
 
3315
  if (name_last[1] != '\0')
 
3316
    ++name_last;
 
3317
 
 
3318
  /* copy one char MORE than the actual number to mark packed files,
 
3319
   * i.e. packed revision file content uses different key space then
 
3320
   * non-packed ones: keys for packed rev file content ends with a dot
 
3321
   * for non-packed rev files they end with a digit. */
 
3322
  name = apr_pstrndup(pool, last_part + 1, name_last - last_part);
 
3323
  return svn_fs_fs__combine_number_and_string(offset, name, pool);
 
3324
}
 
3325
 
 
3326
/* Read the WINDOW_P for the rep state RS from the current FSFS session's
 
3327
 * cache. This will be a no-op and IS_CACHED will be set to FALSE if no
 
3328
 * cache has been given. If a cache is available IS_CACHED will inform
 
3329
 * the caller about the success of the lookup. Allocations (of the window
 
3330
 * in particualar) will be made from POOL.
 
3331
 *
 
3332
 * If the information could be found, put RS and the position within the
 
3333
 * rev file into the same state as if the data had just been read from it.
 
3334
 */
 
3335
static svn_error_t *
 
3336
get_cached_window(svn_txdelta_window_t **window_p,
 
3337
                  struct rep_state *rs,
 
3338
                  svn_boolean_t *is_cached,
 
3339
                  apr_pool_t *pool)
 
3340
{
 
3341
  if (! rs->window_cache)
 
3342
    {
 
3343
      /* txdelta window has not been enabled */
 
3344
      *is_cached = FALSE;
 
3345
    }
 
3346
  else
 
3347
    {
 
3348
      /* ask the cache for the desired txdelta window */
 
3349
      svn_fs_fs__txdelta_cached_window_t *cached_window;
 
3350
      SVN_ERR(svn_cache__get((void **) &cached_window,
 
3351
                             is_cached,
 
3352
                             rs->window_cache,
 
3353
                             get_window_key(rs, rs->off, pool),
 
3354
                             pool));
 
3355
 
 
3356
      if (*is_cached)
 
3357
        {
 
3358
          /* found it. Pass it back to the caller. */
 
3359
          *window_p = cached_window->window;
 
3360
 
 
3361
          /* manipulate the RS as if we just read the data */
 
3362
          rs->chunk_index++;
 
3363
          rs->off = cached_window->end_offset;
 
3364
 
 
3365
          /* manipulate the rev file as if we just read from it */
 
3366
          SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
 
3367
        }
 
3368
    }
 
3369
 
 
3370
  return SVN_NO_ERROR;
 
3371
}
 
3372
 
 
3373
/* Store the WINDOW read at OFFSET for the rep state RS in the current
 
3374
 * FSFS session's cache. This will be a no-op if no cache has been given.
 
3375
 * Temporary allocations will be made from SCRATCH_POOL. */
 
3376
static svn_error_t *
 
3377
set_cached_window(svn_txdelta_window_t *window,
 
3378
                  struct rep_state *rs,
 
3379
                  apr_off_t offset,
 
3380
                  apr_pool_t *scratch_pool)
 
3381
{
 
3382
  if (rs->window_cache)
 
3383
    {
 
3384
      /* store the window and the first offset _past_ it */
 
3385
      svn_fs_fs__txdelta_cached_window_t cached_window = { window, rs->off };
 
3386
 
 
3387
      /* but key it with the start offset because that is the known state
 
3388
       * when we will look it up */
 
3389
      return svn_cache__set(rs->window_cache,
 
3390
                            get_window_key(rs, offset, scratch_pool),
 
3391
                            &cached_window,
 
3392
                            scratch_pool);
 
3393
    }
 
3394
 
 
3395
  return SVN_NO_ERROR;
 
3396
}
 
3397
 
2966
3398
/* Skip forwards to THIS_CHUNK in REP_STATE and then read the next delta
2967
3399
   window into *NWIN. */
2968
3400
static svn_error_t *
2970
3402
            apr_pool_t *pool)
2971
3403
{
2972
3404
  svn_stream_t *stream;
 
3405
  svn_boolean_t is_cached;
 
3406
  apr_off_t old_offset;
2973
3407
 
2974
3408
  SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
2975
3409
 
2986
3420
                                  "representation"));
2987
3421
    }
2988
3422
 
2989
 
  /* Read the next window. */
 
3423
  /* Read the next window. But first, try to find it in the cache. */
 
3424
  SVN_ERR(get_cached_window(nwin, rs, &is_cached, pool));
 
3425
  if (is_cached)
 
3426
    return SVN_NO_ERROR;
 
3427
 
 
3428
  /* Actually read the next window. */
 
3429
  old_offset = rs->off;
2990
3430
  stream = svn_stream_from_aprfile2(rs->file, TRUE, pool);
2991
3431
  SVN_ERR(svn_txdelta_read_svndiff_window(nwin, stream, rs->ver, pool));
2992
3432
  rs->chunk_index++;
2997
3437
                            _("Reading one svndiff window read beyond "
2998
3438
                              "the end of the representation"));
2999
3439
 
3000
 
  return SVN_NO_ERROR;
 
3440
  /* the window has not been cached before, thus cache it now
 
3441
   * (if caching is used for them at all) */
 
3442
  return set_cached_window(*nwin, rs, old_offset, pool);
3001
3443
}
3002
3444
 
3003
3445
/* Get one delta window that is a result of combining all but the last deltas
3042
3484
  return SVN_NO_ERROR;
3043
3485
}
3044
3486
 
 
3487
/* Returns whether or not the expanded fulltext of the file is cachable
 
3488
 * based on its size SIZE.  The decision depends on the cache used by RB.
 
3489
 */
 
3490
static svn_boolean_t
 
3491
fulltext_size_is_cachable(fs_fs_data_t *ffd, svn_filesize_t size)
 
3492
{
 
3493
  return (size < APR_SIZE_MAX)
 
3494
      && svn_cache__is_cachable(ffd->fulltext_cache, (apr_size_t)size);
 
3495
}
 
3496
 
 
3497
/* Close method used on streams returned by read_representation().
 
3498
 */
3045
3499
static svn_error_t *
3046
3500
rep_read_contents_close(void *baton)
3047
3501
{
3072
3526
      rs = rb->src_state;
3073
3527
      if (((apr_off_t) copy_len) > rs->end - rs->off)
3074
3528
        copy_len = (apr_size_t) (rs->end - rs->off);
3075
 
      SVN_ERR(svn_io_file_read_full(rs->file, cur, copy_len, NULL,
3076
 
                                    rb->pool));
 
3529
      SVN_ERR(svn_io_file_read_full2(rs->file, cur, copy_len, NULL,
 
3530
                                     NULL, rb->pool));
3077
3531
      rs->off += copy_len;
3078
3532
      *len = copy_len;
3079
3533
      return SVN_NO_ERROR;
3148
3602
                      SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off,
3149
3603
                                               rb->pool));
3150
3604
                    }
3151
 
                  SVN_ERR(svn_io_file_read_full(rs->file, sbuf,
3152
 
                                                lwindow->sview_len,
3153
 
                                                NULL, rb->pool));
 
3605
                  SVN_ERR(svn_io_file_read_full2(rs->file, sbuf,
 
3606
                                                 lwindow->sview_len,
 
3607
                                                 NULL, NULL, rb->pool));
3154
3608
                  rs->off += lwindow->sview_len;
3155
3609
                }
3156
3610
              else
3227
3681
          svn_checksum_t *md5_checksum;
3228
3682
 
3229
3683
          rb->checksum_finalized = TRUE;
3230
 
          svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx, rb->pool);
 
3684
          SVN_ERR(svn_checksum_final(&md5_checksum, rb->md5_checksum_ctx,
 
3685
                                     rb->pool));
3231
3686
          if (!svn_checksum_match(md5_checksum, rb->md5_checksum))
3232
 
            return svn_error_createf
3233
 
              (SVN_ERR_FS_CORRUPT, NULL,
3234
 
               _("Checksum mismatch while reading representation:\n"
3235
 
                 "   expected:  %s\n"
3236
 
                 "     actual:  %s\n"),
3237
 
               svn_checksum_to_cstring_display(rb->md5_checksum, rb->pool),
3238
 
               svn_checksum_to_cstring_display(md5_checksum, rb->pool));
 
3687
            return svn_error_create(SVN_ERR_FS_CORRUPT,
 
3688
                    svn_checksum_mismatch_err(rb->md5_checksum, md5_checksum,
 
3689
                        rb->pool,
 
3690
                        _("Checksum mismatch while reading representation")),
 
3691
                    NULL);
3239
3692
        }
3240
3693
    }
3241
3694
 
3251
3704
}
3252
3705
 
3253
3706
 
3254
 
/* Returns whether or not the expanded fulltext of the file is
3255
 
 * cachable based on its size SIZE.  Specifically, if it will fit
3256
 
 * into a memcached value.  The memcached cutoff seems to be a bit
3257
 
 * (header length?) under a megabyte; we round down a little to be
3258
 
 * safe.
3259
 
 */
3260
 
static svn_boolean_t
3261
 
fulltext_size_is_cachable(svn_filesize_t size)
3262
 
{
3263
 
  return size < 1000000;
3264
 
}
3265
 
 
3266
3707
/* Return a stream in *CONTENTS_P that will read the contents of a
3267
3708
   representation stored at the location given by REP.  Appropriate
3268
3709
   for any kind of immutable representation, but only for file
3286
3727
    {
3287
3728
      fs_fs_data_t *ffd = fs->fsap_data;
3288
3729
      const char *fulltext_key = NULL;
 
3730
      svn_filesize_t len = rep->expanded_size ? rep->expanded_size : rep->size;
3289
3731
      struct rep_read_baton *rb;
3290
3732
 
3291
3733
      if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
3292
 
          && fulltext_size_is_cachable(rep->expanded_size))
 
3734
          && fulltext_size_is_cachable(ffd, len))
3293
3735
        {
3294
3736
          svn_string_t *fulltext;
3295
3737
          svn_boolean_t is_cached;
3469
3911
    {
3470
3912
      const void *key;
3471
3913
      apr_ssize_t klen;
3472
 
      void *val;
3473
 
      svn_fs_dirent_t *dirent;
 
3914
      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
3474
3915
      const char *new_val;
3475
3916
 
3476
 
      apr_hash_this(hi, &key, &klen, &val);
3477
 
      dirent = val;
 
3917
      apr_hash_this(hi, &key, &klen, NULL);
3478
3918
      new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
3479
3919
      apr_hash_set(*str_entries_p, key, klen,
3480
3920
                   svn_string_create(new_val, pool));
3484
3924
}
3485
3925
 
3486
3926
 
3487
 
svn_error_t *
3488
 
svn_fs_fs__dir_entries_serialize(char **data,
3489
 
                                 apr_size_t *data_len,
3490
 
                                 void *in,
3491
 
                                 apr_pool_t *pool)
3492
 
{
3493
 
  apr_hash_t *entries = in;
3494
 
  svn_stringbuf_t *buf = svn_stringbuf_create("", pool);
3495
 
  svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
3496
 
 
3497
 
  SVN_ERR(unparse_dir_entries(&entries, entries, pool));
3498
 
  SVN_ERR(svn_hash_write2(entries, stream, SVN_HASH_TERMINATOR, pool));
3499
 
 
3500
 
  *data = buf->data;
3501
 
  *data_len = buf->len;
3502
 
 
3503
 
  return SVN_NO_ERROR;
3504
 
}
3505
 
 
3506
 
 
3507
3927
/* Given a hash STR_ENTRIES with values as svn_string_t as specified
3508
3928
   in an FSFS directory contents listing, return a hash of dirents in
3509
3929
   *ENTRIES_P.  Perform allocations in POOL. */
3510
3930
static svn_error_t *
3511
3931
parse_dir_entries(apr_hash_t **entries_p,
3512
3932
                  apr_hash_t *str_entries,
 
3933
                  const char *unparsed_id,
3513
3934
                  apr_pool_t *pool)
3514
3935
{
3515
3936
  apr_hash_index_t *hi;
3519
3940
  /* Translate the string dir entries into real entries. */
3520
3941
  for (hi = apr_hash_first(pool, str_entries); hi; hi = apr_hash_next(hi))
3521
3942
    {
3522
 
      const void *key;
3523
 
      void *val;
3524
 
      svn_string_t *str_val;
 
3943
      const char *name = svn__apr_hash_index_key(hi);
 
3944
      svn_string_t *str_val = svn__apr_hash_index_val(hi);
3525
3945
      char *str, *last_str;
3526
3946
      svn_fs_dirent_t *dirent = apr_pcalloc(pool, sizeof(*dirent));
3527
3947
 
3528
 
      apr_hash_this(hi, &key, NULL, &val);
3529
 
      str_val = val;
3530
3948
      str = apr_pstrdup(pool, str_val->data);
3531
 
      dirent->name = apr_pstrdup(pool, key);
 
3949
      dirent->name = apr_pstrdup(pool, name);
3532
3950
 
3533
3951
      str = apr_strtok(str, " ", &last_str);
3534
3952
      if (str == NULL)
3535
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
3536
 
                                _("Directory entry corrupt"));
 
3953
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
3954
                                 _("Directory entry corrupt in '%s'"),
 
3955
                                 unparsed_id);
3537
3956
 
3538
3957
      if (strcmp(str, KIND_FILE) == 0)
3539
3958
        {
3545
3964
        }
3546
3965
      else
3547
3966
        {
3548
 
          return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
3549
 
                                  _("Directory entry corrupt"));
 
3967
          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
3968
                                   _("Directory entry corrupt in '%s'"),
 
3969
                                   unparsed_id);
3550
3970
        }
3551
3971
 
3552
3972
      str = apr_strtok(NULL, " ", &last_str);
3553
3973
      if (str == NULL)
3554
 
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
3555
 
                                _("Directory entry corrupt"));
 
3974
          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
3975
                                   _("Directory entry corrupt in '%s'"),
 
3976
                                   unparsed_id);
3556
3977
 
3557
3978
      dirent->id = svn_fs_fs__id_parse(str, strlen(str), pool);
3558
3979
 
3562
3983
  return SVN_NO_ERROR;
3563
3984
}
3564
3985
 
3565
 
svn_error_t *
3566
 
svn_fs_fs__dir_entries_deserialize(void **out,
3567
 
                                   const char *data,
3568
 
                                   apr_size_t data_len,
3569
 
                                   apr_pool_t *pool)
 
3986
/* Return the cache object in FS responsible to storing the directory
 
3987
 * the NODEREV. If none exists, return NULL. */
 
3988
static svn_cache__t *
 
3989
locate_dir_cache(svn_fs_t *fs,
 
3990
                 node_revision_t *noderev)
3570
3991
{
3571
 
  apr_hash_t *entries = apr_hash_make(pool);
3572
 
  svn_stringbuf_t *buf = svn_stringbuf_ncreate(data, data_len, pool);
3573
 
  svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
3574
 
 
3575
 
  SVN_ERR(svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool));
3576
 
  SVN_ERR(parse_dir_entries(&entries, entries, pool));
3577
 
 
3578
 
  *out = entries;
3579
 
  return SVN_NO_ERROR;
 
3992
  fs_fs_data_t *ffd = fs->fsap_data;
 
3993
  return svn_fs_fs__id_txn_id(noderev->id)
 
3994
      ? ffd->txn_dir_cache
 
3995
      : ffd->dir_cache;
3580
3996
}
3581
3997
 
3582
 
 
3583
3998
svn_error_t *
3584
3999
svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
3585
4000
                            svn_fs_t *fs,
3586
4001
                            node_revision_t *noderev,
3587
4002
                            apr_pool_t *pool)
3588
4003
{
3589
 
  fs_fs_data_t *ffd = fs->fsap_data;
3590
 
  const char *unparsed_id;
 
4004
  const char *unparsed_id = NULL;
3591
4005
  apr_hash_t *unparsed_entries, *parsed_entries;
3592
4006
 
3593
 
  /* Are we looking for an immutable directory?  We could try the
3594
 
   * cache. */
3595
 
  if (! svn_fs_fs__id_txn_id(noderev->id))
 
4007
  /* find the cache we may use */
 
4008
  svn_cache__t *cache = locate_dir_cache(fs, noderev);
 
4009
  if (cache)
3596
4010
    {
3597
4011
      svn_boolean_t found;
3598
4012
 
3599
4013
      unparsed_id = svn_fs_fs__id_unparse(noderev->id, pool)->data;
3600
 
      SVN_ERR(svn_cache__get((void **) entries_p, &found, ffd->dir_cache,
 
4014
      SVN_ERR(svn_cache__get((void **) entries_p, &found, cache,
3601
4015
                             unparsed_id, pool));
3602
4016
      if (found)
3603
4017
        return SVN_NO_ERROR;
3606
4020
  /* Read in the directory hash. */
3607
4021
  unparsed_entries = apr_hash_make(pool);
3608
4022
  SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
3609
 
  SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries, pool));
 
4023
  SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries,
 
4024
                            unparsed_id, pool));
3610
4025
 
3611
 
  /* If this is an immutable directory, let's cache the contents. */
3612
 
  if (! svn_fs_fs__id_txn_id(noderev->id))
3613
 
    SVN_ERR(svn_cache__set(ffd->dir_cache, unparsed_id, parsed_entries, pool));
 
4026
  /* Update the cache, if we are to use one. */
 
4027
  if (cache)
 
4028
    SVN_ERR(svn_cache__set(cache, unparsed_id, parsed_entries, pool));
3614
4029
 
3615
4030
  *entries_p = parsed_entries;
3616
4031
  return SVN_NO_ERROR;
3617
4032
}
3618
4033
 
3619
4034
svn_error_t *
 
4035
svn_fs_fs__rep_contents_dir_entry(svn_fs_dirent_t **dirent,
 
4036
                                  svn_fs_t *fs,
 
4037
                                  node_revision_t *noderev,
 
4038
                                  const char *name,
 
4039
                                  apr_pool_t *pool)
 
4040
{
 
4041
  svn_boolean_t found = FALSE;
 
4042
 
 
4043
  /* find the cache we may use */
 
4044
  svn_cache__t *cache = locate_dir_cache(fs, noderev);
 
4045
  if (cache)
 
4046
    {
 
4047
      const char *unparsed_id =
 
4048
        svn_fs_fs__id_unparse(noderev->id, pool)->data;
 
4049
 
 
4050
      /* Cache lookup. */
 
4051
      SVN_ERR(svn_cache__get_partial((void **)dirent,
 
4052
                                     &found,
 
4053
                                     cache,
 
4054
                                     unparsed_id,
 
4055
                                     svn_fs_fs__extract_dir_entry,
 
4056
                                     (void*)name,
 
4057
                                     pool));
 
4058
    }
 
4059
 
 
4060
  /* fetch data from disk if we did not find it in the cache */
 
4061
  if (! found)
 
4062
    {
 
4063
      apr_hash_t *entries;
 
4064
      svn_fs_dirent_t *entry;
 
4065
      svn_fs_dirent_t *entry_copy = NULL;
 
4066
 
 
4067
      /* since we don't need the directory content later on, put it into
 
4068
         some sub-pool that will be reclaimed immedeately after exiting
 
4069
         this function successfully. Opon failure, it will live as long
 
4070
         as pool.
 
4071
       */
 
4072
      apr_pool_t *sub_pool = svn_pool_create(pool);
 
4073
 
 
4074
      /* read the dir from the file system. It will probably be put it
 
4075
         into the cache for faster lookup in future calls. */
 
4076
      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, sub_pool));
 
4077
 
 
4078
      /* find desired entry and return a copy in POOL, if found */
 
4079
      entry = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
 
4080
      if (entry != NULL)
 
4081
        {
 
4082
          entry_copy = apr_palloc(pool, sizeof(*entry_copy));
 
4083
          entry_copy->name = apr_pstrdup(pool, entry->name);
 
4084
          entry_copy->id = svn_fs_fs__id_copy(entry->id, pool);
 
4085
          entry_copy->kind = entry->kind;
 
4086
        }
 
4087
 
 
4088
      *dirent = entry_copy;
 
4089
      apr_pool_destroy(sub_pool);
 
4090
    }
 
4091
 
 
4092
  return SVN_NO_ERROR;
 
4093
}
 
4094
 
 
4095
svn_error_t *
3620
4096
svn_fs_fs__get_proplist(apr_hash_t **proplist_p,
3621
4097
                        svn_fs_t *fs,
3622
4098
                        node_revision_t *noderev,
3743
4219
{
3744
4220
  apr_pool_t *pool = apr_hash_pool_get(changes);
3745
4221
  svn_fs_path_change2_t *old_change, *new_change;
3746
 
  const char *path = NULL;
 
4222
  const char *path;
3747
4223
 
3748
4224
  if ((old_change = apr_hash_get(changes, change->path, APR_HASH_KEY_STRING)))
3749
4225
    {
3750
4226
      /* This path already exists in the hash, so we have to merge
3751
4227
         this change into the already existing one. */
3752
4228
 
3753
 
      /* Since the path already exists in the hash, we don't have to
3754
 
         dup the allocation for the path itself. */
3755
 
      path = change->path;
3756
4229
      /* Sanity check:  only allow NULL node revision ID in the
3757
4230
         `reset' case. */
3758
4231
      if ((! change->noderev_id) && (change->kind != svn_fs_path_change_reset))
3875
4348
          new_change->copyfrom_rev = SVN_INVALID_REVNUM;
3876
4349
          new_change->copyfrom_path = NULL;
3877
4350
        }
3878
 
      path = apr_pstrdup(pool, change->path);
3879
4351
    }
3880
4352
 
3881
4353
  if (new_change)
3882
4354
    new_change->node_kind = change->node_kind;
3883
4355
 
3884
 
  /* Add (or update) this path. */
 
4356
  /* Add (or update) this path.
 
4357
 
 
4358
     Note: this key might already be present, and it would be nice to
 
4359
     re-use its value, but there is no way to fetch it. The API makes no
 
4360
     guarantees that this (new) key will not be retained. Thus, we (again)
 
4361
     copy the key into the target pool to ensure a proper lifetime.  */
 
4362
  path = apr_pstrdup(pool, change->path);
3885
4363
  apr_hash_set(changes, path, APR_HASH_KEY_STRING, new_change);
3886
4364
 
3887
4365
  /* Update the copyfrom cache, if any. */
3943
4421
        }
3944
4422
      if ((len == 0) && (! err))
3945
4423
        return SVN_NO_ERROR;
3946
 
      return err;
 
4424
      return svn_error_trace(err);
3947
4425
    }
3948
4426
 
3949
4427
  change = apr_pcalloc(pool, sizeof(*change));
4068
4546
      if (! str)
4069
4547
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
4070
4548
                                _("Invalid changes line in rev-file"));
4071
 
      change->copyfrom_rev = atol(str);
 
4549
      change->copyfrom_rev = SVN_STR_TO_REV(str);
4072
4550
 
4073
4551
      if (! last_str)
4074
4552
        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
4128
4606
               hi = apr_hash_next(hi))
4129
4607
            {
4130
4608
              /* KEY is the path. */
4131
 
              const void *hashkey;
4132
 
              apr_ssize_t klen;
4133
 
              apr_hash_this(hi, &hashkey, &klen, NULL);
 
4609
              const char *path = svn__apr_hash_index_key(hi);
 
4610
              apr_ssize_t klen = svn__apr_hash_index_klen(hi);
4134
4611
 
4135
4612
              /* If we come across our own path, ignore it. */
4136
 
              if (strcmp(change->path, hashkey) == 0)
 
4613
              if (strcmp(change->path, path) == 0)
4137
4614
                continue;
4138
4615
 
4139
4616
              /* If we come across a child of our path, remove it. */
4140
 
              if (svn_path_is_child(change->path, hashkey, iterpool))
4141
 
                apr_hash_set(changed_paths, hashkey, klen, NULL);
 
4617
              if (svn_dirent_is_child(change->path, path, iterpool))
 
4618
                apr_hash_set(changed_paths, path, klen, NULL);
4142
4619
            }
4143
4620
        }
4144
4621
 
4298
4775
  next_txn_id[len] = '\0';
4299
4776
 
4300
4777
  SVN_ERR(svn_io_write_unique(&tmp_filename,
4301
 
                              svn_path_dirname(txn_current_filename, pool),
 
4778
                              svn_dirent_dirname(txn_current_filename, pool),
4302
4779
                              next_txn_id, len, svn_io_file_del_none, pool));
4303
4780
  SVN_ERR(move_into_place(tmp_filename, txn_current_filename,
4304
4781
                          txn_current_filename, pool));
4328
4805
                                pool));
4329
4806
  *id_p = apr_psprintf(pool, "%ld-%s", rev, cb.txn_id);
4330
4807
 
4331
 
  txn_dir = svn_path_join_many(pool,
4332
 
                               fs->path,
4333
 
                               PATH_TXNS_DIR,
4334
 
                               apr_pstrcat(pool, *id_p, PATH_EXT_TXN, NULL),
4335
 
                               NULL);
 
4808
  txn_dir = svn_dirent_join_many(pool,
 
4809
                                 fs->path,
 
4810
                                 PATH_TXNS_DIR,
 
4811
                                 apr_pstrcat(pool, *id_p, PATH_EXT_TXN,
 
4812
                                             (char *)NULL),
 
4813
                                 NULL);
4336
4814
 
4337
4815
  return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, pool);
4338
4816
}
4352
4830
  const char *unique_path, *prefix;
4353
4831
 
4354
4832
  /* Try to create directories named "<txndir>/<rev>-<uniqueifier>.txn". */
4355
 
  prefix = svn_path_join_many(pool, fs->path, PATH_TXNS_DIR,
4356
 
                              apr_psprintf(pool, "%ld", rev), NULL);
 
4833
  prefix = svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
 
4834
                                apr_psprintf(pool, "%ld", rev), NULL);
4357
4835
 
4358
4836
  subpool = svn_pool_create(pool);
4359
4837
  for (i = 1; i <= 99999; i++)
4366
4844
      if (! err)
4367
4845
        {
4368
4846
          /* We succeeded.  Return the basename minus the ".txn" extension. */
4369
 
          const char *name = svn_path_basename(unique_path, subpool);
 
4847
          const char *name = svn_dirent_basename(unique_path, subpool);
4370
4848
          *id_p = apr_pstrndup(pool, name,
4371
4849
                               strlen(name) - strlen(PATH_EXT_TXN));
4372
4850
          svn_pool_destroy(subpool);
4373
4851
          return SVN_NO_ERROR;
4374
4852
        }
4375
4853
      if (! APR_STATUS_IS_EEXIST(err->apr_err))
4376
 
        return err;
 
4854
        return svn_error_trace(err);
4377
4855
      svn_error_clear(err);
4378
4856
    }
4379
4857
 
4381
4859
                           NULL,
4382
4860
                           _("Unable to create transaction directory "
4383
4861
                             "in '%s' for revision %ld"),
4384
 
                           fs->path, rev);
 
4862
                           svn_dirent_local_style(fs->path, pool),
 
4863
                           rev);
4385
4864
}
4386
4865
 
4387
4866
svn_error_t *
4439
4918
{
4440
4919
  svn_stream_t *stream;
4441
4920
 
 
4921
  /* Check for issue #3696. (When we find and fix the cause, we can change
 
4922
   * this to an assertion.) */
 
4923
  if (txn_id == NULL)
 
4924
    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
 
4925
                            _("Internal error: a null transaction id was "
 
4926
                              "passed to get_txn_proplist()"));
 
4927
 
4442
4928
  /* Open the transaction properties file. */
4443
4929
  SVN_ERR(svn_stream_open_readonly(&stream, path_txn_props(fs, txn_id, pool),
4444
4930
                                   pool, pool));
4467
4953
 
4468
4954
svn_error_t *
4469
4955
svn_fs_fs__change_txn_props(svn_fs_txn_t *txn,
4470
 
                            apr_array_header_t *props,
 
4956
                            const apr_array_header_t *props,
4471
4957
                            apr_pool_t *pool)
4472
4958
{
4473
4959
  const char *txn_prop_filename;
4485
4971
  if (err && (APR_STATUS_IS_ENOENT(err->apr_err)))
4486
4972
    svn_error_clear(err);
4487
4973
  else if (err)
4488
 
    return err;
 
4974
    return svn_error_trace(err);
4489
4975
 
4490
4976
  for (i = 0; i < props->nelts; i++)
4491
4977
    {
4633
5119
 
4634
5120
  SVN_ERR(write_next_ids(fs, txn_id, node_id, cur_copy_id, pool));
4635
5121
 
4636
 
  *node_id_p = apr_pstrcat(pool, "_", cur_node_id, NULL);
 
5122
  *node_id_p = apr_pstrcat(pool, "_", cur_node_id, (char *)NULL);
4637
5123
 
4638
5124
  return SVN_NO_ERROR;
4639
5125
}
4681
5167
         directory.  It's OK if they don't exist (for example, if this
4682
5168
         is post-commit and the proto-rev has been moved into
4683
5169
         place). */
4684
 
      svn_error_t *err = svn_io_remove_file(path_txn_proto_rev(fs, txn_id,
4685
 
                                                               pool), pool);
4686
 
      if (err && APR_STATUS_IS_ENOENT(err->apr_err))
4687
 
        {
4688
 
          svn_error_clear(err);
4689
 
          err = NULL;
4690
 
        }
4691
 
      if (err)
4692
 
        return err;
4693
 
 
4694
 
      err = svn_io_remove_file(path_txn_proto_rev_lock(fs, txn_id, pool),
4695
 
                               pool);
4696
 
      if (err && APR_STATUS_IS_ENOENT(err->apr_err))
4697
 
        {
4698
 
          svn_error_clear(err);
4699
 
          err = NULL;
4700
 
        }
4701
 
      if (err)
4702
 
        return err;
 
5170
      SVN_ERR(svn_io_remove_file2(path_txn_proto_rev(fs, txn_id, pool),
 
5171
                                  TRUE, pool));
 
5172
      SVN_ERR(svn_io_remove_file2(path_txn_proto_rev_lock(fs, txn_id, pool),
 
5173
                                  TRUE, pool));
4703
5174
    }
4704
5175
  return SVN_NO_ERROR;
4705
5176
}
4713
5184
 
4714
5185
  /* Now, purge the transaction. */
4715
5186
  SVN_ERR_W(svn_fs_fs__purge_txn(txn->fs, txn->id, pool),
4716
 
            _("Transaction cleanup failed"));
 
5187
            apr_psprintf(pool, _("Transaction '%s' cleanup failed"),
 
5188
                         txn->id));
4717
5189
 
4718
5190
  return SVN_NO_ERROR;
4719
5191
}
4732
5204
  const char *filename = path_txn_node_children(fs, parent_noderev->id, pool);
4733
5205
  apr_file_t *file;
4734
5206
  svn_stream_t *out;
 
5207
  fs_fs_data_t *ffd = fs->fsap_data;
4735
5208
 
4736
5209
  if (!rep || !rep->txn_id)
4737
5210
    {
4773
5246
      out = svn_stream_from_aprfile2(file, TRUE, pool);
4774
5247
    }
4775
5248
 
 
5249
  /* if we have a directory cache for this transaction, update it */
 
5250
  if (ffd->txn_dir_cache)
 
5251
    {
 
5252
      apr_pool_t *subpool = svn_pool_create(pool);
 
5253
 
 
5254
      /* build parameters: (name, new entry) pair */
 
5255
      const char *key =
 
5256
          svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
 
5257
      replace_baton_t baton = {name, NULL};
 
5258
 
 
5259
      if (id)
 
5260
        {
 
5261
          baton.new_entry = apr_pcalloc(subpool, sizeof(*baton.new_entry));
 
5262
          baton.new_entry->name = name;
 
5263
          baton.new_entry->kind = kind;
 
5264
          baton.new_entry->id = id;
 
5265
        }
 
5266
 
 
5267
      /* actually update the cached directory (if cached) */
 
5268
      SVN_ERR(svn_cache__set_partial(ffd->txn_dir_cache, key, svn_fs_fs__replace_dir_entry, &baton, subpool));
 
5269
 
 
5270
      svn_pool_destroy(subpool);
 
5271
    }
 
5272
 
4776
5273
  /* Append an incremental hash entry for the entry change. */
4777
5274
  if (id)
4778
5275
    {
4825
5322
      change_string = ACTION_RESET;
4826
5323
      break;
4827
5324
    default:
4828
 
      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
4829
 
                              _("Invalid change type"));
 
5325
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
5326
                               _("Invalid change type %d"),
 
5327
                               change->change_kind);
4830
5328
    }
4831
5329
 
4832
5330
  if (change->node_rev_id)
4836
5334
 
4837
5335
  if (include_node_kind)
4838
5336
    {
4839
 
      assert(change->node_kind == svn_node_dir
 
5337
      SVN_ERR_ASSERT(change->node_kind == svn_node_dir
4840
5338
                     || change->node_kind == svn_node_file);
4841
5339
      kind_string = apr_psprintf(pool, "-%s",
4842
5340
                                 change->node_kind == svn_node_dir
4880
5378
                           APR_APPEND | APR_WRITE | APR_CREATE
4881
5379
                           | APR_BUFFERED, APR_OS_DEFAULT, pool));
4882
5380
 
4883
 
  change = svn_fs__path_change2_create(id, change_kind, pool);
 
5381
  change = svn_fs__path_change_create_internal(id, change_kind, pool);
4884
5382
  change->text_mod = text_mod;
4885
5383
  change->prop_mod = prop_mod;
4886
5384
  change->node_kind = node_kind;
5013
5511
  svn_txdelta_window_handler_t wh;
5014
5512
  void *whb;
5015
5513
  fs_fs_data_t *ffd = fs->fsap_data;
 
5514
  int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
5016
5515
 
5017
5516
  b = apr_pcalloc(pool, sizeof(*b));
5018
5517
 
5058
5557
  SVN_ERR(get_file_offset(&b->delta_start, file, b->pool));
5059
5558
 
5060
5559
  /* Prepare to write the svndiff data. */
5061
 
  if (ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT)
5062
 
    svn_txdelta_to_svndiff2(&wh, &whb, b->rep_stream, 1, pool);
5063
 
  else
5064
 
    svn_txdelta_to_svndiff2(&wh, &whb, b->rep_stream, 0, pool);
 
5560
  svn_txdelta_to_svndiff3(&wh,
 
5561
                          &whb,
 
5562
                          b->rep_stream,
 
5563
                          diff_version,
 
5564
                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
 
5565
                          pool);
5065
5566
 
5066
5567
  b->delta_stream = svn_txdelta_target_push(wh, whb, source, b->pool);
5067
5568
 
5112
5613
  /* Check and see if we already have a representation somewhere that's
5113
5614
     identical to the one we just wrote out. */
5114
5615
  if (ffd->rep_sharing_allowed)
5115
 
    /* ### TODO: ignore errors opening the DB (issue #3506) * */
5116
 
    SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, b->fs, rep->sha1_checksum,
5117
 
                                         b->parent_pool));
 
5616
    {
 
5617
      svn_error_t *err;
 
5618
      err = svn_fs_fs__get_rep_reference(&old_rep, b->fs, rep->sha1_checksum,
 
5619
                                         b->parent_pool);
 
5620
      /* ### Other error codes that we shouldn't mask out? */
 
5621
      if (err == SVN_NO_ERROR
 
5622
          || err->apr_err == SVN_ERR_FS_CORRUPT
 
5623
          || SVN_ERROR_IN_CATEGORY(err->apr_err,
 
5624
                                   SVN_ERR_MALFUNC_CATEGORY_START))
 
5625
        {
 
5626
          /* Fatal error; don't mask it.
 
5627
 
 
5628
             In particular, this block is triggered when the rep-cache refers
 
5629
             to revisions in the future.  We signal that as a corruption situation
 
5630
             since, once those revisions are less than youngest (because of more
 
5631
             commits), the rep-cache would be invalid.
 
5632
           */
 
5633
          SVN_ERR(err);
 
5634
        }
 
5635
      else
 
5636
        {
 
5637
          /* Something's wrong with the rep-sharing index.  We can continue
 
5638
             without rep-sharing, but warn.
 
5639
           */
 
5640
          (b->fs->warning)(b->fs->warning_baton, err);
 
5641
          svn_error_clear(err);
 
5642
          old_rep = NULL;
 
5643
        }
 
5644
    }
5118
5645
  else
5119
5646
    old_rep = NULL;
5120
5647
 
5161
5688
  struct rep_write_baton *wb;
5162
5689
 
5163
5690
  if (! svn_fs_fs__id_txn_id(noderev->id))
5164
 
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5165
 
                            _("Attempted to write to non-transaction"));
 
5691
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
5692
                             _("Attempted to write to non-transaction '%s'"),
 
5693
                             svn_fs_fs__id_unparse(noderev->id, pool)->data);
5166
5694
 
5167
5695
  SVN_ERR(rep_write_get_baton(&wb, fs, noderev, pool));
5168
5696
 
5265
5793
  str = apr_strtok(buf, " ", &last_str);
5266
5794
  if (! str)
5267
5795
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5268
 
                            _("Corrupt current file"));
 
5796
                            _("Corrupt 'current' file"));
5269
5797
 
5270
5798
  str = apr_strtok(NULL, " ", &last_str);
5271
5799
  if (! str)
5272
5800
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5273
 
                            _("Corrupt current file"));
 
5801
                            _("Corrupt 'current' file"));
5274
5802
 
5275
5803
  *node_id = apr_pstrdup(pool, str);
5276
5804
 
5277
5805
  str = apr_strtok(NULL, " ", &last_str);
5278
5806
  if (! str)
5279
5807
    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
5280
 
                            _("Corrupt current file"));
 
5808
                            _("Corrupt 'current' file"));
5281
5809
 
5282
5810
  *copy_id = apr_pstrdup(pool, str);
5283
5811
 
5339
5867
  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
5340
5868
 
5341
5869
  /* Store the results. */
5342
 
  svn_checksum_final(checksum, whb->checksum_ctx, pool);
 
5870
  SVN_ERR(svn_checksum_final(checksum, whb->checksum_ctx, pool));
5343
5871
  *size = whb->size;
5344
5872
 
5345
5873
  return svn_stream_printf(whb->stream, pool, "ENDREP\n");
5346
5874
}
5347
5875
 
 
5876
/* Sanity check ROOT_NODEREV, a candidate for being the root node-revision
 
5877
   of (not yet committed) revision REV in FS.  Use POOL for temporary
 
5878
   allocations.
 
5879
 */
 
5880
static svn_error_t *
 
5881
validate_root_noderev(svn_fs_t *fs,
 
5882
                      node_revision_t *root_noderev,
 
5883
                      svn_revnum_t rev,
 
5884
                      apr_pool_t *pool)
 
5885
{
 
5886
  svn_revnum_t head_revnum = rev-1;
 
5887
  int head_predecessor_count;
 
5888
 
 
5889
  SVN_ERR_ASSERT(rev > 0);
 
5890
 
 
5891
  /* Compute HEAD_PREDECESSOR_COUNT. */
 
5892
  {
 
5893
    svn_fs_root_t *head_revision;
 
5894
    const svn_fs_id_t *head_root_id;
 
5895
    node_revision_t *head_root_noderev;
 
5896
 
 
5897
    /* Get /@HEAD's noderev. */
 
5898
    SVN_ERR(svn_fs_fs__revision_root(&head_revision, fs, head_revnum, pool));
 
5899
    SVN_ERR(svn_fs_fs__node_id(&head_root_id, head_revision, "/", pool));
 
5900
    SVN_ERR(svn_fs_fs__get_node_revision(&head_root_noderev, fs, head_root_id,
 
5901
                                         pool));
 
5902
 
 
5903
    head_predecessor_count = head_root_noderev->predecessor_count;
 
5904
  }
 
5905
 
 
5906
  /* Check that the root noderev's predecessor count equals REV.
 
5907
 
 
5908
     This kind of corruption was seen on svn.apache.org (both on
 
5909
     the root noderev and on other fspaths' noderevs); see
 
5910
       http://mid.gmane.org/20111002202833.GA12373@daniel3.local
 
5911
 
 
5912
     Normally (rev == root_noderev->predecessor_count), but here we
 
5913
     use a more roundabout check that should only trigger on new instances
 
5914
     of the corruption, rather then trigger on each and every new commit
 
5915
     to a repository that has triggered the bug somewhere in its root
 
5916
     noderev's history.
 
5917
   */
 
5918
  if (root_noderev->predecessor_count != -1
 
5919
      && (root_noderev->predecessor_count - head_predecessor_count)
 
5920
         != (rev - head_revnum))
 
5921
    {
 
5922
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
5923
                               _("predecessor count for "
 
5924
                                 "the root node-revision is wrong: "
 
5925
                                 "found (%d+%ld != %d), committing r%ld"),
 
5926
                                 head_predecessor_count,
 
5927
                                 rev - head_revnum, /* This is equal to 1. */
 
5928
                                 root_noderev->predecessor_count,
 
5929
                                 rev);
 
5930
    }
 
5931
 
 
5932
  return SVN_NO_ERROR;
 
5933
}
 
5934
 
5348
5935
/* Copy a node-revision specified by id ID in fileystem FS from a
5349
 
   transaction into the proto-rev-file FILE.  Return the offset of
5350
 
   the new node-revision in *OFFSET.  If this is a directory, all
5351
 
   children are copied as well.  START_NODE_ID and START_COPY_ID are
 
5936
   transaction into the proto-rev-file FILE.  Set *NEW_ID_P to a
 
5937
   pointer to the new node-id which will be allocated in POOL.
 
5938
   If this is a directory, copy all children as well.
 
5939
 
 
5940
   START_NODE_ID and START_COPY_ID are
5352
5941
   the first available node and copy ids for this filesystem, for older
5353
5942
   FS formats.
5354
5943
 
5360
5949
   If REPS_TO_CACHE is not NULL, append to it a copy (allocated in
5361
5950
   REPS_POOL) of each data rep that is new in this revision.
5362
5951
 
5363
 
   Temporary allocations are from POOL.  */
 
5952
   AT_ROOT is true if the node revision being written is the root
 
5953
   node-revision.  It is only controls additional sanity checking
 
5954
   logic.
 
5955
 
 
5956
   Temporary allocations are also from POOL. */
5364
5957
static svn_error_t *
5365
5958
write_final_rev(const svn_fs_id_t **new_id_p,
5366
5959
                apr_file_t *file,
5372
5965
                apr_off_t initial_offset,
5373
5966
                apr_array_header_t *reps_to_cache,
5374
5967
                apr_pool_t *reps_pool,
 
5968
                svn_boolean_t at_root,
5375
5969
                apr_pool_t *pool)
5376
5970
{
5377
5971
  node_revision_t *noderev;
5394
5988
    {
5395
5989
      apr_pool_t *subpool;
5396
5990
      apr_hash_t *entries, *str_entries;
5397
 
      svn_fs_dirent_t *dirent;
5398
 
      void *val;
5399
5991
      apr_hash_index_t *hi;
5400
5992
 
5401
5993
      /* This is a directory.  Write out all the children first. */
5405
5997
 
5406
5998
      for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
5407
5999
        {
 
6000
          svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
6001
 
5408
6002
          svn_pool_clear(subpool);
5409
 
          apr_hash_this(hi, NULL, NULL, &val);
5410
 
          dirent = val;
5411
6003
          SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
5412
6004
                                  start_node_id, start_copy_id, initial_offset,
5413
 
                                  reps_to_cache, reps_pool,
 
6005
                                  reps_to_cache, reps_pool, FALSE,
5414
6006
                                  subpool));
5415
6007
          if (new_id && (svn_fs_fs__id_rev(new_id) == rev))
5416
6008
            dirent->id = svn_fs_fs__id_copy(new_id, pool);
5508
6100
  noderev->id = new_id;
5509
6101
 
5510
6102
  /* Write out our new node-revision. */
 
6103
  if (at_root)
 
6104
    SVN_ERR(validate_root_noderev(fs, noderev, rev, pool));
5511
6105
  SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_aprfile2(file, TRUE, pool),
5512
6106
                                   noderev, ffd->format,
5513
6107
                                   svn_fs_fs__fs_supports_mergeinfo(fs),
5559
6153
      node_revision_t *noderev;
5560
6154
      const svn_fs_id_t *id;
5561
6155
      svn_fs_path_change2_t *change;
5562
 
      const void *key;
5563
 
      void *val;
 
6156
      const char *path;
5564
6157
 
5565
6158
      svn_pool_clear(iterpool);
5566
6159
 
5567
 
      apr_hash_this(hi, &key, NULL, &val);
5568
 
      change = val;
 
6160
      change = svn__apr_hash_index_val(hi);
 
6161
      path = svn__apr_hash_index_key(hi);
5569
6162
 
5570
6163
      id = change->node_rev_id;
5571
6164
 
5583
6176
        }
5584
6177
 
5585
6178
      /* Write out the new entry into the final rev-file. */
5586
 
      SVN_ERR(write_change_entry(file, key, change, include_node_kinds,
 
6179
      SVN_ERR(write_change_entry(file, path, change, include_node_kinds,
5587
6180
                                 iterpool));
5588
6181
    }
5589
6182
 
5594
6187
  return SVN_NO_ERROR;
5595
6188
}
5596
6189
 
5597
 
 
5598
 
svn_error_t *
5599
 
svn_fs_fs__dup_perms(const char *filename,
5600
 
                     const char *perms_reference,
5601
 
                     apr_pool_t *pool)
5602
 
{
5603
 
#ifndef WIN32
5604
 
  apr_status_t status;
5605
 
  apr_finfo_t finfo;
5606
 
  const char *filename_apr, *perms_reference_apr;
5607
 
 
5608
 
  SVN_ERR(svn_path_cstring_from_utf8(&filename_apr, filename, pool));
5609
 
  SVN_ERR(svn_path_cstring_from_utf8(&perms_reference_apr, perms_reference,
5610
 
                                     pool));
5611
 
 
5612
 
  status = apr_stat(&finfo, perms_reference_apr, APR_FINFO_PROT, pool);
5613
 
  if (status)
5614
 
    return svn_error_wrap_apr(status, _("Can't stat '%s'"),
5615
 
                              svn_path_local_style(perms_reference, pool));
5616
 
  status = apr_file_perms_set(filename_apr, finfo.protection);
5617
 
  if (status)
5618
 
    return svn_error_wrap_apr(status, _("Can't chmod '%s'"),
5619
 
                              svn_path_local_style(filename, pool));
5620
 
#endif
5621
 
  return SVN_NO_ERROR;
5622
 
}
5623
 
 
5624
 
 
5625
 
/* Atomically update the current file to hold the specifed REV,
 
6190
/* Atomically update the 'current' file to hold the specifed REV,
5626
6191
   NEXT_NODE_ID, and NEXT_COPY_ID.  (The two next-ID parameters are
5627
6192
   ignored and may be NULL if the FS format does not use them.)
5628
6193
   Perform temporary allocations in POOL. */
5642
6207
 
5643
6208
  name = svn_fs_fs__path_current(fs, pool);
5644
6209
  SVN_ERR(svn_io_write_unique(&tmp_name,
5645
 
                              svn_path_dirname(name, pool),
 
6210
                              svn_dirent_dirname(name, pool),
5646
6211
                              buf, strlen(buf),
5647
6212
                              svn_io_file_del_none, pool));
5648
6213
 
5649
6214
  return move_into_place(tmp_name, name, name, pool);
5650
6215
}
5651
6216
 
5652
 
/* Update the current file to hold the correct next node and copy_ids
 
6217
/* Update the 'current' file to hold the correct next node and copy_ids
5653
6218
   from transaction TXN_ID in filesystem FS.  The current revision is
5654
6219
   set to REV.  Perform temporary allocations in POOL. */
5655
6220
static svn_error_t *
5669
6234
    return write_current(fs, rev, NULL, NULL, pool);
5670
6235
 
5671
6236
  /* To find the next available ids, we add the id that used to be in
5672
 
     the current file, to the next ids from the transaction file. */
 
6237
     the 'current' file, to the next ids from the transaction file. */
5673
6238
  SVN_ERR(read_next_ids(&txn_node_id, &txn_copy_id, fs, txn_id, pool));
5674
6239
 
5675
6240
  svn_fs_fs__add_keys(start_node_id, txn_node_id, new_node_id);
5700
6265
  changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
5701
6266
                                 sizeof(const char *));
5702
6267
  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
5703
 
    {
5704
 
      const void *key;
5705
 
      apr_hash_this(hi, &key, NULL, NULL);
5706
 
      APR_ARRAY_PUSH(changed_paths, const char *) = key;
5707
 
    }
 
6268
    APR_ARRAY_PUSH(changed_paths, const char *) = svn__apr_hash_index_key(hi);
5708
6269
  qsort(changed_paths->elts, changed_paths->nelts,
5709
6270
        changed_paths->elt_size, svn_sort_compare_paths);
5710
6271
 
5723
6284
      /* If this path has already been verified as part of a recursive
5724
6285
         check of one of its parents, no need to do it again.  */
5725
6286
      if (last_recursed
5726
 
          && svn_path_is_child(last_recursed->data, path, subpool))
 
6287
          && svn_dirent_is_child(last_recursed->data, path, subpool))
5727
6288
        continue;
5728
6289
 
5729
6290
      /* Fetch the change associated with our path.  */
5784
6345
  apr_off_t initial_offset, changed_path_offset;
5785
6346
  char *buf;
5786
6347
  apr_hash_t *txnprops;
 
6348
  apr_array_header_t *txnprop_list;
 
6349
  svn_prop_t prop;
5787
6350
  svn_string_t date;
5788
6351
 
5789
6352
  /* Get the current youngest revision. */
5818
6381
  root_id = svn_fs_fs__id_txn_create("0", "0", cb->txn->id, pool);
5819
6382
  SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id,
5820
6383
                          start_node_id, start_copy_id, initial_offset,
5821
 
                          cb->reps_to_cache, cb->reps_pool,
 
6384
                          cb->reps_to_cache, cb->reps_pool, TRUE,
5822
6385
                          pool));
5823
6386
 
5824
6387
  /* Write the changed-path information. */
5840
6403
 
5841
6404
  /* Remove any temporary txn props representing 'flags'. */
5842
6405
  SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, cb->txn, pool));
5843
 
  if (txnprops)
5844
 
    {
5845
 
      apr_array_header_t *props = apr_array_make(pool, 3, sizeof(svn_prop_t));
5846
 
      svn_prop_t prop;
5847
 
      prop.value = NULL;
5848
 
 
5849
 
      if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_OOD,
5850
 
                       APR_HASH_KEY_STRING))
5851
 
        {
5852
 
          prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
5853
 
          APR_ARRAY_PUSH(props, svn_prop_t) = prop;
5854
 
        }
5855
 
 
5856
 
      if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS,
5857
 
                       APR_HASH_KEY_STRING))
5858
 
        {
5859
 
          prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
5860
 
          APR_ARRAY_PUSH(props, svn_prop_t) = prop;
5861
 
        }
5862
 
 
5863
 
      if (! apr_is_empty_array(props))
5864
 
        SVN_ERR(svn_fs_fs__change_txn_props(cb->txn, props, pool));
5865
 
    }
 
6406
  txnprop_list = apr_array_make(pool, 3, sizeof(svn_prop_t));
 
6407
  prop.value = NULL;
 
6408
 
 
6409
  if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, APR_HASH_KEY_STRING))
 
6410
    {
 
6411
      prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
 
6412
      APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
 
6413
    }
 
6414
 
 
6415
  if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS,
 
6416
                   APR_HASH_KEY_STRING))
 
6417
    {
 
6418
      prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
 
6419
      APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
 
6420
    }
 
6421
 
 
6422
  if (! apr_is_empty_array(txnprop_list))
 
6423
    SVN_ERR(svn_fs_fs__change_txn_props(cb->txn, txnprop_list, pool));
5866
6424
 
5867
6425
  /* Create the shard for the rev and revprop file, if we're sharding and
5868
6426
     this is the first revision of a new shard.  We don't care if this
5869
6427
     fails because the shard already existed for some reason. */
5870
6428
  if (ffd->max_files_per_dir && new_rev % ffd->max_files_per_dir == 0)
5871
6429
    {
5872
 
      svn_error_t *err;
5873
 
      const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
5874
 
      err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
5875
 
      if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
5876
 
        SVN_ERR(err);
5877
 
      svn_error_clear(err);
5878
 
      SVN_ERR(svn_fs_fs__dup_perms(new_dir,
5879
 
                                   svn_path_join(cb->fs->path,
5880
 
                                                 PATH_REVS_DIR,
5881
 
                                                 pool),
5882
 
                                   pool));
 
6430
      if (1)
 
6431
        {
 
6432
          const char *new_dir = path_rev_shard(cb->fs, new_rev, pool);
 
6433
          svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
 
6434
          if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
 
6435
            return svn_error_trace(err);
 
6436
          svn_error_clear(err);
 
6437
          SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
 
6438
                                                    PATH_REVS_DIR,
 
6439
                                                    pool),
 
6440
                                    new_dir, pool));
 
6441
        }
5883
6442
 
5884
 
      new_dir = path_revprops_shard(cb->fs, new_rev, pool);
5885
 
      err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
5886
 
      if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
5887
 
        SVN_ERR(err);
5888
 
      svn_error_clear(err);
5889
 
      SVN_ERR(svn_fs_fs__dup_perms(new_dir,
5890
 
                                   svn_path_join(cb->fs->path,
5891
 
                                                 PATH_REVPROPS_DIR,
5892
 
                                                 pool),
5893
 
                                   pool));
 
6443
      /* Create the revprops shard. */
 
6444
      SVN_ERR_ASSERT(! is_packed_revprop(cb->fs, new_rev));
 
6445
        {
 
6446
          const char *new_dir = path_revprops_shard(cb->fs, new_rev, pool);
 
6447
          svn_error_t *err = svn_io_dir_make(new_dir, APR_OS_DEFAULT, pool);
 
6448
          if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
 
6449
            return svn_error_trace(err);
 
6450
          svn_error_clear(err);
 
6451
          SVN_ERR(svn_io_copy_perms(svn_dirent_join(cb->fs->path,
 
6452
                                                    PATH_REVPROPS_DIR,
 
6453
                                                    pool),
 
6454
                                    new_dir, pool));
 
6455
        }
5894
6456
    }
5895
6457
 
5896
6458
  /* Move the finished rev file into place. */
5915
6477
                                     &date, pool));
5916
6478
 
5917
6479
  /* Move the revprops file into place. */
 
6480
  SVN_ERR_ASSERT(! is_packed_revprop(cb->fs, new_rev));
5918
6481
  revprop_filename = path_txn_props(cb->fs, cb->txn->id, pool);
5919
6482
  final_revprop = path_revprops(cb->fs, new_rev, pool);
5920
 
  SVN_ERR(move_into_place(revprop_filename, final_revprop, old_rev_filename,
5921
 
                          pool));
 
6483
  SVN_ERR(move_into_place(revprop_filename, final_revprop,
 
6484
                          old_rev_filename, pool));
5922
6485
 
5923
6486
  /* Update the 'current' file. */
5924
6487
  SVN_ERR(write_final_current(cb->fs, cb->txn->id, new_rev, start_node_id,
5939
6502
  return SVN_NO_ERROR;
5940
6503
}
5941
6504
 
5942
 
/* Baton for use with an sqlite transaction'd commit body. */
5943
 
struct commit_sqlite_txn_baton
5944
 
{
5945
 
  struct commit_baton *cb;
5946
 
  apr_pool_t *pool;
5947
 
};
5948
 
 
5949
6505
/* Add the representations in REPS_TO_CACHE (an array of representation_t *)
5950
6506
 * to the rep-cache database of FS. */
5951
6507
static svn_error_t *
5952
6508
write_reps_to_cache(svn_fs_t *fs,
5953
 
                    apr_array_header_t *reps_to_cache,
 
6509
                    const apr_array_header_t *reps_to_cache,
5954
6510
                    apr_pool_t *scratch_pool)
5955
6511
{
5956
6512
  int i;
5969
6525
 
5970
6526
/* Implements svn_sqlite__transaction_callback_t. */
5971
6527
static svn_error_t *
5972
 
commit_sqlite_txn_callback(void *baton, svn_sqlite__db_t *db)
 
6528
commit_sqlite_txn_callback(void *baton, svn_sqlite__db_t *db,
 
6529
                           apr_pool_t *scratch_pool)
5973
6530
{
5974
 
  struct commit_sqlite_txn_baton *cstb = baton;
5975
 
  struct commit_baton *cb = cstb->cb;
 
6531
  struct commit_baton *cb = baton;
5976
6532
 
5977
6533
  /* Write new entries to the rep-sharing database.
5978
6534
   *
5979
6535
   * We use an sqlite transcation to speed things up;
5980
6536
   * see <http://www.sqlite.org/faq.html#q19>.
5981
6537
   */
5982
 
  SVN_ERR(write_reps_to_cache(cb->fs, cb->reps_to_cache, cstb->pool));
 
6538
  SVN_ERR(write_reps_to_cache(cb->fs, cb->reps_to_cache, scratch_pool));
5983
6539
 
5984
6540
  return SVN_NO_ERROR;
5985
6541
}
6012
6568
 
6013
6569
  if (ffd->rep_sharing_allowed)
6014
6570
    {
6015
 
      struct commit_sqlite_txn_baton cstb;
6016
 
      cstb.cb = &cb;
6017
 
      cstb.pool = pool;
6018
 
 
6019
 
      /* ### TODO: ignore errors opening the DB (issue #3506) * */
 
6571
      /* At this point, *NEW_REV_P has been set, so errors here won't affect
 
6572
         the success of the commit.  (See svn_fs_commit_txn().)  */
6020
6573
      SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
6021
6574
      SVN_ERR(svn_sqlite__with_transaction(ffd->rep_cache_db,
6022
6575
                                           commit_sqlite_txn_callback,
6023
 
                                           &cstb));
 
6576
                                           &cb, pool));
6024
6577
    }
6025
6578
 
6026
6579
  return SVN_NO_ERROR;
6027
6580
}
6028
6581
 
 
6582
 
6029
6583
svn_error_t *
6030
6584
svn_fs_fs__reserve_copy_id(const char **copy_id_p,
6031
6585
                           svn_fs_t *fs,
6046
6600
 
6047
6601
  SVN_ERR(write_next_ids(fs, txn_id, cur_node_id, copy_id, pool));
6048
6602
 
6049
 
  *copy_id_p = apr_pstrcat(pool, "_", cur_copy_id, NULL);
 
6603
  *copy_id_p = apr_pstrcat(pool, "_", cur_copy_id, (char *)NULL);
6050
6604
 
6051
6605
  return SVN_NO_ERROR;
6052
6606
}
6076
6630
  date.len = strlen(date.data);
6077
6631
  proplist = apr_hash_make(fs->pool);
6078
6632
  apr_hash_set(proplist, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING, &date);
6079
 
  return svn_fs_fs__set_revision_proplist(fs, 0, proplist, fs->pool);
 
6633
  return set_revision_proplist(fs, 0, proplist, fs->pool);
6080
6634
}
6081
6635
 
6082
6636
svn_error_t *
6088
6642
  fs_fs_data_t *ffd = fs->fsap_data;
6089
6643
 
6090
6644
  fs->path = apr_pstrdup(pool, path);
6091
 
  /* See if we had an explicitly requested pre-1.4- or pre-1.5-compatible.  */
 
6645
  /* See if compatibility with older versions was explicitly requested. */
6092
6646
  if (fs->config)
6093
6647
    {
6094
6648
      if (apr_hash_get(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE,
6107
6661
  if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
6108
6662
    ffd->max_files_per_dir = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR;
6109
6663
 
 
6664
  /* Create the revision data directories. */
6110
6665
  if (ffd->max_files_per_dir)
6111
 
    {
6112
 
      SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(fs, 0, pool),
6113
 
                                          pool));
6114
 
      SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(fs, 0, pool),
6115
 
                                          pool));
6116
 
    }
 
6666
    SVN_ERR(svn_io_make_dir_recursively(path_rev_shard(fs, 0, pool), pool));
6117
6667
  else
6118
 
    {
6119
 
      SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_REVS_DIR,
 
6668
    SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR,
6120
6669
                                                        pool),
6121
 
                                          pool));
6122
 
      SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path,
 
6670
                                        pool));
 
6671
 
 
6672
  /* Create the revprops directory. */
 
6673
  if (ffd->max_files_per_dir)
 
6674
    SVN_ERR(svn_io_make_dir_recursively(path_revprops_shard(fs, 0, pool),
 
6675
                                        pool));
 
6676
  else
 
6677
    SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path,
6123
6678
                                                        PATH_REVPROPS_DIR,
6124
6679
                                                        pool),
6125
 
                                          pool));
6126
 
    }
6127
 
  SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_TXNS_DIR,
6128
 
                                                    pool),
 
6680
                                        pool));
 
6681
 
 
6682
  /* Create the transaction directory. */
 
6683
  SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXNS_DIR,
 
6684
                                                      pool),
6129
6685
                                      pool));
6130
6686
 
 
6687
  /* Create the protorevs directory. */
6131
6688
  if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
6132
 
    SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_TXN_PROTOS_DIR,
 
6689
    SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_TXN_PROTOS_DIR,
6133
6690
                                                      pool),
6134
6691
                                        pool));
6135
6692
 
 
6693
  /* Create the 'current' file. */
6136
6694
  SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
6137
6695
                             (format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT
6138
6696
                              ? "0\n" : "0 1 1\n"),
6144
6702
 
6145
6703
  SVN_ERR(write_config(fs, pool));
6146
6704
 
6147
 
  /* Read the configuration file. */
6148
6705
  SVN_ERR(read_config(fs, pool));
6149
6706
 
6150
6707
  /* Create the min unpacked rev file. */
6263
6820
    bytes_to_read = b->remaining;
6264
6821
  b->remaining -= bytes_to_read;
6265
6822
 
6266
 
  return svn_io_file_read_full(b->file, buffer, (apr_size_t) bytes_to_read,
6267
 
                               len, b->pool);
 
6823
  return svn_io_file_read_full2(b->file, buffer, (apr_size_t) bytes_to_read,
 
6824
                                len, NULL, b->pool);
6268
6825
}
6269
6826
 
6270
6827
/* Part of the recovery procedure.  Read the directory noderev at offset
6285
6842
{
6286
6843
  apr_hash_t *headers;
6287
6844
  char *value;
6288
 
  node_revision_t noderev;
 
6845
  representation_t *data_rep;
6289
6846
  struct rep_args *ra;
6290
6847
  struct recover_read_from_file_baton baton;
6291
6848
  svn_stream_t *stream;
6298
6855
                                                               pool),
6299
6856
                            pool));
6300
6857
 
6301
 
  /* We're going to populate a skeletal noderev - just the id and data_rep. */
6302
 
  value = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
6303
 
  noderev.id = svn_fs_fs__id_parse(value, strlen(value), pool);
6304
 
 
6305
6858
  /* Check that this is a directory.  It should be. */
6306
6859
  value = apr_hash_get(headers, HEADER_TYPE, APR_HASH_KEY_STRING);
6307
6860
  if (value == NULL || strcmp(value, KIND_DIR) != 0)
6312
6865
  value = apr_hash_get(headers, HEADER_TEXT, APR_HASH_KEY_STRING);
6313
6866
  if (!value)
6314
6867
    return SVN_NO_ERROR;
6315
 
  SVN_ERR(read_rep_offsets(&noderev.data_rep, value, NULL, FALSE, pool));
 
6868
  SVN_ERR(read_rep_offsets(&data_rep, value, NULL, FALSE, pool));
6316
6869
 
6317
6870
  /* If the directory's data representation wasn't changed in this revision,
6318
6871
     we've already scanned the directory's contents for noderevs, so we don't
6319
6872
     need to again.  This will occur if a property is changed on a directory
6320
6873
     without changing the directory's contents. */
6321
 
  if (noderev.data_rep->revision != rev)
 
6874
  if (data_rep->revision != rev)
6322
6875
    return SVN_NO_ERROR;
6323
6876
 
6324
6877
  /* We could use get_dir_contents(), but this is much cheaper.  It does
6325
6878
     rely on directory entries being stored as PLAIN reps, though. */
6326
 
  offset = noderev.data_rep->offset;
 
6879
  offset = data_rep->offset;
6327
6880
  SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, pool));
6328
6881
  SVN_ERR(read_rep_line(&ra, rev_file, pool));
6329
6882
  if (ra->is_delta)
6335
6888
     stored in the representation. */
6336
6889
  baton.file = rev_file;
6337
6890
  baton.pool = pool;
6338
 
  baton.remaining = noderev.data_rep->expanded_size;
 
6891
  baton.remaining = data_rep->expanded_size;
6339
6892
  stream = svn_stream_create(&baton, pool);
6340
6893
  svn_stream_set_read(stream, read_handler_recover);
6341
6894
 
6347
6900
  /* Now check each of the entries in our directory to find new node and
6348
6901
     copy ids, and recurse into new subdirectories. */
6349
6902
  iterpool = svn_pool_create(pool);
6350
 
  for (hi = apr_hash_first(NULL, entries); hi; hi = apr_hash_next(hi))
 
6903
  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
6351
6904
    {
6352
 
      void *val;
6353
6905
      char *str_val;
6354
6906
      char *str, *last_str;
6355
6907
      svn_node_kind_t kind;
6356
6908
      svn_fs_id_t *id;
6357
6909
      const char *node_id, *copy_id;
6358
6910
      apr_off_t child_dir_offset;
 
6911
      const svn_string_t *path = svn__apr_hash_index_val(hi);
6359
6912
 
6360
6913
      svn_pool_clear(iterpool);
6361
6914
 
6362
 
      apr_hash_this(hi, NULL, NULL, &val);
6363
 
      str_val = apr_pstrdup(iterpool, *((char **)val));
 
6915
      str_val = apr_pstrdup(iterpool, path->data);
6364
6916
 
6365
6917
      str = apr_strtok(str_val, " ", &last_str);
6366
6918
      if (str == NULL)
6395
6947
      copy_id = svn_fs_fs__id_copy_id(id);
6396
6948
 
6397
6949
      if (svn_fs_fs__key_compare(node_id, max_node_id) > 0)
6398
 
        strcpy(max_node_id, node_id);
 
6950
        {
 
6951
          SVN_ERR_ASSERT(strlen(node_id) < MAX_KEY_SIZE);
 
6952
          apr_cpystrn(max_node_id, node_id, MAX_KEY_SIZE);
 
6953
        }
6399
6954
      if (svn_fs_fs__key_compare(copy_id, max_copy_id) > 0)
6400
 
        strcpy(max_copy_id, copy_id);
 
6955
        {
 
6956
          SVN_ERR_ASSERT(strlen(copy_id) < MAX_KEY_SIZE);
 
6957
          apr_cpystrn(max_copy_id, copy_id, MAX_KEY_SIZE);
 
6958
        }
6401
6959
 
6402
6960
      if (kind == svn_node_file)
6403
6961
        continue;
6523
7081
  SVN_ERR(svn_io_check_path(path_revprops(fs, max_rev, pool),
6524
7082
                            &youngest_revprops_kind, pool));
6525
7083
  if (youngest_revprops_kind == svn_node_none)
6526
 
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
6527
 
                             _("Revision %ld has a revs file but no "
6528
 
                               "revprops file"),
6529
 
                             max_rev);
 
7084
    {
 
7085
      if (1)
 
7086
        {
 
7087
          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
7088
                                   _("Revision %ld has a revs file but no "
 
7089
                                     "revprops file"),
 
7090
                                   max_rev);
 
7091
        }
 
7092
    }
6530
7093
  else if (youngest_revprops_kind != svn_node_file)
6531
 
    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
6532
 
                             _("Revision %ld has a non-file where its "
6533
 
                               "revprops file should be"),
6534
 
                             max_rev);
 
7094
    {
 
7095
      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
7096
                               _("Revision %ld has a non-file where its "
 
7097
                                 "revprops file should be"),
 
7098
                               max_rev);
 
7099
    }
 
7100
 
 
7101
  /* Prune younger-than-(newfound-youngest) revisions from the rep cache. */
 
7102
  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
 
7103
    SVN_ERR(svn_fs_fs__del_rep_reference(fs, max_rev, pool));
6535
7104
 
6536
7105
  /* Now store the discovered youngest revision, and the next IDs if
6537
 
     relevant, in a new current file. */
 
7106
     relevant, in a new 'current' file. */
6538
7107
  return write_current(fs, max_rev, next_node_id, next_copy_id, pool);
6539
7108
}
6540
7109
 
6548
7117
 
6549
7118
  /* We have no way to take out an exclusive lock in FSFS, so we're
6550
7119
     restricted as to the types of recovery we can do.  Luckily,
6551
 
     we just want to recreate the current file, and we can do that just
 
7120
     we just want to recreate the 'current' file, and we can do that just
6552
7121
     by blocking other writers. */
6553
7122
  b.fs = fs;
6554
7123
  b.cancel_func = cancel_func;
6582
7151
    uuid = svn_uuid_generate(pool);
6583
7152
 
6584
7153
  /* Make sure we have a copy in FS->POOL, and append a newline. */
6585
 
  my_uuid = apr_pstrcat(fs->pool, uuid, "\n", NULL);
 
7154
  my_uuid = apr_pstrcat(fs->pool, uuid, "\n", (char *)NULL);
6586
7155
  my_uuid_len = strlen(my_uuid);
6587
7156
 
6588
7157
  SVN_ERR(svn_io_write_unique(&tmp_path,
6589
 
                              svn_path_dirname(uuid_path, pool),
 
7158
                              svn_dirent_dirname(uuid_path, pool),
6590
7159
                              my_uuid, my_uuid_len,
6591
7160
                              svn_io_file_del_none, pool));
6592
7161
 
6608
7177
   permissions as FS->path.*/
6609
7178
svn_error_t *
6610
7179
svn_fs_fs__ensure_dir_exists(const char *path,
6611
 
                             svn_fs_t *fs,
 
7180
                             const char *fs_path,
6612
7181
                             apr_pool_t *pool)
6613
7182
{
6614
7183
  svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool);
6621
7190
 
6622
7191
  /* We successfully created a new directory.  Dup the permissions
6623
7192
     from FS->path. */
6624
 
  return svn_fs_fs__dup_perms(path, fs->path, pool);
 
7193
  return svn_io_copy_perms(fs_path, path, pool);
6625
7194
}
6626
7195
 
6627
7196
/* Set *NODE_ORIGINS to a hash mapping 'const char *' node IDs to
6690
7259
  apr_hash_t *origins_hash;
6691
7260
  svn_string_t *old_node_rev_id;
6692
7261
 
6693
 
  SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_path_join(fs->path,
6694
 
                                                     PATH_NODE_ORIGINS_DIR,
6695
 
                                                     pool),
6696
 
                                       fs, pool));
 
7262
  SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs->path,
 
7263
                                                       PATH_NODE_ORIGINS_DIR,
 
7264
                                                       pool),
 
7265
                                       fs->path, pool));
6697
7266
 
6698
7267
  /* Read the previously existing origins (if any), and merge our
6699
7268
     update with it. */
6722
7291
 
6723
7292
  /* Create a temporary file, write out our hash, and close the file. */
6724
7293
  SVN_ERR(svn_stream_open_unique(&stream, &path_tmp,
6725
 
                                 svn_path_dirname(node_origins_path, pool),
 
7294
                                 svn_dirent_dirname(node_origins_path, pool),
6726
7295
                                 svn_io_file_del_none, pool, pool));
6727
7296
  SVN_ERR(svn_hash_write2(origins_hash, stream, SVN_HASH_TERMINATOR, pool));
6728
7297
  SVN_ERR(svn_stream_close(stream));
6751
7320
      svn_error_clear(err);
6752
7321
      err = NULL;
6753
7322
    }
6754
 
  return err;
 
7323
  return svn_error_trace(err);
6755
7324
}
6756
7325
 
6757
7326
 
6769
7338
  names = apr_array_make(pool, 1, sizeof(const char *));
6770
7339
 
6771
7340
  /* Get the transactions directory. */
6772
 
  txn_dir = svn_path_join(fs->path, PATH_TXNS_DIR, pool);
 
7341
  txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
6773
7342
 
6774
7343
  /* Now find a listing of this directory. */
6775
 
  SVN_ERR(svn_io_get_dirents2(&dirents, txn_dir, pool));
 
7344
  SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool));
6776
7345
 
6777
7346
  /* Loop through all the entries and return anything that ends with '.txn'. */
6778
7347
  for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
6779
7348
    {
6780
 
      const void *key;
6781
 
      const char *name, *id;
6782
 
      apr_ssize_t klen;
6783
 
 
6784
 
      apr_hash_this(hi, &key, &klen, NULL);
6785
 
      name = key;
 
7349
      const char *name = svn__apr_hash_index_key(hi);
 
7350
      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
 
7351
      const char *id;
6786
7352
 
6787
7353
      /* The name must end with ".txn" to be considered a transaction. */
6788
7354
      if ((apr_size_t) klen <= ext_len
6814
7380
 
6815
7381
  /* Did we find it? */
6816
7382
  if (kind != svn_node_dir)
6817
 
    return svn_error_create(SVN_ERR_FS_NO_SUCH_TRANSACTION, NULL,
6818
 
                            _("No such transaction"));
 
7383
    return svn_error_createf(SVN_ERR_FS_NO_SUCH_TRANSACTION, NULL,
 
7384
                             _("No such transaction '%s'"),
 
7385
                             name);
6819
7386
 
6820
7387
  txn = apr_pcalloc(pool, sizeof(*txn));
6821
7388
 
6856
7423
 
6857
7424
  /* Delete any mutable property representation. */
6858
7425
  if (noderev->prop_rep && noderev->prop_rep->txn_id)
6859
 
    SVN_ERR(svn_io_remove_file(path_txn_node_props(fs, id, pool), pool));
 
7426
    SVN_ERR(svn_io_remove_file2(path_txn_node_props(fs, id, pool), FALSE,
 
7427
                                pool));
6860
7428
 
6861
7429
  /* Delete any mutable data representation. */
6862
7430
  if (noderev->data_rep && noderev->data_rep->txn_id
6863
7431
      && noderev->kind == svn_node_dir)
6864
 
    SVN_ERR(svn_io_remove_file(path_txn_node_children(fs, id, pool), pool));
6865
 
 
6866
 
  return svn_io_remove_file(path_txn_node_rev(fs, id, pool), pool);
 
7432
    {
 
7433
      fs_fs_data_t *ffd = fs->fsap_data;
 
7434
      SVN_ERR(svn_io_remove_file2(path_txn_node_children(fs, id, pool), FALSE,
 
7435
                                  pool));
 
7436
 
 
7437
      /* remove the corresponding entry from the cache, if such exists */
 
7438
      if (ffd->txn_dir_cache)
 
7439
        {
 
7440
          const char *key = svn_fs_fs__id_unparse(id, pool)->data;
 
7441
          SVN_ERR(svn_cache__set(ffd->txn_dir_cache, key, NULL, pool));
 
7442
        }
 
7443
    }
 
7444
 
 
7445
  return svn_io_remove_file2(path_txn_node_rev(fs, id, pool), FALSE, pool);
6867
7446
}
6868
7447
 
6869
7448
 
6893
7472
  svn_fs_t *fs;
6894
7473
  svn_revnum_t rev;
6895
7474
  const char *name;
 
7475
  const svn_string_t *const *old_value_p;
6896
7476
  const svn_string_t *value;
6897
7477
};
6898
7478
 
6908
7488
 
6909
7489
  SVN_ERR(svn_fs_fs__revision_proplist(&table, cb->fs, cb->rev, pool));
6910
7490
 
 
7491
  if (cb->old_value_p)
 
7492
    {
 
7493
      const svn_string_t *wanted_value = *cb->old_value_p;
 
7494
      const svn_string_t *present_value = apr_hash_get(table, cb->name,
 
7495
                                                       APR_HASH_KEY_STRING);
 
7496
      if ((!wanted_value != !present_value)
 
7497
          || (wanted_value && present_value
 
7498
              && !svn_string_compare(wanted_value, present_value)))
 
7499
        {
 
7500
          /* What we expected isn't what we found. */
 
7501
          return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
 
7502
                                   _("revprop '%s' has unexpected value in "
 
7503
                                     "filesystem"),
 
7504
                                   cb->name);
 
7505
        }
 
7506
      /* Fall through. */
 
7507
    }
6911
7508
  apr_hash_set(table, cb->name, APR_HASH_KEY_STRING, cb->value);
6912
7509
 
6913
 
  return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool);
 
7510
  return set_revision_proplist(cb->fs, cb->rev, table, pool);
6914
7511
}
6915
7512
 
6916
7513
svn_error_t *
6917
7514
svn_fs_fs__change_rev_prop(svn_fs_t *fs,
6918
7515
                           svn_revnum_t rev,
6919
7516
                           const char *name,
 
7517
                           const svn_string_t *const *old_value_p,
6920
7518
                           const svn_string_t *value,
6921
7519
                           apr_pool_t *pool)
6922
7520
{
6927
7525
  cb.fs = fs;
6928
7526
  cb.rev = rev;
6929
7527
  cb.name = name;
 
7528
  cb.old_value_p = old_value_p;
6930
7529
  cb.value = value;
6931
7530
 
6932
7531
  return svn_fs_fs__with_write_lock(fs, change_rev_prop_body, &cb, pool);
7018
7617
 
7019
7618
 
7020
7619
/****** Packing FSFS shards *********/
 
7620
 
 
7621
/* Write a file FILENAME in directory FS_PATH, containing a single line
 
7622
 * with the number REVNUM in ASCII decimal.  Move the file into place
 
7623
 * atomically, overwriting any existing file.
 
7624
 *
 
7625
 * Similar to write_current(). */
 
7626
static svn_error_t *
 
7627
write_revnum_file(const char *fs_path,
 
7628
                  const char *filename,
 
7629
                  svn_revnum_t revnum,
 
7630
                  apr_pool_t *scratch_pool)
 
7631
{
 
7632
  const char *final_path, *tmp_path;
 
7633
  svn_stream_t *tmp_stream;
 
7634
 
 
7635
  final_path = svn_dirent_join(fs_path, filename, scratch_pool);
 
7636
  SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_path, fs_path,
 
7637
                                   svn_io_file_del_none,
 
7638
                                   scratch_pool, scratch_pool));
 
7639
  SVN_ERR(svn_stream_printf(tmp_stream, scratch_pool, "%ld\n", revnum));
 
7640
  SVN_ERR(svn_stream_close(tmp_stream));
 
7641
  SVN_ERR(move_into_place(tmp_path, final_path, final_path, scratch_pool));
 
7642
  return SVN_NO_ERROR;
 
7643
}
 
7644
 
7021
7645
/* Pack a single shard SHARD in REVS_DIR, using POOL for allocations.
7022
7646
   CANCEL_FUNC and CANCEL_BATON are what you think they are.
7023
7647
 
7034
7658
           void *cancel_baton,
7035
7659
           apr_pool_t *pool)
7036
7660
{
7037
 
  const char *tmp_path, *final_path;
7038
7661
  const char *pack_file_path, *manifest_file_path, *shard_path;
7039
7662
  const char *pack_file_dir;
7040
7663
  svn_stream_t *pack_stream, *manifest_stream;
7041
7664
  svn_revnum_t start_rev, end_rev, rev;
7042
 
  svn_stream_t *tmp_stream;
7043
7665
  apr_off_t next_offset;
7044
7666
  apr_pool_t *iterpool;
7045
7667
 
7046
7668
  /* Some useful paths. */
7047
 
  pack_file_dir = svn_path_join(revs_dir,
 
7669
  pack_file_dir = svn_dirent_join(revs_dir,
7048
7670
                        apr_psprintf(pool, "%" APR_INT64_T_FMT ".pack", shard),
7049
7671
                        pool);
7050
 
  pack_file_path = svn_path_join(pack_file_dir, "pack", pool);
7051
 
  manifest_file_path = svn_path_join(pack_file_dir, "manifest", pool);
7052
 
  shard_path = svn_path_join(revs_dir,
7053
 
                             apr_psprintf(pool, "%" APR_INT64_T_FMT, shard),
7054
 
                             pool);
 
7672
  pack_file_path = svn_dirent_join(pack_file_dir, "pack", pool);
 
7673
  manifest_file_path = svn_dirent_join(pack_file_dir, "manifest", pool);
 
7674
  shard_path = svn_dirent_join(revs_dir,
 
7675
                               apr_psprintf(pool, "%" APR_INT64_T_FMT, shard),
 
7676
                               pool);
7055
7677
 
7056
7678
  /* Notify caller we're starting to pack this shard. */
7057
7679
  if (notify_func)
7084
7706
      svn_pool_clear(iterpool);
7085
7707
 
7086
7708
      /* Get the size of the file. */
7087
 
      path = svn_path_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
7088
 
                           iterpool);
 
7709
      path = svn_dirent_join(shard_path, apr_psprintf(iterpool, "%ld", rev),
 
7710
                             iterpool);
7089
7711
      SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, iterpool));
7090
7712
 
7091
7713
      /* Update the manifest. */
7092
 
      svn_stream_printf(manifest_stream, iterpool, "%" APR_OFF_T_FMT "\n",
7093
 
                        next_offset);
 
7714
      SVN_ERR(svn_stream_printf(manifest_stream, iterpool, "%" APR_OFF_T_FMT
 
7715
                                "\n", next_offset));
7094
7716
      next_offset += finfo.size;
7095
7717
 
7096
7718
      /* Copy all the bits from the rev file to the end of the pack file. */
7102
7724
 
7103
7725
  SVN_ERR(svn_stream_close(manifest_stream));
7104
7726
  SVN_ERR(svn_stream_close(pack_stream));
7105
 
  SVN_ERR(svn_fs_fs__dup_perms(pack_file_dir, shard_path, pool));
 
7727
  SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool));
7106
7728
  SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool));
7107
7729
  SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, pool));
7108
7730
 
7109
7731
  /* Update the min-unpacked-rev file to reflect our newly packed shard.
7110
 
   * (ffd->min_unpacked_rev will be updated by open_pack_or_rev_file().)
7111
 
   */
7112
 
  final_path = svn_path_join(fs_path, PATH_MIN_UNPACKED_REV, iterpool);
7113
 
  SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_path, fs_path,
7114
 
                                   svn_io_file_del_none, iterpool, iterpool));
7115
 
  SVN_ERR(svn_stream_printf(tmp_stream, iterpool, "%ld\n",
7116
 
                            (svn_revnum_t) ((shard + 1) * max_files_per_dir)));
7117
 
  SVN_ERR(svn_stream_close(tmp_stream));
7118
 
  SVN_ERR(move_into_place(tmp_path, final_path, final_path, iterpool));
 
7732
   * (This doesn't update ffd->min_unpacked_rev.  That will be updated by
 
7733
   * update_min_unpacked_rev() when necessary.) */
 
7734
  SVN_ERR(write_revnum_file(fs_path, PATH_MIN_UNPACKED_REV,
 
7735
                            (svn_revnum_t)((shard + 1) * max_files_per_dir),
 
7736
                            iterpool));
7119
7737
  svn_pool_destroy(iterpool);
7120
7738
 
7121
7739
  /* Finally, remove the existing shard directory. */
7139
7757
  void *cancel_baton;
7140
7758
};
7141
7759
 
 
7760
 
 
7761
/* The work-horse for svn_fs_fs__pack, called with the FS write lock.
 
7762
   This implements the svn_fs_fs__with_write_lock() 'body' callback
 
7763
   type.  BATON is a 'struct pack_baton *'.
 
7764
 
 
7765
   WARNING: if you add a call to this function, please note:
 
7766
     The code currently assumes that any piece of code running with
 
7767
     the write-lock set can rely on the ffd->min_unpacked_rev and
 
7768
     ffd->min_unpacked_revprop caches to be up-to-date (and, by
 
7769
     extension, on not having to use a retry when calling
 
7770
     svn_fs_fs__path_rev_absolute() and friends).  If you add a call
 
7771
     to this function, consider whether you have to call
 
7772
     update_min_unpacked_rev() and update_min_unpacked_revprop()
 
7773
     afterwards.
 
7774
     See this thread: http://thread.gmane.org/1291206765.3782.3309.camel@edith
 
7775
 */
7142
7776
static svn_error_t *
7143
7777
pack_body(void *baton,
7144
7778
          apr_pool_t *pool)
7145
7779
{
7146
7780
  struct pack_baton *pb = baton;
7147
7781
  int format, max_files_per_dir;
7148
 
  int completed_shards;
 
7782
  apr_int64_t completed_shards;
7149
7783
  apr_int64_t i;
7150
7784
  svn_revnum_t youngest;
7151
7785
  apr_pool_t *iterpool;
7152
7786
  const char *data_path;
7153
7787
  svn_revnum_t min_unpacked_rev;
7154
7788
 
7155
 
  SVN_ERR(read_format(&format, &max_files_per_dir,
7156
 
                      svn_path_join(pb->fs->path, PATH_FORMAT, pool),
 
7789
  SVN_ERR(read_format(&format, &max_files_per_dir, path_format(pb->fs, pool),
7157
7790
                      pool));
 
7791
  SVN_ERR(check_format(format));
7158
7792
 
7159
7793
  /* If the repository isn't a new enough format, we don't support packing.
7160
7794
     Return a friendly error to that effect. */
7161
7795
  if (format < SVN_FS_FS__MIN_PACKED_FORMAT)
7162
 
    return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
7163
 
      _("FS format too old to pack, please upgrade."));
 
7796
    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
 
7797
      _("FSFS format (%d) too old to pack; please upgrade the filesystem."),
 
7798
      format);
7164
7799
 
7165
7800
  /* If we aren't using sharding, we can't do any packing, so quit. */
7166
7801
  if (!max_files_per_dir)
7167
7802
    return SVN_NO_ERROR;
7168
7803
 
7169
7804
  SVN_ERR(read_min_unpacked_rev(&min_unpacked_rev,
7170
 
                                svn_path_join(pb->fs->path,
7171
 
                                              PATH_MIN_UNPACKED_REV, pool),
 
7805
                                path_min_unpacked_rev(pb->fs, pool),
7172
7806
                                pool));
7173
7807
 
7174
7808
  SVN_ERR(get_youngest(&youngest, pb->fs->path, pool));
7178
7812
  if (min_unpacked_rev == (completed_shards * max_files_per_dir))
7179
7813
    return SVN_NO_ERROR;
7180
7814
 
7181
 
  data_path = svn_path_join(pb->fs->path, PATH_REVS_DIR, pool);
 
7815
  data_path = svn_dirent_join(pb->fs->path, PATH_REVS_DIR, pool);
7182
7816
 
7183
7817
  iterpool = svn_pool_create(pool);
7184
7818
  for (i = min_unpacked_rev / max_files_per_dir; i < completed_shards; i++)
7191
7825
      SVN_ERR(pack_shard(data_path, pb->fs->path, i, max_files_per_dir,
7192
7826
                         pb->notify_func, pb->notify_baton,
7193
7827
                         pb->cancel_func, pb->cancel_baton, iterpool));
7194
 
      /* We can't pack revprops, because they aren't immutable :(
7195
 
         If we ever do get clever and figure out how to pack revprops,
7196
 
         this is the place to do it. */
7197
7828
    }
7198
7829
 
7199
7830
  svn_pool_destroy(iterpool);