~svn/ubuntu/oneiric/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_fs/tree.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* tree.c : tree-like filesystem, built on DAG filesystem
 
2
 *
 
3
 * ====================================================================
 
4
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
5
 *
 
6
 * This software is licensed as described in the file COPYING, which
 
7
 * you should have received as part of this distribution.  The terms
 
8
 * are also available at http://subversion.tigris.org/license-1.html.
 
9
 * If newer versions of this license are posted there, you may use a
 
10
 * newer version instead, at your option.
 
11
 *
 
12
 * This software consists of voluntary contributions made by many
 
13
 * individuals.  For exact contribution history, see the revision
 
14
 * history and logs, available at http://subversion.tigris.org/.
 
15
 * ====================================================================
 
16
 */
 
17
 
 
18
 
 
19
/* The job of this layer is to take a filesystem with lots of node
 
20
   sharing going on --- the real DAG filesystem as it appears in the
 
21
   database --- and make it look and act like an ordinary tree
 
22
   filesystem, with no sharing.
 
23
 
 
24
   We do just-in-time cloning: you can walk from some unfinished
 
25
   transaction's root down into directories and files shared with
 
26
   committed revisions; as soon as you try to change something, the
 
27
   appropriate nodes get cloned (and parent directory entries updated)
 
28
   invisibly, behind your back.  Any other references you have to
 
29
   nodes that have been cloned by other changes, even made by other
 
30
   processes, are automatically updated to point to the right clones.  */
 
31
 
 
32
 
 
33
#include <stdlib.h>
 
34
#include <string.h>
 
35
#include <assert.h>
 
36
#include "svn_private_config.h"
 
37
#include "svn_pools.h"
 
38
#include "svn_error.h"
 
39
#include "svn_path.h"
 
40
#include "svn_md5.h"
 
41
#include "svn_fs.h"
 
42
#include "fs.h"
 
43
#include "err.h"
 
44
#include "key-gen.h"
 
45
#include "dag.h"
 
46
#include "lock.h"
 
47
#include "tree.h"
 
48
#include "revs-txns.h"
 
49
#include "fs_fs.h"
 
50
#include "id.h"
 
51
 
 
52
#include "../libsvn_fs/fs-loader.h"
 
53
 
 
54
 
 
55
/* ### I believe this constant will become internal to reps-strings.c.
 
56
   ### see the comment in window_consumer() for more information. */
 
57
 
 
58
/* ### the comment also seems to need tweaking: the log file stuff
 
59
   ### is no longer an issue... */
 
60
/* Data written to the filesystem through the svn_fs_apply_textdelta()
 
61
   interface is cached in memory until the end of the data stream, or
 
62
   until a size trigger is hit.  Define that trigger here (in bytes).
 
63
   Setting the value to 0 will result in no filesystem buffering at
 
64
   all.  The value only really matters when dealing with file contents
 
65
   bigger than the value itself.  Above that point, large values here
 
66
   allow the filesystem to buffer more data in memory before flushing
 
67
   to the database, which increases memory usage but greatly decreases
 
68
   the amount of disk access (and log-file generation) in database.
 
69
   Smaller values will limit your overall memory consumption, but can
 
70
   drastically hurt throughput by necessitating more write operations
 
71
   to the database (which also generates more log-files).  */
 
72
#define SVN_FS_WRITE_BUFFER_SIZE          512000
 
73
 
 
74
/* The maximum number of cache items to maintain in the node cache. */
 
75
#define SVN_FS_NODE_CACHE_MAX_KEYS        32
 
76
 
 
77
 
 
78
 
 
79
/* The root structure.  */
 
80
 
 
81
/* Structure for svn_fs_root_t's node_cache hash values.  Cache items
 
82
   are arranged in a circular LRU list with a dummy entry, and also
 
83
   indexed with a hash table. */
 
84
typedef struct dag_node_cache_t
 
85
{
 
86
  const char *path;               /* Path of cached node */
 
87
  dag_node_t *node;               /* Cached node */
 
88
  struct dag_node_cache_t *prev;  /* Next node in LRU list */
 
89
  struct dag_node_cache_t *next;  /* Previous node in LRU list */
 
90
  apr_pool_t *pool;               /* Pool in which node is allocated */
 
91
} dag_node_cache_t;
 
92
 
 
93
 
 
94
typedef enum root_kind_t {
 
95
  unspecified_root = 0,
 
96
  revision_root,
 
97
  transaction_root
 
98
} root_kind_t;
 
99
 
 
100
 
 
101
typedef struct
 
102
{
 
103
  /* For revision roots, this is a dag node for the revision's root
 
104
     directory.  For transaction roots, we open the root directory
 
105
     afresh every time, since the root may have been cloned, or
 
106
     the transaction may have disappeared altogether.  */
 
107
  dag_node_t *root_dir;
 
108
 
 
109
  /* Dummy entry for circular LRU cache, and associated hash table. */
 
110
  dag_node_cache_t node_list;
 
111
  apr_hash_t *node_cache;
 
112
 
 
113
  /* Cache structure for mapping const char * PATH to const char
 
114
     *COPYFROM_STRING, so that paths_changed can remember all the
 
115
     copyfrom information in the changes file.
 
116
     COPYFROM_STRING has the format "REV PATH", or is the empty string if
 
117
     the path was added without history. */
 
118
  apr_hash_t *copyfrom_cache;
 
119
  
 
120
} fs_root_data_t;
 
121
 
 
122
/* Declared here to resolve the circular dependencies. */
 
123
static svn_error_t * get_dag (dag_node_t **dag_node_p, svn_fs_root_t *root,
 
124
                              const char *path, apr_pool_t *pool);
 
125
 
 
126
static svn_fs_root_t *make_revision_root (svn_fs_t *fs, svn_revnum_t rev,
 
127
                                          dag_node_t *root_dir,
 
128
                                          apr_pool_t *pool);
 
129
 
 
130
static svn_fs_root_t *make_txn_root (svn_fs_t *fs, const char *txn,
 
131
                                     apr_uint32_t flags, apr_pool_t *pool);
 
132
 
 
133
 
 
134
/*** Node Caching in the Roots. ***/
 
135
 
 
136
/* Return NODE for PATH from ROOT's node cache, or NULL if the node
 
137
   isn't cached. */
 
138
static dag_node_t *
 
139
dag_node_cache_get (svn_fs_root_t *root,
 
140
                    const char *path,
 
141
                    apr_pool_t *pool)
 
142
{
 
143
  fs_root_data_t *frd = root->fsap_data;
 
144
  dag_node_cache_t *item;
 
145
 
 
146
  /* Assert valid input. */
 
147
  assert (*path == '/');
 
148
 
 
149
  /* Look in the cache for our desired item. */
 
150
  item = apr_hash_get (frd->node_cache, path, APR_HASH_KEY_STRING);
 
151
  if (item && item->node)
 
152
    {
 
153
      /* Move this cache item to the front of the LRU list. */
 
154
      item->prev->next = item->next;
 
155
      item->next->prev = item->prev;
 
156
      item->prev = &frd->node_list;
 
157
      item->next = frd->node_list.next;
 
158
      item->prev->next = item;
 
159
      item->next->prev = item;
 
160
 
 
161
      /* Return the cached node. */
 
162
      return svn_fs_fs__dag_dup (item->node, pool);
 
163
    }
 
164
 
 
165
  return NULL;
 
166
}
 
167
 
 
168
 
 
169
/* Add the NODE for PATH to ROOT's node cache. */
 
170
static void
 
171
dag_node_cache_set (svn_fs_root_t *root,
 
172
                    const char *path,
 
173
                    dag_node_t *node)
 
174
{
 
175
  fs_root_data_t *frd = root->fsap_data;
 
176
  dag_node_cache_t *item;
 
177
  apr_pool_t *pool;
 
178
 
 
179
  /* What?  No POOL passed to this function?
 
180
 
 
181
     To ensure that our cache values live as long as the svn_fs_root_t
 
182
     in which they are ultimately stored, and to allow us to free()
 
183
     them individually without harming the rest, they are each
 
184
     allocated from a subpool of ROOT's pool.  We'll keep one subpool
 
185
     around for each cache slot -- as we start expiring stuff
 
186
     to make room for more entries, we'll re-use the expired thing's
 
187
     pool. */
 
188
 
 
189
  /* Assert valid input and state. */
 
190
  assert (*path == '/');
 
191
 
 
192
  /* If we have an existing entry for this path, reuse it. */
 
193
  item = apr_hash_get (frd->node_cache, path, APR_HASH_KEY_STRING);
 
194
 
 
195
  /* Otherwise, if the cache is full, reuse the tail of the LRU list. */
 
196
  if (!item && apr_hash_count (frd->node_cache) == SVN_FS_NODE_CACHE_MAX_KEYS)
 
197
    item = frd->node_list.prev;
 
198
 
 
199
  if (item)
 
200
    {
 
201
      /* Remove the existing item from the cache and reuse its pool. */
 
202
      item->prev->next = item->next;
 
203
      item->next->prev = item->prev;
 
204
      apr_hash_set (frd->node_cache, item->path, APR_HASH_KEY_STRING, NULL);
 
205
      pool = item->pool;
 
206
      svn_pool_clear (pool);
 
207
    }
 
208
  else
 
209
    {
 
210
      /* Allocate a new pool. */
 
211
      pool = svn_pool_create (root->pool);
 
212
    }
 
213
 
 
214
  /* Create and fill in the cache item. */
 
215
  item = apr_palloc (pool, sizeof (*item));
 
216
  item->path = apr_pstrdup (pool, path);
 
217
  item->node = svn_fs_fs__dag_dup (node, pool);
 
218
  item->pool = pool;
 
219
 
 
220
  /* Link it into the head of the LRU list and hash table. */
 
221
  item->prev = &frd->node_list;
 
222
  item->next = frd->node_list.next;
 
223
  item->prev->next = item;
 
224
  item->next->prev = item;
 
225
  apr_hash_set (frd->node_cache, item->path, APR_HASH_KEY_STRING, item);
 
226
}
 
227
 
 
228
 
 
229
/* Invalidate cache entries for PATH and any of its children. */
 
230
static void
 
231
dag_node_cache_invalidate (svn_fs_root_t *root,
 
232
                           const char *path)
 
233
{
 
234
  fs_root_data_t *frd = root->fsap_data;
 
235
  apr_size_t len = strlen (path);
 
236
  const char *key;
 
237
  dag_node_cache_t *item;
 
238
 
 
239
  for (item = frd->node_list.next; item != &frd->node_list; item = item->next)
 
240
    {
 
241
      key = item->path;
 
242
      if (strncmp (key, path, len) == 0 && (key[len] == '/' || !key[len]))
 
243
        item->node = NULL;
 
244
    }
 
245
}
 
246
 
 
247
 
 
248
 
 
249
/* Creating transaction and revision root nodes.  */
 
250
 
 
251
svn_error_t *
 
252
svn_fs_fs__txn_root (svn_fs_root_t **root_p,
 
253
                     svn_fs_txn_t *txn,
 
254
                     apr_pool_t *pool)
 
255
{
 
256
  svn_fs_root_t *root;
 
257
  apr_uint32_t flags = 0;
 
258
  apr_hash_t *txnprops;
 
259
 
 
260
  /* Look for the temporary txn props representing 'flags'. */
 
261
  SVN_ERR (svn_fs_fs__txn_proplist (&txnprops, txn, pool));
 
262
  if (txnprops)
 
263
    {
 
264
      if (apr_hash_get (txnprops, SVN_FS_PROP_TXN_CHECK_OOD,
 
265
                        APR_HASH_KEY_STRING))
 
266
        flags |= SVN_FS_TXN_CHECK_OOD;
 
267
      
 
268
      if (apr_hash_get (txnprops, SVN_FS_PROP_TXN_CHECK_LOCKS,
 
269
                        APR_HASH_KEY_STRING))
 
270
        flags |= SVN_FS_TXN_CHECK_LOCKS;
 
271
    }
 
272
  
 
273
  root = make_txn_root (txn->fs, txn->id, flags, pool);
 
274
 
 
275
  *root_p = root;
 
276
  
 
277
  return SVN_NO_ERROR;
 
278
}
 
279
 
 
280
 
 
281
svn_error_t *
 
282
svn_fs_fs__revision_root (svn_fs_root_t **root_p,
 
283
                          svn_fs_t *fs,
 
284
                          svn_revnum_t rev,
 
285
                          apr_pool_t *pool)
 
286
{
 
287
  dag_node_t *root_dir;
 
288
 
 
289
  SVN_ERR (svn_fs_fs__check_fs (fs));
 
290
 
 
291
  SVN_ERR (svn_fs_fs__dag_revision_root (&root_dir, fs, rev, pool));
 
292
 
 
293
  *root_p = make_revision_root (fs, rev, root_dir, pool);
 
294
  
 
295
  return SVN_NO_ERROR;
 
296
}
 
297
 
 
298
 
 
299
 
 
300
/* Constructing nice error messages for roots.  */
 
301
 
 
302
/* Return the error SVN_ERR_FS_NOT_FOUND, with a detailed error text,
 
303
   for PATH in ROOT. */
 
304
static svn_error_t *
 
305
not_found (svn_fs_root_t *root, const char *path)
 
306
{
 
307
  if (root->is_txn_root)
 
308
    return
 
309
      svn_error_createf
 
310
      (SVN_ERR_FS_NOT_FOUND, 0,
 
311
       _("File not found: transaction '%s', path '%s'"),
 
312
       root->txn, path);
 
313
  else
 
314
    return
 
315
      svn_error_createf
 
316
      (SVN_ERR_FS_NOT_FOUND, 0,
 
317
       _("File not found: revision %ld, path '%s'"),
 
318
       root->rev, path);
 
319
}
 
320
 
 
321
 
 
322
/* Return a detailed `file already exists' message for PATH in ROOT.  */
 
323
static svn_error_t *
 
324
already_exists (svn_fs_root_t *root, const char *path)
 
325
{
 
326
  svn_fs_t *fs = root->fs;
 
327
 
 
328
  if (root->is_txn_root)
 
329
    return
 
330
      svn_error_createf
 
331
      (SVN_ERR_FS_ALREADY_EXISTS, 0,
 
332
       _("File already exists: filesystem '%s', transaction '%s', path '%s'"),
 
333
       fs->path, root->txn, path);
 
334
  else
 
335
    return
 
336
      svn_error_createf
 
337
      (SVN_ERR_FS_ALREADY_EXISTS, 0,
 
338
       _("File already exists: filesystem '%s', revision %ld, path '%s'"),
 
339
       fs->path, root->rev, path);
 
340
}
 
341
 
 
342
 
 
343
static svn_error_t *
 
344
not_txn (svn_fs_root_t *root)
 
345
{
 
346
  return svn_error_create
 
347
    (SVN_ERR_FS_NOT_TXN_ROOT, NULL,
 
348
     _("Root object must be a transaction root"));
 
349
}
 
350
 
 
351
 
 
352
 
 
353
/* Getting dag nodes for roots.  */
 
354
 
 
355
 
 
356
/* Set *NODE_P to a freshly opened dag node referring to the root
 
357
   directory of ROOT, allocating from POOL.  */
 
358
static svn_error_t *
 
359
root_node (dag_node_t **node_p,
 
360
           svn_fs_root_t *root,
 
361
           apr_pool_t *pool)
 
362
{
 
363
  fs_root_data_t *frd = root->fsap_data;
 
364
  
 
365
  if (! root->is_txn_root)
 
366
    {
 
367
      /* It's a revision root, so we already have its root directory
 
368
         opened.  */
 
369
      *node_p = svn_fs_fs__dag_dup (frd->root_dir, pool);
 
370
      return SVN_NO_ERROR;
 
371
    }
 
372
  else
 
373
    {
 
374
      /* It's a transaction root.  Open a fresh copy.  */
 
375
      return svn_fs_fs__dag_txn_root (node_p, root->fs, root->txn, pool);
 
376
    }
 
377
}
 
378
 
 
379
 
 
380
/* Set *NODE_P to a mutable root directory for ROOT, cloning if
 
381
   necessary, allocating in POOL.  ROOT must be a transaction root.
 
382
   Use ERROR_PATH in error messages.  */
 
383
static svn_error_t *
 
384
mutable_root_node (dag_node_t **node_p,
 
385
                   svn_fs_root_t *root,
 
386
                   const char *error_path,
 
387
                   apr_pool_t *pool)
 
388
{
 
389
  if (root->is_txn_root)
 
390
    return svn_fs_fs__dag_clone_root (node_p, root->fs, root->txn, pool);
 
391
  else
 
392
    /* If it's not a transaction root, we can't change its contents.  */
 
393
    return svn_fs_fs__err_not_mutable (root->fs, root->rev, error_path);
 
394
}
 
395
 
 
396
 
 
397
 
 
398
/* Traversing directory paths.  */
 
399
 
 
400
typedef enum copy_id_inherit_t
 
401
{
 
402
  copy_id_inherit_unknown = 0,
 
403
  copy_id_inherit_self,
 
404
  copy_id_inherit_parent,
 
405
  copy_id_inherit_new
 
406
    
 
407
} copy_id_inherit_t;
 
408
 
 
409
/* A linked list representing the path from a node up to a root
 
410
   directory.  We use this for cloning, and for operations that need
 
411
   to deal with both a node and its parent directory.  For example, a
 
412
   `delete' operation needs to know that the node actually exists, but
 
413
   also needs to change the parent directory.  */
 
414
typedef struct parent_path_t
 
415
{
 
416
  
 
417
  /* A node along the path.  This could be the final node, one of its
 
418
     parents, or the root.  Every parent path ends with an element for
 
419
     the root directory.  */
 
420
  dag_node_t *node;
 
421
 
 
422
  /* The name NODE has in its parent directory.  This is zero for the
 
423
     root directory, which (obviously) has no name in its parent.  */
 
424
  char *entry;
 
425
 
 
426
  /* The parent of NODE, or zero if NODE is the root directory.  */
 
427
  struct parent_path_t *parent;
 
428
 
 
429
  /* The copy ID inheritence style. */
 
430
  copy_id_inherit_t copy_inherit;
 
431
 
 
432
  /* If copy ID inheritence style is copy_id_inherit_new, this is the
 
433
     path which should be implicitly copied; otherwise, this is NULL. */
 
434
  const char *copy_src_path;
 
435
 
 
436
} parent_path_t;
 
437
 
 
438
/* Return a text string describing the absolute path of parent_path
 
439
   PARENT_PATH.  It will be allocated in POOL. */
 
440
static const char *
 
441
parent_path_path (parent_path_t *parent_path,
 
442
                  apr_pool_t *pool)
 
443
{
 
444
  const char *path_so_far = "/";
 
445
  if (parent_path->parent)
 
446
    path_so_far = parent_path_path (parent_path->parent, pool);
 
447
  return parent_path->entry 
 
448
    ? svn_path_join (path_so_far, parent_path->entry, pool) 
 
449
    : path_so_far;
 
450
}
 
451
 
 
452
 
 
453
/* Choose a copy ID inheritance method *INHERIT_P to be used in the
 
454
   event that immutable node CHILD in FS needs to be made mutable.  If
 
455
   the inheritance method is copy_id_inherit_new, also return a
 
456
   *COPY_SRC_PATH on which to base the new copy ID (else return NULL
 
457
   for that path).  CHILD must have a parent (it cannot be the root
 
458
   node).  TXN_ID is the transaction in which these items might be
 
459
   mutable.  Allocations are taken from POOL. */
 
460
static svn_error_t *
 
461
get_copy_inheritance (copy_id_inherit_t *inherit_p,
 
462
                      const char **copy_src_path,
 
463
                      svn_fs_t *fs,
 
464
                      parent_path_t *child,
 
465
                      const char *txn_id,
 
466
                      apr_pool_t *pool)
 
467
{
 
468
  const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
 
469
  const char *child_copy_id, *parent_copy_id;
 
470
  const char *id_path = NULL;
 
471
  svn_fs_root_t *copyroot_root;
 
472
  dag_node_t *copyroot_node;
 
473
  svn_revnum_t copyroot_rev;
 
474
  const char *copyroot_path;
 
475
 
 
476
  /* Make some assertions about the function input. */
 
477
  assert (child && child->parent && txn_id);
 
478
 
 
479
  /* Initialize some convenience variables. */
 
480
  child_id = svn_fs_fs__dag_get_id (child->node);
 
481
  parent_id = svn_fs_fs__dag_get_id (child->parent->node);
 
482
  child_copy_id = svn_fs_fs__id_copy_id (child_id);
 
483
  parent_copy_id = svn_fs_fs__id_copy_id (parent_id);
 
484
 
 
485
  /* If this child is already mutable, we have nothing to do. */
 
486
  if (svn_fs_fs__id_txn_id (child_id))
 
487
    {
 
488
      *inherit_p = copy_id_inherit_self;
 
489
      *copy_src_path = NULL;
 
490
      return SVN_NO_ERROR;
 
491
    }
 
492
 
 
493
  /* From this point on, we'll assume that the child will just take
 
494
     its copy ID from its parent. */
 
495
  *inherit_p = copy_id_inherit_parent;
 
496
  *copy_src_path = NULL;
 
497
 
 
498
  /* Special case: if the child's copy ID is '0', use the parent's
 
499
     copy ID. */
 
500
  if (strcmp (child_copy_id, "0") == 0)
 
501
    return SVN_NO_ERROR;
 
502
  
 
503
  /* Compare the copy IDs of the child and its parent.  If they are
 
504
     the same, then the child is already on the same branch as the
 
505
     parent, and should use the same mutability copy ID that the
 
506
     parent will use. */
 
507
  if (svn_fs_fs__key_compare (child_copy_id, parent_copy_id) == 0)
 
508
    return SVN_NO_ERROR;
 
509
 
 
510
  /* If the child is on the same branch that the parent is on, the
 
511
     child should just use the same copy ID that the parent would use.
 
512
     Else, the child needs to generate a new copy ID to use should it
 
513
     need to be made mutable.  We will claim that child is on the same
 
514
     branch as its parent if the child itself is not a branch point,
 
515
     or if it is a branch point that we are accessing via its original
 
516
     copy destination path. */
 
517
  SVN_ERR (svn_fs_fs__dag_get_copyroot (&copyroot_rev, &copyroot_path,
 
518
                                        child->node,pool));
 
519
  SVN_ERR (svn_fs_fs__revision_root (&copyroot_root, fs, copyroot_rev, pool));
 
520
  SVN_ERR (get_dag (&copyroot_node, copyroot_root, copyroot_path, pool));
 
521
  copyroot_id = svn_fs_fs__dag_get_id (copyroot_node);
 
522
  
 
523
  if (svn_fs_fs__id_compare (copyroot_id, child_id) == -1)
 
524
    return SVN_NO_ERROR;
 
525
 
 
526
  /* Determine if we are looking at the child via its original path or
 
527
     as a subtree item of a copied tree. */
 
528
  id_path = svn_fs_fs__dag_get_created_path (child->node);
 
529
  if (strcmp (id_path, parent_path_path (child, pool)) == 0)
 
530
    {
 
531
      *inherit_p = copy_id_inherit_self;
 
532
      return SVN_NO_ERROR;
 
533
    }
 
534
 
 
535
  /* We are pretty sure that the child node is an unedited nested
 
536
     branched node.  When it needs to be made mutable, it should claim
 
537
     a new copy ID. */
 
538
  *inherit_p = copy_id_inherit_new;
 
539
  *copy_src_path = id_path;
 
540
  return SVN_NO_ERROR;
 
541
}
 
542
 
 
543
/* Allocate a new parent_path_t node from POOL, referring to NODE,
 
544
   ENTRY, PARENT, and COPY_ID.  */
 
545
static parent_path_t *
 
546
make_parent_path (dag_node_t *node,
 
547
                  char *entry,
 
548
                  parent_path_t *parent,
 
549
                  apr_pool_t *pool)
 
550
{
 
551
  parent_path_t *parent_path = apr_pcalloc (pool, sizeof (*parent_path));
 
552
  parent_path->node = node;
 
553
  parent_path->entry = entry;
 
554
  parent_path->parent = parent;
 
555
  parent_path->copy_inherit = copy_id_inherit_unknown;
 
556
  parent_path->copy_src_path = NULL;
 
557
  return parent_path;
 
558
}
 
559
 
 
560
 
 
561
/* Return a null-terminated copy of the first component of PATH,
 
562
   allocated in POOL.  If path is empty, or consists entirely of
 
563
   slashes, return the empty string.
 
564
 
 
565
   If the component is followed by one or more slashes, we set *NEXT_P
 
566
   to point after the slashes.  If the component ends PATH, we set
 
567
   *NEXT_P to zero.  This means:
 
568
   - If *NEXT_P is zero, then the component ends the PATH, and there
 
569
     are no trailing slashes in the path.
 
570
   - If *NEXT_P points at PATH's terminating null character, then
 
571
     the component returned was the last, and PATH ends with one or more
 
572
     slash characters.
 
573
   - Otherwise, *NEXT_P points to the beginning of the next component
 
574
     of PATH.  You can pass this value to next_entry_name to extract
 
575
     the next component.  */
 
576
 
 
577
static char *
 
578
next_entry_name (const char **next_p,
 
579
                 const char *path,
 
580
                 apr_pool_t *pool)
 
581
{
 
582
  const char *end;
 
583
 
 
584
  /* Find the end of the current component.  */
 
585
  end = strchr (path, '/');
 
586
 
 
587
  if (! end)
 
588
    {
 
589
      /* The path contains only one component, with no trailing
 
590
         slashes.  */
 
591
      *next_p = 0;
 
592
      return apr_pstrdup (pool, path);
 
593
    }
 
594
  else
 
595
    {
 
596
      /* There's a slash after the first component.  Skip over an arbitrary
 
597
         number of slashes to find the next one.  */
 
598
      const char *next = end;
 
599
      while (*next == '/')
 
600
        next++;
 
601
      *next_p = next;
 
602
      return apr_pstrndup (pool, path, end - path);
 
603
    }
 
604
}
 
605
 
 
606
 
 
607
/* Flags for open_path.  */
 
608
typedef enum open_path_flags_t {
 
609
 
 
610
  /* The last component of the PATH need not exist.  (All parent
 
611
     directories must exist, as usual.)  If the last component doesn't
 
612
     exist, simply leave the `node' member of the bottom parent_path
 
613
     component zero.  */
 
614
  open_path_last_optional = 1
 
615
 
 
616
} open_path_flags_t;
 
617
 
 
618
 
 
619
/* Open the node identified by PATH in ROOT, allocating in POOL.  Set
 
620
   *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
 
621
   **PARENT_PATH_P value is guaranteed to contain at least one
 
622
   *element, for the root directory.
 
623
 
 
624
   If resulting *PARENT_PATH_P will eventually be made mutable and
 
625
   modified, or if copy ID inheritance information is otherwise
 
626
   needed, TXN_ID should be the ID of the mutability transaction.  If
 
627
   TXN_ID is NULL, no copy ID in heritance information will be
 
628
   calculated for the *PARENT_PATH_P chain.
 
629
 
 
630
   If FLAGS & open_path_last_optional is zero, return the error
 
631
   SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
 
632
   non-zero, require all the parent directories to exist as normal,
 
633
   but if the final path component doesn't exist, simply return a path
 
634
   whose bottom `node' member is zero.  This option is useful for
 
635
   callers that create new nodes --- we find the parent directory for
 
636
   them, and tell them whether the entry exists already.
 
637
 
 
638
   NOTE: Public interfaces which only *read* from the filesystem
 
639
   should not call this function directly, but should instead use
 
640
   get_dag().
 
641
*/
 
642
static svn_error_t *
 
643
open_path (parent_path_t **parent_path_p,
 
644
           svn_fs_root_t *root,
 
645
           const char *path,
 
646
           int flags,
 
647
           const char *txn_id,
 
648
           apr_pool_t *pool)
 
649
{
 
650
  svn_fs_t *fs = root->fs;
 
651
  const svn_fs_id_t *id;
 
652
  dag_node_t *here; /* The directory we're currently looking at.  */
 
653
  parent_path_t *parent_path; /* The path from HERE up to the root.  */
 
654
  const char *rest; /* The portion of PATH we haven't traversed yet.  */
 
655
  const char *canon_path = svn_fs_fs__canonicalize_abspath (path, pool);
 
656
  const char *path_so_far = "/";
 
657
 
 
658
  /* Make a parent_path item for the root node, using its own current
 
659
     copy id.  */
 
660
  SVN_ERR (root_node (&here, root, pool));
 
661
  id = svn_fs_fs__dag_get_id (here);
 
662
  parent_path = make_parent_path (here, 0, 0, pool);
 
663
  parent_path->copy_inherit = copy_id_inherit_self;
 
664
  
 
665
  rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
 
666
 
 
667
  /* Whenever we are at the top of this loop:
 
668
     - HERE is our current directory,
 
669
     - ID is the node revision ID of HERE,
 
670
     - REST is the path we're going to find in HERE, and 
 
671
     - PARENT_PATH includes HERE and all its parents.  */
 
672
  for (;;)
 
673
    {
 
674
      const char *next;
 
675
      char *entry;
 
676
      dag_node_t *child;
 
677
      
 
678
      /* Parse out the next entry from the path.  */
 
679
      entry = next_entry_name (&next, rest, pool);
 
680
      
 
681
      /* Calculate the path traversed thus far. */
 
682
      path_so_far = svn_path_join (path_so_far, entry, pool);
 
683
 
 
684
      if (*entry == '\0')
 
685
        {
 
686
          /* Given the behavior of next_entry_name, this happens when
 
687
             the path either starts or ends with a slash.  In either
 
688
             case, we stay put: the current directory stays the same,
 
689
             and we add nothing to the parent path.  */
 
690
          child = here;
 
691
        }
 
692
      else
 
693
        {
 
694
          copy_id_inherit_t inherit;
 
695
          const char *copy_path = NULL;
 
696
          svn_error_t *err = SVN_NO_ERROR;
 
697
          dag_node_t *cached_node;
 
698
 
 
699
          /* If we found a directory entry, follow it.  First, we
 
700
             check our node cache, and, failing that, we hit the DAG
 
701
             layer. */
 
702
          cached_node = dag_node_cache_get (root, path_so_far, pool);
 
703
          if (cached_node)
 
704
            child = cached_node;
 
705
          else
 
706
            err = svn_fs_fs__dag_open (&child, here, entry, pool);
 
707
          
 
708
          /* "file not found" requires special handling.  */
 
709
          if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
 
710
            {
 
711
              /* If this was the last path component, and the caller
 
712
                 said it was optional, then don't return an error;
 
713
                 just put a NULL node pointer in the path.  */
 
714
              
 
715
              svn_error_clear (err);
 
716
              
 
717
              if ((flags & open_path_last_optional)
 
718
                  && (! next || *next == '\0'))
 
719
                {
 
720
                  parent_path = make_parent_path (NULL, entry, parent_path, 
 
721
                                                  pool);
 
722
                  break;
 
723
                }
 
724
              else
 
725
                {
 
726
                  /* Build a better error message than svn_fs_fs__dag_open
 
727
                     can provide, giving the root and full path name.  */
 
728
                  return not_found (root, path);
 
729
                }
 
730
            }
 
731
          
 
732
          /* Other errors we return normally.  */
 
733
          SVN_ERR (err);
 
734
 
 
735
          /* Now, make a parent_path item for CHILD. */
 
736
          parent_path = make_parent_path (child, entry, parent_path, pool);
 
737
          if (txn_id)
 
738
            {
 
739
              SVN_ERR (get_copy_inheritance (&inherit, &copy_path, 
 
740
                                             fs, parent_path, txn_id, pool));
 
741
              parent_path->copy_inherit = inherit;
 
742
              parent_path->copy_src_path = apr_pstrdup (pool, copy_path);
 
743
            }
 
744
 
 
745
          /* Cache the node we found (if it wasn't already cached). */
 
746
          if (! cached_node)
 
747
            dag_node_cache_set (root, path_so_far, child);
 
748
        }
 
749
      
 
750
      /* Are we finished traversing the path?  */
 
751
      if (! next)
 
752
        break;
 
753
      
 
754
      /* The path isn't finished yet; we'd better be in a directory.  */
 
755
      if (svn_fs_fs__dag_node_kind (child) != svn_node_dir)
 
756
        SVN_ERR_W (svn_fs_fs__err_not_directory (fs, path_so_far),
 
757
                   apr_psprintf (pool, _("Failure opening '%s'"), path));
 
758
      
 
759
      rest = next;
 
760
      here = child;
 
761
    }
 
762
 
 
763
  *parent_path_p = parent_path;
 
764
  return SVN_NO_ERROR;
 
765
}
 
766
 
 
767
 
 
768
/* Make the node referred to by PARENT_PATH mutable, if it isn't
 
769
   already, allocating from POOL.  ROOT must be the root from which
 
770
   PARENT_PATH descends.  Clone any parent directories as needed.
 
771
   Adjust the dag nodes in PARENT_PATH to refer to the clones.  Use
 
772
   ERROR_PATH in error messages.  */
 
773
static svn_error_t *
 
774
make_path_mutable (svn_fs_root_t *root,
 
775
                   parent_path_t *parent_path,
 
776
                   const char *error_path,
 
777
                   apr_pool_t *pool)
 
778
{
 
779
  dag_node_t *clone;
 
780
  const char *txn_id = root->txn;
 
781
 
 
782
  /* Is the node mutable already?  */
 
783
  if (svn_fs_fs__dag_check_mutable (parent_path->node, txn_id))
 
784
    return SVN_NO_ERROR;
 
785
 
 
786
  /* Are we trying to clone the root, or somebody's child node?  */
 
787
  if (parent_path->parent)
 
788
    {
 
789
      const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
 
790
      const char *copy_id = NULL;
 
791
      copy_id_inherit_t inherit = parent_path->copy_inherit;
 
792
      const char *clone_path, *copyroot_path;
 
793
      svn_revnum_t copyroot_rev;
 
794
      svn_boolean_t is_parent_copyroot = FALSE;
 
795
      svn_fs_root_t *copyroot_root;
 
796
      dag_node_t *copyroot_node;
 
797
  
 
798
      /* We're trying to clone somebody's child.  Make sure our parent
 
799
         is mutable.  */
 
800
      SVN_ERR (make_path_mutable (root, parent_path->parent, 
 
801
                                  error_path, pool));
 
802
 
 
803
      switch (inherit)
 
804
        {
 
805
        case copy_id_inherit_parent:
 
806
          parent_id = svn_fs_fs__dag_get_id (parent_path->parent->node);
 
807
          copy_id = svn_fs_fs__id_copy_id (parent_id);
 
808
          break;
 
809
          
 
810
        case copy_id_inherit_new:
 
811
          SVN_ERR (svn_fs_fs__reserve_copy_id (&copy_id, root->fs, txn_id,
 
812
                                               pool));
 
813
          break;
 
814
 
 
815
        case copy_id_inherit_self:
 
816
          copy_id = NULL;
 
817
          break;
 
818
 
 
819
        case copy_id_inherit_unknown:
 
820
        default:
 
821
          abort(); /* uh-oh -- somebody didn't calculate copy-ID
 
822
                      inheritance data. */
 
823
        }
 
824
 
 
825
      /* Determine what copyroot our new child node should use. */
 
826
      SVN_ERR (svn_fs_fs__dag_get_copyroot (&copyroot_rev, &copyroot_path,
 
827
                                            parent_path->node, pool));
 
828
      SVN_ERR (svn_fs_fs__revision_root (&copyroot_root, root->fs,
 
829
                                         copyroot_rev, pool));
 
830
      SVN_ERR (get_dag (&copyroot_node, copyroot_root, copyroot_path, pool));
 
831
 
 
832
      child_id = svn_fs_fs__dag_get_id (parent_path->node);
 
833
      copyroot_id = svn_fs_fs__dag_get_id (copyroot_node);
 
834
      if (strcmp (svn_fs_fs__id_node_id (child_id),
 
835
                  svn_fs_fs__id_node_id (copyroot_id)) != 0)
 
836
        is_parent_copyroot = TRUE;
 
837
      
 
838
      /* Now make this node mutable.  */
 
839
      clone_path = parent_path_path (parent_path->parent, pool);
 
840
      SVN_ERR (svn_fs_fs__dag_clone_child (&clone,
 
841
                                           parent_path->parent->node,
 
842
                                           clone_path,
 
843
                                           parent_path->entry, 
 
844
                                           copy_id, txn_id,
 
845
                                           is_parent_copyroot, 
 
846
                                           pool));
 
847
 
 
848
      /* Update the path cache. */
 
849
      dag_node_cache_set (root, parent_path_path (parent_path, pool), clone);
 
850
    }
 
851
  else
 
852
    {
 
853
      /* We're trying to clone the root directory.  */
 
854
      SVN_ERR (mutable_root_node (&clone, root, error_path, pool));
 
855
    }
 
856
 
 
857
  /* Update the PARENT_PATH link to refer to the clone.  */
 
858
  parent_path->node = clone;
 
859
 
 
860
  return SVN_NO_ERROR;
 
861
}
 
862
 
 
863
 
 
864
/* Open the node identified by PATH in ROOT.  Set DAG_NODE_P to the
 
865
 *node we find, allocated in POOL.  Return the error
 
866
 *SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
 
867
static svn_error_t *
 
868
get_dag (dag_node_t **dag_node_p,
 
869
         svn_fs_root_t *root,
 
870
         const char *path,
 
871
         apr_pool_t *pool)
 
872
{
 
873
  parent_path_t *parent_path;
 
874
  dag_node_t *node = NULL;
 
875
 
 
876
  /* Canonicalize the input PATH. */
 
877
  path = svn_fs_fs__canonicalize_abspath (path, pool);
 
878
 
 
879
  /* If ROOT is a revision root, we'll look for the DAG in our cache. */
 
880
  node = dag_node_cache_get (root, path, pool);
 
881
  if (! node)
 
882
    {
 
883
      /* Call open_path with no flags, as we want this to return an error
 
884
         if the node for which we are searching doesn't exist. */
 
885
      SVN_ERR (open_path (&parent_path, root, path, 0, NULL, pool));
 
886
      node = parent_path->node;
 
887
 
 
888
      /* No need to cache our find -- open_path() will do that for us. */
 
889
    }
 
890
 
 
891
  *dag_node_p = node;
 
892
  return SVN_NO_ERROR;
 
893
}
 
894
 
 
895
 
 
896
 
 
897
/* Populating the `changes' table. */
 
898
 
 
899
/* Add a change to the changes table in FS, keyed on transaction id
 
900
   TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
 
901
   PATH (whose node revision id is--or was, in the case of a
 
902
   deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
 
903
   occurred.  If the change resulted from a copy, COPYFROM_REV and
 
904
   COPYFROM_PATH specify under which revision and path the node was
 
905
   copied from.  If this was not part of a copy, COPYFROM_REV should
 
906
   be SVN_INVALID_REVNUM.  Do all this as part of POOL.  */
 
907
static svn_error_t *
 
908
add_change (svn_fs_t *fs,
 
909
            const char *txn_id,
 
910
            const char *path,
 
911
            const svn_fs_id_t *noderev_id,
 
912
            svn_fs_path_change_kind_t change_kind,
 
913
            svn_boolean_t text_mod,
 
914
            svn_boolean_t prop_mod,
 
915
            svn_revnum_t copyfrom_rev,
 
916
            const char *copyfrom_path,
 
917
            apr_pool_t *pool)
 
918
{
 
919
  SVN_ERR (svn_fs_fs__add_change (fs, txn_id,
 
920
                                  svn_fs_fs__canonicalize_abspath (path, pool),
 
921
                                  noderev_id, change_kind, text_mod, prop_mod,
 
922
                                  copyfrom_rev, copyfrom_path,
 
923
                                  pool));
 
924
 
 
925
  return SVN_NO_ERROR;
 
926
}
 
927
 
 
928
 
 
929
 
 
930
/* Generic node operations.  */
 
931
 
 
932
/* Get the id of a node referenced by path PATH in ROOT.  Return the
 
933
   id in *ID_P allocated in POOL. */
 
934
static svn_error_t *
 
935
fs_node_id (const svn_fs_id_t **id_p,
 
936
            svn_fs_root_t *root,
 
937
            const char *path,
 
938
            apr_pool_t *pool)
 
939
{
 
940
  fs_root_data_t *frd = root->fsap_data;
 
941
  
 
942
  if ((! root->is_txn_root)
 
943
      && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
 
944
    {
 
945
      /* Optimize the case where we don't need any db access at all. 
 
946
         The root directory ("" or "/") node is stored in the
 
947
         svn_fs_root_t object, and never changes when it's a revision
 
948
         root, so we can just reach in and grab it directly. */
 
949
      *id_p = svn_fs_fs__id_copy (svn_fs_fs__dag_get_id (frd->root_dir), pool);
 
950
    }
 
951
  else
 
952
    {
 
953
      dag_node_t *node;
 
954
 
 
955
      SVN_ERR (get_dag (&node, root, path, pool));
 
956
      *id_p = svn_fs_fs__id_copy (svn_fs_fs__dag_get_id (node), pool);
 
957
    }
 
958
  return SVN_NO_ERROR;
 
959
}
 
960
 
 
961
 
 
962
svn_error_t *
 
963
svn_fs_fs__node_created_rev (svn_revnum_t *revision,
 
964
                             svn_fs_root_t *root,
 
965
                             const char *path,
 
966
                             apr_pool_t *pool)
 
967
{
 
968
  dag_node_t *node;
 
969
 
 
970
  SVN_ERR (get_dag (&node, root, path, pool));
 
971
  SVN_ERR (svn_fs_fs__dag_get_revision (revision, node, pool));
 
972
 
 
973
  return SVN_NO_ERROR;
 
974
}
 
975
 
 
976
/* Set *CREATED_PATH to the path at which PATH under ROOT was created.
 
977
   Return a string allocated in POOL. */
 
978
static svn_error_t *
 
979
fs_node_created_path (const char **created_path,
 
980
                      svn_fs_root_t *root,
 
981
                      const char *path,
 
982
                      apr_pool_t *pool)
 
983
{
 
984
  dag_node_t *node;
 
985
 
 
986
  SVN_ERR (get_dag (&node, root, path, pool));
 
987
  *created_path = svn_fs_fs__dag_get_created_path (node);
 
988
  
 
989
  return SVN_NO_ERROR;
 
990
}
 
991
 
 
992
 
 
993
/* Set *KIND_P to the type of node located at PATH under ROOT.
 
994
   Perform temporary allocations in POOL. */
 
995
static svn_error_t *
 
996
node_kind (svn_node_kind_t *kind_p,
 
997
           svn_fs_root_t *root,
 
998
           const char *path,
 
999
           apr_pool_t *pool)
 
1000
{
 
1001
  const svn_fs_id_t *node_id;
 
1002
  dag_node_t *node;
 
1003
 
 
1004
  /* Get the node id. */
 
1005
  SVN_ERR (fs_node_id (&node_id, root, path, pool));
 
1006
    
 
1007
  /* Use the node id to get the real kind. */
 
1008
  SVN_ERR (svn_fs_fs__dag_get_node (&node, root->fs, node_id, pool));
 
1009
  *kind_p = svn_fs_fs__dag_node_kind (node);
 
1010
  
 
1011
  return SVN_NO_ERROR;
 
1012
}
 
1013
 
 
1014
 
 
1015
/* Set *KIND_P to the type of node present at PATH under ROOT.  If
 
1016
   PATH does not exist under ROOT, set *KIND_P to svn_node_none.  Use
 
1017
   POOL for temporary allocation. */
 
1018
svn_error_t *
 
1019
svn_fs_fs__check_path (svn_node_kind_t *kind_p,
 
1020
                       svn_fs_root_t *root,
 
1021
                       const char *path,
 
1022
                       apr_pool_t *pool)
 
1023
{
 
1024
  svn_error_t *err = node_kind (kind_p, root, path, pool);
 
1025
  if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
 
1026
    {
 
1027
      svn_error_clear (err);
 
1028
      *kind_p = svn_node_none;
 
1029
    }
 
1030
  else if (err)
 
1031
    {
 
1032
      return err;
 
1033
    }
 
1034
  return SVN_NO_ERROR;
 
1035
}
 
1036
 
 
1037
/* Set *VALUE_P to the value of the property named PROPNAME of PATH in
 
1038
   ROOT.  If the node has no property by that name, set *VALUE_P to
 
1039
   zero.  Allocate the result in POOL. */
 
1040
static svn_error_t *
 
1041
fs_node_prop (svn_string_t **value_p,
 
1042
              svn_fs_root_t *root,
 
1043
              const char *path,
 
1044
              const char *propname,
 
1045
              apr_pool_t *pool)
 
1046
{
 
1047
  dag_node_t *node;
 
1048
  apr_hash_t *proplist;
 
1049
 
 
1050
  SVN_ERR (get_dag (&node, root, path, pool));
 
1051
  SVN_ERR (svn_fs_fs__dag_get_proplist (&proplist, node, pool));
 
1052
  *value_p = NULL;
 
1053
  if (proplist)
 
1054
    *value_p = apr_hash_get (proplist, propname, APR_HASH_KEY_STRING);
 
1055
 
 
1056
  return SVN_NO_ERROR;
 
1057
}
 
1058
 
 
1059
 
 
1060
/* Set *TABLE_P to the entire property list of PATH under ROOT, as an
 
1061
   APR hash table allocated in POOL.  The resulting property table
 
1062
   maps property names to pointers to svn_string_t objects containing
 
1063
   the property value. */
 
1064
static svn_error_t *
 
1065
fs_node_proplist (apr_hash_t **table_p,
 
1066
                  svn_fs_root_t *root,
 
1067
                  const char *path,
 
1068
                  apr_pool_t *pool)
 
1069
{
 
1070
  apr_hash_t *table;
 
1071
  dag_node_t *node;
 
1072
 
 
1073
  SVN_ERR (get_dag (&node, root, path, pool));
 
1074
  SVN_ERR (svn_fs_fs__dag_get_proplist (&table, node, pool));
 
1075
  *table_p = table ? table : apr_hash_make (pool);
 
1076
  
 
1077
  return SVN_NO_ERROR;
 
1078
}
 
1079
 
 
1080
 
 
1081
/* Change, add, or delete a node's property value.  The node affect is
 
1082
   PATH under ROOT, the property value to modify is NAME, and VALUE
 
1083
   points to either a string value to set the new contents to, or NULL
 
1084
   if the property should be deleted.  Perform temporary allocations
 
1085
   in POOL. */
 
1086
static svn_error_t *
 
1087
fs_change_node_prop (svn_fs_root_t *root,
 
1088
                     const char *path,
 
1089
                     const char *name,
 
1090
                     const svn_string_t *value,
 
1091
                     apr_pool_t *pool)
 
1092
{
 
1093
  parent_path_t *parent_path;
 
1094
  apr_hash_t *proplist;
 
1095
  const char *txn_id;
 
1096
 
 
1097
  if (! root->is_txn_root)
 
1098
    return not_txn (root);
 
1099
  txn_id = root->txn;
 
1100
 
 
1101
  SVN_ERR (open_path (&parent_path, root, path, 0, txn_id, pool));
 
1102
 
 
1103
  /* Check (non-recursively) to see if path is locked; if so, check
 
1104
     that we can use it. */
 
1105
  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
1106
    SVN_ERR (svn_fs_fs__allow_locked_operation (path, root->fs, FALSE, FALSE,
 
1107
                                                pool));
 
1108
 
 
1109
  SVN_ERR (make_path_mutable (root, parent_path, path, pool));
 
1110
  SVN_ERR (svn_fs_fs__dag_get_proplist (&proplist, parent_path->node, pool));
 
1111
 
 
1112
  /* If there's no proplist, but we're just deleting a property, exit now. */
 
1113
  if ((! proplist) && (! value))
 
1114
    return SVN_NO_ERROR;
 
1115
 
 
1116
  /* Now, if there's no proplist, we know we need to make one. */
 
1117
  if (! proplist)
 
1118
    proplist = apr_hash_make (pool);
 
1119
 
 
1120
  /* Set the property. */
 
1121
  apr_hash_set (proplist, name, APR_HASH_KEY_STRING, value);
 
1122
 
 
1123
  /* Overwrite the node's proplist. */
 
1124
  SVN_ERR (svn_fs_fs__dag_set_proplist (parent_path->node, proplist, 
 
1125
                                        txn_id, pool));
 
1126
 
 
1127
  /* Make a record of this modification in the changes table. */
 
1128
  SVN_ERR (add_change (root->fs, txn_id, path,
 
1129
                       svn_fs_fs__dag_get_id (parent_path->node),
 
1130
                       svn_fs_path_change_modify, 0, 1, SVN_INVALID_REVNUM,
 
1131
                       NULL, pool));
 
1132
 
 
1133
  return SVN_NO_ERROR;
 
1134
}
 
1135
 
 
1136
 
 
1137
/* Determine if the properties of two path/root combinations are
 
1138
   different.  Set *CHANGED_P to TRUE if the properties at PATH1 under
 
1139
   ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
 
1140
   Both roots must be in the same filesystem. */
 
1141
static svn_error_t *
 
1142
fs_props_changed (svn_boolean_t *changed_p,
 
1143
                  svn_fs_root_t *root1,
 
1144
                  const char *path1,
 
1145
                  svn_fs_root_t *root2,
 
1146
                  const char *path2,
 
1147
                  apr_pool_t *pool)
 
1148
{
 
1149
  dag_node_t *node1, *node2;
 
1150
  
 
1151
  /* Check that roots are in the same fs. */
 
1152
  if (root1->fs != root2->fs)
 
1153
    return svn_error_create
 
1154
      (SVN_ERR_FS_GENERAL, NULL,
 
1155
       _("Asking props changed in two different filesystems"));
 
1156
  
 
1157
  SVN_ERR (get_dag (&node1, root1, path1, pool));
 
1158
  SVN_ERR (get_dag (&node2, root2, path2, pool));
 
1159
  SVN_ERR (svn_fs_fs__things_different (changed_p, NULL, 
 
1160
                                        node1, node2, pool));
 
1161
  
 
1162
  return SVN_NO_ERROR;
 
1163
}
 
1164
 
 
1165
 
 
1166
 
 
1167
/* Merges and commits. */
 
1168
 
 
1169
/* Set ARGS->node to the root node of ARGS->root.  */
 
1170
static svn_error_t *
 
1171
get_root (dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
 
1172
{
 
1173
  SVN_ERR (get_dag (node, root, "", pool));
 
1174
  return SVN_NO_ERROR;
 
1175
}
 
1176
 
 
1177
 
 
1178
static svn_error_t *
 
1179
update_ancestry (svn_fs_t *fs,
 
1180
                 const svn_fs_id_t *source_id,
 
1181
                 const svn_fs_id_t *target_id,
 
1182
                 const char *txn_id,
 
1183
                 const char *target_path,
 
1184
                 int source_pred_count,
 
1185
                 apr_pool_t *pool)
 
1186
{
 
1187
  node_revision_t *noderev;
 
1188
 
 
1189
  if (svn_fs_fs__id_txn_id (target_id) == NULL)
 
1190
    return svn_error_createf
 
1191
      (SVN_ERR_FS_NOT_MUTABLE, NULL,
 
1192
       _("Unexpected immutable node at '%s'"), target_path);
 
1193
 
 
1194
  SVN_ERR (svn_fs_fs__get_node_revision (&noderev, fs, target_id, pool));
 
1195
  noderev->predecessor_id = source_id;
 
1196
  noderev->predecessor_count = source_pred_count;
 
1197
  if (noderev->predecessor_count != -1)
 
1198
    noderev->predecessor_count++;
 
1199
  SVN_ERR (svn_fs_fs__put_node_revision (fs, target_id, noderev, pool));
 
1200
 
 
1201
  return SVN_NO_ERROR;
 
1202
}
 
1203
 
 
1204
 
 
1205
/* Set the contents of CONFLICT_PATH to PATH, and return an
 
1206
   SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
 
1207
   at PATH.  Perform all allocations in POOL (except the allocation of
 
1208
   CONFLICT_PATH, which should be handled outside this function).  */
 
1209
static svn_error_t *
 
1210
conflict_err (svn_stringbuf_t *conflict_path,
 
1211
              const char *path)
 
1212
{
 
1213
  svn_stringbuf_set (conflict_path, path);
 
1214
  return svn_error_createf (SVN_ERR_FS_CONFLICT, NULL,
 
1215
                            _("Conflict at '%s'"), path);
 
1216
}
 
1217
 
 
1218
 
 
1219
/* Merge changes between ANCESTOR and SOURCE into TARGET.  ANCESTOR
 
1220
 * and TARGET must be distinct node revisions.  TARGET_PATH should
 
1221
 * correspond to TARGET's full path in its filesystem, and is used for
 
1222
 * reporting conflict location.
 
1223
 *
 
1224
 * SOURCE, TARGET, and ANCESTOR are generally directories; this
 
1225
 * function recursively merges the directories' contents.  If any are
 
1226
 * files, this function simply returns an error whenever SOURCE,
 
1227
 * TARGET, and ANCESTOR are all distinct node revisions.
 
1228
 *
 
1229
 * If there are differences between ANCESTOR and SOURCE that conflict
 
1230
 * with changes between ANCESTOR and TARGET, this function returns an
 
1231
 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
 
1232
 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
 
1233
 *
 
1234
 * If there are no conflicting differences, CONFLICT_P is updated to
 
1235
 * the empty string.
 
1236
 *
 
1237
 * CONFLICT_P must point to a valid svn_stringbuf_t.
 
1238
 *
 
1239
 * Do any necessary temporary allocation in POOL.
 
1240
 */
 
1241
static svn_error_t *
 
1242
merge (svn_stringbuf_t *conflict_p,
 
1243
       const char *target_path,
 
1244
       dag_node_t *target,
 
1245
       dag_node_t *source,
 
1246
       dag_node_t *ancestor,
 
1247
       const char *txn_id,
 
1248
       apr_pool_t *pool)
 
1249
{
 
1250
  const svn_fs_id_t *source_id, *target_id, *ancestor_id;
 
1251
  apr_hash_t *s_entries, *t_entries, *a_entries;
 
1252
  apr_hash_index_t *hi;
 
1253
  svn_fs_t *fs;
 
1254
  apr_pool_t *iterpool;
 
1255
  int pred_count;
 
1256
 
 
1257
  /* Make sure everyone comes from the same filesystem. */
 
1258
  fs = svn_fs_fs__dag_get_fs (ancestor);
 
1259
  if ((fs != svn_fs_fs__dag_get_fs (source))
 
1260
      || (fs != svn_fs_fs__dag_get_fs (target)))
 
1261
    {
 
1262
      return svn_error_create
 
1263
        (SVN_ERR_FS_CORRUPT, NULL,
 
1264
         _("Bad merge; ancestor, source, and target not all in same fs"));
 
1265
    }
 
1266
 
 
1267
  /* We have the same fs, now check it. */
 
1268
  SVN_ERR (svn_fs_fs__check_fs (fs));
 
1269
 
 
1270
  source_id   = svn_fs_fs__dag_get_id (source);
 
1271
  target_id   = svn_fs_fs__dag_get_id (target);
 
1272
  ancestor_id = svn_fs_fs__dag_get_id (ancestor);
 
1273
 
 
1274
  /* It's improper to call this function with ancestor == target. */
 
1275
  if (svn_fs_fs__id_eq (ancestor_id, target_id))
 
1276
    {
 
1277
      svn_string_t *id_str = svn_fs_fs__id_unparse (target_id, pool);
 
1278
      return svn_error_createf
 
1279
        (SVN_ERR_FS_GENERAL, NULL,
 
1280
         _("Bad merge; target '%s' has id '%s', same as ancestor"),
 
1281
         target_path, id_str->data);
 
1282
    }
 
1283
 
 
1284
  svn_stringbuf_setempty (conflict_p);
 
1285
 
 
1286
  /* Base cases:
 
1287
   * Either no change made in source, or same change as made in target.
 
1288
   * Both mean nothing to merge here.
 
1289
   */
 
1290
  if (svn_fs_fs__id_eq (ancestor_id, source_id)
 
1291
      || (svn_fs_fs__id_eq (source_id, target_id)))
 
1292
    return SVN_NO_ERROR;
 
1293
 
 
1294
  /* Else proceed, knowing all three are distinct node revisions.
 
1295
   *
 
1296
   * How to merge from this point: 
 
1297
   *
 
1298
   * if (not all 3 are directories)
 
1299
   *   {
 
1300
   *     early exit with conflict;
 
1301
   *   }
 
1302
   *
 
1303
   * // Property changes may only be made to up-to-date
 
1304
   * // directories, because once the client commits the prop
 
1305
   * // change, it bumps the directory's revision, and therefore
 
1306
   * // must be able to depend on there being no other changes to
 
1307
   * // that directory in the repository.
 
1308
   * if (target's property list differs from ancestor's)
 
1309
   *    conflict;
 
1310
   *
 
1311
   * For each entry NAME in the directory ANCESTOR:
 
1312
   *
 
1313
   *   Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
 
1314
   *   the name within ANCESTOR, SOURCE, and TARGET respectively.
 
1315
   *   (Possibly null if NAME does not exist in SOURCE or TARGET.)
 
1316
   *
 
1317
   *   If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
 
1318
   *     No changes were made to this entry while the transaction was in
 
1319
   *     progress, so do nothing to the target.
 
1320
   *
 
1321
   *   Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
 
1322
   *     A change was made to this entry while the transaction was in
 
1323
   *     process, but the transaction did not touch this entry.  Replace
 
1324
   *     TARGET-ENTRY with SOURCE-ENTRY.
 
1325
   *
 
1326
   *   Else:
 
1327
   *     Changes were made to this entry both within the transaction and
 
1328
   *     to the repository while the transaction was in progress.  They
 
1329
   *     must be merged or declared to be in conflict.
 
1330
   *
 
1331
   *     If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
 
1332
   *     double delete; flag a conflict.
 
1333
   *
 
1334
   *     If any of the three entries is of type file, declare a conflict.
 
1335
   *
 
1336
   *     If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
 
1337
   *     modification of ANCESTOR-ENTRY (determine by comparing the
 
1338
   *     node-id fields), declare a conflict.  A replacement is
 
1339
   *     incompatible with a modification or other replacement--even
 
1340
   *     an identical replacement.
 
1341
   *
 
1342
   *     Direct modifications were made to the directory ANCESTOR-ENTRY
 
1343
   *     in both SOURCE and TARGET.  Recursively merge these
 
1344
   *     modifications.
 
1345
   *
 
1346
   * For each leftover entry NAME in the directory SOURCE:
 
1347
   *
 
1348
   *   If NAME exists in TARGET, declare a conflict.  Even if SOURCE and
 
1349
   *   TARGET are adding exactly the same thing, two additions are not
 
1350
   *   auto-mergeable with each other.
 
1351
   *
 
1352
   *   Add NAME to TARGET with the entry from SOURCE.
 
1353
   *
 
1354
   * Now that we are done merging the changes from SOURCE into the
 
1355
   * directory TARGET, update TARGET's predecessor to be SOURCE.
 
1356
   */
 
1357
 
 
1358
  if ((svn_fs_fs__dag_node_kind (source) != svn_node_dir)
 
1359
      || (svn_fs_fs__dag_node_kind (target) != svn_node_dir)
 
1360
      || (svn_fs_fs__dag_node_kind (ancestor) != svn_node_dir))
 
1361
    {
 
1362
      return conflict_err (conflict_p, target_path);
 
1363
    }
 
1364
 
 
1365
      
 
1366
  /* Possible early merge failure: if target and ancestor have
 
1367
     different property lists, then the merge should fail.
 
1368
     Propchanges can *only* be committed on an up-to-date directory.
 
1369
 
 
1370
     ### TODO: Please see issue #418 about the inelegance of this. */
 
1371
  {
 
1372
    node_revision_t *tgt_nr, *anc_nr;
 
1373
 
 
1374
    /* Get node revisions for our id's. */
 
1375
    
 
1376
    SVN_ERR (svn_fs_fs__get_node_revision (&tgt_nr, fs, target_id, pool));
 
1377
    SVN_ERR (svn_fs_fs__get_node_revision (&anc_nr, fs, ancestor_id, pool));
 
1378
    
 
1379
    /* Now compare the prop-keys of the skels.  Note that just because
 
1380
       the keys are different -doesn't- mean the proplists have
 
1381
       different contents.  But merge() isn't concerned with contents;
 
1382
       it doesn't do a brute-force comparison on textual contents, so
 
1383
       it won't do that here either.  Checking to see if the propkey
 
1384
       atoms are `equal' is enough. */
 
1385
    if (! svn_fs_fs__noderev_same_rep_key (tgt_nr->prop_rep, anc_nr->prop_rep))
 
1386
      {
 
1387
        return conflict_err (conflict_p, target_path);
 
1388
      }
 
1389
  }
 
1390
 
 
1391
  /* ### todo: it would be more efficient to simply check for a NULL
 
1392
     entries hash where necessary below than to allocate an empty hash
 
1393
     here, but another day, another day... */
 
1394
  SVN_ERR (svn_fs_fs__dag_dir_entries (&s_entries, source, pool));
 
1395
  s_entries = svn_fs_fs__copy_dir_entries (s_entries, pool);
 
1396
  SVN_ERR (svn_fs_fs__dag_dir_entries (&t_entries, target, pool));
 
1397
  t_entries = svn_fs_fs__copy_dir_entries (t_entries, pool);
 
1398
  SVN_ERR (svn_fs_fs__dag_dir_entries (&a_entries, ancestor, pool));
 
1399
  a_entries = svn_fs_fs__copy_dir_entries (a_entries, pool);
 
1400
 
 
1401
  /* for each entry E in a_entries... */
 
1402
  iterpool = svn_pool_create (pool);
 
1403
  for (hi = apr_hash_first (pool, a_entries); 
 
1404
       hi; 
 
1405
       hi = apr_hash_next (hi))
 
1406
    {
 
1407
      svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
 
1408
 
 
1409
      const void *key;
 
1410
      void *val;
 
1411
      apr_ssize_t klen;
 
1412
          
 
1413
      svn_pool_clear (iterpool);
 
1414
 
 
1415
      /* KEY will be the entry name in ancestor, VAL the dirent */
 
1416
      apr_hash_this (hi, &key, &klen, &val);
 
1417
      a_entry = val;
 
1418
 
 
1419
      s_entry = apr_hash_get (s_entries, key, klen);
 
1420
      t_entry = apr_hash_get (t_entries, key, klen);
 
1421
 
 
1422
      /* No changes were made to this entry while the transaction was
 
1423
         in progress, so do nothing to the target. */
 
1424
      if (s_entry && svn_fs_fs__id_eq (a_entry->id, s_entry->id))
 
1425
        goto end;
 
1426
 
 
1427
      /* A change was made to this entry while the transaction was in
 
1428
         process, but the transaction did not touch this entry. */
 
1429
      else if (t_entry && svn_fs_fs__id_eq (a_entry->id, t_entry->id))
 
1430
        {
 
1431
          if (s_entry)
 
1432
            {
 
1433
              SVN_ERR (svn_fs_fs__dag_set_entry (target, key,
 
1434
                                                 s_entry->id,
 
1435
                                                 s_entry->kind,
 
1436
                                                 txn_id,
 
1437
                                                 pool));
 
1438
            }
 
1439
          else
 
1440
            {
 
1441
              SVN_ERR (svn_fs_fs__dag_delete (target, key, txn_id, pool));
 
1442
            }
 
1443
        }
 
1444
 
 
1445
      /* Changes were made to this entry both within the transaction
 
1446
         and to the repository while the transaction was in progress.
 
1447
         They must be merged or declared to be in conflict. */
 
1448
      else
 
1449
        {
 
1450
          dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
 
1451
          const char *new_tpath;
 
1452
 
 
1453
          /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
 
1454
             double delete; flag a conflict. */
 
1455
          if (s_entry == NULL || t_entry == NULL)
 
1456
            return conflict_err (conflict_p,
 
1457
                                 svn_path_join (target_path,
 
1458
                                                a_entry->name,
 
1459
                                                iterpool));
 
1460
 
 
1461
          /* If any of the three entries is of type file, flag a conflict. */
 
1462
          if (s_entry->kind == svn_node_file
 
1463
              || t_entry->kind == svn_node_file
 
1464
              || a_entry->kind == svn_node_file)
 
1465
            return conflict_err (conflict_p,
 
1466
                                 svn_path_join (target_path,
 
1467
                                                a_entry->name,
 
1468
                                                iterpool));
 
1469
 
 
1470
          /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
 
1471
             modification of ANCESTOR-ENTRY, declare a conflict. */
 
1472
          if (strcmp (svn_fs_fs__id_node_id (s_entry->id),
 
1473
                       svn_fs_fs__id_node_id (a_entry->id)) != 0
 
1474
              || strcmp (svn_fs_fs__id_copy_id (s_entry->id),
 
1475
                          svn_fs_fs__id_copy_id (a_entry->id)) != 0
 
1476
              || strcmp (svn_fs_fs__id_node_id (t_entry->id),
 
1477
                         svn_fs_fs__id_node_id (a_entry->id)) != 0
 
1478
              || strcmp (svn_fs_fs__id_copy_id (t_entry->id),
 
1479
                         svn_fs_fs__id_copy_id (a_entry->id)) != 0)
 
1480
            return conflict_err (conflict_p,
 
1481
                                 svn_path_join (target_path,
 
1482
                                                a_entry->name,
 
1483
                                                iterpool));
 
1484
 
 
1485
          /* Direct modifications were made to the directory
 
1486
             ANCESTOR-ENTRY in both SOURCE and TARGET.  Recursively
 
1487
             merge these modifications. */
 
1488
          SVN_ERR (svn_fs_fs__dag_get_node (&s_ent_node, fs,
 
1489
                                            s_entry->id, iterpool));
 
1490
          SVN_ERR (svn_fs_fs__dag_get_node (&t_ent_node, fs,
 
1491
                                            t_entry->id, iterpool));
 
1492
          SVN_ERR (svn_fs_fs__dag_get_node (&a_ent_node, fs,
 
1493
                                            a_entry->id, iterpool));
 
1494
          new_tpath = svn_path_join (target_path, t_entry->name, iterpool);
 
1495
          SVN_ERR (merge (conflict_p, new_tpath,
 
1496
                          t_ent_node, s_ent_node, a_ent_node,
 
1497
                          txn_id, iterpool));
 
1498
        }
 
1499
 
 
1500
      /* We've taken care of any possible implications E could have.
 
1501
         Remove it from source_entries, so it's easy later to loop
 
1502
         over all the source entries that didn't exist in
 
1503
         ancestor_entries. */
 
1504
    end:
 
1505
      apr_hash_set (s_entries, key, klen, NULL);
 
1506
    }
 
1507
 
 
1508
  /* For each entry E in source but not in ancestor */
 
1509
  for (hi = apr_hash_first (pool, s_entries); 
 
1510
       hi; 
 
1511
       hi = apr_hash_next (hi))
 
1512
    {
 
1513
      svn_fs_dirent_t *s_entry, *t_entry;
 
1514
      const void *key;
 
1515
      void *val;
 
1516
      apr_ssize_t klen;
 
1517
 
 
1518
      svn_pool_clear (iterpool);
 
1519
 
 
1520
      apr_hash_this (hi, &key, &klen, &val);
 
1521
      s_entry = val;
 
1522
      t_entry = apr_hash_get (t_entries, key, klen);
 
1523
 
 
1524
      /* If NAME exists in TARGET, declare a conflict. */
 
1525
      if (t_entry)
 
1526
        return conflict_err (conflict_p,
 
1527
                             svn_path_join (target_path,
 
1528
                                            t_entry->name,
 
1529
                                            iterpool));
 
1530
 
 
1531
      SVN_ERR (svn_fs_fs__dag_set_entry
 
1532
               (target, s_entry->name, s_entry->id, s_entry->kind,
 
1533
                txn_id, iterpool));
 
1534
    }
 
1535
  apr_pool_destroy (iterpool);
 
1536
 
 
1537
  SVN_ERR (svn_fs_fs__dag_get_predecessor_count (&pred_count, source, pool));
 
1538
  SVN_ERR (update_ancestry (fs, source_id, target_id, txn_id, target_path,
 
1539
                            pred_count, pool));
 
1540
 
 
1541
  return SVN_NO_ERROR;
 
1542
}
 
1543
 
 
1544
/* Merge changes between an ancestor and BATON->source_node into
 
1545
   BATON->txn.  The ancestor is either BATON->ancestor_node, or if
 
1546
   that is null, BATON->txn's base node.
 
1547
 
 
1548
   If the merge is successful, BATON->txn's base will become
 
1549
   BATON->source_node, and its root node will have a new ID, a
 
1550
   successor of BATON->source_node. */
 
1551
static svn_error_t *
 
1552
merge_changes (dag_node_t *ancestor_node,
 
1553
               dag_node_t *source_node,
 
1554
               svn_fs_txn_t *txn,
 
1555
               svn_stringbuf_t *conflict,
 
1556
               apr_pool_t *pool)
 
1557
{
 
1558
  dag_node_t *txn_root_node;
 
1559
  const svn_fs_id_t *source_id;
 
1560
  svn_fs_t *fs = txn->fs;
 
1561
  const char *txn_id = txn->id;
 
1562
 
 
1563
  source_id = svn_fs_fs__dag_get_id (source_node);
 
1564
  
 
1565
  SVN_ERR (svn_fs_fs__dag_txn_root (&txn_root_node, fs, txn_id, pool));
 
1566
 
 
1567
  if (ancestor_node == NULL)
 
1568
    {
 
1569
      SVN_ERR (svn_fs_fs__dag_txn_base_root (&ancestor_node, fs,
 
1570
                                             txn_id, pool));
 
1571
    }
 
1572
  
 
1573
  if (svn_fs_fs__id_eq (svn_fs_fs__dag_get_id (ancestor_node),
 
1574
                     svn_fs_fs__dag_get_id (txn_root_node)))
 
1575
    {
 
1576
      /* If no changes have been made in TXN since its current base,
 
1577
         then it can't conflict with any changes since that base.  So
 
1578
         we just set *both* its base and root to source, making TXN
 
1579
         in effect a repeat of source. */
 
1580
      
 
1581
      /* ### kff todo: this would, of course, be a mighty silly thing
 
1582
         for the caller to do, and we might want to consider whether
 
1583
         this response is really appropriate. */
 
1584
      abort ();
 
1585
    }
 
1586
  else
 
1587
    SVN_ERR (merge (conflict, "/", txn_root_node,
 
1588
                    source_node, ancestor_node, txn_id, pool));
 
1589
 
 
1590
  return SVN_NO_ERROR;
 
1591
}
 
1592
 
 
1593
 
 
1594
/* Note:  it is acceptable for this function to call back into
 
1595
   public FS API interfaces because it does not itself use trails.  */
 
1596
svn_error_t *
 
1597
svn_fs_fs__commit_txn (const char **conflict_p,
 
1598
                       svn_revnum_t *new_rev_p, 
 
1599
                       svn_fs_txn_t *txn,
 
1600
                       apr_pool_t *pool)
 
1601
{
 
1602
  /* How do commits work in Subversion?
 
1603
   *
 
1604
   * When you're ready to commit, here's what you have:
 
1605
   *
 
1606
   *    1. A transaction, with a mutable tree hanging off it.
 
1607
   *    2. A base revision, against which TXN_TREE was made.
 
1608
   *    3. A latest revision, which may be newer than the base rev.
 
1609
   *
 
1610
   * The problem is that if latest != base, then one can't simply
 
1611
   * attach the txn root as the root of the new revision, because that
 
1612
   * would lose all the changes between base and latest.  It is also
 
1613
   * not acceptable to insist that base == latest; in a busy
 
1614
   * repository, commits happen too fast to insist that everyone keep
 
1615
   * their entire tree up-to-date at all times.  Non-overlapping
 
1616
   * changes should not interfere with each other.
 
1617
   *
 
1618
   * The solution is to merge the changes between base and latest into
 
1619
   * the txn tree [see the function merge()].  The txn tree is the
 
1620
   * only one of the three trees that is mutable, so it has to be the
 
1621
   * one to adjust.
 
1622
   *
 
1623
   * You might have to adjust it more than once, if a new latest
 
1624
   * revision gets committed while you were merging in the previous
 
1625
   * one.  For example:
 
1626
   *
 
1627
   *    1. Jane starts txn T, based at revision 6.
 
1628
   *    2. Someone commits (or already committed) revision 7.
 
1629
   *    3. Jane's starts merging the changes between 6 and 7 into T.
 
1630
   *    4. Meanwhile, someone commits revision 8.
 
1631
   *    5. Jane finishes the 6-->7 merge.  T could now be committed
 
1632
   *       against a latest revision of 7, if only that were still the
 
1633
   *       latest.  Unfortunately, 8 is now the latest, so... 
 
1634
   *    6. Jane starts merging the changes between 7 and 8 into T.
 
1635
   *    7. Meanwhile, no one commits any new revisions.  Whew.
 
1636
   *    8. Jane commits T, creating revision 9, whose tree is exactly
 
1637
   *       T's tree, except immutable now.
 
1638
   *
 
1639
   * Lather, rinse, repeat.
 
1640
   */
 
1641
 
 
1642
  svn_error_t *err;
 
1643
  svn_revnum_t new_rev;
 
1644
  svn_fs_t *fs = txn->fs;
 
1645
 
 
1646
  /* Initialize output params. */
 
1647
  new_rev = SVN_INVALID_REVNUM;
 
1648
  if (conflict_p)
 
1649
    *conflict_p = NULL;
 
1650
 
 
1651
  while (1729)
 
1652
    {
 
1653
      svn_revnum_t youngish_rev;
 
1654
      svn_fs_root_t *youngish_root;
 
1655
      dag_node_t *youngish_root_node;
 
1656
      svn_stringbuf_t *conflict = svn_stringbuf_create ("", pool);
 
1657
 
 
1658
      /* Get the *current* youngest revision, in one short-lived
 
1659
         Berkeley transaction.  (We don't want the revisions table
 
1660
         locked while we do the main merge.)  We call it "youngish"
 
1661
         because new revisions might get committed after we've
 
1662
         obtained it. */
 
1663
 
 
1664
      SVN_ERR (svn_fs_fs__youngest_rev (&youngish_rev, fs, pool));
 
1665
      SVN_ERR (svn_fs_fs__revision_root (&youngish_root, fs, youngish_rev,
 
1666
                                         pool));
 
1667
 
 
1668
      /* Get the dag node for the youngest revision, also in one
 
1669
         Berkeley transaction.  Later we'll use it as the SOURCE
 
1670
         argument to a merge, and if the merge succeeds, this youngest
 
1671
         root node will become the new base root for the svn txn that
 
1672
         was the target of the merge (but note that the youngest rev
 
1673
         may have changed by then -- that's why we're careful to get
 
1674
         this root in its own bdb txn here). */
 
1675
      SVN_ERR (get_root (&youngish_root_node, youngish_root, pool));
 
1676
      
 
1677
      /* Try to merge.  If the merge succeeds, the base root node of
 
1678
         TARGET's txn will become the same as youngish_root_node, so
 
1679
         any future merges will only be between that node and whatever
 
1680
         the root node of the youngest rev is by then. */ 
 
1681
      err = merge_changes (NULL, youngish_root_node, txn, conflict, pool);
 
1682
      if (err)
 
1683
        {
 
1684
          if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
 
1685
            *conflict_p = conflict->data;
 
1686
          return err;
 
1687
        }
 
1688
      txn->base_rev = youngish_rev;
 
1689
 
 
1690
      /* Try to commit. */
 
1691
      err = svn_fs_fs__commit (&new_rev, fs, txn, pool);
 
1692
      if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
 
1693
        {
 
1694
          /* Did someone else finish committing a new revision while we
 
1695
             were in mid-merge or mid-commit?  If so, we'll need to
 
1696
             loop again to merge the new changes in, then try to
 
1697
             commit again.  Or if that's not what happened, then just
 
1698
             return the error. */
 
1699
          svn_revnum_t youngest_rev;
 
1700
          SVN_ERR (svn_fs_fs__youngest_rev (&youngest_rev, fs, pool));
 
1701
          if (youngest_rev == youngish_rev)
 
1702
            return err;
 
1703
          else
 
1704
            svn_error_clear (err);
 
1705
        }
 
1706
      else if (err)
 
1707
        {
 
1708
          return err;
 
1709
        }
 
1710
      else
 
1711
        {
 
1712
          /* Set the return value -- our brand spankin' new revision! */
 
1713
          *new_rev_p = new_rev;
 
1714
          return SVN_NO_ERROR;
 
1715
        }
 
1716
    }
 
1717
 
 
1718
  return SVN_NO_ERROR;
 
1719
}
 
1720
 
 
1721
 
 
1722
/* Merge changes between two nodes into a third node.  Given nodes
 
1723
   SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
 
1724
   ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
 
1725
   changes between the ancestor and source.  If there are conflicts,
 
1726
   return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
 
1727
   description of the offending changes.  Perform any temporary
 
1728
   allocations in POOL. */
 
1729
static svn_error_t *
 
1730
fs_merge (const char **conflict_p,
 
1731
          svn_fs_root_t *source_root,
 
1732
          const char *source_path,
 
1733
          svn_fs_root_t *target_root,
 
1734
          const char *target_path,
 
1735
          svn_fs_root_t *ancestor_root,
 
1736
          const char *ancestor_path,
 
1737
          apr_pool_t *pool)
 
1738
{
 
1739
  dag_node_t *source, *ancestor;
 
1740
  svn_fs_txn_t *txn;
 
1741
  svn_error_t *err;
 
1742
  svn_stringbuf_t *conflict = svn_stringbuf_create ("", pool);
 
1743
 
 
1744
  if (! target_root->is_txn_root)
 
1745
    return not_txn (target_root);
 
1746
 
 
1747
  /* Paranoia. */
 
1748
  if ((source_root->fs != ancestor_root->fs)
 
1749
      || (target_root->fs != ancestor_root->fs))
 
1750
    {
 
1751
      return svn_error_create
 
1752
        (SVN_ERR_FS_CORRUPT, NULL,
 
1753
         _("Bad merge; ancestor, source, and target not all in same fs"));
 
1754
    }
 
1755
 
 
1756
  /* ### kff todo: is there any compelling reason to get the nodes in
 
1757
     one db transaction?  Right now we don't; txn_body_get_root() gets
 
1758
     one node at a time.  This will probably need to change:
 
1759
 
 
1760
     Jim Blandy <jimb@zwingli.cygnus.com> writes:
 
1761
     > svn_fs_merge needs to be a single transaction, to protect it against
 
1762
     > people deleting parents of nodes it's working on, etc.
 
1763
  */
 
1764
 
 
1765
  /* Get the ancestor node. */
 
1766
  SVN_ERR (get_root (&ancestor, ancestor_root, pool));
 
1767
 
 
1768
  /* Get the source node. */
 
1769
  SVN_ERR (get_root (&source, source_root, pool));
 
1770
  
 
1771
  /* Open a txn for the txn root into which we're merging. */
 
1772
  SVN_ERR (svn_fs_fs__open_txn (&txn, ancestor_root->fs, target_root->txn,
 
1773
                                pool));
 
1774
 
 
1775
  /* Merge changes between ANCESTOR and SOURCE into TXN. */
 
1776
  err = merge_changes (ancestor, source, txn, conflict, pool);
 
1777
  if (err)
 
1778
    {
 
1779
      if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
 
1780
        *conflict_p = conflict->data;
 
1781
      return err;
 
1782
    }
 
1783
 
 
1784
  return SVN_NO_ERROR;
 
1785
}
 
1786
 
 
1787
svn_error_t *
 
1788
svn_fs_fs__deltify (svn_fs_t *fs,
 
1789
                    svn_revnum_t revision,
 
1790
                    apr_pool_t *pool)
 
1791
{
 
1792
  /* Deltify is a no-op for fs_fs. */
 
1793
 
 
1794
  return SVN_NO_ERROR;
 
1795
}
 
1796
 
 
1797
 
 
1798
 
 
1799
/* Directories.  */
 
1800
 
 
1801
/* Set *TABLE_P to a newly allocated APR hash table containing the
 
1802
   entries of the directory at PATH in ROOT.  The keys of the table
 
1803
   are entry names, as byte strings, excluding the final null
 
1804
   character; the table's values are pointers to svn_fs_dirent_t
 
1805
   structures.  Allocate the table and its contents in POOL. */
 
1806
static svn_error_t *
 
1807
fs_dir_entries (apr_hash_t **table_p,
 
1808
                svn_fs_root_t *root,
 
1809
                const char *path,
 
1810
                apr_pool_t *pool)
 
1811
{
 
1812
  dag_node_t *node;
 
1813
  apr_hash_t *entries;
 
1814
 
 
1815
  /* Get the entries for this path and copy them into the callers's
 
1816
     pool. */
 
1817
  SVN_ERR (get_dag (&node, root, path, pool));
 
1818
  SVN_ERR (svn_fs_fs__dag_dir_entries (&entries, node, pool));
 
1819
  *table_p = svn_fs_fs__copy_dir_entries (entries, pool);
 
1820
  return SVN_NO_ERROR;
 
1821
}
 
1822
 
 
1823
 
 
1824
/* Create a new directory named PATH in ROOT.  The new directory has
 
1825
   no entries, and no properties.  ROOT must be the root of a
 
1826
   transaction, not a revision.  Do any necessary temporary allocation
 
1827
   in POOL.  */
 
1828
static svn_error_t *
 
1829
fs_make_dir (svn_fs_root_t *root,
 
1830
             const char *path,
 
1831
             apr_pool_t *pool)
 
1832
{
 
1833
  parent_path_t *parent_path;
 
1834
  dag_node_t *sub_dir;
 
1835
  const char *txn_id = root->txn;
 
1836
 
 
1837
  SVN_ERR (open_path (&parent_path, root, path, open_path_last_optional,
 
1838
                      txn_id, pool));
 
1839
 
 
1840
  /* Check (recursively) to see if some lock is 'reserving' a path at
 
1841
     that location, or even some child-path; if so, check that we can
 
1842
     use it. */
 
1843
  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
1844
    SVN_ERR (svn_fs_fs__allow_locked_operation (path, root->fs, TRUE, FALSE,
 
1845
                                                pool));
 
1846
 
 
1847
  /* If there's already a sub-directory by that name, complain.  This
 
1848
     also catches the case of trying to make a subdirectory named `/'.  */
 
1849
  if (parent_path->node)
 
1850
    return already_exists (root, path);
 
1851
 
 
1852
  /* Create the subdirectory.  */
 
1853
  SVN_ERR (make_path_mutable (root, parent_path->parent, path, pool));
 
1854
  SVN_ERR (svn_fs_fs__dag_make_dir (&sub_dir,
 
1855
                                    parent_path->parent->node, 
 
1856
                                    parent_path_path (parent_path->parent, 
 
1857
                                                      pool),
 
1858
                                    parent_path->entry,
 
1859
                                    txn_id,
 
1860
                                    pool));
 
1861
 
 
1862
  /* Add this directory to the path cache. */
 
1863
  dag_node_cache_set (root, parent_path_path (parent_path, pool), sub_dir);
 
1864
 
 
1865
  /* Make a record of this modification in the changes table. */
 
1866
  SVN_ERR (add_change (root->fs, txn_id, path, svn_fs_fs__dag_get_id (sub_dir),
 
1867
                       svn_fs_path_change_add, 0, 0, SVN_INVALID_REVNUM, NULL,
 
1868
                       pool));
 
1869
 
 
1870
  return SVN_NO_ERROR;
 
1871
}
 
1872
                              
 
1873
 
 
1874
/* Delete the node at PATH under ROOT.  ROOT must be a transaction
 
1875
   root.  Perform temporary allocations in POOL. */
 
1876
static svn_error_t *
 
1877
fs_delete_node (svn_fs_root_t *root,
 
1878
                const char *path,
 
1879
                apr_pool_t *pool)
 
1880
{
 
1881
  parent_path_t *parent_path;
 
1882
  const char *txn_id = root->txn;
 
1883
 
 
1884
  if (! root->is_txn_root)
 
1885
    return not_txn (root);
 
1886
 
 
1887
  SVN_ERR (open_path (&parent_path, root, path, 0, txn_id, pool));
 
1888
 
 
1889
  /* We can't remove the root of the filesystem.  */
 
1890
  if (! parent_path->parent)
 
1891
    return svn_error_create (SVN_ERR_FS_ROOT_DIR, NULL,
 
1892
                             _("The root directory cannot be deleted"));
 
1893
 
 
1894
  /* Check to see if path (or any child thereof) is locked; if so,
 
1895
     check that we can use the existing lock(s). */
 
1896
  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
1897
    SVN_ERR (svn_fs_fs__allow_locked_operation (path, root->fs, TRUE, FALSE,
 
1898
                                                pool));
 
1899
 
 
1900
  /* Make the parent directory mutable, and do the deletion.  */
 
1901
  SVN_ERR (make_path_mutable (root, parent_path->parent, path, pool));
 
1902
  SVN_ERR (svn_fs_fs__dag_delete (parent_path->parent->node,
 
1903
                                  parent_path->entry,
 
1904
                                  txn_id, pool));
 
1905
 
 
1906
  /* Remove this node and any children from the path cache. */
 
1907
  dag_node_cache_invalidate (root, parent_path_path (parent_path, pool));
 
1908
 
 
1909
  /* Make a record of this modification in the changes table. */
 
1910
  SVN_ERR (add_change (root->fs, txn_id, path,
 
1911
                       svn_fs_fs__dag_get_id (parent_path->node),
 
1912
                       svn_fs_path_change_delete, 0, 0, SVN_INVALID_REVNUM,
 
1913
                       NULL, pool));
 
1914
  
 
1915
  return SVN_NO_ERROR;
 
1916
}
 
1917
 
 
1918
 
 
1919
/* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
 
1920
   TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
 
1921
   the copies table.  Perform temporary allocations in POOL. */
 
1922
static svn_error_t *
 
1923
copy_helper (svn_fs_root_t *from_root,
 
1924
             const char *from_path,
 
1925
             svn_fs_root_t *to_root,
 
1926
             const char *to_path,
 
1927
             svn_boolean_t preserve_history,
 
1928
             apr_pool_t *pool)
 
1929
{
 
1930
  dag_node_t *from_node;
 
1931
  parent_path_t *to_parent_path;
 
1932
  const char *txn_id = to_root->txn;
 
1933
 
 
1934
  assert (from_root->fs == to_root->fs);
 
1935
 
 
1936
  if (from_root->is_txn_root)
 
1937
    return svn_error_create
 
1938
      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
 
1939
       _("Copy from mutable tree not currently supported"));
 
1940
 
 
1941
  /* Get the NODE for FROM_PATH in FROM_ROOT.*/
 
1942
  SVN_ERR (get_dag (&from_node, from_root, from_path, pool));
 
1943
 
 
1944
  /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
 
1945
     component does not exist, it's not that big a deal.  We'll just
 
1946
     make one there. */
 
1947
  SVN_ERR (open_path (&to_parent_path, to_root, to_path, 
 
1948
                      open_path_last_optional, txn_id, pool));
 
1949
 
 
1950
  /* Check to see if path (or any child thereof) is locked; if so,
 
1951
     check that we can use the existing lock(s). */
 
1952
  if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
1953
    SVN_ERR (svn_fs_fs__allow_locked_operation (to_path, to_root->fs, 
 
1954
                                                TRUE, FALSE, pool));
 
1955
  
 
1956
  /* If the destination node already exists as the same node as the
 
1957
     source (in other words, this operation would result in nothing
 
1958
     happening at all), just do nothing an return successfully,
 
1959
     proud that you saved yourself from a tiresome task. */
 
1960
  if (to_parent_path->node &&
 
1961
      svn_fs_fs__id_eq (svn_fs_fs__dag_get_id (from_node),
 
1962
                        svn_fs_fs__dag_get_id (to_parent_path->node)))
 
1963
    return SVN_NO_ERROR;
 
1964
 
 
1965
  if (! from_root->is_txn_root)
 
1966
    {
 
1967
      svn_fs_path_change_kind_t kind;
 
1968
      dag_node_t *new_node;
 
1969
      const char *from_canonpath;
 
1970
 
 
1971
      /* If TO_PATH already existed prior to the copy, note that this
 
1972
         operation is a replacement, not an addition. */
 
1973
      if (to_parent_path->node)
 
1974
        kind = svn_fs_path_change_replace;
 
1975
      else
 
1976
        kind = svn_fs_path_change_add;
 
1977
 
 
1978
      /* Make sure the target node's parents are mutable.  */
 
1979
      SVN_ERR (make_path_mutable (to_root, to_parent_path->parent, 
 
1980
                                  to_path, pool));
 
1981
 
 
1982
      /* Canonicalize the copyfrom path. */
 
1983
      from_canonpath = svn_fs_fs__canonicalize_abspath (from_path, pool);
 
1984
 
 
1985
      SVN_ERR (svn_fs_fs__dag_copy (to_parent_path->parent->node,
 
1986
                                    to_parent_path->entry,
 
1987
                                    from_node,
 
1988
                                    preserve_history,
 
1989
                                    from_root->rev,
 
1990
                                    from_canonpath,
 
1991
                                    txn_id, pool));
 
1992
 
 
1993
      /* Make a record of this modification in the changes table. */
 
1994
      SVN_ERR (get_dag (&new_node, to_root, to_path, pool));
 
1995
      SVN_ERR (add_change (to_root->fs, txn_id, to_path,
 
1996
                           svn_fs_fs__dag_get_id (new_node), kind, 0, 0,
 
1997
                           from_root->rev, from_canonpath, pool));
 
1998
    }
 
1999
  else
 
2000
    {
 
2001
      /* See IZ Issue #436 */
 
2002
      /* Copying from transaction roots not currently available.
 
2003
 
 
2004
         ### cmpilato todo someday: make this not so. :-) Note that
 
2005
         when copying from mutable trees, you have to make sure that
 
2006
         you aren't creating a cyclic graph filesystem, and a simple
 
2007
         referencing operation won't cut it.  Currently, we should not
 
2008
         be able to reach this clause, and the interface reports that
 
2009
         this only works from immutable trees anyway, but JimB has
 
2010
         stated that this requirement need not be necessary in the
 
2011
         future. */
 
2012
 
 
2013
      abort ();
 
2014
    }
 
2015
  
 
2016
  return SVN_NO_ERROR;
 
2017
}
 
2018
 
 
2019
 
 
2020
/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
 
2021
   If FROM_PATH is a directory, copy it recursively.  Temporary
 
2022
   allocations are from POOL.*/
 
2023
static svn_error_t *
 
2024
fs_copy (svn_fs_root_t *from_root,
 
2025
         const char *from_path,
 
2026
         svn_fs_root_t *to_root,
 
2027
         const char *to_path,
 
2028
         apr_pool_t *pool)
 
2029
{
 
2030
  return copy_helper (from_root, from_path, to_root, to_path, TRUE, pool);
 
2031
}
 
2032
 
 
2033
 
 
2034
/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
 
2035
   If FROM_PATH is a directory, copy it recursively.  No history is
 
2036
   preserved.  Temporary allocations are from POOL. */
 
2037
static svn_error_t *
 
2038
fs_revision_link (svn_fs_root_t *from_root,
 
2039
                  svn_fs_root_t *to_root,
 
2040
                  const char *path,
 
2041
                  apr_pool_t *pool)
 
2042
{
 
2043
  if (! to_root->is_txn_root)
 
2044
    return not_txn (to_root);
 
2045
 
 
2046
  return copy_helper (from_root, path, to_root, path, FALSE, pool);
 
2047
}
 
2048
 
 
2049
 
 
2050
/* Discover the copy ancestry of PATH under ROOT.  Return a relevant
 
2051
   ancestor/revision combination in *PATH_P and *REV_P.  Temporary
 
2052
   allocations are in POOL. */
 
2053
static svn_error_t *
 
2054
fs_copied_from (svn_revnum_t *rev_p,
 
2055
                const char **path_p,
 
2056
                svn_fs_root_t *root,
 
2057
                const char *path,
 
2058
                apr_pool_t *pool)
 
2059
{
 
2060
  dag_node_t *node;
 
2061
  const char *copyfrom_path, *copyfrom_str;
 
2062
  svn_revnum_t copyfrom_rev;
 
2063
  fs_root_data_t *frd = root->fsap_data;
 
2064
  char *str, *last_str, *buf;
 
2065
 
 
2066
  /* Check to see if there is a cached version of this copyfrom
 
2067
     entry. */
 
2068
  copyfrom_str = apr_hash_get (frd->copyfrom_cache, path, APR_HASH_KEY_STRING);
 
2069
  if (copyfrom_str)
 
2070
    {
 
2071
      if (strlen (copyfrom_str) == 0)
 
2072
        {
 
2073
          /* We have a cached entry that says there is no copyfrom
 
2074
             here. */
 
2075
          copyfrom_rev = SVN_INVALID_REVNUM;
 
2076
          copyfrom_path = NULL;
 
2077
        }
 
2078
      else
 
2079
        {
 
2080
          /* Parse the copyfrom string for our cached entry. */
 
2081
          buf = apr_pstrdup (pool, copyfrom_str);
 
2082
          str = apr_strtok (buf, " ", &last_str);
 
2083
          copyfrom_rev = atol (str);
 
2084
          copyfrom_path = last_str;
 
2085
        }
 
2086
    }
 
2087
  else
 
2088
    {
 
2089
      /* There is no cached entry, look it up the old-fashioned
 
2090
         way. */
 
2091
      SVN_ERR (get_dag (&node, root, path, pool));
 
2092
      SVN_ERR (svn_fs_fs__dag_get_copyfrom_rev (&copyfrom_rev, node, pool));
 
2093
      SVN_ERR (svn_fs_fs__dag_get_copyfrom_path (&copyfrom_path, node, pool));
 
2094
    }
 
2095
      
 
2096
  *rev_p  = copyfrom_rev;
 
2097
  *path_p = copyfrom_path;
 
2098
 
 
2099
  return SVN_NO_ERROR;
 
2100
}
 
2101
 
 
2102
 
 
2103
 
 
2104
/* Files.  */
 
2105
 
 
2106
/* Create the empty file PATH under ROOT.  Temporary allocations are
 
2107
   in POOL. */
 
2108
static svn_error_t *
 
2109
fs_make_file (svn_fs_root_t *root,
 
2110
              const char *path,
 
2111
              apr_pool_t *pool)
 
2112
{
 
2113
  parent_path_t *parent_path;
 
2114
  dag_node_t *child;
 
2115
  const char *txn_id = root->txn;
 
2116
 
 
2117
  SVN_ERR (open_path (&parent_path, root, path, open_path_last_optional,
 
2118
                      txn_id, pool));
 
2119
 
 
2120
  /* If there's already a file by that name, complain.
 
2121
     This also catches the case of trying to make a file named `/'.  */
 
2122
  if (parent_path->node)
 
2123
    return already_exists (root, path);
 
2124
 
 
2125
  /* Check (non-recursively) to see if path is locked;  if so, check
 
2126
     that we can use it. */
 
2127
  if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
2128
    SVN_ERR (svn_fs_fs__allow_locked_operation (path, root->fs, FALSE, FALSE,
 
2129
                                                pool));
 
2130
 
 
2131
  /* Create the file.  */
 
2132
  SVN_ERR (make_path_mutable (root, parent_path->parent, path, pool));
 
2133
  SVN_ERR (svn_fs_fs__dag_make_file (&child,
 
2134
                                     parent_path->parent->node, 
 
2135
                                     parent_path_path (parent_path->parent,
 
2136
                                                       pool),
 
2137
                                     parent_path->entry,
 
2138
                                     txn_id,
 
2139
                                     pool));
 
2140
 
 
2141
  /* Add this file to the path cache. */
 
2142
  dag_node_cache_set (root, parent_path_path (parent_path, pool), child);
 
2143
 
 
2144
  /* Make a record of this modification in the changes table. */
 
2145
  SVN_ERR (add_change (root->fs, txn_id, path, svn_fs_fs__dag_get_id (child),
 
2146
                       svn_fs_path_change_add, 0, 0, SVN_INVALID_REVNUM, NULL,
 
2147
                       pool));
 
2148
 
 
2149
  return SVN_NO_ERROR;
 
2150
}
 
2151
 
 
2152
 
 
2153
/* Set *LENGTH_P to the size of the file PATH under ROOT.  Temporary
 
2154
   allocations are in POOL. */
 
2155
static svn_error_t *
 
2156
fs_file_length (svn_filesize_t *length_p,
 
2157
                svn_fs_root_t *root,
 
2158
                const char *path,
 
2159
                apr_pool_t *pool)
 
2160
{
 
2161
  dag_node_t *file;
 
2162
  
 
2163
  /* First create a dag_node_t from the root/path pair. */
 
2164
  SVN_ERR (get_dag (&file, root, path, pool));
 
2165
 
 
2166
  /* Now fetch its length */
 
2167
  SVN_ERR (svn_fs_fs__dag_file_length (length_p, file, pool));
 
2168
 
 
2169
  return SVN_NO_ERROR;
 
2170
}
 
2171
 
 
2172
 
 
2173
/* Set DIGEST to the MD5 checksum of PATH under ROOT.  Temporary
 
2174
   allocations are from POOL. */
 
2175
static svn_error_t *
 
2176
fs_file_md5_checksum (unsigned char digest[],
 
2177
                      svn_fs_root_t *root,
 
2178
                      const char *path,
 
2179
                      apr_pool_t *pool)
 
2180
{
 
2181
  dag_node_t *file;
 
2182
  
 
2183
  SVN_ERR (get_dag (&file, root, path, pool));
 
2184
  return svn_fs_fs__dag_file_checksum (digest, file, pool);
 
2185
}
 
2186
 
 
2187
 
 
2188
/* --- Machinery for svn_fs_file_contents() ---  */
 
2189
 
 
2190
/* Set *CONTENTS to a readable stream that will return the contents of
 
2191
   PATH under ROOT.  The stream is allocated in POOL. */
 
2192
static svn_error_t *
 
2193
fs_file_contents (svn_stream_t **contents,
 
2194
                  svn_fs_root_t *root,
 
2195
                  const char *path,
 
2196
                  apr_pool_t *pool)
 
2197
{
 
2198
  dag_node_t *node;
 
2199
  svn_stream_t *file_stream;
 
2200
 
 
2201
  /* First create a dag_node_t from the root/path pair. */
 
2202
  SVN_ERR (get_dag (&node, root, path, pool));
 
2203
  
 
2204
  /* Then create a readable stream from the dag_node_t. */
 
2205
  SVN_ERR (svn_fs_fs__dag_get_contents (&file_stream, node, pool));
 
2206
  
 
2207
  *contents = file_stream;
 
2208
  return SVN_NO_ERROR;
 
2209
}
 
2210
 
 
2211
/* --- End machinery for svn_fs_file_contents() ---  */
 
2212
 
 
2213
 
 
2214
 
 
2215
/* --- Machinery for svn_fs_apply_textdelta() ---  */
 
2216
 
 
2217
 
 
2218
/* Local baton type for all the helper functions below. */
 
2219
typedef struct txdelta_baton_t
 
2220
{
 
2221
  /* This is the custom-built window consumer given to us by the delta
 
2222
     library;  it uniquely knows how to read data from our designated
 
2223
     "source" stream, interpret the window, and write data to our
 
2224
     designated "target" stream (in this case, our repos file.) */
 
2225
  svn_txdelta_window_handler_t interpreter;
 
2226
  void *interpreter_baton;
 
2227
 
 
2228
  /* The original file info */
 
2229
  svn_fs_root_t *root;
 
2230
  const char *path;
 
2231
  
 
2232
  /* Derived from the file info */
 
2233
  dag_node_t *node;
 
2234
 
 
2235
  svn_stream_t *source_stream;
 
2236
  svn_stream_t *target_stream;
 
2237
  svn_stream_t *string_stream;
 
2238
  svn_stringbuf_t *target_string;
 
2239
 
 
2240
  /* Hex MD5 digest for the base text against which a delta is to be
 
2241
     applied, and for the resultant fulltext, respectively.  Either or
 
2242
     both may be null, in which case ignored. */
 
2243
  const char *base_checksum;
 
2244
  const char *result_checksum;
 
2245
 
 
2246
  /* Pool used by db txns */
 
2247
  apr_pool_t *pool;
 
2248
 
 
2249
} txdelta_baton_t;
 
2250
 
 
2251
 
 
2252
/* ### see comment in window_consumer() regarding this function. */
 
2253
 
 
2254
/* Helper function of generic type `svn_write_fn_t'.  Implements a
 
2255
   writable stream which appends to an svn_stringbuf_t. */
 
2256
static svn_error_t *
 
2257
write_to_string (void *baton, const char *data, apr_size_t *len)
 
2258
{
 
2259
  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
 
2260
  svn_stringbuf_appendbytes (tb->target_string, data, *len);
 
2261
  return SVN_NO_ERROR;
 
2262
}
 
2263
 
 
2264
 
 
2265
 
 
2266
/* The main window handler returned by svn_fs_apply_textdelta. */
 
2267
static svn_error_t *
 
2268
window_consumer (svn_txdelta_window_t *window, void *baton)
 
2269
{
 
2270
  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
 
2271
 
 
2272
  /* Send the window right through to the custom window interpreter.
 
2273
     In theory, the interpreter will then write more data to
 
2274
     cb->target_string. */
 
2275
  SVN_ERR (tb->interpreter (window, tb->interpreter_baton));
 
2276
 
 
2277
  /* ### the write_to_string() callback for the txdelta's output stream
 
2278
     ### should be doing all the flush determination logic, not here.
 
2279
     ### in a drastic case, a window could generate a LOT more than the
 
2280
     ### maximum buffer size. we want to flush to the underlying target
 
2281
     ### stream much sooner (e.g. also in a streamy fashion). also, by
 
2282
     ### moving this logic inside the stream, the stream becomes nice
 
2283
     ### and encapsulated: it holds all the logic about buffering and
 
2284
     ### flushing.
 
2285
     ###
 
2286
     ### further: I believe the buffering should be removed from tree.c
 
2287
     ### the buffering should go into the target_stream itself, which
 
2288
     ### is defined by reps-string.c. Specifically, I think the
 
2289
     ### rep_write_contents() function will handle the buffering and
 
2290
     ### the spill to the underlying DB. by locating it there, then
 
2291
     ### anybody who gets a writable stream for FS content can take
 
2292
     ### advantage of the buffering capability. this will be important
 
2293
     ### when we export an FS API function for writing a fulltext into
 
2294
     ### the FS, rather than forcing that fulltext thru apply_textdelta.
 
2295
  */
 
2296
 
 
2297
  /* Check to see if we need to purge the portion of the contents that
 
2298
     have been written thus far. */
 
2299
  if ((! window) || (tb->target_string->len > SVN_FS_WRITE_BUFFER_SIZE))
 
2300
    {
 
2301
      apr_size_t len = tb->target_string->len;
 
2302
      SVN_ERR (svn_stream_write (tb->target_stream,
 
2303
                                 tb->target_string->data,
 
2304
                                 &len));
 
2305
      svn_stringbuf_set (tb->target_string, "");
 
2306
    }
 
2307
 
 
2308
  /* Is the window NULL?  If so, we're done. */
 
2309
  if (! window)
 
2310
    {
 
2311
      /* Close the internal-use stream.  ### This used to be inside of
 
2312
         txn_body_fulltext_finalize_edits(), but that invoked a nested
 
2313
         Berkeley DB transaction -- scandalous! */
 
2314
      SVN_ERR (svn_stream_close (tb->target_stream));
 
2315
 
 
2316
      SVN_ERR (svn_fs_fs__dag_finalize_edits (tb->node, tb->result_checksum,
 
2317
                                              tb->root->txn, tb->pool));
 
2318
    }
 
2319
 
 
2320
  return SVN_NO_ERROR;
 
2321
}
 
2322
 
 
2323
/* Helper function for fs_apply_textdelta.  BATON is of type
 
2324
   txdelta_baton_t. */
 
2325
static svn_error_t *
 
2326
apply_textdelta (void *baton, apr_pool_t *pool)
 
2327
{
 
2328
  txdelta_baton_t *tb = (txdelta_baton_t *) baton;
 
2329
  parent_path_t *parent_path;
 
2330
  const char *txn_id = tb->root->txn;
 
2331
 
 
2332
  /* Call open_path with no flags, as we want this to return an error
 
2333
     if the node for which we are searching doesn't exist. */
 
2334
  SVN_ERR (open_path (&parent_path, tb->root, tb->path, 0, txn_id, pool));
 
2335
 
 
2336
  /* Check (non-recursively) to see if path is locked; if so, check
 
2337
     that we can use it. */
 
2338
  if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
2339
    SVN_ERR (svn_fs_fs__allow_locked_operation (tb->path, tb->root->fs, 
 
2340
                                                FALSE, FALSE, pool));
 
2341
 
 
2342
  /* Now, make sure this path is mutable. */
 
2343
  SVN_ERR (make_path_mutable (tb->root, parent_path, tb->path, pool));
 
2344
  tb->node = parent_path->node;
 
2345
 
 
2346
  if (tb->base_checksum)
 
2347
    {
 
2348
      unsigned char digest[APR_MD5_DIGESTSIZE];
 
2349
      const char *hex;
 
2350
 
 
2351
      /* Until we finalize the node, its data_key points to the old
 
2352
         contents, in other words, the base text. */
 
2353
      SVN_ERR (svn_fs_fs__dag_file_checksum (digest, tb->node, pool));
 
2354
      hex = svn_md5_digest_to_cstring (digest, pool);
 
2355
      if (hex && (strcmp (tb->base_checksum, hex) != 0))
 
2356
        return svn_error_createf
 
2357
          (SVN_ERR_CHECKSUM_MISMATCH, 
 
2358
           NULL,
 
2359
           _("Base checksum mismatch on '%s':\n"
 
2360
             "   expected:  %s\n"
 
2361
             "     actual:  %s\n"),
 
2362
           tb->path, tb->base_checksum, hex);
 
2363
    }
 
2364
 
 
2365
  /* Make a readable "source" stream out of the current contents of
 
2366
     ROOT/PATH; obviously, this must done in the context of a db_txn.
 
2367
     The stream is returned in tb->source_stream. */
 
2368
  SVN_ERR (svn_fs_fs__dag_get_contents (&(tb->source_stream),
 
2369
                                        tb->node, tb->pool));
 
2370
 
 
2371
  /* Make a writable "target" stream */
 
2372
  SVN_ERR (svn_fs_fs__dag_get_edit_stream (&(tb->target_stream), tb->node, 
 
2373
                                           txn_id, tb->pool));
 
2374
 
 
2375
  /* Make a writable "string" stream which writes data to
 
2376
     tb->target_string. */
 
2377
  tb->target_string = svn_stringbuf_create ("", tb->pool);
 
2378
  tb->string_stream = svn_stream_create (tb, tb->pool);
 
2379
  svn_stream_set_write (tb->string_stream, write_to_string);
 
2380
 
 
2381
  /* Now, create a custom window handler that uses our two streams. */
 
2382
  svn_txdelta_apply (tb->source_stream,
 
2383
                     tb->string_stream,
 
2384
                     NULL,
 
2385
                     tb->path,
 
2386
                     tb->pool,
 
2387
                     &(tb->interpreter),
 
2388
                     &(tb->interpreter_baton));
 
2389
 
 
2390
  /* Make a record of this modification in the changes table. */
 
2391
  SVN_ERR (add_change (tb->root->fs, txn_id, tb->path,
 
2392
                       svn_fs_fs__dag_get_id (tb->node),
 
2393
                       svn_fs_path_change_modify, 1, 0, SVN_INVALID_REVNUM,
 
2394
                       NULL, pool));
 
2395
 
 
2396
  return SVN_NO_ERROR;
 
2397
}
 
2398
 
 
2399
 
 
2400
/* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
 
2401
   that will accept text delta windows to modify the contents of PATH
 
2402
   under ROOT.  Allocations are in POOL. */
 
2403
static svn_error_t *
 
2404
fs_apply_textdelta (svn_txdelta_window_handler_t *contents_p,
 
2405
                    void **contents_baton_p,
 
2406
                    svn_fs_root_t *root,
 
2407
                    const char *path,
 
2408
                    const char *base_checksum,
 
2409
                    const char *result_checksum,
 
2410
                    apr_pool_t *pool)
 
2411
{
 
2412
  txdelta_baton_t *tb = apr_pcalloc (pool, sizeof(*tb));
 
2413
 
 
2414
  tb->root = root;
 
2415
  tb->path = path;
 
2416
  tb->pool = pool;
 
2417
 
 
2418
  if (base_checksum)
 
2419
    tb->base_checksum = apr_pstrdup (pool, base_checksum);
 
2420
  else
 
2421
    tb->base_checksum = NULL;
 
2422
 
 
2423
  if (result_checksum)
 
2424
    tb->result_checksum = apr_pstrdup (pool, result_checksum);
 
2425
  else
 
2426
    tb->result_checksum = NULL;
 
2427
 
 
2428
  
 
2429
  SVN_ERR (apply_textdelta(tb, pool));
 
2430
  
 
2431
  *contents_p = window_consumer;
 
2432
  *contents_baton_p = tb;
 
2433
  return SVN_NO_ERROR;
 
2434
}
 
2435
 
 
2436
/* --- End machinery for svn_fs_apply_textdelta() ---  */
 
2437
 
 
2438
/* --- Machinery for svn_fs_apply_text() ---  */
 
2439
 
 
2440
/* Baton for svn_fs_apply_text(). */
 
2441
struct text_baton_t
 
2442
{
 
2443
  /* The original file info */
 
2444
  svn_fs_root_t *root;
 
2445
  const char *path;
 
2446
  
 
2447
  /* Derived from the file info */
 
2448
  dag_node_t *node;
 
2449
  
 
2450
  /* The returned stream that will accept the file's new contents. */
 
2451
  svn_stream_t *stream;
 
2452
 
 
2453
  /* The actual fs stream that the returned stream will write to. */
 
2454
  svn_stream_t *file_stream;
 
2455
 
 
2456
  /* Hex MD5 digest for the final fulltext written to the file.  May
 
2457
     be null, in which case ignored. */
 
2458
  const char *result_checksum;
 
2459
 
 
2460
  /* Pool used by db txns */
 
2461
  apr_pool_t *pool;
 
2462
};
 
2463
 
 
2464
 
 
2465
/* A trail-ready wrapper around svn_fs_fs__dag_finalize_edits, but for
 
2466
 * fulltext data, not text deltas.  Closes BATON->file_stream. 
 
2467
 *
 
2468
 * Note: If you're confused about how this function relates to another
 
2469
 * of similar name, think of it this way:
 
2470
 *
 
2471
 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
 
2472
 * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
 
2473
 */
 
2474
 
 
2475
/* Write function for the publically returned stream. */
 
2476
static svn_error_t *
 
2477
text_stream_writer (void *baton,
 
2478
                    const char *data,
 
2479
                    apr_size_t *len)
 
2480
{
 
2481
  struct text_baton_t *tb = baton;
 
2482
 
 
2483
  /* Psst, here's some data.  Pass it on to the -real- file stream. */
 
2484
  return svn_stream_write (tb->file_stream, data, len);
 
2485
}
 
2486
 
 
2487
/* Close function for the publically returned stream. */
 
2488
static svn_error_t *
 
2489
text_stream_closer (void *baton)
 
2490
{
 
2491
  struct text_baton_t *tb = baton;
 
2492
 
 
2493
  /* Close the internal-use stream.  ### This used to be inside of
 
2494
     txn_body_fulltext_finalize_edits(), but that invoked a nested
 
2495
     Berkeley DB transaction -- scandalous! */
 
2496
  SVN_ERR (svn_stream_close (tb->file_stream));
 
2497
 
 
2498
  /* Need to tell fs that we're done sending text */
 
2499
  SVN_ERR (svn_fs_fs__dag_finalize_edits (tb->node, tb->result_checksum,
 
2500
                                          tb->root->txn, tb->pool));
 
2501
 
 
2502
  return SVN_NO_ERROR;
 
2503
}
 
2504
 
 
2505
 
 
2506
/* Helper function for fs_apply_text.  BATON is of type
 
2507
   text_baton_t. */
 
2508
static svn_error_t *
 
2509
apply_text (void *baton, apr_pool_t *pool)
 
2510
{
 
2511
  struct text_baton_t *tb = baton;
 
2512
  parent_path_t *parent_path;
 
2513
  const char *txn_id = tb->root->txn;
 
2514
 
 
2515
  /* Call open_path with no flags, as we want this to return an error
 
2516
     if the node for which we are searching doesn't exist. */
 
2517
  SVN_ERR (open_path (&parent_path, tb->root, tb->path, 0, txn_id, pool));
 
2518
 
 
2519
  /* Check (non-recursively) to see if path is locked; if so, check
 
2520
     that we can use it. */
 
2521
  if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
 
2522
    SVN_ERR (svn_fs_fs__allow_locked_operation (tb->path, tb->root->fs,
 
2523
                                                FALSE, FALSE, pool));
 
2524
 
 
2525
  /* Now, make sure this path is mutable. */
 
2526
  SVN_ERR (make_path_mutable (tb->root, parent_path, tb->path, pool));
 
2527
  tb->node = parent_path->node;
 
2528
 
 
2529
  /* Make a writable stream for replacing the file's text. */
 
2530
  SVN_ERR (svn_fs_fs__dag_get_edit_stream (&(tb->file_stream), tb->node, 
 
2531
                                           txn_id, tb->pool));
 
2532
 
 
2533
  /* Create a 'returnable' stream which writes to the file_stream. */
 
2534
  tb->stream = svn_stream_create (tb, tb->pool);
 
2535
  svn_stream_set_write (tb->stream, text_stream_writer);
 
2536
  svn_stream_set_close (tb->stream, text_stream_closer);
 
2537
 
 
2538
  /* Make a record of this modification in the changes table. */
 
2539
  SVN_ERR (add_change (tb->root->fs, txn_id, tb->path,
 
2540
                       svn_fs_fs__dag_get_id (tb->node),
 
2541
                       svn_fs_path_change_modify, 1, 0, SVN_INVALID_REVNUM,
 
2542
                       NULL, pool));
 
2543
 
 
2544
  return SVN_NO_ERROR;
 
2545
}
 
2546
 
 
2547
 
 
2548
/* Return a writable stream that will set the contents of PATH under
 
2549
   ROOT.  RESULT_CHECKSUM is the MD5 checksum of the final result.
 
2550
   Temporary allocations are in POOL. */
 
2551
static svn_error_t *
 
2552
fs_apply_text (svn_stream_t **contents_p,
 
2553
               svn_fs_root_t *root,
 
2554
               const char *path,
 
2555
               const char *result_checksum,
 
2556
               apr_pool_t *pool)
 
2557
{
 
2558
  struct text_baton_t *tb = apr_pcalloc (pool, sizeof(*tb));
 
2559
 
 
2560
  tb->root = root;
 
2561
  tb->path = path;
 
2562
  tb->pool = pool;
 
2563
 
 
2564
  if (result_checksum)
 
2565
    tb->result_checksum = apr_pstrdup (pool, result_checksum);
 
2566
  else
 
2567
    tb->result_checksum = NULL;
 
2568
 
 
2569
  SVN_ERR (apply_text (tb, pool));
 
2570
  
 
2571
  *contents_p = tb->stream;
 
2572
  return SVN_NO_ERROR;
 
2573
}
 
2574
 
 
2575
/* --- End machinery for svn_fs_apply_text() ---  */
 
2576
 
 
2577
 
 
2578
/* Check if the contents of PATH1 under ROOT1 are different from the
 
2579
   contents of PATH2 under ROOT2.  If they are different set
 
2580
   *CHANGED_P to TRUE, otherwise set it to FALSE. */
 
2581
static svn_error_t *
 
2582
fs_contents_changed (svn_boolean_t *changed_p,
 
2583
                     svn_fs_root_t *root1,
 
2584
                     const char *path1,
 
2585
                     svn_fs_root_t *root2,
 
2586
                     const char *path2,
 
2587
                     apr_pool_t *pool)
 
2588
{
 
2589
  dag_node_t *node1, *node2;
 
2590
  
 
2591
  /* Check that roots are in the same fs. */
 
2592
  if (root1->fs != root2->fs)
 
2593
    return svn_error_create
 
2594
      (SVN_ERR_FS_GENERAL, NULL,
 
2595
       _("Asking contents changed in two different filesystems"));
 
2596
  
 
2597
  /* Check that both paths are files. */
 
2598
  {
 
2599
    svn_node_kind_t kind;
 
2600
 
 
2601
    SVN_ERR (svn_fs_fs__check_path (&kind, root1, path1, pool));
 
2602
    if (kind != svn_node_file)
 
2603
      return svn_error_createf
 
2604
        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
 
2605
      
 
2606
    SVN_ERR (svn_fs_fs__check_path (&kind, root2, path2, pool));
 
2607
    if (kind != svn_node_file)
 
2608
      return svn_error_createf
 
2609
        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
 
2610
  }
 
2611
 
 
2612
  SVN_ERR (get_dag (&node1, root1, path1, pool));
 
2613
  SVN_ERR (get_dag (&node2, root2, path2, pool));
 
2614
  SVN_ERR (svn_fs_fs__things_different (NULL, changed_p, node1, node2, pool));
 
2615
  
 
2616
  return SVN_NO_ERROR;
 
2617
}
 
2618
 
 
2619
 
 
2620
 
 
2621
/* Public interface to computing file text deltas.  */
 
2622
 
 
2623
/* Note:  it is acceptable for this function to call back into
 
2624
   public FS API interfaces because it does not itself use trails.  */
 
2625
static svn_error_t *
 
2626
fs_get_file_delta_stream (svn_txdelta_stream_t **stream_p,
 
2627
                          svn_fs_root_t *source_root,
 
2628
                          const char *source_path,
 
2629
                          svn_fs_root_t *target_root,
 
2630
                          const char *target_path,
 
2631
                          apr_pool_t *pool)
 
2632
{
 
2633
  svn_stream_t *source, *target;
 
2634
  svn_txdelta_stream_t *delta_stream;
 
2635
 
 
2636
  /* Get read functions for the source file contents.  */
 
2637
  if (source_root && source_path)
 
2638
    SVN_ERR (fs_file_contents (&source, source_root, source_path, pool));
 
2639
  else
 
2640
    source = svn_stream_empty (pool);
 
2641
 
 
2642
  /* Get read functions for the target file contents.  */
 
2643
  SVN_ERR (fs_file_contents (&target, target_root, target_path, pool));
 
2644
 
 
2645
  /* Create a delta stream that turns the ancestor into the target.  */
 
2646
  svn_txdelta (&delta_stream, source, target, pool);
 
2647
 
 
2648
  *stream_p = delta_stream;
 
2649
  return SVN_NO_ERROR;
 
2650
}
 
2651
 
 
2652
 
 
2653
 
 
2654
/* Finding Changes */
 
2655
 
 
2656
/* Set *CHANGED_PATHS_P to a newly allocated hash containing
 
2657
   descriptions of the paths changed under ROOT.  The hash is keyed
 
2658
   with const char * paths an dhas svn_fs_path_change_t * values.  Use
 
2659
   POOL for all allocations. */
 
2660
static svn_error_t *
 
2661
fs_paths_changed (apr_hash_t **changed_paths_p,
 
2662
                  svn_fs_root_t *root,
 
2663
                  apr_pool_t *pool)
 
2664
{
 
2665
  fs_root_data_t *frd = root->fsap_data;
 
2666
 
 
2667
  if (root->is_txn_root)
 
2668
    return svn_fs_fs__txn_changes_fetch (changed_paths_p, root->fs, root->txn,
 
2669
                                         NULL, pool);
 
2670
  else
 
2671
    return svn_fs_fs__paths_changed (changed_paths_p, root->fs, root->rev,
 
2672
                                     frd->copyfrom_cache, pool);
 
2673
}
 
2674
 
 
2675
 
 
2676
 
 
2677
/* Our coolio opaque history object. */
 
2678
typedef struct
 
2679
{
 
2680
  /* filesystem object */
 
2681
  svn_fs_t *fs;
 
2682
 
 
2683
  /* path and revision of historical location */
 
2684
  const char *path;
 
2685
  svn_revnum_t revision;
 
2686
 
 
2687
  /* internal-use hints about where to resume the history search. */
 
2688
  const char *path_hint;
 
2689
  svn_revnum_t rev_hint;
 
2690
 
 
2691
  /* FALSE until the first call to svn_fs_history_prev(). */
 
2692
  svn_boolean_t is_interesting;
 
2693
} fs_history_data_t;
 
2694
 
 
2695
static svn_fs_history_t *
 
2696
assemble_history (svn_fs_t *fs,
 
2697
                  const char *path,
 
2698
                  svn_revnum_t revision,
 
2699
                  svn_boolean_t is_interesting,
 
2700
                  const char *path_hint,
 
2701
                  svn_revnum_t rev_hint,
 
2702
                  apr_pool_t *pool);
 
2703
 
 
2704
 
 
2705
/* Set *HISTORY_P to an opaque node history object which represents
 
2706
   PATH under ROOT.  ROOT must be a revision root.  Use POOL for all
 
2707
   allocations. */
 
2708
static svn_error_t *
 
2709
fs_node_history (svn_fs_history_t **history_p,
 
2710
                 svn_fs_root_t *root,
 
2711
                 const char *path,
 
2712
                 apr_pool_t *pool)
 
2713
{
 
2714
  svn_node_kind_t kind;
 
2715
 
 
2716
  /* We require a revision root. */
 
2717
  if (root->is_txn_root)
 
2718
    return svn_error_create (SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
 
2719
 
 
2720
  /* And we require that the path exist in the root. */
 
2721
  SVN_ERR (svn_fs_fs__check_path (&kind, root, path, pool));
 
2722
  if (kind == svn_node_none)
 
2723
    return not_found (root, path);
 
2724
 
 
2725
  /* Okay, all seems well.  Build our history object and return it. */
 
2726
  *history_p = assemble_history (root->fs,
 
2727
                                 svn_fs_fs__canonicalize_abspath (path, pool),
 
2728
                                 root->rev, FALSE, NULL, 
 
2729
                                 SVN_INVALID_REVNUM, pool);
 
2730
  return SVN_NO_ERROR;
 
2731
}
 
2732
 
 
2733
/* Find the youngest copyroot for path PARENT_PATH or its parents in
 
2734
   filesystem FS, and store the node-id for this copyroot in
 
2735
   *COPYROOT_P.  Perform all allocations in POOL. */
 
2736
static svn_error_t *
 
2737
find_youngest_copyroot (svn_revnum_t *rev_p,
 
2738
                        const char **path_p,
 
2739
                        svn_fs_t *fs,
 
2740
                        parent_path_t *parent_path,
 
2741
                        apr_pool_t *pool)
 
2742
{
 
2743
  svn_revnum_t rev_mine, rev_parent = -1;
 
2744
  const char *path_mine, *path_parent;
 
2745
 
 
2746
  /* First find our parent's youngest copyroot. */
 
2747
  if (parent_path->parent)
 
2748
    SVN_ERR (find_youngest_copyroot (&rev_parent, &path_parent, fs,
 
2749
                                     parent_path->parent, pool));
 
2750
 
 
2751
  /* Find our copyroot. */
 
2752
  SVN_ERR (svn_fs_fs__dag_get_copyroot (&rev_mine, &path_mine,
 
2753
                                        parent_path->node, pool));
 
2754
 
 
2755
  /* If a parent and child were copied to in the same revision, prefer
 
2756
     the child copy target, since it is the copy relevant to the
 
2757
     history of the child. */
 
2758
  if (rev_mine >= rev_parent)
 
2759
    {
 
2760
      *rev_p = rev_mine;
 
2761
      *path_p = path_mine;
 
2762
    }
 
2763
  else
 
2764
    {
 
2765
      *rev_p = rev_parent;
 
2766
      *path_p = path_parent;
 
2767
    }
 
2768
 
 
2769
  return SVN_NO_ERROR;
 
2770
}
 
2771
  
 
2772
 
 
2773
struct history_prev_args
 
2774
{
 
2775
  svn_fs_history_t **prev_history_p;
 
2776
  svn_fs_history_t *history;
 
2777
  svn_boolean_t cross_copies;
 
2778
  apr_pool_t *pool;
 
2779
};
 
2780
 
 
2781
 
 
2782
static svn_error_t *
 
2783
history_prev (void *baton, apr_pool_t *pool)
 
2784
{
 
2785
  struct history_prev_args *args = baton;
 
2786
  svn_fs_history_t **prev_history = args->prev_history_p;
 
2787
  svn_fs_history_t *history = args->history;
 
2788
  fs_history_data_t *fhd = history->fsap_data;
 
2789
  const char *commit_path, *src_path, *path = fhd->path;
 
2790
  svn_revnum_t commit_rev, src_rev, dst_rev;
 
2791
  svn_revnum_t revision = fhd->revision;
 
2792
  apr_pool_t *retpool = args->pool;
 
2793
  svn_fs_t *fs = fhd->fs;
 
2794
  parent_path_t *parent_path;
 
2795
  dag_node_t *node;
 
2796
  svn_fs_root_t *root;
 
2797
  const svn_fs_id_t *node_id;
 
2798
  svn_boolean_t reported = fhd->is_interesting;
 
2799
  svn_boolean_t retry = FALSE;
 
2800
  svn_revnum_t copyroot_rev;
 
2801
  const char *copyroot_path;
 
2802
 
 
2803
  /* Initialize our return value. */
 
2804
  *prev_history = NULL;
 
2805
 
 
2806
  /* If our last history report left us hints about where to pickup
 
2807
     the chase, then our last report was on the destination of a
 
2808
     copy.  If we are crossing copies, start from those locations,
 
2809
     otherwise, we're all done here.  */
 
2810
  if (fhd->path_hint && SVN_IS_VALID_REVNUM (fhd->rev_hint))
 
2811
    {
 
2812
      reported = FALSE;
 
2813
      if (! args->cross_copies)
 
2814
        return SVN_NO_ERROR;
 
2815
      path = fhd->path_hint;
 
2816
      revision = fhd->rev_hint;
 
2817
    }
 
2818
  
 
2819
  /* Construct a ROOT for the current revision. */
 
2820
  SVN_ERR (svn_fs_fs__revision_root (&root, fs, revision, pool));
 
2821
 
 
2822
  /* Open PATH/REVISION, and get its node and a bunch of other
 
2823
     goodies.  */
 
2824
  SVN_ERR (open_path (&parent_path, root, path, 0, NULL, pool));
 
2825
  node = parent_path->node;
 
2826
  node_id = svn_fs_fs__dag_get_id (node);
 
2827
  commit_path = svn_fs_fs__dag_get_created_path (node);
 
2828
  SVN_ERR (svn_fs_fs__dag_get_revision (&commit_rev, node, pool));
 
2829
 
 
2830
  /* The Subversion filesystem is written in such a way that a given
 
2831
     line of history may have at most one interesting history point
 
2832
     per filesystem revision.  Either that node was edited (and
 
2833
     possibly copied), or it was copied but not edited.  And a copy
 
2834
     source cannot be from the same revision as its destination.  So,
 
2835
     if our history revision matches its node's commit revision, we
 
2836
     know that ... */
 
2837
  if (revision == commit_rev)
 
2838
    {
 
2839
      if (! reported)
 
2840
        {
 
2841
          /* ... we either have not yet reported on this revision (and
 
2842
             need now to do so) ... */
 
2843
          *prev_history = assemble_history (fs, 
 
2844
                                            apr_pstrdup (retpool, commit_path),
 
2845
                                            commit_rev, TRUE, NULL, 
 
2846
                                            SVN_INVALID_REVNUM, retpool);
 
2847
          return SVN_NO_ERROR;
 
2848
        }
 
2849
      else
 
2850
        {
 
2851
          /* ... or we *have* reported on this revision, and must now
 
2852
             progress toward this node's predecessor (unless there is
 
2853
             no predecessor, in which case we're all done!). */
 
2854
          const svn_fs_id_t *pred_id;
 
2855
 
 
2856
          SVN_ERR (svn_fs_fs__dag_get_predecessor_id (&pred_id, node, pool));
 
2857
          if (! pred_id)
 
2858
            return SVN_NO_ERROR;
 
2859
 
 
2860
          /* Replace NODE and friends with the information from its
 
2861
             predecessor. */
 
2862
          SVN_ERR (svn_fs_fs__dag_get_node (&node, fs, pred_id, pool));
 
2863
          node_id = svn_fs_fs__dag_get_id (node);
 
2864
          commit_path = svn_fs_fs__dag_get_created_path (node);
 
2865
          SVN_ERR (svn_fs_fs__dag_get_revision (&commit_rev, node, pool));
 
2866
        }
 
2867
    }
 
2868
 
 
2869
  /* Find the youngest copyroot in the path of this node, including
 
2870
     itself. */
 
2871
  SVN_ERR (find_youngest_copyroot (&copyroot_rev, &copyroot_path, fs,
 
2872
                                   parent_path, pool));
 
2873
 
 
2874
  /* Initialize some state variables. */
 
2875
  src_path = NULL;
 
2876
  src_rev = SVN_INVALID_REVNUM;
 
2877
  dst_rev = SVN_INVALID_REVNUM;
 
2878
 
 
2879
  if (copyroot_rev > commit_rev)
 
2880
    {
 
2881
      const char *remainder;
 
2882
      const char *copy_dst, *copy_src;
 
2883
      svn_fs_root_t *copyroot_root;
 
2884
 
 
2885
      SVN_ERR (svn_fs_fs__revision_root (&copyroot_root, fs, copyroot_rev,
 
2886
                                         pool));
 
2887
      SVN_ERR (get_dag (&node, copyroot_root, copyroot_path, pool));
 
2888
      copy_dst = svn_fs_fs__dag_get_created_path (node);
 
2889
 
 
2890
      /* If our current path was the very destination of the copy,
 
2891
         then our new current path will be the copy source.  If our
 
2892
         current path was instead the *child* of the destination of
 
2893
         the copy, then figure out its previous location by taking its
 
2894
         path relative to the copy destination and appending that to
 
2895
         the copy source.  Finally, if our current path doesn't meet
 
2896
         one of these other criteria ... ### for now just fallback to
 
2897
         the old copy hunt algorithm. */
 
2898
      if (strcmp (path, copy_dst) == 0)
 
2899
        remainder = "";
 
2900
      else
 
2901
        remainder = svn_path_is_child (copy_dst, path, pool);
 
2902
 
 
2903
      if (remainder)
 
2904
        {
 
2905
          /* If we get here, then our current path is the destination 
 
2906
             of, or the child of the destination of, a copy.  Fill
 
2907
             in the return values and get outta here.  */
 
2908
          SVN_ERR (svn_fs_fs__dag_get_copyfrom_rev (&src_rev, node, pool));
 
2909
          SVN_ERR (svn_fs_fs__dag_get_copyfrom_path (&copy_src, node, pool));
 
2910
          
 
2911
          dst_rev = copyroot_rev;
 
2912
          src_path = svn_path_join (copy_src, remainder, pool);
 
2913
        }
 
2914
    }
 
2915
 
 
2916
  /* If we calculated a copy source path and revision, we'll make a
 
2917
     'copy-style' history object. */
 
2918
  if (src_path && SVN_IS_VALID_REVNUM (src_rev))
 
2919
    {
 
2920
      /* It's possible for us to find a copy location that is the same
 
2921
         as the history point we've just reported.  If that happens,
 
2922
         we simply need to take another trip through this history
 
2923
         search. */
 
2924
      if ((dst_rev == revision) && reported)
 
2925
        retry = TRUE;
 
2926
 
 
2927
      *prev_history = assemble_history (fs, apr_pstrdup (retpool, path), 
 
2928
                                        dst_rev, retry ? FALSE : TRUE, 
 
2929
                                        src_path, src_rev, retpool);
 
2930
    }
 
2931
  else
 
2932
    {
 
2933
      *prev_history = assemble_history (fs, apr_pstrdup (retpool, commit_path),
 
2934
                                        commit_rev, TRUE, NULL, 
 
2935
                                        SVN_INVALID_REVNUM, retpool);
 
2936
    }
 
2937
 
 
2938
  return SVN_NO_ERROR;
 
2939
}
 
2940
 
 
2941
 
 
2942
/* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
 
2943
   svn_fs_history_t object that represents the predecessory of
 
2944
   HISTORY.  If CROSS_COPIES is true, *PREV_HISTORY_P may be related
 
2945
   only through a copy operation.  Perform all allocations in POOL. */
 
2946
static svn_error_t *
 
2947
fs_history_prev (svn_fs_history_t **prev_history_p,
 
2948
                 svn_fs_history_t *history,
 
2949
                 svn_boolean_t cross_copies,
 
2950
                 apr_pool_t *pool)
 
2951
{
 
2952
  svn_fs_history_t *prev_history = NULL;
 
2953
  fs_history_data_t *fhd = history->fsap_data;
 
2954
  svn_fs_t *fs = fhd->fs;
 
2955
 
 
2956
  /* Special case: the root directory changes in every single
 
2957
     revision, no exceptions.  And, the root can't be the target (or
 
2958
     child of a target -- duh) of a copy.  So, if that's our path,
 
2959
     then we need only decrement our revision by 1, and there you go. */
 
2960
  if (strcmp (fhd->path, "/") == 0)
 
2961
    {
 
2962
      if (! fhd->is_interesting)
 
2963
        prev_history = assemble_history (fs, "/", fhd->revision,
 
2964
                                         1, NULL, SVN_INVALID_REVNUM, pool);
 
2965
      else if (fhd->revision > 0)
 
2966
        prev_history = assemble_history (fs, "/", fhd->revision - 1,
 
2967
                                         1, NULL, SVN_INVALID_REVNUM, pool);
 
2968
    }
 
2969
  else
 
2970
    {
 
2971
      struct history_prev_args args;
 
2972
      prev_history = history;
 
2973
 
 
2974
      while (1)
 
2975
        {
 
2976
          /* Get a trail, and get to work. */
 
2977
          
 
2978
          args.prev_history_p = &prev_history;
 
2979
          args.history = prev_history;
 
2980
          args.cross_copies = cross_copies;
 
2981
          args.pool = pool;
 
2982
          SVN_ERR (history_prev (&args, pool));
 
2983
 
 
2984
          if (! prev_history)
 
2985
            break;
 
2986
          fhd = prev_history->fsap_data;
 
2987
          if (fhd->is_interesting)
 
2988
            break;
 
2989
        }
 
2990
    }
 
2991
 
 
2992
  *prev_history_p = prev_history;
 
2993
  return SVN_NO_ERROR;
 
2994
}
 
2995
 
 
2996
 
 
2997
/* Set *PATH and *REVISION to the path and revision for the HISTORY
 
2998
   object.  Use POOL for all allocations. */
 
2999
static svn_error_t *
 
3000
fs_history_location (const char **path,
 
3001
                     svn_revnum_t *revision,
 
3002
                     svn_fs_history_t *history,
 
3003
                     apr_pool_t *pool)
 
3004
{
 
3005
  fs_history_data_t *fhd = history->fsap_data;
 
3006
 
 
3007
  *path = apr_pstrdup (pool, fhd->path);
 
3008
  *revision = fhd->revision;
 
3009
  return SVN_NO_ERROR;
 
3010
}
 
3011
 
 
3012
static history_vtable_t history_vtable = {
 
3013
  fs_history_prev,
 
3014
  fs_history_location
 
3015
};
 
3016
 
 
3017
/* Return a new history object (marked as "interesting") for PATH and
 
3018
   REVISION, allocated in POOL, and with its members set to the values
 
3019
   of the parameters provided.  Note that PATH and PATH_HINT are not
 
3020
   duped into POOL -- it is the responsibility of the caller to ensure
 
3021
   that this happens. */
 
3022
static svn_fs_history_t *
 
3023
assemble_history (svn_fs_t *fs,
 
3024
                  const char *path,
 
3025
                  svn_revnum_t revision,
 
3026
                  svn_boolean_t is_interesting,
 
3027
                  const char *path_hint,
 
3028
                  svn_revnum_t rev_hint,
 
3029
                  apr_pool_t *pool)
 
3030
{
 
3031
  svn_fs_history_t *history = apr_pcalloc (pool, sizeof (*history));
 
3032
  fs_history_data_t *fhd = apr_pcalloc (pool, sizeof (*fhd));
 
3033
  fhd->path = path;
 
3034
  fhd->revision = revision;
 
3035
  fhd->is_interesting = is_interesting;
 
3036
  fhd->path_hint = path_hint;
 
3037
  fhd->rev_hint = rev_hint;
 
3038
  fhd->fs = fs;
 
3039
  
 
3040
  history->vtable = &history_vtable;
 
3041
  history->fsap_data = fhd;
 
3042
  return history;
 
3043
}
 
3044
 
 
3045
/* The vtable associated with root objects. */
 
3046
static root_vtable_t root_vtable = {
 
3047
  fs_paths_changed,
 
3048
  svn_fs_fs__check_path,
 
3049
  fs_node_history,
 
3050
  fs_node_id,
 
3051
  svn_fs_fs__node_created_rev,
 
3052
  fs_node_created_path,
 
3053
  fs_delete_node,
 
3054
  fs_copied_from,
 
3055
  fs_node_prop,
 
3056
  fs_node_proplist,
 
3057
  fs_change_node_prop,
 
3058
  fs_props_changed,
 
3059
  fs_dir_entries,
 
3060
  fs_make_dir,
 
3061
  fs_copy,
 
3062
  fs_revision_link,
 
3063
  fs_file_length,
 
3064
  fs_file_md5_checksum,
 
3065
  fs_file_contents,
 
3066
  fs_make_file,
 
3067
  fs_apply_textdelta,
 
3068
  fs_apply_text,
 
3069
  fs_contents_changed,
 
3070
  fs_get_file_delta_stream,
 
3071
  fs_merge
 
3072
};
 
3073
 
 
3074
/* Construct a new root object in FS, allocated from POOL.  */
 
3075
static svn_fs_root_t *
 
3076
make_root (svn_fs_t *fs,
 
3077
           apr_pool_t *pool)
 
3078
{
 
3079
  /* We create a subpool for each root object to allow us to implement
 
3080
     svn_fs_close_root.  */
 
3081
  apr_pool_t *subpool = svn_pool_create (pool);
 
3082
  svn_fs_root_t *root = apr_pcalloc (subpool, sizeof (*root));
 
3083
  fs_root_data_t *frd = apr_pcalloc (subpool, sizeof (*frd));
 
3084
 
 
3085
  root->fs = fs;
 
3086
  root->pool = subpool;
 
3087
 
 
3088
  /* Init the node ID cache. */
 
3089
  frd->node_cache = apr_hash_make (subpool);
 
3090
  frd->node_list.prev = &frd->node_list;
 
3091
  frd->node_list.next = &frd->node_list;
 
3092
  frd->copyfrom_cache = apr_hash_make (subpool);
 
3093
  root->vtable = &root_vtable;
 
3094
  root->fsap_data = frd;
 
3095
 
 
3096
  return root;
 
3097
}
 
3098
 
 
3099
 
 
3100
/* Construct a root object referring to the root of REVISION in FS,
 
3101
   whose root directory is ROOT_DIR.  Create the new root in POOL.  */
 
3102
static svn_fs_root_t *
 
3103
make_revision_root (svn_fs_t *fs,
 
3104
                    svn_revnum_t rev,
 
3105
                    dag_node_t *root_dir,
 
3106
                    apr_pool_t *pool)
 
3107
{
 
3108
  svn_fs_root_t *root = make_root (fs, pool);
 
3109
  fs_root_data_t *frd = root->fsap_data;
 
3110
  
 
3111
  root->is_txn_root = FALSE;
 
3112
  root->rev = rev;
 
3113
  frd->root_dir = root_dir;
 
3114
 
 
3115
  return root;
 
3116
}
 
3117
 
 
3118
 
 
3119
/* Construct a root object referring to the root of the transaction
 
3120
   named TXN in FS, with FLAGS to describe transaction's behavior.
 
3121
   Create the new root in POOL.  */
 
3122
static svn_fs_root_t *
 
3123
make_txn_root (svn_fs_t *fs,
 
3124
               const char *txn,
 
3125
               apr_uint32_t flags,
 
3126
               apr_pool_t *pool)
 
3127
{
 
3128
  svn_fs_root_t *root = make_root (fs, pool);
 
3129
  root->is_txn_root = TRUE;
 
3130
  root->txn = apr_pstrdup (root->pool, txn);
 
3131
  root->txn_flags = flags;
 
3132
 
 
3133
  return root;
 
3134
}