1251881Speter/* dag.c : DAG-like interface filesystem, private to libsvn_fs
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter#include <string.h>
24251881Speter
25251881Speter#include "svn_path.h"
26251881Speter#include "svn_time.h"
27251881Speter#include "svn_error.h"
28251881Speter#include "svn_fs.h"
29251881Speter#include "svn_hash.h"
30251881Speter#include "svn_props.h"
31251881Speter#include "svn_pools.h"
32251881Speter
33251881Speter#include "dag.h"
34251881Speter#include "err.h"
35251881Speter#include "fs.h"
36251881Speter#include "key-gen.h"
37251881Speter#include "node-rev.h"
38251881Speter#include "trail.h"
39251881Speter#include "reps-strings.h"
40251881Speter#include "revs-txns.h"
41251881Speter#include "id.h"
42251881Speter
43251881Speter#include "util/fs_skels.h"
44251881Speter
45251881Speter#include "bdb/txn-table.h"
46251881Speter#include "bdb/rev-table.h"
47251881Speter#include "bdb/nodes-table.h"
48251881Speter#include "bdb/copies-table.h"
49251881Speter#include "bdb/reps-table.h"
50251881Speter#include "bdb/strings-table.h"
51251881Speter#include "bdb/checksum-reps-table.h"
52251881Speter#include "bdb/changes-table.h"
53251881Speter#include "bdb/node-origins-table.h"
54251881Speter
55251881Speter#include "private/svn_skel.h"
56251881Speter#include "private/svn_fs_util.h"
57251881Speter#include "private/svn_fspath.h"
58251881Speter#include "../libsvn_fs/fs-loader.h"
59251881Speter
60251881Speter#include "svn_private_config.h"
61251881Speter
62251881Speter
63251881Speter/* Initializing a filesystem.  */
64251881Speter
65251881Speterstruct dag_node_t
66251881Speter{
67251881Speter  /*** NOTE: Keeping in-memory representations of disk data that can
68251881Speter       be changed by other accessors is a nasty business.  Such
69251881Speter       representations are basically a cache with some pretty complex
70251881Speter       invalidation rules.  For example, the "node revision"
71251881Speter       associated with a DAG node ID can look completely different to
72251881Speter       a process that has modified that information as part of a
73251881Speter       Berkeley DB transaction than it does to some other process.
74251881Speter       That said, there are some aspects of a "node revision" which
75251881Speter       never change, like its 'id' or 'kind'.  Our best bet is to
76251881Speter       limit ourselves to exposing outside of this interface only
77251881Speter       those immutable aspects of a DAG node representation.  ***/
78251881Speter
79251881Speter  /* The filesystem this dag node came from. */
80251881Speter  svn_fs_t *fs;
81251881Speter
82251881Speter  /* The node revision ID for this dag node. */
83251881Speter  svn_fs_id_t *id;
84251881Speter
85251881Speter  /* The node's type (file, dir, etc.) */
86251881Speter  svn_node_kind_t kind;
87251881Speter
88251881Speter  /* the path at which this node was created. */
89251881Speter  const char *created_path;
90251881Speter};
91251881Speter
92251881Speter
93251881Speter
94251881Speter/* Trivial helper/accessor functions. */
95251881Spetersvn_node_kind_t svn_fs_base__dag_node_kind(dag_node_t *node)
96251881Speter{
97251881Speter  return node->kind;
98251881Speter}
99251881Speter
100251881Speter
101251881Speterconst svn_fs_id_t *
102251881Spetersvn_fs_base__dag_get_id(dag_node_t *node)
103251881Speter{
104251881Speter  return node->id;
105251881Speter}
106251881Speter
107251881Speter
108251881Speterconst char *
109251881Spetersvn_fs_base__dag_get_created_path(dag_node_t *node)
110251881Speter{
111251881Speter  return node->created_path;
112251881Speter}
113251881Speter
114251881Speter
115251881Spetersvn_fs_t *
116251881Spetersvn_fs_base__dag_get_fs(dag_node_t *node)
117251881Speter{
118251881Speter  return node->fs;
119251881Speter}
120251881Speter
121251881Speter
122251881Spetersvn_boolean_t svn_fs_base__dag_check_mutable(dag_node_t *node,
123251881Speter                                             const char *txn_id)
124251881Speter{
125251881Speter  return (strcmp(svn_fs_base__id_txn_id(svn_fs_base__dag_get_id(node)),
126251881Speter                 txn_id) == 0);
127251881Speter}
128251881Speter
129251881Speter
130251881Spetersvn_error_t *
131251881Spetersvn_fs_base__dag_get_node(dag_node_t **node,
132251881Speter                          svn_fs_t *fs,
133251881Speter                          const svn_fs_id_t *id,
134251881Speter                          trail_t *trail,
135251881Speter                          apr_pool_t *pool)
136251881Speter{
137251881Speter  dag_node_t *new_node;
138251881Speter  node_revision_t *noderev;
139251881Speter
140251881Speter  /* Construct the node. */
141251881Speter  new_node = apr_pcalloc(pool, sizeof(*new_node));
142251881Speter  new_node->fs = fs;
143251881Speter  new_node->id = svn_fs_base__id_copy(id, pool);
144251881Speter
145251881Speter  /* Grab the contents so we can cache some of the immutable parts of it. */
146251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, id, trail, pool));
147251881Speter
148251881Speter  /* Initialize the KIND and CREATED_PATH attributes */
149251881Speter  new_node->kind = noderev->kind;
150251881Speter  new_node->created_path = noderev->created_path;
151251881Speter
152251881Speter  /* Return a fresh new node */
153251881Speter  *node = new_node;
154251881Speter  return SVN_NO_ERROR;
155251881Speter}
156251881Speter
157251881Speter
158251881Spetersvn_error_t *
159251881Spetersvn_fs_base__dag_get_revision(svn_revnum_t *rev,
160251881Speter                              dag_node_t *node,
161251881Speter                              trail_t *trail,
162251881Speter                              apr_pool_t *pool)
163251881Speter{
164251881Speter  /* Use the txn ID from the NODE's id to look up the transaction and
165251881Speter     get its revision number.  */
166251881Speter  return svn_fs_base__txn_get_revision
167251881Speter    (rev, svn_fs_base__dag_get_fs(node),
168251881Speter     svn_fs_base__id_txn_id(svn_fs_base__dag_get_id(node)), trail, pool);
169251881Speter}
170251881Speter
171251881Speter
172251881Spetersvn_error_t *
173251881Spetersvn_fs_base__dag_get_predecessor_id(const svn_fs_id_t **id_p,
174251881Speter                                    dag_node_t *node,
175251881Speter                                    trail_t *trail,
176251881Speter                                    apr_pool_t *pool)
177251881Speter{
178251881Speter  node_revision_t *noderev;
179251881Speter
180251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id,
181251881Speter                                        trail, pool));
182251881Speter  *id_p = noderev->predecessor_id;
183251881Speter  return SVN_NO_ERROR;
184251881Speter}
185251881Speter
186251881Speter
187251881Spetersvn_error_t *
188251881Spetersvn_fs_base__dag_get_predecessor_count(int *count,
189251881Speter                                       dag_node_t *node,
190251881Speter                                       trail_t *trail,
191251881Speter                                       apr_pool_t *pool)
192251881Speter{
193251881Speter  node_revision_t *noderev;
194251881Speter
195251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id,
196251881Speter                                        trail, pool));
197251881Speter  *count = noderev->predecessor_count;
198251881Speter  return SVN_NO_ERROR;
199251881Speter}
200251881Speter
201251881Speter
202251881Speter/* Trail body for svn_fs_base__dag_init_fs. */
203251881Speterstatic svn_error_t *
204251881Spetertxn_body_dag_init_fs(void *baton,
205251881Speter                     trail_t *trail)
206251881Speter{
207251881Speter  node_revision_t noderev;
208251881Speter  revision_t revision;
209251881Speter  svn_revnum_t rev = SVN_INVALID_REVNUM;
210251881Speter  svn_fs_t *fs = trail->fs;
211251881Speter  svn_string_t date;
212251881Speter  const char *txn_id;
213251881Speter  const char *copy_id;
214251881Speter  svn_fs_id_t *root_id = svn_fs_base__id_create("0", "0", "0", trail->pool);
215251881Speter
216251881Speter  /* Create empty root directory with node revision 0.0.0. */
217251881Speter  memset(&noderev, 0, sizeof(noderev));
218251881Speter  noderev.kind = svn_node_dir;
219251881Speter  noderev.created_path = "/";
220251881Speter  SVN_ERR(svn_fs_bdb__put_node_revision(fs, root_id, &noderev,
221251881Speter                                        trail, trail->pool));
222251881Speter
223251881Speter  /* Create a new transaction (better have an id of "0") */
224251881Speter  SVN_ERR(svn_fs_bdb__create_txn(&txn_id, fs, root_id, trail, trail->pool));
225251881Speter  if (strcmp(txn_id, "0"))
226251881Speter    return svn_error_createf
227251881Speter      (SVN_ERR_FS_CORRUPT, 0,
228251881Speter       _("Corrupt DB: initial transaction id not '0' in filesystem '%s'"),
229251881Speter       fs->path);
230251881Speter
231251881Speter  /* Create a default copy (better have an id of "0") */
232251881Speter  SVN_ERR(svn_fs_bdb__reserve_copy_id(&copy_id, fs, trail, trail->pool));
233251881Speter  if (strcmp(copy_id, "0"))
234251881Speter    return svn_error_createf
235251881Speter      (SVN_ERR_FS_CORRUPT, 0,
236251881Speter       _("Corrupt DB: initial copy id not '0' in filesystem '%s'"), fs->path);
237251881Speter  SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, NULL, NULL, root_id,
238251881Speter                                  copy_kind_real, trail, trail->pool));
239251881Speter
240251881Speter  /* Link it into filesystem revision 0. */
241251881Speter  revision.txn_id = txn_id;
242251881Speter  SVN_ERR(svn_fs_bdb__put_rev(&rev, fs, &revision, trail, trail->pool));
243251881Speter  if (rev != 0)
244251881Speter    return svn_error_createf(SVN_ERR_FS_CORRUPT, 0,
245251881Speter                             _("Corrupt DB: initial revision number "
246251881Speter                               "is not '0' in filesystem '%s'"), fs->path);
247251881Speter
248251881Speter  /* Promote our transaction to a "committed" transaction. */
249251881Speter  SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, rev,
250251881Speter                                          trail, trail->pool));
251251881Speter
252251881Speter  /* Set a date on revision 0. */
253251881Speter  date.data = svn_time_to_cstring(apr_time_now(), trail->pool);
254251881Speter  date.len = strlen(date.data);
255251881Speter  return svn_fs_base__set_rev_prop(fs, 0, SVN_PROP_REVISION_DATE, NULL, &date,
256251881Speter                                   trail, trail->pool);
257251881Speter}
258251881Speter
259251881Speter
260251881Spetersvn_error_t *
261251881Spetersvn_fs_base__dag_init_fs(svn_fs_t *fs)
262251881Speter{
263251881Speter  return svn_fs_base__retry_txn(fs, txn_body_dag_init_fs, NULL,
264251881Speter                                TRUE, fs->pool);
265251881Speter}
266251881Speter
267251881Speter
268251881Speter
269251881Speter/*** Directory node functions ***/
270251881Speter
271251881Speter/* Some of these are helpers for functions outside this section. */
272251881Speter
273251881Speter/* Given directory NODEREV in FS, set *ENTRIES_P to its entries list
274251881Speter   hash, as part of TRAIL, or to NULL if NODEREV has no entries.  The
275251881Speter   entries list will be allocated in POOL, and the entries in that
276251881Speter   list will not have interesting value in their 'kind' fields.  If
277251881Speter   NODEREV is not a directory, return the error SVN_ERR_FS_NOT_DIRECTORY. */
278251881Speterstatic svn_error_t *
279251881Speterget_dir_entries(apr_hash_t **entries_p,
280251881Speter                svn_fs_t *fs,
281251881Speter                node_revision_t *noderev,
282251881Speter                trail_t *trail,
283251881Speter                apr_pool_t *pool)
284251881Speter{
285251881Speter  apr_hash_t *entries = NULL;
286251881Speter  apr_hash_index_t *hi;
287251881Speter  svn_string_t entries_raw;
288251881Speter  svn_skel_t *entries_skel;
289251881Speter
290251881Speter  /* Error if this is not a directory. */
291251881Speter  if (noderev->kind != svn_node_dir)
292251881Speter    return svn_error_create
293251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
294251881Speter       _("Attempted to get entries of a non-directory node"));
295251881Speter
296251881Speter  /* If there's a DATA-KEY, there might be entries to fetch. */
297251881Speter  if (noderev->data_key)
298251881Speter    {
299251881Speter      /* Now we have a rep, follow through to get the entries. */
300251881Speter      SVN_ERR(svn_fs_base__rep_contents(&entries_raw, fs, noderev->data_key,
301251881Speter                                        trail, pool));
302251881Speter      entries_skel = svn_skel__parse(entries_raw.data, entries_raw.len, pool);
303251881Speter
304251881Speter      /* Were there entries?  Make a hash from them. */
305251881Speter      if (entries_skel)
306251881Speter        SVN_ERR(svn_fs_base__parse_entries_skel(&entries, entries_skel,
307251881Speter                                                pool));
308251881Speter    }
309251881Speter
310251881Speter  /* No hash?  No problem.  */
311251881Speter  *entries_p = NULL;
312251881Speter  if (! entries)
313251881Speter    return SVN_NO_ERROR;
314251881Speter
315251881Speter  /* Else, convert the hash from a name->id mapping to a name->dirent one.  */
316251881Speter  *entries_p = apr_hash_make(pool);
317251881Speter  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
318251881Speter    {
319251881Speter      const void *key;
320251881Speter      apr_ssize_t klen;
321251881Speter      void *val;
322251881Speter      svn_fs_dirent_t *dirent = apr_palloc(pool, sizeof(*dirent));
323251881Speter
324251881Speter      /* KEY will be the entry name in ancestor, VAL the id.  */
325251881Speter      apr_hash_this(hi, &key, &klen, &val);
326251881Speter      dirent->name = key;
327251881Speter      dirent->id = val;
328251881Speter      dirent->kind = svn_node_unknown;
329251881Speter      apr_hash_set(*entries_p, key, klen, dirent);
330251881Speter    }
331251881Speter
332251881Speter  /* Return our findings. */
333251881Speter  return SVN_NO_ERROR;
334251881Speter}
335251881Speter
336251881Speter
337251881Speter/* Set *ID_P to the node-id for entry NAME in PARENT, as part of
338251881Speter   TRAIL.  If no such entry, set *ID_P to NULL but do not error.  The
339251881Speter   entry is allocated in POOL or in the same pool as PARENT;
340251881Speter   the caller should copy if it cares.  */
341251881Speterstatic svn_error_t *
342251881Speterdir_entry_id_from_node(const svn_fs_id_t **id_p,
343251881Speter                       dag_node_t *parent,
344251881Speter                       const char *name,
345251881Speter                       trail_t *trail,
346251881Speter                       apr_pool_t *pool)
347251881Speter{
348251881Speter  apr_hash_t *entries;
349251881Speter  svn_fs_dirent_t *dirent;
350251881Speter
351251881Speter  SVN_ERR(svn_fs_base__dag_dir_entries(&entries, parent, trail, pool));
352251881Speter  if (entries)
353251881Speter    dirent = svn_hash_gets(entries, name);
354251881Speter  else
355251881Speter    dirent = NULL;
356251881Speter
357251881Speter  *id_p = dirent ? dirent->id : NULL;
358251881Speter  return SVN_NO_ERROR;
359251881Speter}
360251881Speter
361251881Speter
362251881Speter/* Add or set in PARENT a directory entry NAME pointing to ID.
363251881Speter   Allocations are done in TRAIL.
364251881Speter
365251881Speter   Assumptions:
366251881Speter   - PARENT is a mutable directory.
367251881Speter   - ID does not refer to an ancestor of parent
368251881Speter   - NAME is a single path component
369251881Speter*/
370251881Speterstatic svn_error_t *
371251881Speterset_entry(dag_node_t *parent,
372251881Speter          const char *name,
373251881Speter          const svn_fs_id_t *id,
374251881Speter          const char *txn_id,
375251881Speter          trail_t *trail,
376251881Speter          apr_pool_t *pool)
377251881Speter{
378251881Speter  node_revision_t *parent_noderev;
379251881Speter  const char *rep_key, *mutable_rep_key;
380251881Speter  apr_hash_t *entries = NULL;
381251881Speter  svn_stream_t *wstream;
382251881Speter  apr_size_t len;
383251881Speter  svn_string_t raw_entries;
384251881Speter  svn_stringbuf_t *raw_entries_buf;
385251881Speter  svn_skel_t *entries_skel;
386251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(parent);
387251881Speter
388251881Speter  /* Get the parent's node-revision. */
389251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&parent_noderev, fs, parent->id,
390251881Speter                                        trail, pool));
391251881Speter  rep_key = parent_noderev->data_key;
392251881Speter  SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, rep_key,
393251881Speter                                       fs, txn_id, trail, pool));
394251881Speter
395251881Speter  /* If the parent node already pointed at a mutable representation,
396251881Speter     we don't need to do anything.  But if it didn't, either because
397251881Speter     the parent didn't refer to any rep yet or because it referred to
398251881Speter     an immutable one, we must make the parent refer to the mutable
399251881Speter     rep we just created. */
400251881Speter  if (! svn_fs_base__same_keys(rep_key, mutable_rep_key))
401251881Speter    {
402251881Speter      parent_noderev->data_key = mutable_rep_key;
403251881Speter      SVN_ERR(svn_fs_bdb__put_node_revision(fs, parent->id, parent_noderev,
404251881Speter                                            trail, pool));
405251881Speter    }
406251881Speter
407251881Speter  /* If the new representation inherited nothing, start a new entries
408251881Speter     list for it.  Else, go read its existing entries list. */
409251881Speter  if (rep_key)
410251881Speter    {
411251881Speter      SVN_ERR(svn_fs_base__rep_contents(&raw_entries, fs, rep_key,
412251881Speter                                        trail, pool));
413251881Speter      entries_skel = svn_skel__parse(raw_entries.data, raw_entries.len, pool);
414251881Speter      if (entries_skel)
415251881Speter        SVN_ERR(svn_fs_base__parse_entries_skel(&entries, entries_skel,
416251881Speter                                                pool));
417251881Speter    }
418251881Speter
419251881Speter  /* If we still have no ENTRIES hash, make one here.  */
420251881Speter  if (! entries)
421251881Speter    entries = apr_hash_make(pool);
422251881Speter
423251881Speter  /* Now, add our new entry to the entries list. */
424251881Speter  svn_hash_sets(entries, name, id);
425251881Speter
426251881Speter  /* Finally, replace the old entries list with the new one. */
427251881Speter  SVN_ERR(svn_fs_base__unparse_entries_skel(&entries_skel, entries,
428251881Speter                                            pool));
429251881Speter  raw_entries_buf = svn_skel__unparse(entries_skel, pool);
430251881Speter  SVN_ERR(svn_fs_base__rep_contents_write_stream(&wstream, fs,
431251881Speter                                                 mutable_rep_key, txn_id,
432251881Speter                                                 TRUE, trail, pool));
433251881Speter  len = raw_entries_buf->len;
434251881Speter  SVN_ERR(svn_stream_write(wstream, raw_entries_buf->data, &len));
435251881Speter  return svn_stream_close(wstream);
436251881Speter}
437251881Speter
438251881Speter
439251881Speter/* Make a new entry named NAME in PARENT, as part of TRAIL.  If IS_DIR
440251881Speter   is true, then the node revision the new entry points to will be a
441251881Speter   directory, else it will be a file.  The new node will be allocated
442251881Speter   in POOL.  PARENT must be mutable, and must not have an entry
443251881Speter   named NAME.  */
444251881Speterstatic svn_error_t *
445251881Spetermake_entry(dag_node_t **child_p,
446251881Speter           dag_node_t *parent,
447251881Speter           const char *parent_path,
448251881Speter           const char *name,
449251881Speter           svn_boolean_t is_dir,
450251881Speter           const char *txn_id,
451251881Speter           trail_t *trail,
452251881Speter           apr_pool_t *pool)
453251881Speter{
454251881Speter  const svn_fs_id_t *new_node_id;
455251881Speter  node_revision_t new_noderev;
456251881Speter
457251881Speter  /* Make sure that NAME is a single path component. */
458251881Speter  if (! svn_path_is_single_path_component(name))
459251881Speter    return svn_error_createf
460251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
461251881Speter       _("Attempted to create a node with an illegal name '%s'"), name);
462251881Speter
463251881Speter  /* Make sure that parent is a directory */
464251881Speter  if (parent->kind != svn_node_dir)
465251881Speter    return svn_error_create
466251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
467251881Speter       _("Attempted to create entry in non-directory parent"));
468251881Speter
469251881Speter  /* Check that the parent is mutable. */
470251881Speter  if (! svn_fs_base__dag_check_mutable(parent, txn_id))
471251881Speter    return svn_error_createf
472251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
473251881Speter       _("Attempted to clone child of non-mutable node"));
474251881Speter
475251881Speter  /* Check that parent does not already have an entry named NAME. */
476251881Speter  SVN_ERR(dir_entry_id_from_node(&new_node_id, parent, name, trail, pool));
477251881Speter  if (new_node_id)
478251881Speter    return svn_error_createf
479251881Speter      (SVN_ERR_FS_ALREADY_EXISTS, NULL,
480251881Speter       _("Attempted to create entry that already exists"));
481251881Speter
482251881Speter  /* Create the new node's NODE-REVISION */
483251881Speter  memset(&new_noderev, 0, sizeof(new_noderev));
484251881Speter  new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
485251881Speter  new_noderev.created_path = svn_fspath__join(parent_path, name, pool);
486251881Speter  SVN_ERR(svn_fs_base__create_node
487251881Speter          (&new_node_id, svn_fs_base__dag_get_fs(parent), &new_noderev,
488251881Speter           svn_fs_base__id_copy_id(svn_fs_base__dag_get_id(parent)),
489251881Speter           txn_id, trail, pool));
490251881Speter
491251881Speter  /* Create a new dag_node_t for our new node */
492251881Speter  SVN_ERR(svn_fs_base__dag_get_node(child_p,
493251881Speter                                    svn_fs_base__dag_get_fs(parent),
494251881Speter                                    new_node_id, trail, pool));
495251881Speter
496251881Speter  /* We can safely call set_entry because we already know that
497251881Speter     PARENT is mutable, and we just created CHILD, so we know it has
498251881Speter     no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
499251881Speter  return set_entry(parent, name, svn_fs_base__dag_get_id(*child_p),
500251881Speter                   txn_id, trail, pool);
501251881Speter}
502251881Speter
503251881Speter
504251881Spetersvn_error_t *
505251881Spetersvn_fs_base__dag_dir_entries(apr_hash_t **entries,
506251881Speter                             dag_node_t *node,
507251881Speter                             trail_t *trail,
508251881Speter                             apr_pool_t *pool)
509251881Speter{
510251881Speter  node_revision_t *noderev;
511251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id,
512251881Speter                                        trail, pool));
513251881Speter  return get_dir_entries(entries, node->fs, noderev, trail, pool);
514251881Speter}
515251881Speter
516251881Speter
517251881Spetersvn_error_t *
518251881Spetersvn_fs_base__dag_set_entry(dag_node_t *node,
519251881Speter                           const char *entry_name,
520251881Speter                           const svn_fs_id_t *id,
521251881Speter                           const char *txn_id,
522251881Speter                           trail_t *trail,
523251881Speter                           apr_pool_t *pool)
524251881Speter{
525251881Speter  /* Check it's a directory. */
526251881Speter  if (node->kind != svn_node_dir)
527251881Speter    return svn_error_create
528251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
529251881Speter       _("Attempted to set entry in non-directory node"));
530251881Speter
531251881Speter  /* Check it's mutable. */
532251881Speter  if (! svn_fs_base__dag_check_mutable(node, txn_id))
533251881Speter    return svn_error_create
534251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
535251881Speter       _("Attempted to set entry in immutable node"));
536251881Speter
537251881Speter  return set_entry(node, entry_name, id, txn_id, trail, pool);
538251881Speter}
539251881Speter
540251881Speter
541251881Speter
542251881Speter/*** Proplists. ***/
543251881Speter
544251881Spetersvn_error_t *
545251881Spetersvn_fs_base__dag_get_proplist(apr_hash_t **proplist_p,
546251881Speter                              dag_node_t *node,
547251881Speter                              trail_t *trail,
548251881Speter                              apr_pool_t *pool)
549251881Speter{
550251881Speter  node_revision_t *noderev;
551251881Speter  apr_hash_t *proplist = NULL;
552251881Speter  svn_string_t raw_proplist;
553251881Speter  svn_skel_t *proplist_skel;
554251881Speter
555251881Speter  /* Go get a fresh NODE-REVISION for this node. */
556251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id,
557251881Speter                                        trail, pool));
558251881Speter
559251881Speter  /* Get property key (returning early if there isn't one) . */
560251881Speter  if (! noderev->prop_key)
561251881Speter    {
562251881Speter      *proplist_p = NULL;
563251881Speter      return SVN_NO_ERROR;
564251881Speter    }
565251881Speter
566251881Speter  /* Get the string associated with the property rep, parsing it as a
567251881Speter     skel, and then attempt to parse *that* into a property hash.  */
568251881Speter  SVN_ERR(svn_fs_base__rep_contents(&raw_proplist,
569251881Speter                                    svn_fs_base__dag_get_fs(node),
570251881Speter                                    noderev->prop_key, trail, pool));
571251881Speter  proplist_skel = svn_skel__parse(raw_proplist.data, raw_proplist.len, pool);
572251881Speter  if (proplist_skel)
573251881Speter    SVN_ERR(svn_skel__parse_proplist(&proplist, proplist_skel, pool));
574251881Speter
575251881Speter  *proplist_p = proplist;
576251881Speter  return SVN_NO_ERROR;
577251881Speter}
578251881Speter
579251881Speter
580251881Spetersvn_error_t *
581251881Spetersvn_fs_base__dag_set_proplist(dag_node_t *node,
582251881Speter                              const apr_hash_t *proplist,
583251881Speter                              const char *txn_id,
584251881Speter                              trail_t *trail,
585251881Speter                              apr_pool_t *pool)
586251881Speter{
587251881Speter  node_revision_t *noderev;
588251881Speter  const char *rep_key, *mutable_rep_key;
589251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(node);
590251881Speter  svn_stream_t *wstream;
591251881Speter  apr_size_t len;
592251881Speter  svn_skel_t *proplist_skel;
593251881Speter  svn_stringbuf_t *raw_proplist_buf;
594251881Speter  base_fs_data_t *bfd = fs->fsap_data;
595251881Speter
596251881Speter  /* Sanity check: this node better be mutable! */
597251881Speter  if (! svn_fs_base__dag_check_mutable(node, txn_id))
598251881Speter    {
599251881Speter      svn_string_t *idstr = svn_fs_base__id_unparse(node->id, pool);
600251881Speter      return svn_error_createf
601251881Speter        (SVN_ERR_FS_NOT_MUTABLE, NULL,
602251881Speter         _("Can't set proplist on *immutable* node-revision %s"),
603251881Speter         idstr->data);
604251881Speter    }
605251881Speter
606251881Speter  /* Go get a fresh NODE-REVISION for this node. */
607251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, node->id,
608251881Speter                                        trail, pool));
609251881Speter  rep_key = noderev->prop_key;
610251881Speter
611251881Speter  /* Flatten the proplist into a string. */
612251881Speter  SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, proplist, pool));
613251881Speter  raw_proplist_buf = svn_skel__unparse(proplist_skel, pool);
614251881Speter
615251881Speter  /* If this repository supports representation sharing, and the
616251881Speter     resulting property list is exactly the same as another string in
617251881Speter     the database, just use the previously existing string and get
618251881Speter     outta here. */
619251881Speter  if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
620251881Speter    {
621251881Speter      svn_error_t *err;
622251881Speter      const char *dup_rep_key;
623251881Speter      svn_checksum_t *checksum;
624251881Speter
625251881Speter      SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1, raw_proplist_buf->data,
626251881Speter                           raw_proplist_buf->len, pool));
627251881Speter
628251881Speter      err = svn_fs_bdb__get_checksum_rep(&dup_rep_key, fs, checksum,
629251881Speter                                         trail, pool);
630251881Speter      if (! err)
631251881Speter        {
632251881Speter          if (noderev->prop_key)
633251881Speter            SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->prop_key,
634251881Speter                                                       txn_id, trail, pool));
635251881Speter          noderev->prop_key = dup_rep_key;
636251881Speter          return svn_fs_bdb__put_node_revision(fs, node->id, noderev,
637251881Speter                                               trail, pool);
638251881Speter        }
639251881Speter      else if (err)
640251881Speter        {
641251881Speter          if (err->apr_err != SVN_ERR_FS_NO_SUCH_CHECKSUM_REP)
642251881Speter            return svn_error_trace(err);
643251881Speter
644251881Speter          svn_error_clear(err);
645251881Speter          err = SVN_NO_ERROR;
646251881Speter        }
647251881Speter    }
648251881Speter
649251881Speter  /* Get a mutable version of this rep (updating the node revision if
650251881Speter     this isn't a NOOP)  */
651251881Speter  SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, rep_key,
652251881Speter                                       fs, txn_id, trail, pool));
653251881Speter  if (! svn_fs_base__same_keys(mutable_rep_key, rep_key))
654251881Speter    {
655251881Speter      noderev->prop_key = mutable_rep_key;
656251881Speter      SVN_ERR(svn_fs_bdb__put_node_revision(fs, node->id, noderev,
657251881Speter                                            trail, pool));
658251881Speter    }
659251881Speter
660251881Speter  /* Replace the old property list with the new one. */
661251881Speter  SVN_ERR(svn_fs_base__rep_contents_write_stream(&wstream, fs,
662251881Speter                                                 mutable_rep_key, txn_id,
663251881Speter                                                 TRUE, trail, pool));
664251881Speter  len = raw_proplist_buf->len;
665251881Speter  SVN_ERR(svn_stream_write(wstream, raw_proplist_buf->data, &len));
666251881Speter  SVN_ERR(svn_stream_close(wstream));
667251881Speter
668251881Speter  return SVN_NO_ERROR;
669251881Speter}
670251881Speter
671251881Speter
672251881Speter
673251881Speter/*** Roots. ***/
674251881Speter
675251881Spetersvn_error_t *
676251881Spetersvn_fs_base__dag_revision_root(dag_node_t **node_p,
677251881Speter                               svn_fs_t *fs,
678251881Speter                               svn_revnum_t rev,
679251881Speter                               trail_t *trail,
680251881Speter                               apr_pool_t *pool)
681251881Speter{
682251881Speter  const svn_fs_id_t *root_id;
683251881Speter
684251881Speter  SVN_ERR(svn_fs_base__rev_get_root(&root_id, fs, rev, trail, pool));
685251881Speter  return svn_fs_base__dag_get_node(node_p, fs, root_id, trail, pool);
686251881Speter}
687251881Speter
688251881Speter
689251881Spetersvn_error_t *
690251881Spetersvn_fs_base__dag_txn_root(dag_node_t **node_p,
691251881Speter                          svn_fs_t *fs,
692251881Speter                          const char *txn_id,
693251881Speter                          trail_t *trail,
694251881Speter                          apr_pool_t *pool)
695251881Speter{
696251881Speter  const svn_fs_id_t *root_id, *ignored;
697251881Speter
698251881Speter  SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &ignored, fs, txn_id,
699251881Speter                                   trail, pool));
700251881Speter  return svn_fs_base__dag_get_node(node_p, fs, root_id, trail, pool);
701251881Speter}
702251881Speter
703251881Speter
704251881Spetersvn_error_t *
705251881Spetersvn_fs_base__dag_txn_base_root(dag_node_t **node_p,
706251881Speter                               svn_fs_t *fs,
707251881Speter                               const char *txn_id,
708251881Speter                               trail_t *trail,
709251881Speter                               apr_pool_t *pool)
710251881Speter{
711251881Speter  const svn_fs_id_t *base_root_id, *ignored;
712251881Speter
713251881Speter  SVN_ERR(svn_fs_base__get_txn_ids(&ignored, &base_root_id, fs, txn_id,
714251881Speter                                   trail, pool));
715251881Speter  return svn_fs_base__dag_get_node(node_p, fs, base_root_id, trail, pool);
716251881Speter}
717251881Speter
718251881Speter
719251881Spetersvn_error_t *
720251881Spetersvn_fs_base__dag_clone_child(dag_node_t **child_p,
721251881Speter                             dag_node_t *parent,
722251881Speter                             const char *parent_path,
723251881Speter                             const char *name,
724251881Speter                             const char *copy_id,
725251881Speter                             const char *txn_id,
726251881Speter                             trail_t *trail,
727251881Speter                             apr_pool_t *pool)
728251881Speter{
729251881Speter  dag_node_t *cur_entry; /* parent's current entry named NAME */
730251881Speter  const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
731251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(parent);
732251881Speter
733251881Speter  /* First check that the parent is mutable. */
734251881Speter  if (! svn_fs_base__dag_check_mutable(parent, txn_id))
735251881Speter    return svn_error_createf
736251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
737251881Speter       _("Attempted to clone child of non-mutable node"));
738251881Speter
739251881Speter  /* Make sure that NAME is a single path component. */
740251881Speter  if (! svn_path_is_single_path_component(name))
741251881Speter    return svn_error_createf
742251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
743251881Speter       _("Attempted to make a child clone with an illegal name '%s'"), name);
744251881Speter
745251881Speter  /* Find the node named NAME in PARENT's entries list if it exists. */
746251881Speter  SVN_ERR(svn_fs_base__dag_open(&cur_entry, parent, name, trail, pool));
747251881Speter
748251881Speter  /* Check for mutability in the node we found.  If it's mutable, we
749251881Speter     don't need to clone it. */
750251881Speter  if (svn_fs_base__dag_check_mutable(cur_entry, txn_id))
751251881Speter    {
752251881Speter      /* This has already been cloned */
753251881Speter      new_node_id = cur_entry->id;
754251881Speter    }
755251881Speter  else
756251881Speter    {
757251881Speter      node_revision_t *noderev;
758251881Speter
759251881Speter      /* Go get a fresh NODE-REVISION for current child node. */
760251881Speter      SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, cur_entry->id,
761251881Speter                                            trail, pool));
762251881Speter
763251881Speter      /* Do the clone thingy here. */
764251881Speter      noderev->predecessor_id = cur_entry->id;
765251881Speter      if (noderev->predecessor_count != -1)
766251881Speter        noderev->predecessor_count++;
767251881Speter      noderev->created_path = svn_fspath__join(parent_path, name, pool);
768251881Speter      SVN_ERR(svn_fs_base__create_successor(&new_node_id, fs, cur_entry->id,
769251881Speter                                            noderev, copy_id, txn_id,
770251881Speter                                            trail, pool));
771251881Speter
772251881Speter      /* Replace the ID in the parent's ENTRY list with the ID which
773251881Speter         refers to the mutable clone of this child. */
774251881Speter      SVN_ERR(set_entry(parent, name, new_node_id, txn_id, trail, pool));
775251881Speter    }
776251881Speter
777251881Speter  /* Initialize the youngster. */
778251881Speter  return svn_fs_base__dag_get_node(child_p, fs, new_node_id, trail, pool);
779251881Speter}
780251881Speter
781251881Speter
782251881Speter
783251881Spetersvn_error_t *
784251881Spetersvn_fs_base__dag_clone_root(dag_node_t **root_p,
785251881Speter                            svn_fs_t *fs,
786251881Speter                            const char *txn_id,
787251881Speter                            trail_t *trail,
788251881Speter                            apr_pool_t *pool)
789251881Speter{
790251881Speter  const svn_fs_id_t *base_root_id, *root_id;
791251881Speter  node_revision_t *noderev;
792251881Speter
793251881Speter  /* Get the node ID's of the root directories of the transaction and
794251881Speter     its base revision.  */
795251881Speter  SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs, txn_id,
796251881Speter                                   trail, pool));
797251881Speter
798251881Speter  /* Oh, give me a clone...
799251881Speter     (If they're the same, we haven't cloned the transaction's root
800251881Speter     directory yet.)  */
801251881Speter  if (svn_fs_base__id_eq(root_id, base_root_id))
802251881Speter    {
803251881Speter      const char *base_copy_id = svn_fs_base__id_copy_id(base_root_id);
804251881Speter
805251881Speter      /* Of my own flesh and bone...
806251881Speter         (Get the NODE-REVISION for the base node, and then write
807251881Speter         it back out as the clone.) */
808251881Speter      SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, base_root_id,
809251881Speter                                            trail, pool));
810251881Speter
811251881Speter      /* With its Y-chromosome changed to X...
812251881Speter         (Store it with an updated predecessor count.) */
813251881Speter      /* ### TODO: Does it even makes sense to have a different copy id for
814251881Speter         the root node?  That is, does this function need a copy_id
815251881Speter         passed in?  */
816251881Speter      noderev->predecessor_id = svn_fs_base__id_copy(base_root_id, pool);
817251881Speter      if (noderev->predecessor_count != -1)
818251881Speter        noderev->predecessor_count++;
819251881Speter      SVN_ERR(svn_fs_base__create_successor(&root_id, fs, base_root_id,
820251881Speter                                            noderev, base_copy_id,
821251881Speter                                            txn_id, trail, pool));
822251881Speter
823251881Speter      /* ... And when it is grown
824251881Speter       *      Then my own little clone
825251881Speter       *        Will be of the opposite sex!
826251881Speter       */
827251881Speter      SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, root_id, trail, pool));
828251881Speter    }
829251881Speter
830251881Speter  /*
831251881Speter   * (Sung to the tune of "Home, Home on the Range", with thanks to
832251881Speter   * Randall Garrett and Isaac Asimov.)
833251881Speter   */
834251881Speter
835251881Speter  /* One way or another, root_id now identifies a cloned root node. */
836251881Speter  return svn_fs_base__dag_get_node(root_p, fs, root_id, trail, pool);
837251881Speter}
838251881Speter
839251881Speter
840251881Spetersvn_error_t *
841251881Spetersvn_fs_base__dag_delete(dag_node_t *parent,
842251881Speter                        const char *name,
843251881Speter                        const char *txn_id,
844251881Speter                        trail_t *trail,
845251881Speter                        apr_pool_t *pool)
846251881Speter{
847251881Speter  node_revision_t *parent_noderev;
848251881Speter  const char *rep_key, *mutable_rep_key;
849251881Speter  apr_hash_t *entries = NULL;
850251881Speter  svn_skel_t *entries_skel;
851251881Speter  svn_fs_t *fs = parent->fs;
852251881Speter  svn_string_t str;
853251881Speter  svn_fs_id_t *id = NULL;
854251881Speter  dag_node_t *node;
855251881Speter
856251881Speter  /* Make sure parent is a directory. */
857251881Speter  if (parent->kind != svn_node_dir)
858251881Speter    return svn_error_createf
859251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
860251881Speter       _("Attempted to delete entry '%s' from *non*-directory node"), name);
861251881Speter
862251881Speter  /* Make sure parent is mutable. */
863251881Speter  if (! svn_fs_base__dag_check_mutable(parent, txn_id))
864251881Speter    return svn_error_createf
865251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
866251881Speter       _("Attempted to delete entry '%s' from immutable directory node"),
867251881Speter       name);
868251881Speter
869251881Speter  /* Make sure that NAME is a single path component. */
870251881Speter  if (! svn_path_is_single_path_component(name))
871251881Speter    return svn_error_createf
872251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
873251881Speter       _("Attempted to delete a node with an illegal name '%s'"), name);
874251881Speter
875251881Speter  /* Get a fresh NODE-REVISION for the parent node. */
876251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&parent_noderev, fs, parent->id,
877251881Speter                                        trail, pool));
878251881Speter
879251881Speter  /* Get the key for the parent's entries list (data) representation. */
880251881Speter  rep_key = parent_noderev->data_key;
881251881Speter
882251881Speter  /* No REP_KEY means no representation, and no representation means
883251881Speter     no data, and no data means no entries...there's nothing here to
884251881Speter     delete! */
885251881Speter  if (! rep_key)
886251881Speter    return svn_error_createf
887251881Speter      (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
888251881Speter       _("Delete failed: directory has no entry '%s'"), name);
889251881Speter
890251881Speter  /* Ensure we have a key to a mutable representation of the entries
891251881Speter     list.  We'll have to update the NODE-REVISION if it points to an
892251881Speter     immutable version.  */
893251881Speter  SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, rep_key,
894251881Speter                                       fs, txn_id, trail, pool));
895251881Speter  if (! svn_fs_base__same_keys(mutable_rep_key, rep_key))
896251881Speter    {
897251881Speter      parent_noderev->data_key = mutable_rep_key;
898251881Speter      SVN_ERR(svn_fs_bdb__put_node_revision(fs, parent->id, parent_noderev,
899251881Speter                                            trail, pool));
900251881Speter    }
901251881Speter
902251881Speter  /* Read the representation, then use it to get the string that holds
903251881Speter     the entries list.  Parse that list into a skel, and parse *that*
904251881Speter     into a hash. */
905251881Speter
906251881Speter  SVN_ERR(svn_fs_base__rep_contents(&str, fs, rep_key, trail, pool));
907251881Speter  entries_skel = svn_skel__parse(str.data, str.len, pool);
908251881Speter  if (entries_skel)
909251881Speter    SVN_ERR(svn_fs_base__parse_entries_skel(&entries, entries_skel, pool));
910251881Speter
911251881Speter  /* Find NAME in the ENTRIES skel.  */
912251881Speter  if (entries)
913251881Speter    id = svn_hash_gets(entries, name);
914251881Speter
915251881Speter  /* If we never found ID in ENTRIES (perhaps because there are no
916251881Speter     ENTRIES, perhaps because ID just isn't in the existing ENTRIES
917251881Speter     ... it doesn't matter), return an error.  */
918251881Speter  if (! id)
919251881Speter    return svn_error_createf
920251881Speter      (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
921251881Speter       _("Delete failed: directory has no entry '%s'"), name);
922251881Speter
923251881Speter  /* Use the ID of this ENTRY to get the entry's node.  */
924251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&node, svn_fs_base__dag_get_fs(parent),
925251881Speter                                    id, trail, pool));
926251881Speter
927251881Speter  /* If mutable, remove it and any mutable children from db. */
928251881Speter  SVN_ERR(svn_fs_base__dag_delete_if_mutable(parent->fs, id, txn_id,
929251881Speter                                             trail, pool));
930251881Speter
931251881Speter  /* Remove this entry from its parent's entries list. */
932251881Speter  svn_hash_sets(entries, name, NULL);
933251881Speter
934251881Speter  /* Replace the old entries list with the new one. */
935251881Speter  {
936251881Speter    svn_stream_t *ws;
937251881Speter    svn_stringbuf_t *unparsed_entries;
938251881Speter    apr_size_t len;
939251881Speter
940251881Speter    SVN_ERR(svn_fs_base__unparse_entries_skel(&entries_skel, entries, pool));
941251881Speter    unparsed_entries = svn_skel__unparse(entries_skel, pool);
942251881Speter    SVN_ERR(svn_fs_base__rep_contents_write_stream(&ws, fs, mutable_rep_key,
943251881Speter                                                   txn_id, TRUE, trail,
944251881Speter                                                   pool));
945251881Speter    len = unparsed_entries->len;
946251881Speter    SVN_ERR(svn_stream_write(ws, unparsed_entries->data, &len));
947251881Speter    SVN_ERR(svn_stream_close(ws));
948251881Speter  }
949251881Speter
950251881Speter  return SVN_NO_ERROR;
951251881Speter}
952251881Speter
953251881Speter
954251881Spetersvn_error_t *
955251881Spetersvn_fs_base__dag_remove_node(svn_fs_t *fs,
956251881Speter                             const svn_fs_id_t *id,
957251881Speter                             const char *txn_id,
958251881Speter                             trail_t *trail,
959251881Speter                             apr_pool_t *pool)
960251881Speter{
961251881Speter  dag_node_t *node;
962251881Speter  node_revision_t *noderev;
963251881Speter
964251881Speter  /* Fetch the node. */
965251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&node, fs, id, trail, pool));
966251881Speter
967251881Speter  /* If immutable, do nothing and return immediately. */
968251881Speter  if (! svn_fs_base__dag_check_mutable(node, txn_id))
969251881Speter    return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
970251881Speter                             _("Attempted removal of immutable node"));
971251881Speter
972251881Speter  /* Get a fresh node-revision. */
973251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, id, trail, pool));
974251881Speter
975251881Speter  /* Delete any mutable property representation. */
976251881Speter  if (noderev->prop_key)
977251881Speter    SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->prop_key,
978251881Speter                                               txn_id, trail, pool));
979251881Speter
980251881Speter  /* Delete any mutable data representation. */
981251881Speter  if (noderev->data_key)
982251881Speter    SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->data_key,
983251881Speter                                               txn_id, trail, pool));
984251881Speter
985251881Speter  /* Delete any mutable edit representation (files only). */
986251881Speter  if (noderev->edit_key)
987251881Speter    SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->edit_key,
988251881Speter                                               txn_id, trail, pool));
989251881Speter
990251881Speter  /* Delete the node revision itself. */
991251881Speter  return svn_fs_base__delete_node_revision(fs, id,
992251881Speter                                           noderev->predecessor_id == NULL,
993251881Speter                                           trail, pool);
994251881Speter}
995251881Speter
996251881Speter
997251881Spetersvn_error_t *
998251881Spetersvn_fs_base__dag_delete_if_mutable(svn_fs_t *fs,
999251881Speter                                   const svn_fs_id_t *id,
1000251881Speter                                   const char *txn_id,
1001251881Speter                                   trail_t *trail,
1002251881Speter                                   apr_pool_t *pool)
1003251881Speter{
1004251881Speter  dag_node_t *node;
1005251881Speter
1006251881Speter  /* Get the node. */
1007251881Speter  SVN_ERR(svn_fs_base__dag_get_node(&node, fs, id, trail, pool));
1008251881Speter
1009251881Speter  /* If immutable, do nothing and return immediately. */
1010251881Speter  if (! svn_fs_base__dag_check_mutable(node, txn_id))
1011251881Speter    return SVN_NO_ERROR;
1012251881Speter
1013251881Speter  /* Else it's mutable.  Recurse on directories... */
1014251881Speter  if (node->kind == svn_node_dir)
1015251881Speter    {
1016251881Speter      apr_hash_t *entries;
1017251881Speter      apr_hash_index_t *hi;
1018251881Speter
1019251881Speter      /* Loop over hash entries */
1020251881Speter      SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, pool));
1021251881Speter      if (entries)
1022251881Speter        {
1023251881Speter          apr_pool_t *subpool = svn_pool_create(pool);
1024251881Speter          for (hi = apr_hash_first(pool, entries);
1025251881Speter               hi;
1026251881Speter               hi = apr_hash_next(hi))
1027251881Speter            {
1028251881Speter              void *val;
1029251881Speter              svn_fs_dirent_t *dirent;
1030251881Speter
1031299742Sdim              svn_pool_clear(subpool);
1032251881Speter              apr_hash_this(hi, NULL, NULL, &val);
1033251881Speter              dirent = val;
1034251881Speter              SVN_ERR(svn_fs_base__dag_delete_if_mutable(fs, dirent->id,
1035251881Speter                                                         txn_id, trail,
1036251881Speter                                                         subpool));
1037251881Speter            }
1038299742Sdim          svn_pool_destroy(subpool);
1039251881Speter        }
1040251881Speter    }
1041251881Speter
1042251881Speter  /* ... then delete the node itself, any mutable representations and
1043251881Speter     strings it points to, and possibly its node-origins record. */
1044251881Speter  return svn_fs_base__dag_remove_node(fs, id, txn_id, trail, pool);
1045251881Speter}
1046251881Speter
1047251881Speter
1048251881Spetersvn_error_t *
1049251881Spetersvn_fs_base__dag_make_file(dag_node_t **child_p,
1050251881Speter                           dag_node_t *parent,
1051251881Speter                           const char *parent_path,
1052251881Speter                           const char *name,
1053251881Speter                           const char *txn_id,
1054251881Speter                           trail_t *trail,
1055251881Speter                           apr_pool_t *pool)
1056251881Speter{
1057251881Speter  /* Call our little helper function */
1058251881Speter  return make_entry(child_p, parent, parent_path, name, FALSE,
1059251881Speter                    txn_id, trail, pool);
1060251881Speter}
1061251881Speter
1062251881Speter
1063251881Spetersvn_error_t *
1064251881Spetersvn_fs_base__dag_make_dir(dag_node_t **child_p,
1065251881Speter                          dag_node_t *parent,
1066251881Speter                          const char *parent_path,
1067251881Speter                          const char *name,
1068251881Speter                          const char *txn_id,
1069251881Speter                          trail_t *trail,
1070251881Speter                          apr_pool_t *pool)
1071251881Speter{
1072251881Speter  /* Call our little helper function */
1073251881Speter  return make_entry(child_p, parent, parent_path, name, TRUE,
1074251881Speter                    txn_id, trail, pool);
1075251881Speter}
1076251881Speter
1077251881Speter
1078251881Spetersvn_error_t *
1079251881Spetersvn_fs_base__dag_get_contents(svn_stream_t **contents,
1080251881Speter                              dag_node_t *file,
1081251881Speter                              trail_t *trail,
1082251881Speter                              apr_pool_t *pool)
1083251881Speter{
1084251881Speter  node_revision_t *noderev;
1085251881Speter
1086251881Speter  /* Make sure our node is a file. */
1087251881Speter  if (file->kind != svn_node_file)
1088251881Speter    return svn_error_createf
1089251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1090251881Speter       _("Attempted to get textual contents of a *non*-file node"));
1091251881Speter
1092251881Speter  /* Go get a fresh node-revision for FILE. */
1093251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, file->fs, file->id,
1094251881Speter                                        trail, pool));
1095251881Speter
1096251881Speter  /* Our job is to _return_ a stream on the file's contents, so the
1097251881Speter     stream has to be trail-independent.  Here, we pass NULL to tell
1098251881Speter     the stream that we're not providing it a trail that lives across
1099251881Speter     reads.  This means the stream will do each read in a one-off,
1100251881Speter     temporary trail.  */
1101251881Speter  return svn_fs_base__rep_contents_read_stream(contents, file->fs,
1102251881Speter                                               noderev->data_key,
1103251881Speter                                               FALSE, trail, pool);
1104251881Speter
1105251881Speter  /* Note that we're not registering any `close' func, because there's
1106251881Speter     nothing to cleanup outside of our trail.  When the trail is
1107251881Speter     freed, the stream/baton will be too. */
1108251881Speter}
1109251881Speter
1110251881Speter
1111251881Spetersvn_error_t *
1112251881Spetersvn_fs_base__dag_file_length(svn_filesize_t *length,
1113251881Speter                             dag_node_t *file,
1114251881Speter                             trail_t *trail,
1115251881Speter                             apr_pool_t *pool)
1116251881Speter{
1117251881Speter  node_revision_t *noderev;
1118251881Speter
1119251881Speter  /* Make sure our node is a file. */
1120251881Speter  if (file->kind != svn_node_file)
1121251881Speter    return svn_error_createf
1122251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1123251881Speter       _("Attempted to get length of a *non*-file node"));
1124251881Speter
1125251881Speter  /* Go get a fresh node-revision for FILE, and . */
1126251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, file->fs, file->id,
1127251881Speter                                        trail, pool));
1128251881Speter  if (noderev->data_key)
1129251881Speter    SVN_ERR(svn_fs_base__rep_contents_size(length, file->fs,
1130251881Speter                                           noderev->data_key, trail, pool));
1131251881Speter  else
1132251881Speter    *length = 0;
1133251881Speter
1134251881Speter  return SVN_NO_ERROR;
1135251881Speter}
1136251881Speter
1137251881Speter
1138251881Spetersvn_error_t *
1139251881Spetersvn_fs_base__dag_file_checksum(svn_checksum_t **checksum,
1140251881Speter                               svn_checksum_kind_t checksum_kind,
1141251881Speter                               dag_node_t *file,
1142251881Speter                               trail_t *trail,
1143251881Speter                               apr_pool_t *pool)
1144251881Speter{
1145251881Speter  node_revision_t *noderev;
1146251881Speter
1147251881Speter  if (file->kind != svn_node_file)
1148251881Speter    return svn_error_createf
1149251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1150251881Speter       _("Attempted to get checksum of a *non*-file node"));
1151251881Speter
1152251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, file->fs, file->id,
1153251881Speter                                        trail, pool));
1154251881Speter  if (! noderev->data_key)
1155251881Speter    {
1156251881Speter      *checksum = NULL;
1157251881Speter      return SVN_NO_ERROR;
1158251881Speter    }
1159251881Speter
1160251881Speter  if (checksum_kind == svn_checksum_md5)
1161251881Speter    return svn_fs_base__rep_contents_checksums(checksum, NULL, file->fs,
1162251881Speter                                               noderev->data_key,
1163251881Speter                                               trail, pool);
1164251881Speter  else if (checksum_kind == svn_checksum_sha1)
1165251881Speter    return svn_fs_base__rep_contents_checksums(NULL, checksum, file->fs,
1166251881Speter                                               noderev->data_key,
1167251881Speter                                               trail, pool);
1168251881Speter  else
1169251881Speter    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
1170251881Speter}
1171251881Speter
1172251881Speter
1173251881Spetersvn_error_t *
1174251881Spetersvn_fs_base__dag_get_edit_stream(svn_stream_t **contents,
1175251881Speter                                 dag_node_t *file,
1176251881Speter                                 const char *txn_id,
1177251881Speter                                 trail_t *trail,
1178251881Speter                                 apr_pool_t *pool)
1179251881Speter{
1180251881Speter  svn_fs_t *fs = file->fs;   /* just for nicer indentation */
1181251881Speter  node_revision_t *noderev;
1182251881Speter  const char *mutable_rep_key;
1183251881Speter  svn_stream_t *ws;
1184251881Speter
1185251881Speter  /* Make sure our node is a file. */
1186251881Speter  if (file->kind != svn_node_file)
1187251881Speter    return svn_error_createf
1188251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1189251881Speter       _("Attempted to set textual contents of a *non*-file node"));
1190251881Speter
1191251881Speter  /* Make sure our node is mutable. */
1192251881Speter  if (! svn_fs_base__dag_check_mutable(file, txn_id))
1193251881Speter    return svn_error_createf
1194251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
1195251881Speter       _("Attempted to set textual contents of an immutable node"));
1196251881Speter
1197251881Speter  /* Get the node revision. */
1198251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, file->id,
1199251881Speter                                        trail, pool));
1200251881Speter
1201251881Speter  /* If this node already has an EDIT-DATA-KEY, destroy the data
1202251881Speter     associated with that key.  */
1203251881Speter  if (noderev->edit_key)
1204251881Speter    SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->edit_key,
1205251881Speter                                               txn_id, trail, pool));
1206251881Speter
1207251881Speter  /* Now, let's ensure that we have a new EDIT-DATA-KEY available for
1208251881Speter     use. */
1209251881Speter  SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, NULL, fs,
1210251881Speter                                       txn_id, trail, pool));
1211251881Speter
1212251881Speter  /* We made a new rep, so update the node revision. */
1213251881Speter  noderev->edit_key = mutable_rep_key;
1214251881Speter  SVN_ERR(svn_fs_bdb__put_node_revision(fs, file->id, noderev,
1215251881Speter                                        trail, pool));
1216251881Speter
1217251881Speter  /* Return a writable stream with which to set new contents. */
1218251881Speter  SVN_ERR(svn_fs_base__rep_contents_write_stream(&ws, fs, mutable_rep_key,
1219251881Speter                                                 txn_id, FALSE, trail,
1220251881Speter                                                 pool));
1221251881Speter  *contents = ws;
1222251881Speter
1223251881Speter  return SVN_NO_ERROR;
1224251881Speter}
1225251881Speter
1226251881Speter
1227251881Speter
1228251881Spetersvn_error_t *
1229251881Spetersvn_fs_base__dag_finalize_edits(dag_node_t *file,
1230251881Speter                                const svn_checksum_t *checksum,
1231251881Speter                                const char *txn_id,
1232251881Speter                                trail_t *trail,
1233251881Speter                                apr_pool_t *pool)
1234251881Speter{
1235251881Speter  svn_fs_t *fs = file->fs;   /* just for nicer indentation */
1236251881Speter  node_revision_t *noderev;
1237251881Speter  const char *old_data_key, *new_data_key, *useless_data_key = NULL;
1238251881Speter  const char *data_key_uniquifier = NULL;
1239251881Speter  svn_checksum_t *md5_checksum, *sha1_checksum;
1240251881Speter  base_fs_data_t *bfd = fs->fsap_data;
1241251881Speter
1242251881Speter  /* Make sure our node is a file. */
1243251881Speter  if (file->kind != svn_node_file)
1244251881Speter    return svn_error_createf
1245251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1246251881Speter       _("Attempted to set textual contents of a *non*-file node"));
1247251881Speter
1248251881Speter  /* Make sure our node is mutable. */
1249251881Speter  if (! svn_fs_base__dag_check_mutable(file, txn_id))
1250251881Speter    return svn_error_createf
1251251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
1252251881Speter       _("Attempted to set textual contents of an immutable node"));
1253251881Speter
1254251881Speter  /* Get the node revision. */
1255251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, file->id,
1256251881Speter                                        trail, pool));
1257251881Speter
1258251881Speter  /* If this node has no EDIT-DATA-KEY, this is a no-op. */
1259251881Speter  if (! noderev->edit_key)
1260251881Speter    return SVN_NO_ERROR;
1261251881Speter
1262251881Speter  /* Get our representation's checksums. */
1263251881Speter  SVN_ERR(svn_fs_base__rep_contents_checksums(&md5_checksum, &sha1_checksum,
1264251881Speter                                              fs, noderev->edit_key,
1265251881Speter                                              trail, pool));
1266251881Speter
1267251881Speter  /* If our caller provided a checksum of the right kind to compare, do so. */
1268251881Speter  if (checksum)
1269251881Speter    {
1270251881Speter      svn_checksum_t *test_checksum;
1271251881Speter
1272251881Speter      if (checksum->kind == svn_checksum_md5)
1273251881Speter        test_checksum = md5_checksum;
1274251881Speter      else if (checksum->kind == svn_checksum_sha1)
1275251881Speter        test_checksum = sha1_checksum;
1276251881Speter      else
1277251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
1278251881Speter
1279251881Speter      if (! svn_checksum_match(checksum, test_checksum))
1280251881Speter        return svn_checksum_mismatch_err(checksum, test_checksum, pool,
1281251881Speter                        _("Checksum mismatch on representation '%s'"),
1282251881Speter                        noderev->edit_key);
1283251881Speter    }
1284251881Speter
1285251881Speter  /* Now, we want to delete the old representation and replace it with
1286251881Speter     the new.  Of course, we don't actually delete anything until
1287251881Speter     everything is being properly referred to by the node-revision
1288251881Speter     skel.
1289251881Speter
1290251881Speter     Now, if the result of all this editing is that we've created a
1291251881Speter     representation that describes content already represented
1292251881Speter     immutably in our database, we don't even need to keep these edits.
1293251881Speter     We can simply point our data_key at that pre-existing
1294251881Speter     representation and throw away our work!  In this situation,
1295251881Speter     though, we'll need a unique ID to help other code distinguish
1296251881Speter     between "the contents weren't touched" and "the contents were
1297251881Speter     touched but still look the same" (to state it oversimply).  */
1298251881Speter  old_data_key = noderev->data_key;
1299251881Speter  if (sha1_checksum && bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1300251881Speter    {
1301251881Speter      svn_error_t *err = svn_fs_bdb__get_checksum_rep(&new_data_key, fs,
1302251881Speter                                                      sha1_checksum,
1303251881Speter                                                      trail, pool);
1304251881Speter      if (! err)
1305251881Speter        {
1306251881Speter          useless_data_key = noderev->edit_key;
1307251881Speter          err = svn_fs_bdb__reserve_rep_reuse_id(&data_key_uniquifier,
1308251881Speter                                                 trail->fs, trail, pool);
1309251881Speter        }
1310251881Speter      else if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_CHECKSUM_REP))
1311251881Speter        {
1312251881Speter          svn_error_clear(err);
1313251881Speter          err = SVN_NO_ERROR;
1314251881Speter          new_data_key = noderev->edit_key;
1315251881Speter        }
1316251881Speter      SVN_ERR(err);
1317251881Speter    }
1318251881Speter  else
1319251881Speter    {
1320251881Speter      new_data_key = noderev->edit_key;
1321251881Speter    }
1322251881Speter
1323251881Speter  noderev->data_key = new_data_key;
1324251881Speter  noderev->data_key_uniquifier = data_key_uniquifier;
1325251881Speter  noderev->edit_key = NULL;
1326251881Speter
1327251881Speter  SVN_ERR(svn_fs_bdb__put_node_revision(fs, file->id, noderev, trail, pool));
1328251881Speter
1329251881Speter  /* Only *now* can we safely destroy the old representation (if it
1330251881Speter     even existed in the first place). */
1331251881Speter  if (old_data_key)
1332251881Speter    SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, old_data_key, txn_id,
1333251881Speter                                               trail, pool));
1334251881Speter
1335251881Speter  /* If we've got a discardable rep (probably because we ended up
1336251881Speter     re-using a preexisting one), throw out the discardable rep. */
1337251881Speter  if (useless_data_key)
1338251881Speter    SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, useless_data_key,
1339251881Speter                                               txn_id, trail, pool));
1340251881Speter
1341251881Speter  return SVN_NO_ERROR;
1342251881Speter}
1343251881Speter
1344251881Speter
1345251881Speter
1346251881Speterdag_node_t *
1347299742Sdimsvn_fs_base__dag_dup(const dag_node_t *node,
1348251881Speter                     apr_pool_t *pool)
1349251881Speter{
1350251881Speter  /* Allocate our new node. */
1351251881Speter  dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
1352251881Speter
1353251881Speter  new_node->fs = node->fs;
1354251881Speter  new_node->id = svn_fs_base__id_copy(node->id, pool);
1355251881Speter  new_node->kind = node->kind;
1356251881Speter  new_node->created_path = apr_pstrdup(pool, node->created_path);
1357251881Speter  return new_node;
1358251881Speter}
1359251881Speter
1360251881Speter
1361251881Spetersvn_error_t *
1362251881Spetersvn_fs_base__dag_open(dag_node_t **child_p,
1363251881Speter                      dag_node_t *parent,
1364251881Speter                      const char *name,
1365251881Speter                      trail_t *trail,
1366251881Speter                      apr_pool_t *pool)
1367251881Speter{
1368251881Speter  const svn_fs_id_t *node_id;
1369251881Speter
1370251881Speter  /* Ensure that NAME exists in PARENT's entry list. */
1371251881Speter  SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, trail, pool));
1372251881Speter  if (! node_id)
1373251881Speter    return svn_error_createf
1374251881Speter      (SVN_ERR_FS_NOT_FOUND, NULL,
1375251881Speter       _("Attempted to open non-existent child node '%s'"), name);
1376251881Speter
1377251881Speter  /* Make sure that NAME is a single path component. */
1378251881Speter  if (! svn_path_is_single_path_component(name))
1379251881Speter    return svn_error_createf
1380251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
1381251881Speter       _("Attempted to open node with an illegal name '%s'"), name);
1382251881Speter
1383251881Speter  /* Now get the node that was requested. */
1384251881Speter  return svn_fs_base__dag_get_node(child_p, svn_fs_base__dag_get_fs(parent),
1385251881Speter                                   node_id, trail, pool);
1386251881Speter}
1387251881Speter
1388251881Speter
1389251881Spetersvn_error_t *
1390251881Spetersvn_fs_base__dag_copy(dag_node_t *to_node,
1391251881Speter                      const char *entry,
1392251881Speter                      dag_node_t *from_node,
1393251881Speter                      svn_boolean_t preserve_history,
1394251881Speter                      svn_revnum_t from_rev,
1395251881Speter                      const char *from_path,
1396251881Speter                      const char *txn_id,
1397251881Speter                      trail_t *trail,
1398251881Speter                      apr_pool_t *pool)
1399251881Speter{
1400251881Speter  const svn_fs_id_t *id;
1401251881Speter
1402251881Speter  if (preserve_history)
1403251881Speter    {
1404251881Speter      node_revision_t *noderev;
1405251881Speter      const char *copy_id;
1406251881Speter      svn_fs_t *fs = svn_fs_base__dag_get_fs(from_node);
1407251881Speter      const svn_fs_id_t *src_id = svn_fs_base__dag_get_id(from_node);
1408251881Speter      const char *from_txn_id = NULL;
1409251881Speter
1410251881Speter      /* Make a copy of the original node revision. */
1411251881Speter      SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, from_node->id,
1412251881Speter                                            trail, pool));
1413251881Speter
1414251881Speter      /* Reserve a copy ID for this new copy. */
1415251881Speter      SVN_ERR(svn_fs_bdb__reserve_copy_id(&copy_id, fs, trail, pool));
1416251881Speter
1417251881Speter      /* Create a successor with its predecessor pointing at the copy
1418251881Speter         source. */
1419251881Speter      noderev->predecessor_id = svn_fs_base__id_copy(src_id, pool);
1420251881Speter      if (noderev->predecessor_count != -1)
1421251881Speter        noderev->predecessor_count++;
1422251881Speter      noderev->created_path = svn_fspath__join
1423251881Speter        (svn_fs_base__dag_get_created_path(to_node), entry, pool);
1424251881Speter      SVN_ERR(svn_fs_base__create_successor(&id, fs, src_id, noderev,
1425251881Speter                                            copy_id, txn_id, trail, pool));
1426251881Speter
1427251881Speter      /* Translate FROM_REV into a transaction ID. */
1428251881Speter      SVN_ERR(svn_fs_base__rev_get_txn_id(&from_txn_id, fs, from_rev,
1429251881Speter                                          trail, pool));
1430251881Speter
1431251881Speter      /* Now that we've done the copy, we need to add the information
1432251881Speter         about the copy to the `copies' table, using the COPY_ID we
1433251881Speter         reserved above.  */
1434251881Speter      SVN_ERR(svn_fs_bdb__create_copy
1435251881Speter              (fs, copy_id,
1436251881Speter               svn_fs__canonicalize_abspath(from_path, pool),
1437251881Speter               from_txn_id, id, copy_kind_real, trail, pool));
1438251881Speter
1439251881Speter      /* Finally, add the COPY_ID to the transaction's list of copies
1440251881Speter         so that, if this transaction is aborted, the `copies' table
1441251881Speter         entry we added above will be cleaned up. */
1442251881Speter      SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id, trail, pool));
1443251881Speter    }
1444251881Speter  else  /* don't preserve history */
1445251881Speter    {
1446251881Speter      id = svn_fs_base__dag_get_id(from_node);
1447251881Speter    }
1448251881Speter
1449251881Speter  /* Set the entry in to_node to the new id. */
1450251881Speter  return svn_fs_base__dag_set_entry(to_node, entry, id, txn_id,
1451251881Speter                                    trail, pool);
1452251881Speter}
1453251881Speter
1454251881Speter
1455251881Speter
1456251881Speter/*** Deltification ***/
1457251881Speter
1458251881Speter/* Maybe change the representation identified by TARGET_REP_KEY to be
1459251881Speter   a delta against the representation identified by SOURCE_REP_KEY.
1460251881Speter   Some reasons why we wouldn't include:
1461251881Speter
1462251881Speter      - TARGET_REP_KEY and SOURCE_REP_KEY are the same key.
1463251881Speter
1464251881Speter      - TARGET_REP_KEY's representation isn't mutable in TXN_ID (if
1465251881Speter        TXN_ID is non-NULL).
1466251881Speter
1467251881Speter      - The delta provides less space savings that a fulltext (this is
1468251881Speter        a detail handled by lower logic layers, not this function).
1469251881Speter
1470251881Speter   Do this work in TRAIL, using POOL for necessary allocations.
1471251881Speter*/
1472251881Speterstatic svn_error_t *
1473251881Spetermaybe_deltify_mutable_rep(const char *target_rep_key,
1474251881Speter                          const char *source_rep_key,
1475251881Speter                          const char *txn_id,
1476251881Speter                          trail_t *trail,
1477251881Speter                          apr_pool_t *pool)
1478251881Speter{
1479251881Speter  if (! (target_rep_key && source_rep_key
1480251881Speter         && (strcmp(target_rep_key, source_rep_key) != 0)))
1481251881Speter    return SVN_NO_ERROR;
1482251881Speter
1483251881Speter  if (txn_id)
1484251881Speter    {
1485251881Speter      representation_t *target_rep;
1486251881Speter      SVN_ERR(svn_fs_bdb__read_rep(&target_rep, trail->fs, target_rep_key,
1487251881Speter                                   trail, pool));
1488251881Speter      if (strcmp(target_rep->txn_id, txn_id) != 0)
1489251881Speter        return SVN_NO_ERROR;
1490251881Speter    }
1491251881Speter
1492251881Speter  return svn_fs_base__rep_deltify(trail->fs, target_rep_key, source_rep_key,
1493251881Speter                                  trail, pool);
1494251881Speter}
1495251881Speter
1496251881Speter
1497251881Spetersvn_error_t *
1498251881Spetersvn_fs_base__dag_deltify(dag_node_t *target,
1499251881Speter                         dag_node_t *source,
1500251881Speter                         svn_boolean_t props_only,
1501251881Speter                         const char *txn_id,
1502251881Speter                         trail_t *trail,
1503251881Speter                         apr_pool_t *pool)
1504251881Speter{
1505251881Speter  node_revision_t *source_nr, *target_nr;
1506251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(target);
1507251881Speter
1508251881Speter  /* Get node revisions for the two nodes.  */
1509251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&target_nr, fs, target->id,
1510251881Speter                                        trail, pool));
1511251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&source_nr, fs, source->id,
1512251881Speter                                        trail, pool));
1513251881Speter
1514251881Speter  /* If TARGET and SOURCE both have properties, and are not sharing a
1515251881Speter     property key, deltify TARGET's properties.  */
1516251881Speter  SVN_ERR(maybe_deltify_mutable_rep(target_nr->prop_key, source_nr->prop_key,
1517251881Speter                                    txn_id, trail, pool));
1518251881Speter
1519251881Speter  /* If we are not only attending to properties, and if TARGET and
1520251881Speter     SOURCE both have data, and are not sharing a data key, deltify
1521251881Speter     TARGET's data.  */
1522251881Speter  if (! props_only)
1523251881Speter    SVN_ERR(maybe_deltify_mutable_rep(target_nr->data_key, source_nr->data_key,
1524251881Speter                                      txn_id, trail, pool));
1525251881Speter
1526251881Speter  return SVN_NO_ERROR;
1527251881Speter}
1528251881Speter
1529251881Speter/* Maybe store a `checksum-reps' index record for the representation whose
1530251881Speter   key is REP.  (If there's already a rep for this checksum, we don't
1531251881Speter   bother overwriting it.)  */
1532251881Speterstatic svn_error_t *
1533251881Spetermaybe_store_checksum_rep(const char *rep,
1534251881Speter                         trail_t *trail,
1535251881Speter                         apr_pool_t *pool)
1536251881Speter{
1537251881Speter  svn_error_t *err = SVN_NO_ERROR;
1538251881Speter  svn_fs_t *fs = trail->fs;
1539251881Speter  svn_checksum_t *sha1_checksum;
1540251881Speter
1541251881Speter  /* We want the SHA1 checksum, if any. */
1542251881Speter  SVN_ERR(svn_fs_base__rep_contents_checksums(NULL, &sha1_checksum,
1543251881Speter                                              fs, rep, trail, pool));
1544251881Speter  if (sha1_checksum)
1545251881Speter    {
1546251881Speter      err = svn_fs_bdb__set_checksum_rep(fs, sha1_checksum, rep, trail, pool);
1547251881Speter      if (err && (err->apr_err == SVN_ERR_FS_ALREADY_EXISTS))
1548251881Speter        {
1549251881Speter          svn_error_clear(err);
1550251881Speter          err = SVN_NO_ERROR;
1551251881Speter        }
1552251881Speter    }
1553251881Speter  return svn_error_trace(err);
1554251881Speter}
1555251881Speter
1556251881Spetersvn_error_t *
1557251881Spetersvn_fs_base__dag_index_checksums(dag_node_t *node,
1558251881Speter                                 trail_t *trail,
1559251881Speter                                 apr_pool_t *pool)
1560251881Speter{
1561251881Speter  node_revision_t *node_rev;
1562251881Speter
1563251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, trail->fs, node->id,
1564251881Speter                                        trail, pool));
1565251881Speter  if ((node_rev->kind == svn_node_file) && node_rev->data_key)
1566251881Speter    SVN_ERR(maybe_store_checksum_rep(node_rev->data_key, trail, pool));
1567251881Speter  if (node_rev->prop_key)
1568251881Speter    SVN_ERR(maybe_store_checksum_rep(node_rev->prop_key, trail, pool));
1569251881Speter
1570251881Speter  return SVN_NO_ERROR;
1571251881Speter}
1572251881Speter
1573251881Speter
1574251881Speter
1575251881Speter/*** Committing ***/
1576251881Speter
1577251881Spetersvn_error_t *
1578251881Spetersvn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,
1579251881Speter                            svn_fs_txn_t *txn,
1580251881Speter                            trail_t *trail,
1581251881Speter                            apr_pool_t *pool)
1582251881Speter{
1583251881Speter  revision_t revision;
1584251881Speter  apr_hash_t *txnprops;
1585251881Speter  svn_fs_t *fs = txn->fs;
1586251881Speter  const char *txn_id = txn->id;
1587299742Sdim  const svn_string_t *client_date;
1588251881Speter
1589251881Speter  /* Remove any temporary transaction properties initially created by
1590251881Speter     begin_txn().  */
1591251881Speter  SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, txn_id, trail));
1592251881Speter
1593251881Speter  /* Add new revision entry to `revisions' table. */
1594251881Speter  revision.txn_id = txn_id;
1595251881Speter  *new_rev = SVN_INVALID_REVNUM;
1596251881Speter  SVN_ERR(svn_fs_bdb__put_rev(new_rev, fs, &revision, trail, pool));
1597251881Speter
1598251881Speter  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
1599251881Speter    SVN_ERR(svn_fs_base__set_txn_prop
1600251881Speter            (fs, txn_id, SVN_FS__PROP_TXN_CHECK_OOD, NULL, trail, pool));
1601251881Speter
1602251881Speter  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
1603251881Speter    SVN_ERR(svn_fs_base__set_txn_prop
1604251881Speter            (fs, txn_id, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL, trail, pool));
1605251881Speter
1606299742Sdim  client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE);
1607299742Sdim  if (client_date)
1608299742Sdim    SVN_ERR(svn_fs_base__set_txn_prop
1609299742Sdim            (fs, txn_id, SVN_FS__PROP_TXN_CLIENT_DATE, NULL, trail, pool));
1610299742Sdim
1611251881Speter  /* Promote the unfinished transaction to a committed one. */
1612251881Speter  SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, *new_rev,
1613251881Speter                                          trail, pool));
1614251881Speter
1615299742Sdim  if (!client_date || strcmp(client_date->data, "1"))
1616299742Sdim    {
1617299742Sdim      /* Set a date on the commit if requested.  We wait until now to fetch the
1618299742Sdim         date, so it's definitely newer than any previous revision's date. */
1619299742Sdim      svn_string_t date;
1620299742Sdim      date.data = svn_time_to_cstring(apr_time_now(), pool);
1621299742Sdim      date.len = strlen(date.data);
1622299742Sdim      SVN_ERR(svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE,
1623299742Sdim                                        NULL, &date, trail, pool));
1624299742Sdim    }
1625299742Sdim
1626299742Sdim  return SVN_NO_ERROR;
1627251881Speter}
1628251881Speter
1629251881Speter
1630251881Speter/*** Comparison. ***/
1631251881Speter
1632251881Spetersvn_error_t *
1633251881Spetersvn_fs_base__things_different(svn_boolean_t *props_changed,
1634251881Speter                              svn_boolean_t *contents_changed,
1635251881Speter                              dag_node_t *node1,
1636251881Speter                              dag_node_t *node2,
1637251881Speter                              trail_t *trail,
1638251881Speter                              apr_pool_t *pool)
1639251881Speter{
1640251881Speter  node_revision_t *noderev1, *noderev2;
1641251881Speter
1642251881Speter  /* If we have no place to store our results, don't bother doing
1643251881Speter     anything. */
1644251881Speter  if (! props_changed && ! contents_changed)
1645251881Speter    return SVN_NO_ERROR;
1646251881Speter
1647251881Speter  /* The node revision skels for these two nodes. */
1648251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev1, node1->fs, node1->id,
1649251881Speter                                        trail, pool));
1650251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&noderev2, node2->fs, node2->id,
1651251881Speter                                        trail, pool));
1652251881Speter
1653251881Speter  /* Compare property keys. */
1654251881Speter  if (props_changed != NULL)
1655251881Speter    *props_changed = (! svn_fs_base__same_keys(noderev1->prop_key,
1656251881Speter                                               noderev2->prop_key));
1657251881Speter
1658251881Speter  /* Compare contents keys and their (optional) uniquifiers. */
1659251881Speter  if (contents_changed != NULL)
1660251881Speter    *contents_changed =
1661251881Speter      (! (svn_fs_base__same_keys(noderev1->data_key,
1662251881Speter                                 noderev2->data_key)
1663251881Speter          /* Technically, these uniquifiers aren't used and "keys",
1664251881Speter             but keys are base-36 stringified numbers, so we'll take
1665251881Speter             this liberty. */
1666251881Speter          && (svn_fs_base__same_keys(noderev1->data_key_uniquifier,
1667251881Speter                                     noderev2->data_key_uniquifier))));
1668251881Speter
1669251881Speter  return SVN_NO_ERROR;
1670251881Speter}
1671251881Speter
1672251881Speter
1673251881Speter
1674251881Speter/*** Mergeinfo tracking stuff ***/
1675251881Speter
1676251881Spetersvn_error_t *
1677251881Spetersvn_fs_base__dag_get_mergeinfo_stats(svn_boolean_t *has_mergeinfo,
1678251881Speter                                     apr_int64_t *count,
1679251881Speter                                     dag_node_t *node,
1680251881Speter                                     trail_t *trail,
1681251881Speter                                     apr_pool_t *pool)
1682251881Speter{
1683251881Speter  node_revision_t *node_rev;
1684251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(node);
1685251881Speter  const svn_fs_id_t *id = svn_fs_base__dag_get_id(node);
1686251881Speter
1687251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, id, trail, pool));
1688251881Speter  if (has_mergeinfo)
1689251881Speter    *has_mergeinfo = node_rev->has_mergeinfo;
1690251881Speter  if (count)
1691251881Speter    *count = node_rev->mergeinfo_count;
1692251881Speter  return SVN_NO_ERROR;
1693251881Speter}
1694251881Speter
1695251881Speter
1696251881Spetersvn_error_t *
1697251881Spetersvn_fs_base__dag_set_has_mergeinfo(dag_node_t *node,
1698251881Speter                                   svn_boolean_t has_mergeinfo,
1699251881Speter                                   svn_boolean_t *had_mergeinfo,
1700251881Speter                                   const char *txn_id,
1701251881Speter                                   trail_t *trail,
1702251881Speter                                   apr_pool_t *pool)
1703251881Speter{
1704251881Speter  node_revision_t *node_rev;
1705251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(node);
1706251881Speter  const svn_fs_id_t *id = svn_fs_base__dag_get_id(node);
1707251881Speter
1708251881Speter  SVN_ERR(svn_fs_base__test_required_feature_format
1709251881Speter          (trail->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
1710251881Speter
1711251881Speter  if (! svn_fs_base__dag_check_mutable(node, txn_id))
1712251881Speter    return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
1713251881Speter                             _("Attempted merge tracking info change on "
1714251881Speter                               "immutable node"));
1715251881Speter
1716251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, id, trail, pool));
1717251881Speter  *had_mergeinfo = node_rev->has_mergeinfo;
1718251881Speter
1719251881Speter  /* Are we changing the node? */
1720251881Speter  if ((! has_mergeinfo) != (! *had_mergeinfo))
1721251881Speter    {
1722251881Speter      /* Note the new has-mergeinfo state. */
1723251881Speter      node_rev->has_mergeinfo = has_mergeinfo;
1724251881Speter
1725251881Speter      /* Increment or decrement the mergeinfo count as necessary. */
1726251881Speter      if (has_mergeinfo)
1727251881Speter        node_rev->mergeinfo_count++;
1728251881Speter      else
1729251881Speter        node_rev->mergeinfo_count--;
1730251881Speter
1731251881Speter      SVN_ERR(svn_fs_bdb__put_node_revision(fs, id, node_rev, trail, pool));
1732251881Speter    }
1733251881Speter  return SVN_NO_ERROR;
1734251881Speter}
1735251881Speter
1736251881Speter
1737251881Spetersvn_error_t *
1738251881Spetersvn_fs_base__dag_adjust_mergeinfo_count(dag_node_t *node,
1739251881Speter                                        apr_int64_t count_delta,
1740251881Speter                                        const char *txn_id,
1741251881Speter                                        trail_t *trail,
1742251881Speter                                        apr_pool_t *pool)
1743251881Speter{
1744251881Speter  node_revision_t *node_rev;
1745251881Speter  svn_fs_t *fs = svn_fs_base__dag_get_fs(node);
1746251881Speter  const svn_fs_id_t *id = svn_fs_base__dag_get_id(node);
1747251881Speter
1748251881Speter  SVN_ERR(svn_fs_base__test_required_feature_format
1749251881Speter          (trail->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
1750251881Speter
1751251881Speter  if (! svn_fs_base__dag_check_mutable(node, txn_id))
1752251881Speter    return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
1753251881Speter                             _("Attempted mergeinfo count change on "
1754251881Speter                               "immutable node"));
1755251881Speter
1756251881Speter  if (count_delta == 0)
1757251881Speter    return SVN_NO_ERROR;
1758251881Speter
1759251881Speter  SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, id, trail, pool));
1760251881Speter  node_rev->mergeinfo_count = node_rev->mergeinfo_count + count_delta;
1761251881Speter  if ((node_rev->mergeinfo_count < 0)
1762251881Speter      || ((node->kind == svn_node_file) && (node_rev->mergeinfo_count > 1)))
1763251881Speter    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
1764251881Speter                             apr_psprintf(pool,
1765251881Speter                                          _("Invalid value (%%%s) for node "
1766251881Speter                                            "revision mergeinfo count"),
1767251881Speter                                          APR_INT64_T_FMT),
1768251881Speter                             node_rev->mergeinfo_count);
1769251881Speter
1770251881Speter  return svn_fs_bdb__put_node_revision(fs, id, node_rev, trail, pool);
1771251881Speter}
1772