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

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_fs/tree.c

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
49
49
#include "svn_mergeinfo.h"
50
50
#include "svn_fs.h"
51
51
#include "svn_props.h"
 
52
#include "svn_sorts.h"
52
53
 
53
54
#include "fs.h"
54
 
#include "key-gen.h"
 
55
#include "cached_data.h"
55
56
#include "dag.h"
56
57
#include "lock.h"
57
58
#include "tree.h"
58
59
#include "fs_fs.h"
59
60
#include "id.h"
 
61
#include "pack.h"
60
62
#include "temp_serializer.h"
 
63
#include "transaction.h"
 
64
#include "util.h"
61
65
 
62
66
#include "private/svn_mergeinfo_private.h"
63
67
#include "private/svn_subr_private.h"
66
70
#include "../libsvn_fs/fs-loader.h"
67
71
 
68
72
 
69
 
/* ### I believe this constant will become internal to reps-strings.c.
70
 
   ### see the comment in window_consumer() for more information. */
71
 
 
72
 
/* ### the comment also seems to need tweaking: the log file stuff
73
 
   ### is no longer an issue... */
74
 
/* Data written to the filesystem through the svn_fs_apply_textdelta()
75
 
   interface is cached in memory until the end of the data stream, or
76
 
   until a size trigger is hit.  Define that trigger here (in bytes).
77
 
   Setting the value to 0 will result in no filesystem buffering at
78
 
   all.  The value only really matters when dealing with file contents
79
 
   bigger than the value itself.  Above that point, large values here
80
 
   allow the filesystem to buffer more data in memory before flushing
81
 
   to the database, which increases memory usage but greatly decreases
82
 
   the amount of disk access (and log-file generation) in database.
83
 
   Smaller values will limit your overall memory consumption, but can
84
 
   drastically hurt throughput by necessitating more write operations
85
 
   to the database (which also generates more log-files).  */
86
 
#define WRITE_BUFFER_SIZE          512000
87
 
 
88
 
 
89
73
 
90
74
/* The root structures.
91
75
 
100
84
   kept in the FS object and shared among multiple revision root
101
85
   objects.
102
86
*/
103
 
typedef struct fs_rev_root_data_t
104
 
{
105
 
  /* A dag node for the revision's root directory. */
106
 
  dag_node_t *root_dir;
107
 
 
108
 
  /* Cache structure for mapping const char * PATH to const char
109
 
     *COPYFROM_STRING, so that paths_changed can remember all the
110
 
     copyfrom information in the changes file.
111
 
     COPYFROM_STRING has the format "REV PATH", or is the empty string if
112
 
     the path was added without history. */
113
 
  apr_hash_t *copyfrom_cache;
114
 
 
115
 
} fs_rev_root_data_t;
 
87
typedef dag_node_t fs_rev_root_data_t;
116
88
 
117
89
typedef struct fs_txn_root_data_t
118
90
{
119
 
  const char *txn_id;
 
91
  /* TXN_ID value from the main struct but as a struct instead of a string */
 
92
  svn_fs_fs__id_part_t txn_id;
120
93
 
121
94
  /* Cache of txn DAG nodes (without their nested noderevs, because
122
95
   * it's mutable). Same keys/values as ffd->rev_node_cache. */
134
107
                                         apr_pool_t *pool);
135
108
 
136
109
static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
137
 
                                  svn_fs_t *fs, const char *txn,
138
 
                                  svn_revnum_t base_rev, apr_uint32_t flags,
 
110
                                  svn_fs_t *fs,
 
111
                                  const svn_fs_fs__id_part_t *txn,
 
112
                                  svn_revnum_t base_rev,
 
113
                                  apr_uint32_t flags,
139
114
                                  apr_pool_t *pool);
140
115
 
 
116
static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
 
117
                                    const char **path_p,
 
118
                                    svn_fs_root_t *root,
 
119
                                    const char *path,
 
120
                                    apr_pool_t *pool);
 
121
 
141
122
 
142
123
/*** Node Caching ***/
143
124
 
196
177
  /* Property lookups etc. have a very high locality (75% re-hit).
197
178
     Thus, remember the last hit location for optimistic lookup. */
198
179
  apr_size_t last_hit;
 
180
 
 
181
  /* Position of the last bucket hit that actually had a DAG node in it.
 
182
     LAST_HIT may refer to a bucket that matches path@rev but has not
 
183
     its NODE element set, yet.
 
184
     This value is a mere hint for optimistic lookup and any value is
 
185
     valid (as long as it is < BUCKET_COUNT). */
 
186
  apr_size_t last_non_empty;
199
187
};
200
188
 
201
189
fs_fs_dag_cache_t*
245
233
      && (result->path_len == path_len)
246
234
      && !memcmp(result->path, path, path_len))
247
235
    {
 
236
      /* Remember the position of the last node we found in this cache. */
 
237
      if (result->node)
 
238
        cache->last_non_empty = cache->last_hit;
 
239
 
248
240
      return result;
249
241
    }
250
242
 
251
243
  /* need to do a full lookup.  Calculate the hash value
252
 
     (HASH_VALUE has been initialized to REVISION). */
 
244
     (HASH_VALUE has been initialized to REVISION).
 
245
 
 
246
     Note that the actual hash function is arbitrary as long as its result
 
247
     in HASH_VALUE only depends on REVISION and *PATH.  However, we try to
 
248
     make as much of *PATH influence the result as possible to get an "even"
 
249
     spread across the hash buckets (maximizes our cache retention rate and
 
250
     thus the hit rates).
 
251
 
 
252
     When chunked access is possible (independent of the PATH pointer's
 
253
     value!), we read 4 bytes at once and multiply the hash value with a
 
254
     FACTOR that mirror / pattern / shift all 4 input bytes to various bits
 
255
     of the result.  The final result will be taken from the MSBs.
 
256
 
 
257
     When chunked access is not possible (not supported by CPU or odd bytes
 
258
     at the end of *PATH), we use the simple traditional "* 33" hash
 
259
     function that works very well with texts / paths and that e.g. APR uses.
 
260
 
 
261
     Please note that the bytewise and the chunked calculation are *NOT*
 
262
     interchangeable as they will yield different results for the same input.
 
263
     For any given machine and *PATH, we must use a fixed combination of the
 
264
     two functions.
 
265
   */
253
266
  i = 0;
254
267
#if SVN_UNALIGNED_ACCESS_IS_OK
255
268
  /* We relax the dependency chain between iterations by processing
295
308
 
296
309
      cache->insertions++;
297
310
    }
 
311
  else if (result->node)
 
312
    {
 
313
      /* This bucket is valid & has a suitable DAG node in it.
 
314
         Remember its location. */
 
315
      cache->last_non_empty = bucket_index;
 
316
    }
298
317
 
299
318
  return result;
300
319
}
301
320
 
 
321
/* Optimistic lookup using the last seen non-empty location in CACHE.
 
322
   Return the node of that entry, if it is still in use and matches PATH.
 
323
   Return NULL otherwise.  Since the caller usually already knows the path
 
324
   length, provide it in PATH_LEN. */
 
325
static dag_node_t *
 
326
cache_lookup_last_path(fs_fs_dag_cache_t *cache,
 
327
                       const char *path,
 
328
                       apr_size_t path_len)
 
329
{
 
330
  cache_entry_t *result = &cache->buckets[cache->last_non_empty];
 
331
  assert(strlen(path) == path_len);
 
332
 
 
333
  if (   result->node
 
334
      && (result->path_len == path_len)
 
335
      && !memcmp(result->path, path, path_len))
 
336
    {
 
337
      return result->node;
 
338
    }
 
339
 
 
340
  return NULL;
 
341
}
 
342
 
302
343
/* 2nd level cache */
303
344
 
304
345
/* Find and return the DAG node cache for ROOT and the key that
305
 
   should be used for PATH. */
 
346
   should be used for PATH.
 
347
 
 
348
   Pool will only be used for allocating a new keys if necessary */
306
349
static void
307
350
locate_cache(svn_cache__t **cache,
308
351
             const char **key,
313
356
  if (root->is_txn_root)
314
357
    {
315
358
      fs_txn_root_data_t *frd = root->fsap_data;
316
 
      if (cache) *cache = frd->txn_node_cache;
317
 
      if (key && path) *key = path;
 
359
 
 
360
      if (cache)
 
361
        *cache = frd->txn_node_cache;
 
362
      if (key && path)
 
363
        *key = path;
318
364
    }
319
365
  else
320
366
    {
321
367
      fs_fs_data_t *ffd = root->fs->fsap_data;
322
 
      if (cache) *cache = ffd->rev_node_cache;
323
 
      if (key && path) *key
324
 
        = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
 
368
 
 
369
      if (cache)
 
370
        *cache = ffd->rev_node_cache;
 
371
      if (key && path)
 
372
        *key = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
325
373
    }
326
374
}
327
375
 
328
 
/* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
329
 
   isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
 
376
/* In *NODE_P, return the DAG node for PATH from ROOT's node cache, or NULL
 
377
   if the node isn't cached.  *NODE_P is allocated in POOL. */
330
378
static svn_error_t *
331
379
dag_node_cache_get(dag_node_t **node_p,
332
380
                   svn_fs_root_t *root,
356
404
          if (found && node)
357
405
            {
358
406
              /* Patch up the FS, since this might have come from an old FS
359
 
              * object. */
 
407
               * object. */
360
408
              svn_fs_fs__dag_set_fs(node, root->fs);
361
409
 
362
410
              /* Retain the DAG node in L1 cache. */
380
428
      if (found && node)
381
429
        {
382
430
          /* Patch up the FS, since this might have come from an old FS
383
 
          * object. */
 
431
           * object. */
384
432
          svn_fs_fs__dag_set_fs(node, root->fs);
385
433
        }
386
434
    }
403
451
 
404
452
  SVN_ERR_ASSERT(*path == '/');
405
453
 
406
 
  /* Do *not* attempt to dup and put the node into L1.
407
 
   * dup() is twice as expensive as an L2 lookup (which will set also L1).
408
 
   */
409
454
  locate_cache(&cache, &key, root, path, pool);
410
 
 
411
455
  return svn_cache__set(cache, key, node, pool);
412
456
}
413
457
 
414
458
 
415
 
/* Baton for find_descendents_in_cache. */
 
459
/* Baton for find_descendants_in_cache. */
416
460
struct fdic_baton {
417
461
  const char *path;
418
462
  apr_array_header_t *list;
419
463
  apr_pool_t *pool;
420
464
};
421
465
 
422
 
/* If the given item is a descendent of BATON->PATH, push
 
466
/* If the given item is a descendant of BATON->PATH, push
423
467
 * it onto BATON->LIST (copying into BATON->POOL).  Implements
424
468
 * the svn_iter_apr_hash_cb_t prototype. */
425
469
static svn_error_t *
426
 
find_descendents_in_cache(void *baton,
 
470
find_descendants_in_cache(void *baton,
427
471
                          const void *key,
428
472
                          apr_ssize_t klen,
429
473
                          void *val,
458
502
  locate_cache(&cache, NULL, root, NULL, b.pool);
459
503
 
460
504
 
461
 
  SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
 
505
  SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
462
506
                          &b, b.pool));
463
507
 
464
508
  iterpool = svn_pool_create(b.pool);
465
509
 
466
510
  for (i = 0; i < b.list->nelts; i++)
467
511
    {
468
 
      const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
 
512
      const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
469
513
      svn_pool_clear(iterpool);
470
 
      SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
 
514
      SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
471
515
    }
472
516
 
473
517
  svn_pool_destroy(iterpool);
498
542
        flags |= SVN_FS_TXN_CHECK_LOCKS;
499
543
    }
500
544
 
501
 
  return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
 
545
  return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn),
 
546
                       txn->base_rev, flags, pool);
502
547
}
503
548
 
504
549
 
523
568
 
524
569
/* Getting dag nodes for roots.  */
525
570
 
 
571
/* Return the transaction ID to a given transaction ROOT. */
 
572
static const svn_fs_fs__id_part_t *
 
573
root_txn_id(svn_fs_root_t *root)
 
574
{
 
575
  fs_txn_root_data_t *frd = root->fsap_data;
 
576
  assert(root->is_txn_root);
 
577
 
 
578
  return &frd->txn_id;
 
579
}
526
580
 
527
581
/* Set *NODE_P to a freshly opened dag node referring to the root
528
582
   directory of ROOT, allocating from POOL.  */
534
588
  if (root->is_txn_root)
535
589
    {
536
590
      /* It's a transaction root.  Open a fresh copy.  */
537
 
      return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
 
591
      return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
 
592
                                     pool);
538
593
    }
539
594
  else
540
595
    {
541
596
      /* It's a revision root, so we already have its root directory
542
597
         opened.  */
543
 
      fs_rev_root_data_t *frd = root->fsap_data;
544
 
      *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
 
598
      dag_node_t *root_dir = root->fsap_data;
 
599
      *node_p = svn_fs_fs__dag_dup(root_dir, pool);
545
600
      return SVN_NO_ERROR;
546
601
    }
547
602
}
557
612
                  apr_pool_t *pool)
558
613
{
559
614
  if (root->is_txn_root)
560
 
    return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
 
615
    {
 
616
      /* It's a transaction root.  Open a fresh copy.  */
 
617
      return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
 
618
                                       pool);
 
619
    }
561
620
  else
562
621
    /* If it's not a transaction root, we can't change its contents.  */
563
622
    return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
645
704
   the inheritance method is copy_id_inherit_new, also return a
646
705
   *COPY_SRC_PATH on which to base the new copy ID (else return NULL
647
706
   for that path).  CHILD must have a parent (it cannot be the root
648
 
   node).  TXN_ID is the transaction in which these items might be
649
 
   mutable.  Allocations are taken from POOL. */
 
707
   node).  Allocations are taken from POOL. */
650
708
static svn_error_t *
651
709
get_copy_inheritance(copy_id_inherit_t *inherit_p,
652
710
                     const char **copy_src_path,
653
711
                     svn_fs_t *fs,
654
712
                     parent_path_t *child,
655
 
                     const char *txn_id,
656
713
                     apr_pool_t *pool)
657
714
{
658
715
  const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
659
 
  const char *child_copy_id, *parent_copy_id;
 
716
  const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
660
717
  const char *id_path = NULL;
661
718
  svn_fs_root_t *copyroot_root;
662
719
  dag_node_t *copyroot_node;
663
720
  svn_revnum_t copyroot_rev;
664
721
  const char *copyroot_path;
665
722
 
666
 
  SVN_ERR_ASSERT(child && child->parent && txn_id);
 
723
  SVN_ERR_ASSERT(child && child->parent);
667
724
 
668
725
  /* Initialize some convenience variables. */
669
726
  child_id = svn_fs_fs__dag_get_id(child->node);
672
729
  parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
673
730
 
674
731
  /* If this child is already mutable, we have nothing to do. */
675
 
  if (svn_fs_fs__id_txn_id(child_id))
 
732
  if (svn_fs_fs__id_is_txn(child_id))
676
733
    {
677
734
      *inherit_p = copy_id_inherit_self;
678
735
      *copy_src_path = NULL;
686
743
 
687
744
  /* Special case: if the child's copy ID is '0', use the parent's
688
745
     copy ID. */
689
 
  if (strcmp(child_copy_id, "0") == 0)
 
746
  if (svn_fs_fs__id_part_is_root(child_copy_id))
690
747
    return SVN_NO_ERROR;
691
748
 
692
749
  /* Compare the copy IDs of the child and its parent.  If they are
693
750
     the same, then the child is already on the same branch as the
694
751
     parent, and should use the same mutability copy ID that the
695
752
     parent will use. */
696
 
  if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
 
753
  if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
697
754
    return SVN_NO_ERROR;
698
755
 
699
756
  /* If the child is on the same branch that the parent is on, the
709
766
  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
710
767
  copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
711
768
 
712
 
  if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
 
769
  if (svn_fs_fs__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated)
713
770
    return SVN_NO_ERROR;
714
771
 
715
772
  /* Determine if we are looking at the child via its original path or
763
820
 
764
821
  /* The caller does not care about the parent node chain but only
765
822
     the final DAG node. */
766
 
  open_path_node_only = 4
 
823
  open_path_node_only = 4,
 
824
 
 
825
  /* The caller wants a NULL path object instead of an error if the
 
826
     path cannot be found. */
 
827
  open_path_allow_null = 8
767
828
} open_path_flags_t;
768
829
 
 
830
/* Try a short-cut for the open_path() function using the last node accessed.
 
831
 * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is
 
832
 * its "created path", return the node in *NODE_P.  Set it to NULL otherwise.
 
833
 *
 
834
 * This function is used to support ra_serf-style access patterns where we
 
835
 * are first asked for path@rev and then for path@c_rev of the same node.
 
836
 * The shortcut works by ignoring the "rev" part of the cache key and then
 
837
 * checking whether we got lucky.  Lookup and verification are both quick
 
838
 * plus there are many early outs for common types of mismatch.
 
839
 */
 
840
static svn_error_t *
 
841
try_match_last_node(dag_node_t **node_p,
 
842
                    svn_fs_root_t *root,
 
843
                    const char *path,
 
844
                    apr_size_t path_len,
 
845
                    apr_pool_t *scratch_pool)
 
846
{
 
847
  fs_fs_data_t *ffd = root->fs->fsap_data;
 
848
 
 
849
  /* Optimistic lookup: if the last node returned from the cache applied to
 
850
     the same PATH, return it in NODE. */
 
851
  dag_node_t *node
 
852
    = cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
 
853
 
 
854
  /* Did we get a bucket with a committed node? */
 
855
  if (node && !svn_fs_fs__dag_check_mutable(node))
 
856
    {
 
857
      /* Get the path&rev pair at which this node was created.
 
858
         This is repository location for which this node is _known_ to be
 
859
         the right lookup result irrespective of how we found it. */
 
860
      const char *created_path
 
861
        = svn_fs_fs__dag_get_created_path(node);
 
862
      svn_revnum_t revision;
 
863
      SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
 
864
 
 
865
      /* Is it an exact match? */
 
866
      if (revision == root->rev && strcmp(created_path, path) == 0)
 
867
        {
 
868
          /* Cache it under its full path@rev access path. */
 
869
          SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
 
870
 
 
871
          *node_p = node;
 
872
          return SVN_NO_ERROR;
 
873
        }
 
874
    }
 
875
 
 
876
  *node_p = NULL;
 
877
  return SVN_NO_ERROR;
 
878
}
 
879
 
769
880
 
770
881
/* Open the node identified by PATH in ROOT, allocating in POOL.  Set
771
882
   *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
773
884
   *element, for the root directory.  PATH must be in canonical form.
774
885
 
775
886
   If resulting *PARENT_PATH_P will eventually be made mutable and
776
 
   modified, or if copy ID inheritance information is otherwise
777
 
   needed, TXN_ID should be the ID of the mutability transaction.  If
778
 
   TXN_ID is NULL, no copy ID inheritance information will be
779
 
   calculated for the *PARENT_PATH_P chain.
 
887
   modified, or if copy ID inheritance information is otherwise needed,
 
888
   IS_TXN_PATH must be set.  If IS_TXN_PATH is FALSE, no copy ID
 
889
   inheritance information will be calculated for the *PARENT_PATH_P chain.
780
890
 
781
891
   If FLAGS & open_path_last_optional is zero, return the error
782
892
   SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
800
910
          svn_fs_root_t *root,
801
911
          const char *path,
802
912
          int flags,
803
 
          const char *txn_id,
 
913
          svn_boolean_t is_txn_path,
804
914
          apr_pool_t *pool)
805
915
{
806
916
  svn_fs_t *fs = root->fs;
807
917
  dag_node_t *here = NULL; /* The directory we're currently looking at.  */
808
918
  parent_path_t *parent_path; /* The path from HERE up to the root. */
809
 
  const char *rest; /* The portion of PATH we haven't traversed yet.  */
810
 
 
811
 
  /* ensure a canonical path representation */
812
 
  const char *path_so_far = "/";
 
919
  const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */
813
920
  apr_pool_t *iterpool = svn_pool_create(pool);
814
921
 
815
 
  /* callers often traverse the tree in some path-based order.  That means
816
 
     a sibling of PATH has been presently accessed.  Try to start the lookup
817
 
     directly at the parent node, if the caller did not requested the full
818
 
     parent chain. */
819
 
  const char *directory;
 
922
  /* path to the currently processed entry without trailing '/'.
 
923
     We will reuse this across iterations by simply putting a NUL terminator
 
924
     at the respective position and replacing that with a '/' in the next
 
925
     iteration.  This is correct as we assert() PATH to be canonical. */
 
926
  svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
 
927
  apr_size_t path_len = path_so_far->len;
 
928
 
 
929
  /* Callers often traverse the DAG in some path-based order or along the
 
930
     history segments.  That allows us to try a few guesses about where to
 
931
     find the next item.  This is only useful if the caller didn't request
 
932
     the full parent chain. */
820
933
  assert(svn_fs__is_canonical_abspath(path));
 
934
  path_so_far->len = 0; /* "" */
821
935
  if (flags & open_path_node_only)
822
936
    {
 
937
      const char *directory;
 
938
 
 
939
      /* First attempt: Assume that we access the DAG for the same path as
 
940
         in the last lookup but for a different revision that happens to be
 
941
         the last revision that touched the respective node.  This is a
 
942
         common pattern when e.g. checking out over ra_serf.  Note that this
 
943
         will only work for committed data as the revision info for nodes in
 
944
         txns is bogus.
 
945
 
 
946
         This shortcut is quick and will exit this function upon success.
 
947
         So, try it first. */
 
948
      if (!root->is_txn_root)
 
949
        {
 
950
          dag_node_t *node;
 
951
          SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
 
952
 
 
953
          /* Did the shortcut work? */
 
954
          if (node)
 
955
            {
 
956
              /* Construct and return the result. */
 
957
              svn_pool_destroy(iterpool);
 
958
 
 
959
              parent_path = make_parent_path(node, 0, 0, pool);
 
960
              parent_path->copy_inherit = copy_id_inherit_self;
 
961
              *parent_path_p = parent_path;
 
962
 
 
963
              return SVN_NO_ERROR;
 
964
            }
 
965
        }
 
966
 
 
967
      /* Second attempt: Try starting the lookup immediately at the parent
 
968
         node.  We will often have recently accessed either a sibling or
 
969
         said parent DIRECTORY itself for the same revision. */
823
970
      directory = svn_dirent_dirname(path, pool);
824
971
      if (directory[1] != 0) /* root nodes are covered anyway */
825
 
        SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
 
972
        {
 
973
          SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
 
974
 
 
975
          /* Did the shortcut work? */
 
976
          if (here)
 
977
            {
 
978
              apr_size_t dirname_len = strlen(directory);
 
979
              path_so_far->len = dirname_len;
 
980
              rest = path + dirname_len + 1;
 
981
            }
 
982
        }
826
983
    }
827
984
 
828
985
  /* did the shortcut work? */
829
 
  if (here)
830
 
    {
831
 
      path_so_far = directory;
832
 
      rest = path + strlen(directory) + 1;
833
 
    }
834
 
  else
 
986
  if (!here)
835
987
    {
836
988
      /* Make a parent_path item for the root node, using its own current
837
989
         copy id.  */
839
991
      rest = path + 1; /* skip the leading '/', it saves in iteration */
840
992
    }
841
993
 
 
994
  path_so_far->data[path_so_far->len] = '\0';
842
995
  parent_path = make_parent_path(here, 0, 0, pool);
843
996
  parent_path->copy_inherit = copy_id_inherit_self;
844
997
 
858
1011
      /* Parse out the next entry from the path.  */
859
1012
      entry = svn_fs__next_entry_name(&next, rest, pool);
860
1013
 
861
 
      /* Calculate the path traversed thus far. */
862
 
      path_so_far = svn_fspath__join(path_so_far, entry, pool);
 
1014
      /* Update the path traversed thus far. */
 
1015
      path_so_far->data[path_so_far->len] = '/';
 
1016
      path_so_far->len += strlen(entry) + 1;
 
1017
      path_so_far->data[path_so_far->len] = '\0';
863
1018
 
864
1019
      if (*entry == '\0')
865
1020
        {
873
1028
        {
874
1029
          copy_id_inherit_t inherit;
875
1030
          const char *copy_path = NULL;
876
 
          svn_error_t *err = SVN_NO_ERROR;
877
1031
          dag_node_t *cached_node = NULL;
878
1032
 
879
1033
          /* If we found a directory entry, follow it.  First, we
882
1036
             element if we already know the lookup to fail for the
883
1037
             complete path. */
884
1038
          if (next || !(flags & open_path_uncached))
885
 
            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
886
 
 
 
1039
            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
 
1040
                                       pool));
887
1041
          if (cached_node)
888
1042
            child = cached_node;
889
1043
          else
890
 
            err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
 
1044
            SVN_ERR(svn_fs_fs__dag_open(&child, here, entry, pool, iterpool));
891
1045
 
892
1046
          /* "file not found" requires special handling.  */
893
 
          if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
 
1047
          if (child == NULL)
894
1048
            {
895
1049
              /* If this was the last path component, and the caller
896
1050
                 said it was optional, then don't return an error;
897
1051
                 just put a NULL node pointer in the path.  */
898
1052
 
899
 
              svn_error_clear(err);
900
 
 
901
1053
              if ((flags & open_path_last_optional)
902
1054
                  && (! next || *next == '\0'))
903
1055
                {
905
1057
                                                 pool);
906
1058
                  break;
907
1059
                }
 
1060
              else if (flags & open_path_allow_null)
 
1061
                {
 
1062
                  parent_path = NULL;
 
1063
                  break;
 
1064
                }
908
1065
              else
909
1066
                {
910
1067
                  /* Build a better error message than svn_fs_fs__dag_open
913
1070
                }
914
1071
            }
915
1072
 
916
 
          /* Other errors we return normally.  */
917
 
          SVN_ERR(err);
918
 
 
919
1073
          if (flags & open_path_node_only)
920
1074
            {
921
 
              /* Shortcut: the caller only wan'ts the final DAG node. */
 
1075
              /* Shortcut: the caller only wants the final DAG node. */
922
1076
              parent_path->node = child;
923
1077
            }
924
1078
          else
925
1079
            {
926
1080
              /* Now, make a parent_path item for CHILD. */
927
1081
              parent_path = make_parent_path(child, entry, parent_path, pool);
928
 
              if (txn_id)
 
1082
              if (is_txn_path)
929
1083
                {
930
1084
                  SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
931
 
                                               parent_path, txn_id, iterpool));
 
1085
                                               parent_path, iterpool));
932
1086
                  parent_path->copy_inherit = inherit;
933
1087
                  parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
934
1088
                }
936
1090
 
937
1091
          /* Cache the node we found (if it wasn't already cached). */
938
1092
          if (! cached_node)
939
 
            SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
 
1093
            SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
 
1094
                                       iterpool));
940
1095
        }
941
1096
 
942
1097
      /* Are we finished traversing the path?  */
945
1100
 
946
1101
      /* The path isn't finished yet; we'd better be in a directory.  */
947
1102
      if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
948
 
        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
 
1103
        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data),
949
1104
                  apr_psprintf(iterpool, _("Failure opening '%s'"), path));
950
1105
 
951
1106
      rest = next;
970
1125
                  apr_pool_t *pool)
971
1126
{
972
1127
  dag_node_t *clone;
973
 
  const char *txn_id = root->txn;
 
1128
  const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
974
1129
 
975
1130
  /* Is the node mutable already?  */
976
1131
  if (svn_fs_fs__dag_check_mutable(parent_path->node))
980
1135
  if (parent_path->parent)
981
1136
    {
982
1137
      const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
983
 
      const char *copy_id = NULL;
 
1138
      svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 };
 
1139
      svn_fs_fs__id_part_t *copy_id_ptr = &copy_id;
984
1140
      copy_id_inherit_t inherit = parent_path->copy_inherit;
985
1141
      const char *clone_path, *copyroot_path;
986
1142
      svn_revnum_t copyroot_rev;
997
1153
        {
998
1154
        case copy_id_inherit_parent:
999
1155
          parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1000
 
          copy_id = svn_fs_fs__id_copy_id(parent_id);
 
1156
          copy_id = *svn_fs_fs__id_copy_id(parent_id);
1001
1157
          break;
1002
1158
 
1003
1159
        case copy_id_inherit_new:
1006
1162
          break;
1007
1163
 
1008
1164
        case copy_id_inherit_self:
1009
 
          copy_id = NULL;
 
1165
          copy_id_ptr = NULL;
1010
1166
          break;
1011
1167
 
1012
1168
        case copy_id_inherit_unknown:
1024
1180
 
1025
1181
      child_id = svn_fs_fs__dag_get_id(parent_path->node);
1026
1182
      copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1027
 
      if (strcmp(svn_fs_fs__id_node_id(child_id),
1028
 
                 svn_fs_fs__id_node_id(copyroot_id)) != 0)
 
1183
      if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
 
1184
                                 svn_fs_fs__id_node_id(copyroot_id)))
1029
1185
        is_parent_copyroot = TRUE;
1030
1186
 
1031
1187
      /* Now make this node mutable.  */
1034
1190
                                         parent_path->parent->node,
1035
1191
                                         clone_path,
1036
1192
                                         parent_path->entry,
1037
 
                                         copy_id, txn_id,
 
1193
                                         copy_id_ptr, txn_id,
1038
1194
                                         is_parent_copyroot,
1039
1195
                                         pool));
1040
1196
 
1074
1230
 
1075
1231
  if (! node)
1076
1232
    {
1077
 
      /* Canonicalize the input PATH. */
1078
 
      if (! svn_fs__is_canonical_abspath(path))
1079
 
        {
1080
 
          path = svn_fs__canonicalize_abspath(path, pool);
1081
 
 
1082
 
          /* Try again with the corrected path. */
1083
 
          SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1084
 
        }
 
1233
      /* Canonicalize the input PATH.  As it turns out, >95% of all paths
 
1234
       * seen here during e.g. svnadmin verify are non-canonical, i.e.
 
1235
       * miss the leading '/'.  Unconditional canonicalization has a net
 
1236
       * performance benefit over previously checking path for being
 
1237
       * canonical. */
 
1238
      path = svn_fs__canonicalize_abspath(path, pool);
 
1239
      SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1085
1240
 
1086
1241
      if (! node)
1087
1242
        {
1089
1244
           * error if the node for which we are searching doesn't exist. */
1090
1245
          SVN_ERR(open_path(&parent_path, root, path,
1091
1246
                            open_path_uncached | open_path_node_only,
1092
 
                            NULL, pool));
 
1247
                            FALSE, pool));
1093
1248
          node = parent_path->node;
1094
1249
 
1095
1250
          /* No need to cache our find -- open_path() will do that for us. */
1107
1262
/* Add a change to the changes table in FS, keyed on transaction id
1108
1263
   TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1109
1264
   PATH (whose node revision id is--or was, in the case of a
1110
 
   deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
1111
 
   occurred.  If the change resulted from a copy, COPYFROM_REV and
1112
 
   COPYFROM_PATH specify under which revision and path the node was
1113
 
   copied from.  If this was not part of a copy, COPYFROM_REV should
1114
 
   be SVN_INVALID_REVNUM.  Do all this as part of POOL.  */
 
1265
   deletion--NODEREV_ID), and optionally that TEXT_MODs, PROP_MODs or
 
1266
   MERGEINFO_MODs occurred.  If the change resulted from a copy,
 
1267
   COPYFROM_REV and COPYFROM_PATH specify under which revision and path
 
1268
   the node was copied from.  If this was not part of a copy, COPYFROM_REV
 
1269
   should be SVN_INVALID_REVNUM.  Do all this as part of POOL.  */
1115
1270
static svn_error_t *
1116
1271
add_change(svn_fs_t *fs,
1117
 
           const char *txn_id,
 
1272
           const svn_fs_fs__id_part_t *txn_id,
1118
1273
           const char *path,
1119
1274
           const svn_fs_id_t *noderev_id,
1120
1275
           svn_fs_path_change_kind_t change_kind,
1121
1276
           svn_boolean_t text_mod,
1122
1277
           svn_boolean_t prop_mod,
 
1278
           svn_boolean_t mergeinfo_mod,
1123
1279
           svn_node_kind_t node_kind,
1124
1280
           svn_revnum_t copyfrom_rev,
1125
1281
           const char *copyfrom_path,
1127
1283
{
1128
1284
  return svn_fs_fs__add_change(fs, txn_id,
1129
1285
                               svn_fs__canonicalize_abspath(path, pool),
1130
 
                               noderev_id, change_kind, text_mod, prop_mod,
 
1286
                               noderev_id, change_kind,
 
1287
                               text_mod, prop_mod, mergeinfo_mod,
1131
1288
                               node_kind, copyfrom_rev, copyfrom_path,
1132
1289
                               pool);
1133
1290
}
1151
1308
         The root directory ("" or "/") node is stored in the
1152
1309
         svn_fs_root_t object, and never changes when it's a revision
1153
1310
         root, so we can just reach in and grab it directly. */
1154
 
      fs_rev_root_data_t *frd = root->fsap_data;
1155
 
      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
 
1311
      dag_node_t *root_dir = root->fsap_data;
 
1312
      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
1156
1313
    }
1157
1314
  else
1158
1315
    {
1164
1321
  return SVN_NO_ERROR;
1165
1322
}
1166
1323
 
 
1324
static svn_error_t *
 
1325
fs_node_relation(svn_fs_node_relation_t *relation,
 
1326
                 svn_fs_root_t *root_a, const char *path_a,
 
1327
                 svn_fs_root_t *root_b, const char *path_b,
 
1328
                 apr_pool_t *pool)
 
1329
{
 
1330
  dag_node_t *node;
 
1331
  const svn_fs_id_t *id_a, *id_b;
 
1332
  svn_fs_fs__id_part_t node_id_a, node_id_b;
 
1333
 
 
1334
  /* Root paths are a common special case. */
 
1335
  svn_boolean_t a_is_root_dir
 
1336
    = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
 
1337
  svn_boolean_t b_is_root_dir
 
1338
    = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
 
1339
 
 
1340
  /* Another useful thing to know: Both are txns but not the same txn. */
 
1341
  svn_boolean_t different_txn
 
1342
    = root_a->is_txn_root && root_b->is_txn_root
 
1343
        && strcmp(root_a->txn, root_b->txn);
 
1344
 
 
1345
  /* Path from different repository are always unrelated. */
 
1346
  if (root_a->fs != root_b->fs)
 
1347
    {
 
1348
      *relation = svn_fs_node_unrelated;
 
1349
      return SVN_NO_ERROR;
 
1350
    }
 
1351
 
 
1352
  /* Are both (!) root paths? Then, they are related and we only test how
 
1353
   * direct the relation is. */
 
1354
  if (a_is_root_dir && b_is_root_dir)
 
1355
    {
 
1356
      /* For txn roots, root->REV is the base revision of that TXN. */
 
1357
      *relation = (   (root_a->rev == root_b->rev)
 
1358
                   && (root_a->is_txn_root == root_b->is_txn_root)
 
1359
                   && !different_txn)
 
1360
                ? svn_fs_node_unchanged
 
1361
                : svn_fs_node_common_ancestor;
 
1362
      return SVN_NO_ERROR;
 
1363
    }
 
1364
 
 
1365
  /* We checked for all separations between ID spaces (repos, txn).
 
1366
   * Now, we can simply test for the ID values themselves. */
 
1367
  SVN_ERR(get_dag(&node, root_a, path_a, pool));
 
1368
  id_a = svn_fs_fs__dag_get_id(node);
 
1369
  node_id_a = *svn_fs_fs__id_node_id(id_a);
 
1370
 
 
1371
  SVN_ERR(get_dag(&node, root_b, path_b, pool));
 
1372
  id_b = svn_fs_fs__dag_get_id(node);
 
1373
  node_id_b = *svn_fs_fs__id_node_id(id_b);
 
1374
 
 
1375
  /* Noderevs from different nodes are unrelated. */
 
1376
  if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
 
1377
    {
 
1378
      *relation = svn_fs_node_unrelated;
 
1379
      return SVN_NO_ERROR;
 
1380
    }
 
1381
 
 
1382
  /* Noderevs have the same node-ID now. So, they *seem* to be related.
 
1383
   *
 
1384
   * Special case: Different txns may create the same (txn-local) node ID.
 
1385
   * Only when they are committed can they actually be related to others. */
 
1386
  if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
 
1387
    {
 
1388
      *relation = svn_fs_node_unrelated;
 
1389
      return SVN_NO_ERROR;
 
1390
    }
 
1391
 
 
1392
  /* The noderevs are actually related.  Are they the same? */
 
1393
  if (svn_fs_fs__id_eq(id_a, id_b))
 
1394
    *relation = svn_fs_node_unchanged;
 
1395
  else
 
1396
    *relation = svn_fs_node_common_ancestor;
 
1397
 
 
1398
  return SVN_NO_ERROR;
 
1399
}
1167
1400
 
1168
1401
svn_error_t *
1169
1402
svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1282
1515
  return SVN_NO_ERROR;
1283
1516
}
1284
1517
 
 
1518
static svn_error_t *
 
1519
fs_node_has_props(svn_boolean_t *has_props,
 
1520
                  svn_fs_root_t *root,
 
1521
                  const char *path,
 
1522
                  apr_pool_t *scratch_pool)
 
1523
{
 
1524
  dag_node_t *node;
 
1525
 
 
1526
  SVN_ERR(get_dag(&node, root, path, scratch_pool));
 
1527
 
 
1528
  return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node,
 
1529
                                                  scratch_pool));
 
1530
}
1285
1531
 
1286
1532
static svn_error_t *
1287
1533
increment_mergeinfo_up_tree(parent_path_t *pp,
1310
1556
{
1311
1557
  parent_path_t *parent_path;
1312
1558
  apr_hash_t *proplist;
1313
 
  const char *txn_id;
 
1559
  const svn_fs_fs__id_part_t *txn_id;
 
1560
  svn_boolean_t mergeinfo_mod = FALSE;
1314
1561
 
1315
1562
  if (! root->is_txn_root)
1316
1563
    return SVN_FS__NOT_TXN(root);
1317
 
  txn_id = root->txn;
 
1564
  txn_id = root_txn_id(root);
1318
1565
 
1319
1566
  path = svn_fs__canonicalize_abspath(path, pool);
1320
 
  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
 
1567
  SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
1321
1568
 
1322
1569
  /* Check (non-recursively) to see if path is locked; if so, check
1323
1570
     that we can use it. */
1354
1601
          SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1355
1602
                                                   (value != NULL), pool));
1356
1603
        }
 
1604
 
 
1605
      mergeinfo_mod = TRUE;
1357
1606
    }
1358
1607
 
1359
1608
  /* Set the property. */
1366
1615
  /* Make a record of this modification in the changes table. */
1367
1616
  return add_change(root->fs, txn_id, path,
1368
1617
                    svn_fs_fs__dag_get_id(parent_path->node),
1369
 
                    svn_fs_path_change_modify, FALSE, TRUE,
 
1618
                    svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
1370
1619
                    svn_fs_fs__dag_node_kind(parent_path->node),
1371
1620
                    SVN_INVALID_REVNUM, NULL, pool);
1372
1621
}
1382
1631
                 const char *path1,
1383
1632
                 svn_fs_root_t *root2,
1384
1633
                 const char *path2,
 
1634
                 svn_boolean_t strict,
1385
1635
                 apr_pool_t *pool)
1386
1636
{
1387
1637
  dag_node_t *node1, *node2;
1395
1645
  SVN_ERR(get_dag(&node1, root1, path1, pool));
1396
1646
  SVN_ERR(get_dag(&node2, root2, path2, pool));
1397
1647
  return svn_fs_fs__dag_things_different(changed_p, NULL,
1398
 
                                         node1, node2);
 
1648
                                         node1, node2, strict, pool);
1399
1649
}
1400
1650
 
1401
1651
 
1423
1673
                           _("Conflict at '%s'"), path);
1424
1674
}
1425
1675
 
 
1676
/* Compare the directory representations at nodes LHS and RHS and set
 
1677
 * *CHANGED to TRUE, if at least one entry has been added or removed them.
 
1678
 * Use POOL for temporary allocations.
 
1679
 */
 
1680
static svn_error_t *
 
1681
compare_dir_structure(svn_boolean_t *changed,
 
1682
                      dag_node_t *lhs,
 
1683
                      dag_node_t *rhs,
 
1684
                      apr_pool_t *pool)
 
1685
{
 
1686
  apr_array_header_t *lhs_entries;
 
1687
  apr_array_header_t *rhs_entries;
 
1688
  int i;
 
1689
 
 
1690
  SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool));
 
1691
  SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool));
 
1692
 
 
1693
  /* different number of entries -> some addition / removal */
 
1694
  if (lhs_entries->nelts != rhs_entries->nelts)
 
1695
    {
 
1696
      *changed = TRUE;
 
1697
      return SVN_NO_ERROR;
 
1698
    }
 
1699
 
 
1700
  /* Since directories are sorted by name, we can simply compare their
 
1701
     entries one-by-one without binary lookup etc. */
 
1702
  for (i = 0; i < lhs_entries->nelts; ++i)
 
1703
    {
 
1704
      svn_fs_dirent_t *lhs_entry
 
1705
        = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
 
1706
      svn_fs_dirent_t *rhs_entry
 
1707
        = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
 
1708
 
 
1709
      if (strcmp(lhs_entry->name, rhs_entry->name)
 
1710
          || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id),
 
1711
                                    svn_fs_fs__id_node_id(rhs_entry->id))
 
1712
          || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id),
 
1713
                                    svn_fs_fs__id_copy_id(rhs_entry->id)))
 
1714
        {
 
1715
          *changed = TRUE;
 
1716
          return SVN_NO_ERROR;
 
1717
        }
 
1718
    }
 
1719
 
 
1720
  *changed = FALSE;
 
1721
  return SVN_NO_ERROR;
 
1722
}
1426
1723
 
1427
1724
/* Merge changes between ANCESTOR and SOURCE into TARGET.  ANCESTOR
1428
1725
 * and TARGET must be distinct node revisions.  TARGET_PATH should
1452
1749
      dag_node_t *target,
1453
1750
      dag_node_t *source,
1454
1751
      dag_node_t *ancestor,
1455
 
      const char *txn_id,
 
1752
      const svn_fs_fs__id_part_t *txn_id,
1456
1753
      apr_int64_t *mergeinfo_increment_out,
1457
1754
      apr_pool_t *pool)
1458
1755
{
1459
1756
  const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1460
 
  apr_hash_t *s_entries, *t_entries, *a_entries;
1461
 
  apr_hash_index_t *hi;
 
1757
  apr_array_header_t *s_entries, *t_entries, *a_entries;
 
1758
  int i, s_idx = -1, t_idx = -1;
1462
1759
  svn_fs_t *fs;
1463
1760
  apr_pool_t *iterpool;
1464
1761
  apr_int64_t mergeinfo_increment = 0;
1585
1882
  */
1586
1883
  {
1587
1884
    node_revision_t *tgt_nr, *anc_nr, *src_nr;
 
1885
    svn_boolean_t same;
 
1886
    apr_pool_t *scratch_pool;
1588
1887
 
1589
1888
    /* Get node revisions for our id's. */
1590
 
    SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
1591
 
    SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
1592
 
    SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
 
1889
    scratch_pool = svn_pool_create(pool);
 
1890
    SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool,
 
1891
                                         scratch_pool));
 
1892
    svn_pool_clear(scratch_pool);
 
1893
    SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool,
 
1894
                                         scratch_pool));
 
1895
    svn_pool_clear(scratch_pool);
 
1896
    SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool,
 
1897
                                         scratch_pool));
 
1898
    svn_pool_destroy(scratch_pool);
1593
1899
 
1594
1900
    /* Now compare the prop-keys of the skels.  Note that just because
1595
1901
       the keys are different -doesn't- mean the proplists have
1596
 
       different contents.  But merge() isn't concerned with contents;
1597
 
       it doesn't do a brute-force comparison on textual contents, so
1598
 
       it won't do that here either.  Checking to see if the propkey
1599
 
       atoms are `equal' is enough. */
1600
 
    if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
1601
 
      return conflict_err(conflict_p, target_path);
1602
 
    if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
1603
 
      return conflict_err(conflict_p, target_path);
 
1902
       different contents. */
 
1903
    SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, TRUE, pool));
 
1904
    if (! same)
 
1905
      return conflict_err(conflict_p, target_path);
 
1906
 
 
1907
    /* The directory entries got changed in the repository but the directory
 
1908
       properties did not. */
 
1909
    SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, TRUE, pool));
 
1910
    if (! same)
 
1911
      {
 
1912
        /* There is an incoming prop change for this directory.
 
1913
           We will accept it only if the directory changes were mere updates
 
1914
           to its entries, i.e. there were no additions or removals.
 
1915
           Those could cause update problems to the working copy. */
 
1916
        svn_boolean_t changed;
 
1917
        SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool));
 
1918
 
 
1919
        if (changed)
 
1920
          return conflict_err(conflict_p, target_path);
 
1921
      }
1604
1922
  }
1605
1923
 
1606
1924
  /* ### todo: it would be more efficient to simply check for a NULL
1614
1932
 
1615
1933
  /* for each entry E in a_entries... */
1616
1934
  iterpool = svn_pool_create(pool);
1617
 
  for (hi = apr_hash_first(pool, a_entries);
1618
 
       hi;
1619
 
       hi = apr_hash_next(hi))
 
1935
  for (i = 0; i < a_entries->nelts; ++i)
1620
1936
    {
1621
1937
      svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1622
 
      const char *name;
1623
 
      apr_ssize_t klen;
1624
 
 
1625
1938
      svn_pool_clear(iterpool);
1626
1939
 
1627
 
      name = svn__apr_hash_index_key(hi);
1628
 
      klen = svn__apr_hash_index_klen(hi);
1629
 
      a_entry = svn__apr_hash_index_val(hi);
1630
 
 
1631
 
      s_entry = apr_hash_get(s_entries, name, klen);
1632
 
      t_entry = apr_hash_get(t_entries, name, klen);
 
1940
      a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
 
1941
      s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
 
1942
      t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
1633
1943
 
1634
1944
      /* No changes were made to this entry while the transaction was
1635
1945
         in progress, so do nothing to the target. */
1636
1946
      if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1637
 
        goto end;
 
1947
        continue;
1638
1948
 
1639
1949
      /* A change was made to this entry while the transaction was in
1640
1950
         process, but the transaction did not touch this entry. */
1665
1975
                  mergeinfo_increment += mergeinfo_end;
1666
1976
                }
1667
1977
 
1668
 
              SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
 
1978
              SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
1669
1979
                                               s_entry->id,
1670
1980
                                               s_entry->kind,
1671
1981
                                               txn_id,
1672
 
                                               iterpool));
 
1982
                                               pool));
1673
1983
            }
1674
1984
          else
1675
1985
            {
1676
 
              SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
 
1986
              SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
 
1987
                                            iterpool));
1677
1988
            }
1678
1989
        }
1679
1990
 
1706
2017
 
1707
2018
          /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1708
2019
             modification of ANCESTOR-ENTRY, declare a conflict. */
1709
 
          if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
1710
 
                     svn_fs_fs__id_node_id(a_entry->id)) != 0
1711
 
              || strcmp(svn_fs_fs__id_copy_id(s_entry->id),
1712
 
                        svn_fs_fs__id_copy_id(a_entry->id)) != 0
1713
 
              || strcmp(svn_fs_fs__id_node_id(t_entry->id),
1714
 
                        svn_fs_fs__id_node_id(a_entry->id)) != 0
1715
 
              || strcmp(svn_fs_fs__id_copy_id(t_entry->id),
1716
 
                        svn_fs_fs__id_copy_id(a_entry->id)) != 0)
 
2020
          if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
 
2021
                                     svn_fs_fs__id_node_id(a_entry->id))
 
2022
              || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
 
2023
                                        svn_fs_fs__id_copy_id(a_entry->id))
 
2024
              || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
 
2025
                                        svn_fs_fs__id_node_id(a_entry->id))
 
2026
              || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
 
2027
                                        svn_fs_fs__id_copy_id(a_entry->id)))
1717
2028
            return conflict_err(conflict_p,
1718
2029
                                svn_fspath__join(target_path,
1719
2030
                                                 a_entry->name,
1737
2048
          if (fs_supports_mergeinfo)
1738
2049
            mergeinfo_increment += sub_mergeinfo_increment;
1739
2050
        }
1740
 
 
1741
 
      /* We've taken care of any possible implications E could have.
1742
 
         Remove it from source_entries, so it's easy later to loop
1743
 
         over all the source entries that didn't exist in
1744
 
         ancestor_entries. */
1745
 
    end:
1746
 
      apr_hash_set(s_entries, name, klen, NULL);
1747
2051
    }
1748
2052
 
1749
2053
  /* For each entry E in source but not in ancestor */
1750
 
  for (hi = apr_hash_first(pool, s_entries);
1751
 
       hi;
1752
 
       hi = apr_hash_next(hi))
 
2054
  for (i = 0; i < s_entries->nelts; ++i)
1753
2055
    {
1754
 
      svn_fs_dirent_t *s_entry, *t_entry;
1755
 
      const char *name = svn__apr_hash_index_key(hi);
1756
 
      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
 
2056
      svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
1757
2057
      dag_node_t *s_ent_node;
1758
2058
 
1759
2059
      svn_pool_clear(iterpool);
1760
2060
 
1761
 
      s_entry = svn__apr_hash_index_val(hi);
1762
 
      t_entry = apr_hash_get(t_entries, name, klen);
 
2061
      s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
 
2062
      a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
 
2063
      t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
 
2064
 
 
2065
      /* Process only entries in source that are NOT in ancestor. */
 
2066
      if (a_entry)
 
2067
        continue;
1763
2068
 
1764
2069
      /* If NAME exists in TARGET, declare a conflict. */
1765
2070
      if (t_entry)
1816
2121
{
1817
2122
  dag_node_t *txn_root_node;
1818
2123
  svn_fs_t *fs = txn->fs;
1819
 
  const char *txn_id = txn->id;
 
2124
  const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn);
1820
2125
 
1821
2126
  SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
1822
2127
 
1891
2196
  svn_error_t *err = SVN_NO_ERROR;
1892
2197
  svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
1893
2198
  svn_fs_t *fs = txn->fs;
 
2199
  fs_fs_data_t *ffd = fs->fsap_data;
1894
2200
 
1895
2201
  /* Limit memory usage when the repository has a high commit rate and
1896
2202
     needs to run the following while loop multiple times.  The memory
1973
2279
  svn_fs_fs__reset_txn_caches(fs);
1974
2280
 
1975
2281
  svn_pool_destroy(iterpool);
1976
 
  return svn_error_trace(err);
 
2282
 
 
2283
  SVN_ERR(err);
 
2284
 
 
2285
  if (ffd->pack_after_commit)
 
2286
    {
 
2287
      SVN_ERR(svn_fs_fs__pack(fs, NULL, NULL, NULL, NULL, pool));
 
2288
    }
 
2289
 
 
2290
  return SVN_NO_ERROR;
1977
2291
}
1978
2292
 
1979
2293
 
2068
2382
               apr_pool_t *pool)
2069
2383
{
2070
2384
  dag_node_t *node;
 
2385
  apr_hash_t *hash = svn_hash__make(pool);
 
2386
  apr_array_header_t *table;
 
2387
  int i;
2071
2388
 
2072
2389
  /* Get the entries for this path in the caller's pool. */
2073
2390
  SVN_ERR(get_dag(&node, root, path, pool));
2074
 
  return svn_fs_fs__dag_dir_entries(table_p, node, pool);
 
2391
  SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
 
2392
 
 
2393
  /* Convert directory array to hash. */
 
2394
  for (i = 0; i < table->nelts; ++i)
 
2395
    {
 
2396
      svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
 
2397
      svn_hash_sets(hash, entry->name, entry);
 
2398
    }
 
2399
 
 
2400
  *table_p = hash;
 
2401
  return SVN_NO_ERROR;
 
2402
}
 
2403
 
 
2404
static svn_error_t *
 
2405
fs_dir_optimal_order(apr_array_header_t **ordered_p,
 
2406
                     svn_fs_root_t *root,
 
2407
                     apr_hash_t *entries,
 
2408
                     apr_pool_t *result_pool,
 
2409
                     apr_pool_t *scratch_pool)
 
2410
{
 
2411
  *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
 
2412
                                            scratch_pool);
 
2413
 
 
2414
  return SVN_NO_ERROR;
2075
2415
}
2076
2416
 
2077
2417
/* Raise an error if PATH contains a newline because FSFS cannot handle
2100
2440
{
2101
2441
  parent_path_t *parent_path;
2102
2442
  dag_node_t *sub_dir;
2103
 
  const char *txn_id = root->txn;
 
2443
  const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2104
2444
 
2105
2445
  SVN_ERR(check_newline(path, pool));
2106
2446
 
2107
2447
  path = svn_fs__canonicalize_abspath(path, pool);
2108
2448
  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2109
 
                    txn_id, pool));
 
2449
                    TRUE, pool));
2110
2450
 
2111
2451
  /* Check (recursively) to see if some lock is 'reserving' a path at
2112
2452
     that location, or even some child-path; if so, check that we can
2136
2476
 
2137
2477
  /* Make a record of this modification in the changes table. */
2138
2478
  return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2139
 
                    svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
2140
 
                    SVN_INVALID_REVNUM, NULL, pool);
 
2479
                    svn_fs_path_change_add, FALSE, FALSE, FALSE,
 
2480
                    svn_node_dir, SVN_INVALID_REVNUM, NULL, pool);
2141
2481
}
2142
2482
 
2143
2483
 
2149
2489
               apr_pool_t *pool)
2150
2490
{
2151
2491
  parent_path_t *parent_path;
2152
 
  const char *txn_id = root->txn;
 
2492
  const svn_fs_fs__id_part_t *txn_id;
2153
2493
  apr_int64_t mergeinfo_count = 0;
2154
2494
  svn_node_kind_t kind;
2155
2495
 
2156
2496
  if (! root->is_txn_root)
2157
2497
    return SVN_FS__NOT_TXN(root);
2158
2498
 
 
2499
  txn_id = root_txn_id(root);
2159
2500
  path = svn_fs__canonicalize_abspath(path, pool);
2160
 
  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
 
2501
  SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
2161
2502
  kind = svn_fs_fs__dag_node_kind(parent_path->node);
2162
2503
 
2163
2504
  /* We can't remove the root of the filesystem.  */
2193
2534
  /* Make a record of this modification in the changes table. */
2194
2535
  return add_change(root->fs, txn_id, path,
2195
2536
                    svn_fs_fs__dag_get_id(parent_path->node),
2196
 
                    svn_fs_path_change_delete, FALSE, FALSE, kind,
 
2537
                    svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
2197
2538
                    SVN_INVALID_REVNUM, NULL, pool);
2198
2539
}
2199
2540
 
2224
2565
{
2225
2566
  dag_node_t *from_node;
2226
2567
  parent_path_t *to_parent_path;
2227
 
  const char *txn_id = to_root->txn;
 
2568
  const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
2228
2569
  svn_boolean_t same_p;
2229
2570
 
2230
2571
  /* Use an error check, not an assert, because even the caller cannot
2236
2577
       _("Cannot copy between two different filesystems ('%s' and '%s')"),
2237
2578
       from_root->fs->path, to_root->fs->path);
2238
2579
 
 
2580
  /* more things that we can't do ATM */
2239
2581
  if (from_root->is_txn_root)
2240
2582
    return svn_error_create
2241
2583
      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2242
2584
       _("Copy from mutable tree not currently supported"));
2243
2585
 
 
2586
  if (! to_root->is_txn_root)
 
2587
    return svn_error_create
 
2588
      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
 
2589
       _("Copy immutable tree not supported"));
 
2590
 
2244
2591
  /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2245
2592
  SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2246
2593
 
2248
2595
     component does not exist, it's not that big a deal.  We'll just
2249
2596
     make one there. */
2250
2597
  SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2251
 
                    open_path_last_optional, txn_id, pool));
 
2598
                    open_path_last_optional, TRUE, pool));
2252
2599
 
2253
2600
  /* Check to see if path (or any child thereof) is locked; if so,
2254
2601
     check that we can use the existing lock(s). */
2307
2654
                                  from_canonpath,
2308
2655
                                  txn_id, pool));
2309
2656
 
2310
 
      if (kind == svn_fs_path_change_replace)
 
2657
      if (kind != svn_fs_path_change_add)
2311
2658
        SVN_ERR(dag_node_cache_invalidate(to_root,
2312
2659
                                          parent_path_path(to_parent_path,
2313
2660
                                                           pool), pool));
2321
2668
      /* Make a record of this modification in the changes table. */
2322
2669
      SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2323
2670
      SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2324
 
                         svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2325
 
                         svn_fs_fs__dag_node_kind(from_node),
 
2671
                         svn_fs_fs__dag_get_id(new_node), kind, FALSE,
 
2672
                         FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
2326
2673
                         from_root->rev, from_canonpath, pool));
2327
2674
    }
2328
2675
  else
2397
2744
               apr_pool_t *pool)
2398
2745
{
2399
2746
  dag_node_t *node;
2400
 
  const char *copyfrom_path, *copyfrom_str = NULL;
2401
 
  svn_revnum_t copyfrom_rev;
2402
 
  char *str, *buf;
2403
 
 
2404
 
  /* Check to see if there is a cached version of this copyfrom
2405
 
     entry. */
2406
 
  if (! root->is_txn_root) {
2407
 
    fs_rev_root_data_t *frd = root->fsap_data;
2408
 
    copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2409
 
  }
2410
 
 
2411
 
  if (copyfrom_str)
2412
 
    {
2413
 
      if (*copyfrom_str == 0)
2414
 
        {
2415
 
          /* We have a cached entry that says there is no copyfrom
2416
 
             here. */
2417
 
          copyfrom_rev = SVN_INVALID_REVNUM;
2418
 
          copyfrom_path = NULL;
2419
 
        }
2420
 
      else
2421
 
        {
2422
 
          /* Parse the copyfrom string for our cached entry. */
2423
 
          buf = apr_pstrdup(pool, copyfrom_str);
2424
 
          str = svn_cstring_tokenize(" ", &buf);
2425
 
          copyfrom_rev = SVN_STR_TO_REV(str);
2426
 
          copyfrom_path = buf;
2427
 
        }
2428
 
    }
2429
 
  else
2430
 
    {
2431
 
      /* There is no cached entry, look it up the old-fashioned
2432
 
         way. */
2433
 
      SVN_ERR(get_dag(&node, root, path, pool));
2434
 
      SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
2435
 
      SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
2436
 
    }
2437
 
 
2438
 
  *rev_p  = copyfrom_rev;
2439
 
  *path_p = copyfrom_path;
 
2747
 
 
2748
  /* There is no cached entry, look it up the old-fashioned
 
2749
      way. */
 
2750
  SVN_ERR(get_dag(&node, root, path, pool));
 
2751
  SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
 
2752
  SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
2440
2753
 
2441
2754
  return SVN_NO_ERROR;
2442
2755
}
2454
2767
{
2455
2768
  parent_path_t *parent_path;
2456
2769
  dag_node_t *child;
2457
 
  const char *txn_id = root->txn;
 
2770
  const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2458
2771
 
2459
2772
  SVN_ERR(check_newline(path, pool));
2460
2773
 
2461
2774
  path = svn_fs__canonicalize_abspath(path, pool);
2462
2775
  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2463
 
                   txn_id, pool));
 
2776
                    TRUE, pool));
2464
2777
 
2465
2778
  /* If there's already a file by that name, complain.
2466
2779
     This also catches the case of trying to make a file named `/'.  */
2489
2802
 
2490
2803
  /* Make a record of this modification in the changes table. */
2491
2804
  return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2492
 
                    svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
2493
 
                    SVN_INVALID_REVNUM, NULL, pool);
 
2805
                    svn_fs_path_change_add, TRUE, FALSE, FALSE,
 
2806
                    svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2494
2807
}
2495
2808
 
2496
2809
 
2597
2910
 
2598
2911
  svn_stream_t *source_stream;
2599
2912
  svn_stream_t *target_stream;
2600
 
  svn_stream_t *string_stream;
2601
 
  svn_stringbuf_t *target_string;
2602
2913
 
2603
2914
  /* MD5 digest for the base text against which a delta is to be
2604
2915
     applied, and for the resultant fulltext, respectively.  Either or
2612
2923
} txdelta_baton_t;
2613
2924
 
2614
2925
 
2615
 
/* ### see comment in window_consumer() regarding this function. */
2616
 
 
2617
 
/* Helper function of generic type `svn_write_fn_t'.  Implements a
2618
 
   writable stream which appends to an svn_stringbuf_t. */
2619
 
static svn_error_t *
2620
 
write_to_string(void *baton, const char *data, apr_size_t *len)
2621
 
{
2622
 
  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2623
 
  svn_stringbuf_appendbytes(tb->target_string, data, *len);
2624
 
  return SVN_NO_ERROR;
2625
 
}
2626
 
 
2627
 
 
2628
 
 
2629
2926
/* The main window handler returned by svn_fs_apply_textdelta. */
2630
2927
static svn_error_t *
2631
2928
window_consumer(svn_txdelta_window_t *window, void *baton)
2637
2934
     cb->target_string. */
2638
2935
  SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2639
2936
 
2640
 
  /* ### the write_to_string() callback for the txdelta's output stream
2641
 
     ### should be doing all the flush determination logic, not here.
2642
 
     ### in a drastic case, a window could generate a LOT more than the
2643
 
     ### maximum buffer size. we want to flush to the underlying target
2644
 
     ### stream much sooner (e.g. also in a streamy fashion). also, by
2645
 
     ### moving this logic inside the stream, the stream becomes nice
2646
 
     ### and encapsulated: it holds all the logic about buffering and
2647
 
     ### flushing.
2648
 
     ###
2649
 
     ### further: I believe the buffering should be removed from tree.c
2650
 
     ### the buffering should go into the target_stream itself, which
2651
 
     ### is defined by reps-string.c. Specifically, I think the
2652
 
     ### rep_write_contents() function will handle the buffering and
2653
 
     ### the spill to the underlying DB. by locating it there, then
2654
 
     ### anybody who gets a writable stream for FS content can take
2655
 
     ### advantage of the buffering capability. this will be important
2656
 
     ### when we export an FS API function for writing a fulltext into
2657
 
     ### the FS, rather than forcing that fulltext thru apply_textdelta.
2658
 
  */
2659
 
 
2660
 
  /* Check to see if we need to purge the portion of the contents that
2661
 
     have been written thus far. */
2662
 
  if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
2663
 
    {
2664
 
      apr_size_t len = tb->target_string->len;
2665
 
      SVN_ERR(svn_stream_write(tb->target_stream,
2666
 
                               tb->target_string->data,
2667
 
                               &len));
2668
 
      svn_stringbuf_setempty(tb->target_string);
2669
 
    }
2670
 
 
2671
 
  /* Is the window NULL?  If so, we're done. */
 
2937
  /* Is the window NULL?  If so, we're done.  The stream has already been
 
2938
     closed by the interpreter. */
2672
2939
  if (! window)
2673
 
    {
2674
 
      /* Close the internal-use stream.  ### This used to be inside of
2675
 
         txn_body_fulltext_finalize_edits(), but that invoked a nested
2676
 
         Berkeley DB transaction -- scandalous! */
2677
 
      SVN_ERR(svn_stream_close(tb->target_stream));
2678
 
 
2679
 
      SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2680
 
                                            tb->pool));
2681
 
    }
 
2940
    SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
 
2941
                                          tb->pool));
2682
2942
 
2683
2943
  return SVN_NO_ERROR;
2684
2944
}
2690
2950
{
2691
2951
  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2692
2952
  parent_path_t *parent_path;
2693
 
  const char *txn_id = tb->root->txn;
 
2953
  const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
2694
2954
 
2695
2955
  /* Call open_path with no flags, as we want this to return an error
2696
2956
     if the node for which we are searching doesn't exist. */
2697
 
  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
 
2957
  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
2698
2958
 
2699
2959
  /* Check (non-recursively) to see if path is locked; if so, check
2700
2960
     that we can use it. */
2730
2990
  SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2731
2991
                                         tb->pool));
2732
2992
 
2733
 
  /* Make a writable "string" stream which writes data to
2734
 
     tb->target_string. */
2735
 
  tb->target_string = svn_stringbuf_create_empty(tb->pool);
2736
 
  tb->string_stream = svn_stream_create(tb, tb->pool);
2737
 
  svn_stream_set_write(tb->string_stream, write_to_string);
2738
 
 
2739
2993
  /* Now, create a custom window handler that uses our two streams. */
2740
2994
  svn_txdelta_apply(tb->source_stream,
2741
 
                    tb->string_stream,
 
2995
                    tb->target_stream,
2742
2996
                    NULL,
2743
2997
                    tb->path,
2744
2998
                    tb->pool,
2748
3002
  /* Make a record of this modification in the changes table. */
2749
3003
  return add_change(tb->root->fs, txn_id, tb->path,
2750
3004
                    svn_fs_fs__dag_get_id(tb->node),
2751
 
                    svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2752
 
                    SVN_INVALID_REVNUM, NULL, pool);
 
3005
                    svn_fs_path_change_modify, TRUE, FALSE, FALSE,
 
3006
                    svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2753
3007
}
2754
3008
 
2755
3009
 
2855
3109
{
2856
3110
  struct text_baton_t *tb = baton;
2857
3111
  parent_path_t *parent_path;
2858
 
  const char *txn_id = tb->root->txn;
 
3112
  const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
2859
3113
 
2860
3114
  /* Call open_path with no flags, as we want this to return an error
2861
3115
     if the node for which we are searching doesn't exist. */
2862
 
  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
 
3116
  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
2863
3117
 
2864
3118
  /* Check (non-recursively) to see if path is locked; if so, check
2865
3119
     that we can use it. */
2883
3137
  /* Make a record of this modification in the changes table. */
2884
3138
  return add_change(tb->root->fs, txn_id, tb->path,
2885
3139
                    svn_fs_fs__dag_get_id(tb->node),
2886
 
                    svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2887
 
                    SVN_INVALID_REVNUM, NULL, pool);
 
3140
                    svn_fs_path_change_modify, TRUE, FALSE, FALSE,
 
3141
                    svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2888
3142
}
2889
3143
 
2890
3144
 
2923
3177
                    const char *path1,
2924
3178
                    svn_fs_root_t *root2,
2925
3179
                    const char *path2,
 
3180
                    svn_boolean_t strict,
2926
3181
                    apr_pool_t *pool)
2927
3182
{
2928
3183
  dag_node_t *node1, *node2;
2951
3206
  SVN_ERR(get_dag(&node1, root1, path1, pool));
2952
3207
  SVN_ERR(get_dag(&node2, root2, path2, pool));
2953
3208
  return svn_fs_fs__dag_things_different(NULL, changed_p,
2954
 
                                         node1, node2);
 
3209
                                         node1, node2, strict, pool);
2955
3210
}
2956
3211
 
2957
3212
 
2993
3248
                 apr_pool_t *pool)
2994
3249
{
2995
3250
  if (root->is_txn_root)
2996
 
    return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
2997
 
                                        pool);
 
3251
    return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
 
3252
                                        root_txn_id(root), pool);
2998
3253
  else
2999
 
    {
3000
 
      fs_rev_root_data_t *frd = root->fsap_data;
3001
 
      return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3002
 
                                      frd->copyfrom_cache, pool);
3003
 
    }
 
3254
    return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
 
3255
                                    pool);
3004
3256
}
3005
3257
 
3006
3258
 
3040
3292
fs_node_history(svn_fs_history_t **history_p,
3041
3293
                svn_fs_root_t *root,
3042
3294
                const char *path,
3043
 
                apr_pool_t *pool)
 
3295
                apr_pool_t *result_pool,
 
3296
                apr_pool_t *scratch_pool)
3044
3297
{
3045
3298
  svn_node_kind_t kind;
3046
3299
 
3049
3302
    return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3050
3303
 
3051
3304
  /* And we require that the path exist in the root. */
3052
 
  SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
 
3305
  SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_pool));
3053
3306
  if (kind == svn_node_none)
3054
3307
    return SVN_FS__NOT_FOUND(root, path);
3055
3308
 
3056
3309
  /* Okay, all seems well.  Build our history object and return it. */
3057
 
  *history_p = assemble_history(root->fs,
3058
 
                                svn_fs__canonicalize_abspath(path, pool),
3059
 
                                root->rev, FALSE, NULL,
3060
 
                                SVN_INVALID_REVNUM, pool);
 
3310
  *history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
 
3311
                                SVN_INVALID_REVNUM, result_pool);
3061
3312
  return SVN_NO_ERROR;
3062
3313
}
3063
3314
 
3115
3366
  const char *copy_dst_path;
3116
3367
  svn_fs_root_t *copy_dst_root;
3117
3368
  dag_node_t *copy_dst_node;
3118
 
  svn_node_kind_t kind;
3119
3369
 
3120
3370
  /* Initialize return values. */
3121
3371
  *root_p = NULL;
3122
3372
  *path_p = NULL;
3123
3373
 
3124
3374
  path = svn_fs__canonicalize_abspath(path, pool);
3125
 
  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
 
3375
  SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
3126
3376
 
3127
3377
  /* Find the youngest copyroot in the path of this node-rev, which
3128
3378
     will indicate the target of the innermost copy affecting the
3136
3386
     revision between COPY_DST_REV and REV.  Make sure that PATH
3137
3387
     exists as of COPY_DST_REV and is related to this node-rev. */
3138
3388
  SVN_ERR(svn_fs_fs__revision_root(&copy_dst_root, fs, copy_dst_rev, pool));
3139
 
  SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
3140
 
  if (kind == svn_node_none)
3141
 
    return SVN_NO_ERROR;
3142
3389
  SVN_ERR(open_path(&copy_dst_parent_path, copy_dst_root, path,
3143
 
                    open_path_node_only, NULL, pool));
 
3390
                    open_path_node_only | open_path_allow_null, FALSE, pool));
 
3391
  if (copy_dst_parent_path == NULL)
 
3392
    return SVN_NO_ERROR;
 
3393
 
3144
3394
  copy_dst_node = copy_dst_parent_path->node;
3145
3395
  if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3146
3396
                                    svn_fs_fs__dag_get_id(parent_path->node)))
3236
3486
{
3237
3487
  svn_fs_t *fs = root->fs;
3238
3488
  const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3239
 
  const char *node_id, *dash;
 
3489
  const svn_fs_fs__id_part_t *node_id;
3240
3490
 
3241
3491
  path = svn_fs__canonicalize_abspath(path, pool);
3242
3492
 
3244
3494
  SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3245
3495
  node_id = svn_fs_fs__id_node_id(given_noderev_id);
3246
3496
 
3247
 
  /* Is it a brand new uncommitted node? */
3248
 
  if (node_id[0] == '_')
3249
 
    {
3250
 
      *revision = SVN_INVALID_REVNUM;
3251
 
      return SVN_NO_ERROR;
3252
 
    }
3253
 
 
3254
 
  /* Maybe this is a new-style node ID that just has the revision
3255
 
     sitting right in it. */
3256
 
  dash = strchr(node_id, '-');
3257
 
  if (dash && *(dash+1))
3258
 
    {
3259
 
      *revision = SVN_STR_TO_REV(dash + 1);
3260
 
      return SVN_NO_ERROR;
3261
 
    }
3262
 
 
3263
 
  /* The root node always has ID 0, created in revision 0 and will never
3264
 
     use the new-style ID format. */
3265
 
  if (strcmp(node_id, "0") == 0)
3266
 
    {
3267
 
      *revision = 0;
 
3497
  /* Is it a brand new uncommitted node or a new-style node ID?
 
3498
   * (committed old-style nodes will have a 0 revision value;
 
3499
   * rev 0, number 0 is rev 0 root node). Note that != 0 includes
 
3500
   * SVN_INVALID_REVNUM for uncommitted nodes. */
 
3501
  if (node_id->revision != 0 || node_id->number == 0)
 
3502
    {
 
3503
      *revision = node_id->revision;
3268
3504
      return SVN_NO_ERROR;
3269
3505
    }
3270
3506
 
3344
3580
 
3345
3581
    /* Wow, I don't want to have to do all that again.  Let's cache
3346
3582
       the result. */
3347
 
    if (node_id[0] != '_')
 
3583
    if (node_id->revision != SVN_INVALID_REVNUM)
3348
3584
      SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3349
3585
                                         svn_fs_fs__dag_get_id(node), pool));
3350
3586
 
3355
3591
}
3356
3592
 
3357
3593
 
3358
 
struct history_prev_args
3359
 
{
3360
 
  svn_fs_history_t **prev_history_p;
3361
 
  svn_fs_history_t *history;
3362
 
  svn_boolean_t cross_copies;
3363
 
  apr_pool_t *pool;
3364
 
};
3365
 
 
3366
 
 
3367
3594
static svn_error_t *
3368
 
history_prev(void *baton, apr_pool_t *pool)
 
3595
history_prev(svn_fs_history_t **prev_history,
 
3596
             svn_fs_history_t *history,
 
3597
             svn_boolean_t cross_copies,
 
3598
             apr_pool_t *result_pool,
 
3599
             apr_pool_t *scratch_pool)
3369
3600
{
3370
 
  struct history_prev_args *args = baton;
3371
 
  svn_fs_history_t **prev_history = args->prev_history_p;
3372
 
  svn_fs_history_t *history = args->history;
3373
3601
  fs_history_data_t *fhd = history->fsap_data;
3374
3602
  const char *commit_path, *src_path, *path = fhd->path;
3375
3603
  svn_revnum_t commit_rev, src_rev, dst_rev;
3376
3604
  svn_revnum_t revision = fhd->revision;
3377
 
  apr_pool_t *retpool = args->pool;
3378
3605
  svn_fs_t *fs = fhd->fs;
3379
3606
  parent_path_t *parent_path;
3380
3607
  dag_node_t *node;
3393
3620
  if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3394
3621
    {
3395
3622
      reported = FALSE;
3396
 
      if (! args->cross_copies)
 
3623
      if (! cross_copies)
3397
3624
        return SVN_NO_ERROR;
3398
3625
      path = fhd->path_hint;
3399
3626
      revision = fhd->rev_hint;
3400
3627
    }
3401
3628
 
3402
3629
  /* Construct a ROOT for the current revision. */
3403
 
  SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
 
3630
  SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
3404
3631
 
3405
3632
  /* Open PATH/REVISION, and get its node and a bunch of other
3406
3633
     goodies.  */
3407
 
  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
 
3634
  SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
3408
3635
  node = parent_path->node;
3409
3636
  commit_path = svn_fs_fs__dag_get_created_path(node);
3410
 
  SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
 
3637
  SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3411
3638
 
3412
3639
  /* The Subversion filesystem is written in such a way that a given
3413
3640
     line of history may have at most one interesting history point
3422
3649
        {
3423
3650
          /* ... we either have not yet reported on this revision (and
3424
3651
             need now to do so) ... */
3425
 
          *prev_history = assemble_history(fs,
3426
 
                                           apr_pstrdup(retpool, commit_path),
 
3652
          *prev_history = assemble_history(fs, commit_path,
3427
3653
                                           commit_rev, TRUE, NULL,
3428
 
                                           SVN_INVALID_REVNUM, retpool);
 
3654
                                           SVN_INVALID_REVNUM, result_pool);
3429
3655
          return SVN_NO_ERROR;
3430
3656
        }
3431
3657
      else
3441
3667
 
3442
3668
          /* Replace NODE and friends with the information from its
3443
3669
             predecessor. */
3444
 
          SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
 
3670
          SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
3445
3671
          commit_path = svn_fs_fs__dag_get_created_path(node);
3446
 
          SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
 
3672
          SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3447
3673
        }
3448
3674
    }
3449
3675
 
3450
3676
  /* Find the youngest copyroot in the path of this node, including
3451
3677
     itself. */
3452
3678
  SVN_ERR(find_youngest_copyroot(&copyroot_rev, &copyroot_path, fs,
3453
 
                                 parent_path, pool));
 
3679
                                 parent_path, scratch_pool));
3454
3680
 
3455
3681
  /* Initialize some state variables. */
3456
3682
  src_path = NULL;
3464
3690
      svn_fs_root_t *copyroot_root;
3465
3691
 
3466
3692
      SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
3467
 
                                       pool));
3468
 
      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
 
3693
                                       scratch_pool));
 
3694
      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
3469
3695
      copy_dst = svn_fs_fs__dag_get_created_path(node);
3470
3696
 
3471
3697
      /* If our current path was the very destination of the copy,
3487
3713
          SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node));
3488
3714
 
3489
3715
          dst_rev = copyroot_rev;
3490
 
          src_path = svn_fspath__join(copy_src, remainder_path, pool);
 
3716
          src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
3491
3717
        }
3492
3718
    }
3493
3719
 
3504
3730
      if ((dst_rev == revision) && reported)
3505
3731
        retry = TRUE;
3506
3732
 
3507
 
      *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3508
 
                                       dst_rev, ! retry,
3509
 
                                       src_path, src_rev, retpool);
 
3733
      *prev_history = assemble_history(fs, path, dst_rev, ! retry,
 
3734
                                       src_path, src_rev, result_pool);
3510
3735
    }
3511
3736
  else
3512
3737
    {
3513
 
      *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3514
 
                                       commit_rev, TRUE, NULL,
3515
 
                                       SVN_INVALID_REVNUM, retpool);
 
3738
      *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
 
3739
                                       NULL, SVN_INVALID_REVNUM, result_pool);
3516
3740
    }
3517
3741
 
3518
3742
  return SVN_NO_ERROR;
3527
3751
fs_history_prev(svn_fs_history_t **prev_history_p,
3528
3752
                svn_fs_history_t *history,
3529
3753
                svn_boolean_t cross_copies,
3530
 
                apr_pool_t *pool)
 
3754
                apr_pool_t *result_pool,
 
3755
                apr_pool_t *scratch_pool)
3531
3756
{
3532
3757
  svn_fs_history_t *prev_history = NULL;
3533
3758
  fs_history_data_t *fhd = history->fsap_data;
3541
3766
    {
3542
3767
      if (! fhd->is_interesting)
3543
3768
        prev_history = assemble_history(fs, "/", fhd->revision,
3544
 
                                        1, NULL, SVN_INVALID_REVNUM, pool);
 
3769
                                        1, NULL, SVN_INVALID_REVNUM,
 
3770
                                        result_pool);
3545
3771
      else if (fhd->revision > 0)
3546
3772
        prev_history = assemble_history(fs, "/", fhd->revision - 1,
3547
 
                                        1, NULL, SVN_INVALID_REVNUM, pool);
 
3773
                                        1, NULL, SVN_INVALID_REVNUM,
 
3774
                                        result_pool);
3548
3775
    }
3549
3776
  else
3550
3777
    {
3551
 
      struct history_prev_args args;
 
3778
      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3552
3779
      prev_history = history;
3553
3780
 
3554
3781
      while (1)
3555
3782
        {
3556
 
          args.prev_history_p = &prev_history;
3557
 
          args.history = prev_history;
3558
 
          args.cross_copies = cross_copies;
3559
 
          args.pool = pool;
3560
 
          SVN_ERR(history_prev(&args, pool));
 
3783
          svn_pool_clear(iterpool);
 
3784
          SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
 
3785
                               result_pool, iterpool));
3561
3786
 
3562
3787
          if (! prev_history)
3563
3788
            break;
3565
3790
          if (fhd->is_interesting)
3566
3791
            break;
3567
3792
        }
 
3793
 
 
3794
      svn_pool_destroy(iterpool);
3568
3795
    }
3569
3796
 
3570
3797
  *prev_history_p = prev_history;
3594
3821
 
3595
3822
/* Return a new history object (marked as "interesting") for PATH and
3596
3823
   REVISION, allocated in POOL, and with its members set to the values
3597
 
   of the parameters provided.  Note that PATH and PATH_HINT are not
3598
 
   duped into POOL -- it is the responsibility of the caller to ensure
3599
 
   that this happens. */
 
3824
   of the parameters provided.  Note that PATH and PATH_HINT get
 
3825
   normalized and duplicated in POOL. */
3600
3826
static svn_fs_history_t *
3601
3827
assemble_history(svn_fs_t *fs,
3602
3828
                 const char *path,
3611
3837
  fhd->path = svn_fs__canonicalize_abspath(path, pool);
3612
3838
  fhd->revision = revision;
3613
3839
  fhd->is_interesting = is_interesting;
3614
 
  fhd->path_hint = path_hint;
 
3840
  fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool)
 
3841
                             : NULL;
3615
3842
  fhd->rev_hint = rev_hint;
3616
3843
  fhd->fs = fs;
3617
3844
 
3643
3870
                                  apr_pool_t *result_pool,
3644
3871
                                  apr_pool_t *scratch_pool)
3645
3872
{
3646
 
  apr_hash_t *entries;
3647
 
  apr_hash_index_t *hi;
 
3873
  apr_array_header_t *entries;
 
3874
  int i;
3648
3875
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3649
3876
 
3650
 
  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3651
 
                                     scratch_pool));
3652
 
 
3653
 
  for (hi = apr_hash_first(scratch_pool, entries);
3654
 
       hi;
3655
 
       hi = apr_hash_next(hi))
 
3877
  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
 
3878
  for (i = 0; i < entries->nelts; ++i)
3656
3879
    {
3657
 
      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
3880
      svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
3658
3881
      const char *kid_path;
3659
3882
      dag_node_t *kid_dag;
3660
3883
      svn_boolean_t has_mergeinfo, go_down;
3758
3981
 
3759
3982
  path = svn_fs__canonicalize_abspath(path, scratch_pool);
3760
3983
 
3761
 
  SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
 
3984
  SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
3762
3985
 
3763
3986
  if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3764
3987
    return SVN_NO_ERROR;
4017
4240
  svn_fs_fs__check_path,
4018
4241
  fs_node_history,
4019
4242
  svn_fs_fs__node_id,
 
4243
  fs_node_relation,
4020
4244
  svn_fs_fs__node_created_rev,
4021
4245
  fs_node_origin_rev,
4022
4246
  fs_node_created_path,
4023
4247
  fs_delete_node,
 
4248
  fs_copy,
 
4249
  fs_revision_link,
4024
4250
  fs_copied_from,
4025
4251
  fs_closest_copy,
4026
4252
  fs_node_prop,
4027
4253
  fs_node_proplist,
 
4254
  fs_node_has_props,
4028
4255
  fs_change_node_prop,
4029
4256
  fs_props_changed,
4030
4257
  fs_dir_entries,
 
4258
  fs_dir_optimal_order,
4031
4259
  fs_make_dir,
4032
 
  fs_copy,
4033
 
  fs_revision_link,
4034
4260
  fs_file_length,
4035
4261
  fs_file_checksum,
4036
4262
  fs_file_contents,
4068
4294
                   apr_pool_t *pool)
4069
4295
{
4070
4296
  svn_fs_root_t *root = make_root(fs, pool);
4071
 
  fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4072
4297
 
4073
4298
  root->is_txn_root = FALSE;
4074
4299
  root->rev = rev;
4075
 
 
4076
 
  frd->root_dir = root_dir;
4077
 
  frd->copyfrom_cache = svn_hash__make(root->pool);
4078
 
 
4079
 
  root->fsap_data = frd;
 
4300
  root->fsap_data = root_dir;
4080
4301
 
4081
4302
  return root;
4082
4303
}
4088
4309
static svn_error_t *
4089
4310
make_txn_root(svn_fs_root_t **root_p,
4090
4311
              svn_fs_t *fs,
4091
 
              const char *txn,
 
4312
              const svn_fs_fs__id_part_t *txn,
4092
4313
              svn_revnum_t base_rev,
4093
4314
              apr_uint32_t flags,
4094
4315
              apr_pool_t *pool)
4095
4316
{
4096
4317
  svn_fs_root_t *root = make_root(fs, pool);
4097
4318
  fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
 
4319
  frd->txn_id = *txn;
4098
4320
 
4099
4321
  root->is_txn_root = TRUE;
4100
 
  root->txn = apr_pstrdup(root->pool, txn);
 
4322
  root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
4101
4323
  root->txn_flags = flags;
4102
4324
  root->rev = base_rev;
4103
4325
 
4104
 
  frd->txn_id = txn;
4105
 
 
4106
4326
  /* Because this cache actually tries to invalidate elements, keep
4107
4327
     the number of elements per page down.
4108
4328
 
4114
4334
                                      APR_HASH_KEY_STRING,
4115
4335
                                      32, 20, FALSE,
4116
4336
                                      apr_pstrcat(pool, txn, ":TXN",
4117
 
                                                  (char *)NULL),
 
4337
                                                  SVN_VA_NULL),
4118
4338
                                      root->pool));
4119
4339
 
4120
4340
  /* Initialize transaction-local caches in FS.
4121
4341
 
4122
4342
     Note that we cannot put those caches in frd because that content
4123
4343
     fs root object is not available where we would need it. */
4124
 
  SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
 
4344
  SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
4125
4345
 
4126
4346
  root->fsap_data = frd;
4127
4347
 
4132
4352
 
4133
4353
 
4134
4354
/* Verify. */
4135
 
static APR_INLINE const char *
 
4355
static const char *
4136
4356
stringify_node(dag_node_t *node,
4137
4357
               apr_pool_t *pool)
4138
4358
{
4142
4362
 
4143
4363
/* Check metadata sanity on NODE, and on its children.  Manually verify
4144
4364
   information for DAG nodes in revision REV, and trust the metadata
4145
 
   accuracy for nodes belonging to older revisions. */
 
4365
   accuracy for nodes belonging to older revisions.  To detect cycles,
 
4366
   provide all parent dag_node_t * in PARENT_NODES. */
4146
4367
static svn_error_t *
4147
4368
verify_node(dag_node_t *node,
4148
4369
            svn_revnum_t rev,
 
4370
            apr_array_header_t *parent_nodes,
4149
4371
            apr_pool_t *pool)
4150
4372
{
4151
4373
  svn_boolean_t has_mergeinfo;
4155
4377
  int pred_count;
4156
4378
  svn_node_kind_t kind;
4157
4379
  apr_pool_t *iterpool = svn_pool_create(pool);
 
4380
  int i;
 
4381
 
 
4382
  /* Detect (non-)DAG cycles. */
 
4383
  for (i = 0; i < parent_nodes->nelts; ++i)
 
4384
    {
 
4385
      dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *);
 
4386
      if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent),
 
4387
                           svn_fs_fs__dag_get_id(node)))
 
4388
        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
 
4389
                                "Node is its own direct or indirect parent '%s'",
 
4390
                                stringify_node(node, iterpool));
 
4391
    }
4158
4392
 
4159
4393
  /* Fetch some data. */
4160
4394
  SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4205
4439
    }
4206
4440
  if (kind == svn_node_dir)
4207
4441
    {
4208
 
      apr_hash_t *entries;
4209
 
      apr_hash_index_t *hi;
 
4442
      apr_array_header_t *entries;
4210
4443
      apr_int64_t children_mergeinfo = 0;
 
4444
      APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node;
4211
4445
 
4212
4446
      SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4213
4447
 
4214
4448
      /* Compute CHILDREN_MERGEINFO. */
4215
 
      for (hi = apr_hash_first(pool, entries);
4216
 
           hi;
4217
 
           hi = apr_hash_next(hi))
 
4449
      for (i = 0; i < entries->nelts; ++i)
4218
4450
        {
4219
 
          svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
4451
          svn_fs_dirent_t *dirent
 
4452
            = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4220
4453
          dag_node_t *child;
4221
 
          svn_revnum_t child_rev;
4222
4454
          apr_int64_t child_mergeinfo;
4223
4455
 
4224
4456
          svn_pool_clear(iterpool);
4225
4457
 
4226
4458
          /* Compute CHILD_REV. */
4227
 
          SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
4228
 
          SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
4229
 
 
4230
 
          if (child_rev == rev)
4231
 
            SVN_ERR(verify_node(child, rev, iterpool));
4232
 
 
4233
 
          SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
 
4459
          if (svn_fs_fs__id_rev(dirent->id) == rev)
 
4460
            {
 
4461
              SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
 
4462
                                              iterpool));
 
4463
              SVN_ERR(verify_node(child, rev, parent_nodes, iterpool));
 
4464
              SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
 
4465
                                                         child));
 
4466
            }
 
4467
          else
 
4468
            {
 
4469
              /* access mergeinfo counter with minimal overhead */
 
4470
              node_revision_t *noderev;
 
4471
              SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
 
4472
                                                   iterpool, iterpool));
 
4473
              child_mergeinfo = noderev->mergeinfo_count;
 
4474
            }
 
4475
 
4234
4476
          children_mergeinfo += child_mergeinfo;
4235
4477
        }
4236
4478
 
4243
4485
                                 stringify_node(node, iterpool),
4244
4486
                                 mergeinfo_count, has_mergeinfo,
4245
4487
                                 children_mergeinfo);
 
4488
 
 
4489
      /* If we don't make it here, there was an error / corruption.
 
4490
       * In that case, nobody will need PARENT_NODES anymore. */
 
4491
      apr_array_pop(parent_nodes);
4246
4492
    }
4247
4493
 
4248
4494
  svn_pool_destroy(iterpool);
4255
4501
{
4256
4502
  svn_fs_t *fs = root->fs;
4257
4503
  dag_node_t *root_dir;
 
4504
  apr_array_header_t *parent_nodes;
4258
4505
 
4259
4506
  /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4260
4507
     (and elsewhere).  This code makes more thorough checks than the
4270
4517
  if (root->is_txn_root)
4271
4518
    {
4272
4519
      fs_txn_root_data_t *frd = root->fsap_data;
4273
 
      SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
 
4520
      SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
4274
4521
    }
4275
4522
  else
4276
4523
    {
4277
 
      fs_rev_root_data_t *frd = root->fsap_data;
4278
 
      root_dir = frd->root_dir;
 
4524
      root_dir = root->fsap_data;
4279
4525
    }
4280
4526
 
4281
4527
  /* Recursively verify ROOT_DIR. */
4282
 
  SVN_ERR(verify_node(root_dir, root->rev, pool));
 
4528
  parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *));
 
4529
  SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool));
4283
4530
 
4284
4531
  /* Verify explicitly the predecessor of the root. */
4285
4532
  {