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_error.h"
27251881Speter#include "svn_fs.h"
28251881Speter#include "svn_props.h"
29251881Speter#include "svn_pools.h"
30251881Speter
31299742Sdim#include "cached_data.h"
32251881Speter#include "dag.h"
33251881Speter#include "fs.h"
34251881Speter#include "fs_fs.h"
35251881Speter#include "id.h"
36299742Sdim#include "transaction.h"
37251881Speter
38251881Speter#include "../libsvn_fs/fs-loader.h"
39251881Speter
40251881Speter#include "private/svn_fspath.h"
41251881Speter#include "svn_private_config.h"
42251881Speter#include "private/svn_temp_serializer.h"
43251881Speter#include "temp_serializer.h"
44251881Speter
45251881Speter
46251881Speter/* Initializing a filesystem.  */
47251881Speter
48251881Speterstruct dag_node_t
49251881Speter{
50251881Speter  /* The filesystem this dag node came from. */
51251881Speter  svn_fs_t *fs;
52251881Speter
53251881Speter  /* The node revision ID for this dag node, allocated in POOL.  */
54251881Speter  svn_fs_id_t *id;
55251881Speter
56251881Speter  /* In the special case that this node is the root of a transaction
57251881Speter     that has not yet been modified, the node revision ID for this dag
58251881Speter     node's predecessor; otherwise NULL. (Used in
59251881Speter     svn_fs_node_created_rev.) */
60251881Speter  const svn_fs_id_t *fresh_root_predecessor_id;
61251881Speter
62251881Speter  /* The node's type (file, dir, etc.) */
63251881Speter  svn_node_kind_t kind;
64251881Speter
65251881Speter  /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
66251881Speter     This is allocated in this node's POOL.
67251881Speter
68251881Speter     If you're willing to respect all the rules above, you can munge
69251881Speter     this yourself, but you're probably better off just calling
70251881Speter     `get_node_revision' and `set_node_revision', which take care of
71251881Speter     things for you.  */
72251881Speter  node_revision_t *node_revision;
73251881Speter
74251881Speter  /* The pool to allocate NODE_REVISION in. */
75251881Speter  apr_pool_t *node_pool;
76251881Speter
77251881Speter  /* the path at which this node was created. */
78251881Speter  const char *created_path;
79251881Speter};
80251881Speter
81251881Speter
82251881Speter
83251881Speter/* Trivial helper/accessor functions. */
84251881Spetersvn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node)
85251881Speter{
86251881Speter  return node->kind;
87251881Speter}
88251881Speter
89251881Speter
90251881Speterconst svn_fs_id_t *
91251881Spetersvn_fs_fs__dag_get_id(const dag_node_t *node)
92251881Speter{
93251881Speter  return node->id;
94251881Speter}
95251881Speter
96251881Speter
97251881Speterconst char *
98251881Spetersvn_fs_fs__dag_get_created_path(dag_node_t *node)
99251881Speter{
100251881Speter  return node->created_path;
101251881Speter}
102251881Speter
103251881Speter
104251881Spetersvn_fs_t *
105251881Spetersvn_fs_fs__dag_get_fs(dag_node_t *node)
106251881Speter{
107251881Speter  return node->fs;
108251881Speter}
109251881Speter
110251881Spetervoid
111251881Spetersvn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs)
112251881Speter{
113251881Speter  node->fs = fs;
114251881Speter}
115251881Speter
116251881Speter
117251881Speter/* Dup NODEREV and all associated data into POOL.
118251881Speter   Leaves the id and is_fresh_txn_root fields as zero bytes. */
119251881Speterstatic node_revision_t *
120251881Spetercopy_node_revision(node_revision_t *noderev,
121251881Speter                   apr_pool_t *pool)
122251881Speter{
123251881Speter  node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr));
124251881Speter  nr->kind = noderev->kind;
125251881Speter  if (noderev->predecessor_id)
126251881Speter    nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
127251881Speter  nr->predecessor_count = noderev->predecessor_count;
128251881Speter  if (noderev->copyfrom_path)
129251881Speter    nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path);
130251881Speter  nr->copyfrom_rev = noderev->copyfrom_rev;
131251881Speter  nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path);
132251881Speter  nr->copyroot_rev = noderev->copyroot_rev;
133251881Speter  nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool);
134251881Speter  nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool);
135251881Speter  nr->mergeinfo_count = noderev->mergeinfo_count;
136251881Speter  nr->has_mergeinfo = noderev->has_mergeinfo;
137251881Speter
138251881Speter  if (noderev->created_path)
139251881Speter    nr->created_path = apr_pstrdup(pool, noderev->created_path);
140251881Speter  return nr;
141251881Speter}
142251881Speter
143251881Speter
144251881Speter/* Set *NODEREV_P to the cached node-revision for NODE.
145251881Speter   If the node-revision was not already cached in NODE, read it in,
146251881Speter   allocating the cache in NODE->NODE_POOL.
147251881Speter
148251881Speter   If you plan to change the contents of NODE, be careful!  We're
149251881Speter   handing you a pointer directly to our cached node-revision, not
150251881Speter   your own copy.  If you change it as part of some operation, but
151251881Speter   then some Berkeley DB function deadlocks or gets an error, you'll
152251881Speter   need to back out your changes, or else the cache will reflect
153251881Speter   changes that never got committed.  It's probably best not to change
154251881Speter   the structure at all.  */
155251881Speterstatic svn_error_t *
156251881Speterget_node_revision(node_revision_t **noderev_p,
157251881Speter                  dag_node_t *node)
158251881Speter{
159251881Speter  /* If we've already got a copy, there's no need to read it in.  */
160251881Speter  if (! node->node_revision)
161251881Speter    {
162251881Speter      node_revision_t *noderev;
163299742Sdim      apr_pool_t *scratch_pool = svn_pool_create(node->node_pool);
164251881Speter
165251881Speter      SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs,
166299742Sdim                                           node->id, node->node_pool,
167299742Sdim                                           scratch_pool));
168251881Speter      node->node_revision = noderev;
169299742Sdim      svn_pool_destroy(scratch_pool);
170251881Speter    }
171251881Speter
172251881Speter  /* Now NODE->node_revision is set.  */
173251881Speter  *noderev_p = node->node_revision;
174251881Speter  return SVN_NO_ERROR;
175251881Speter}
176251881Speter
177251881Speter
178251881Spetersvn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node)
179251881Speter{
180299742Sdim  return svn_fs_fs__id_is_txn(svn_fs_fs__dag_get_id(node));
181251881Speter}
182251881Speter
183251881Speter
184251881Spetersvn_error_t *
185251881Spetersvn_fs_fs__dag_get_node(dag_node_t **node,
186251881Speter                        svn_fs_t *fs,
187251881Speter                        const svn_fs_id_t *id,
188251881Speter                        apr_pool_t *pool)
189251881Speter{
190251881Speter  dag_node_t *new_node;
191251881Speter  node_revision_t *noderev;
192251881Speter
193251881Speter  /* Construct the node. */
194251881Speter  new_node = apr_pcalloc(pool, sizeof(*new_node));
195251881Speter  new_node->fs = fs;
196251881Speter  new_node->id = svn_fs_fs__id_copy(id, pool);
197251881Speter
198251881Speter  /* Grab the contents so we can inspect the node's kind and created path. */
199251881Speter  new_node->node_pool = pool;
200251881Speter  SVN_ERR(get_node_revision(&noderev, new_node));
201251881Speter
202251881Speter  /* Initialize the KIND and CREATED_PATH attributes */
203251881Speter  new_node->kind = noderev->kind;
204251881Speter  new_node->created_path = apr_pstrdup(pool, noderev->created_path);
205251881Speter
206251881Speter  if (noderev->is_fresh_txn_root)
207251881Speter    new_node->fresh_root_predecessor_id = noderev->predecessor_id;
208251881Speter  else
209251881Speter    new_node->fresh_root_predecessor_id = NULL;
210251881Speter
211251881Speter  /* Return a fresh new node */
212251881Speter  *node = new_node;
213251881Speter  return SVN_NO_ERROR;
214251881Speter}
215251881Speter
216251881Speter
217251881Spetersvn_error_t *
218251881Spetersvn_fs_fs__dag_get_revision(svn_revnum_t *rev,
219251881Speter                            dag_node_t *node,
220251881Speter                            apr_pool_t *pool)
221251881Speter{
222251881Speter  /* In the special case that this is an unmodified transaction root,
223251881Speter     we need to actually get the revision of the noderev's predecessor
224251881Speter     (the revision root); see Issue #2608. */
225251881Speter  const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
226251881Speter    ? node->fresh_root_predecessor_id : node->id;
227251881Speter
228251881Speter  /* Look up the committed revision from the Node-ID. */
229251881Speter  *rev = svn_fs_fs__id_rev(correct_id);
230251881Speter
231251881Speter  return SVN_NO_ERROR;
232251881Speter}
233251881Speter
234251881Speter
235251881Spetersvn_error_t *
236251881Spetersvn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p,
237251881Speter                                  dag_node_t *node)
238251881Speter{
239251881Speter  node_revision_t *noderev;
240251881Speter
241251881Speter  SVN_ERR(get_node_revision(&noderev, node));
242251881Speter  *id_p = noderev->predecessor_id;
243251881Speter  return SVN_NO_ERROR;
244251881Speter}
245251881Speter
246251881Speter
247251881Spetersvn_error_t *
248251881Spetersvn_fs_fs__dag_get_predecessor_count(int *count,
249251881Speter                                     dag_node_t *node)
250251881Speter{
251251881Speter  node_revision_t *noderev;
252251881Speter
253251881Speter  SVN_ERR(get_node_revision(&noderev, node));
254251881Speter  *count = noderev->predecessor_count;
255251881Speter  return SVN_NO_ERROR;
256251881Speter}
257251881Speter
258251881Spetersvn_error_t *
259251881Spetersvn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count,
260251881Speter                                   dag_node_t *node)
261251881Speter{
262251881Speter  node_revision_t *noderev;
263251881Speter
264251881Speter  SVN_ERR(get_node_revision(&noderev, node));
265251881Speter  *count = noderev->mergeinfo_count;
266251881Speter  return SVN_NO_ERROR;
267251881Speter}
268251881Speter
269251881Spetersvn_error_t *
270251881Spetersvn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
271251881Speter                             dag_node_t *node)
272251881Speter{
273251881Speter  node_revision_t *noderev;
274251881Speter
275251881Speter  SVN_ERR(get_node_revision(&noderev, node));
276251881Speter  *has_mergeinfo = noderev->has_mergeinfo;
277251881Speter  return SVN_NO_ERROR;
278251881Speter}
279251881Speter
280251881Spetersvn_error_t *
281251881Spetersvn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
282251881Speter                                              dag_node_t *node)
283251881Speter{
284251881Speter  node_revision_t *noderev;
285251881Speter
286251881Speter  if (node->kind != svn_node_dir)
287251881Speter    {
288251881Speter      *do_they = FALSE;
289251881Speter      return SVN_NO_ERROR;
290251881Speter    }
291251881Speter
292251881Speter  SVN_ERR(get_node_revision(&noderev, node));
293251881Speter  if (noderev->mergeinfo_count > 1)
294251881Speter    *do_they = TRUE;
295251881Speter  else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
296251881Speter    *do_they = TRUE;
297251881Speter  else
298251881Speter    *do_they = FALSE;
299251881Speter  return SVN_NO_ERROR;
300251881Speter}
301251881Speter
302251881Speter
303251881Speter/*** Directory node functions ***/
304251881Speter
305251881Speter/* Some of these are helpers for functions outside this section. */
306251881Speter
307251881Speter/* Set *ID_P to the node-id for entry NAME in PARENT.  If no such
308251881Speter   entry, set *ID_P to NULL but do not error.  The node-id is
309251881Speter   allocated in POOL. */
310251881Speterstatic svn_error_t *
311251881Speterdir_entry_id_from_node(const svn_fs_id_t **id_p,
312251881Speter                       dag_node_t *parent,
313251881Speter                       const char *name,
314251881Speter                       apr_pool_t *result_pool,
315251881Speter                       apr_pool_t *scratch_pool)
316251881Speter{
317251881Speter  svn_fs_dirent_t *dirent;
318251881Speter
319299742Sdim  SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, result_pool,
320299742Sdim                                   scratch_pool));
321299742Sdim  *id_p = dirent ? dirent->id : NULL;
322251881Speter
323251881Speter  return SVN_NO_ERROR;
324251881Speter}
325251881Speter
326251881Speter
327251881Speter/* Add or set in PARENT a directory entry NAME pointing to ID.
328251881Speter   Allocations are done in POOL.
329251881Speter
330251881Speter   Assumptions:
331251881Speter   - PARENT is a mutable directory.
332251881Speter   - ID does not refer to an ancestor of parent
333251881Speter   - NAME is a single path component
334251881Speter*/
335251881Speterstatic svn_error_t *
336251881Speterset_entry(dag_node_t *parent,
337251881Speter          const char *name,
338251881Speter          const svn_fs_id_t *id,
339251881Speter          svn_node_kind_t kind,
340299742Sdim          const svn_fs_fs__id_part_t *txn_id,
341251881Speter          apr_pool_t *pool)
342251881Speter{
343251881Speter  node_revision_t *parent_noderev;
344251881Speter
345251881Speter  /* Get the parent's node-revision. */
346251881Speter  SVN_ERR(get_node_revision(&parent_noderev, parent));
347251881Speter
348251881Speter  /* Set the new entry. */
349251881Speter  return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, id,
350251881Speter                              kind, pool);
351251881Speter}
352251881Speter
353251881Speter
354251881Speter/* Make a new entry named NAME in PARENT.  If IS_DIR is true, then the
355251881Speter   node revision the new entry points to will be a directory, else it
356251881Speter   will be a file.  The new node will be allocated in POOL.  PARENT
357251881Speter   must be mutable, and must not have an entry named NAME.
358251881Speter
359251881Speter   Use POOL for all allocations, except caching the node_revision in PARENT.
360251881Speter */
361251881Speterstatic svn_error_t *
362251881Spetermake_entry(dag_node_t **child_p,
363251881Speter           dag_node_t *parent,
364251881Speter           const char *parent_path,
365251881Speter           const char *name,
366251881Speter           svn_boolean_t is_dir,
367299742Sdim           const svn_fs_fs__id_part_t *txn_id,
368251881Speter           apr_pool_t *pool)
369251881Speter{
370251881Speter  const svn_fs_id_t *new_node_id;
371251881Speter  node_revision_t new_noderev, *parent_noderev;
372251881Speter
373251881Speter  /* Make sure that NAME is a single path component. */
374251881Speter  if (! svn_path_is_single_path_component(name))
375251881Speter    return svn_error_createf
376251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
377251881Speter       _("Attempted to create a node with an illegal name '%s'"), name);
378251881Speter
379251881Speter  /* Make sure that parent is a directory */
380251881Speter  if (parent->kind != svn_node_dir)
381251881Speter    return svn_error_create
382251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
383251881Speter       _("Attempted to create entry in non-directory parent"));
384251881Speter
385251881Speter  /* Check that the parent is mutable. */
386251881Speter  if (! svn_fs_fs__dag_check_mutable(parent))
387251881Speter    return svn_error_createf
388251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
389251881Speter       _("Attempted to clone child of non-mutable node"));
390251881Speter
391251881Speter  /* Create the new node's NODE-REVISION */
392251881Speter  memset(&new_noderev, 0, sizeof(new_noderev));
393251881Speter  new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
394251881Speter  new_noderev.created_path = svn_fspath__join(parent_path, name, pool);
395251881Speter
396251881Speter  SVN_ERR(get_node_revision(&parent_noderev, parent));
397251881Speter  new_noderev.copyroot_path = apr_pstrdup(pool,
398251881Speter                                          parent_noderev->copyroot_path);
399251881Speter  new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
400251881Speter  new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
401251881Speter  new_noderev.copyfrom_path = NULL;
402251881Speter
403251881Speter  SVN_ERR(svn_fs_fs__create_node
404251881Speter          (&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev,
405251881Speter           svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)),
406251881Speter           txn_id, pool));
407251881Speter
408251881Speter  /* Create a new dag_node_t for our new node */
409251881Speter  SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
410251881Speter                                  new_node_id, pool));
411251881Speter
412251881Speter  /* We can safely call set_entry because we already know that
413251881Speter     PARENT is mutable, and we just created CHILD, so we know it has
414251881Speter     no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
415251881Speter  return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p),
416251881Speter                   new_noderev.kind, txn_id, pool);
417251881Speter}
418251881Speter
419251881Speter
420251881Spetersvn_error_t *
421299742Sdimsvn_fs_fs__dag_dir_entries(apr_array_header_t **entries,
422251881Speter                           dag_node_t *node,
423251881Speter                           apr_pool_t *pool)
424251881Speter{
425251881Speter  node_revision_t *noderev;
426251881Speter
427251881Speter  SVN_ERR(get_node_revision(&noderev, node));
428251881Speter
429251881Speter  if (noderev->kind != svn_node_dir)
430251881Speter    return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
431251881Speter                            _("Can't get entries of non-directory"));
432251881Speter
433299742Sdim  return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool);
434251881Speter}
435251881Speter
436251881Spetersvn_error_t *
437251881Spetersvn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent,
438251881Speter                         dag_node_t *node,
439251881Speter                         const char* name,
440299742Sdim                         apr_pool_t *result_pool,
441299742Sdim                         apr_pool_t *scratch_pool)
442251881Speter{
443251881Speter  node_revision_t *noderev;
444251881Speter  SVN_ERR(get_node_revision(&noderev, node));
445251881Speter
446251881Speter  if (noderev->kind != svn_node_dir)
447251881Speter    return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
448251881Speter                            _("Can't get entries of non-directory"));
449251881Speter
450251881Speter  /* Get a dirent hash for this directory. */
451299742Sdim  return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, noderev, name,
452299742Sdim                                           result_pool, scratch_pool);
453251881Speter}
454251881Speter
455251881Speter
456251881Spetersvn_error_t *
457251881Spetersvn_fs_fs__dag_set_entry(dag_node_t *node,
458251881Speter                         const char *entry_name,
459251881Speter                         const svn_fs_id_t *id,
460251881Speter                         svn_node_kind_t kind,
461299742Sdim                         const svn_fs_fs__id_part_t *txn_id,
462251881Speter                         apr_pool_t *pool)
463251881Speter{
464251881Speter  /* Check it's a directory. */
465251881Speter  if (node->kind != svn_node_dir)
466251881Speter    return svn_error_create
467251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
468251881Speter       _("Attempted to set entry in non-directory node"));
469251881Speter
470251881Speter  /* Check it's mutable. */
471251881Speter  if (! svn_fs_fs__dag_check_mutable(node))
472251881Speter    return svn_error_create
473251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
474251881Speter       _("Attempted to set entry in immutable node"));
475251881Speter
476251881Speter  return set_entry(node, entry_name, id, kind, txn_id, pool);
477251881Speter}
478251881Speter
479251881Speter
480251881Speter
481251881Speter/*** Proplists. ***/
482251881Speter
483251881Spetersvn_error_t *
484251881Spetersvn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p,
485251881Speter                            dag_node_t *node,
486251881Speter                            apr_pool_t *pool)
487251881Speter{
488251881Speter  node_revision_t *noderev;
489251881Speter  apr_hash_t *proplist = NULL;
490251881Speter
491251881Speter  SVN_ERR(get_node_revision(&noderev, node));
492251881Speter
493251881Speter  SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
494251881Speter                                  noderev, pool));
495251881Speter
496251881Speter  *proplist_p = proplist;
497251881Speter
498251881Speter  return SVN_NO_ERROR;
499251881Speter}
500251881Speter
501299742Sdimsvn_error_t *
502299742Sdimsvn_fs_fs__dag_has_props(svn_boolean_t *has_props,
503299742Sdim                         dag_node_t *node,
504299742Sdim                         apr_pool_t *scratch_pool)
505299742Sdim{
506299742Sdim  node_revision_t *noderev;
507251881Speter
508299742Sdim  SVN_ERR(get_node_revision(&noderev, node));
509299742Sdim
510299742Sdim  if (! noderev->prop_rep)
511299742Sdim    {
512299742Sdim      *has_props = FALSE; /* Easy out */
513299742Sdim      return SVN_NO_ERROR;
514299742Sdim    }
515299742Sdim
516299742Sdim  if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id))
517299742Sdim    {
518299742Sdim      /* We are in a commit or something. Check actual properties */
519299742Sdim      apr_hash_t *proplist;
520299742Sdim
521299742Sdim      SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
522299742Sdim                                      noderev, scratch_pool));
523299742Sdim
524299742Sdim      *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE;
525299742Sdim    }
526299742Sdim  else
527299742Sdim    {
528299742Sdim      /* Properties are stored as a standard hash stream,
529299742Sdim         always ending with "END\n" (4 bytes) */
530299742Sdim      *has_props = (noderev->prop_rep->expanded_size > 4
531299742Sdim                    || (noderev->prop_rep->expanded_size == 0
532299742Sdim                        && noderev->prop_rep->size > 4));
533299742Sdim    }
534299742Sdim
535299742Sdim  return SVN_NO_ERROR;
536299742Sdim}
537299742Sdim
538251881Spetersvn_error_t *
539251881Spetersvn_fs_fs__dag_set_proplist(dag_node_t *node,
540251881Speter                            apr_hash_t *proplist,
541251881Speter                            apr_pool_t *pool)
542251881Speter{
543251881Speter  node_revision_t *noderev;
544251881Speter
545251881Speter  /* Sanity check: this node better be mutable! */
546251881Speter  if (! svn_fs_fs__dag_check_mutable(node))
547251881Speter    {
548251881Speter      svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
549251881Speter      return svn_error_createf
550251881Speter        (SVN_ERR_FS_NOT_MUTABLE, NULL,
551251881Speter         "Can't set proplist on *immutable* node-revision %s",
552251881Speter         idstr->data);
553251881Speter    }
554251881Speter
555251881Speter  /* Go get a fresh NODE-REVISION for this node. */
556251881Speter  SVN_ERR(get_node_revision(&noderev, node));
557251881Speter
558251881Speter  /* Set the new proplist. */
559251881Speter  return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool);
560251881Speter}
561251881Speter
562251881Speter
563251881Spetersvn_error_t *
564251881Spetersvn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node,
565251881Speter                                         apr_int64_t increment,
566251881Speter                                         apr_pool_t *pool)
567251881Speter{
568251881Speter  node_revision_t *noderev;
569251881Speter
570251881Speter  /* Sanity check: this node better be mutable! */
571251881Speter  if (! svn_fs_fs__dag_check_mutable(node))
572251881Speter    {
573251881Speter      svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
574251881Speter      return svn_error_createf
575251881Speter        (SVN_ERR_FS_NOT_MUTABLE, NULL,
576251881Speter         "Can't increment mergeinfo count on *immutable* node-revision %s",
577251881Speter         idstr->data);
578251881Speter    }
579251881Speter
580251881Speter  if (increment == 0)
581251881Speter    return SVN_NO_ERROR;
582251881Speter
583251881Speter  /* Go get a fresh NODE-REVISION for this node. */
584251881Speter  SVN_ERR(get_node_revision(&noderev, node));
585251881Speter
586251881Speter  noderev->mergeinfo_count += increment;
587251881Speter  if (noderev->mergeinfo_count < 0)
588251881Speter    {
589251881Speter      svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
590251881Speter      return svn_error_createf
591251881Speter        (SVN_ERR_FS_CORRUPT, NULL,
592251881Speter         apr_psprintf(pool,
593251881Speter                      _("Can't increment mergeinfo count on node-revision %%s "
594251881Speter                        "to negative value %%%s"),
595251881Speter                      APR_INT64_T_FMT),
596251881Speter         idstr->data, noderev->mergeinfo_count);
597251881Speter    }
598251881Speter  if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
599251881Speter    {
600251881Speter      svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
601251881Speter      return svn_error_createf
602251881Speter        (SVN_ERR_FS_CORRUPT, NULL,
603251881Speter         apr_psprintf(pool,
604251881Speter                      _("Can't increment mergeinfo count on *file* "
605251881Speter                        "node-revision %%s to %%%s (> 1)"),
606251881Speter                      APR_INT64_T_FMT),
607251881Speter         idstr->data, noderev->mergeinfo_count);
608251881Speter    }
609251881Speter
610251881Speter  /* Flush it out. */
611251881Speter  return svn_fs_fs__put_node_revision(node->fs, noderev->id,
612251881Speter                                      noderev, FALSE, pool);
613251881Speter}
614251881Speter
615251881Spetersvn_error_t *
616251881Spetersvn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node,
617251881Speter                                 svn_boolean_t has_mergeinfo,
618251881Speter                                 apr_pool_t *pool)
619251881Speter{
620251881Speter  node_revision_t *noderev;
621251881Speter
622251881Speter  /* Sanity check: this node better be mutable! */
623251881Speter  if (! svn_fs_fs__dag_check_mutable(node))
624251881Speter    {
625251881Speter      svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
626251881Speter      return svn_error_createf
627251881Speter        (SVN_ERR_FS_NOT_MUTABLE, NULL,
628251881Speter         "Can't set mergeinfo flag on *immutable* node-revision %s",
629251881Speter         idstr->data);
630251881Speter    }
631251881Speter
632251881Speter  /* Go get a fresh NODE-REVISION for this node. */
633251881Speter  SVN_ERR(get_node_revision(&noderev, node));
634251881Speter
635251881Speter  noderev->has_mergeinfo = has_mergeinfo;
636251881Speter
637251881Speter  /* Flush it out. */
638251881Speter  return svn_fs_fs__put_node_revision(node->fs, noderev->id,
639251881Speter                                      noderev, FALSE, pool);
640251881Speter}
641251881Speter
642251881Speter
643251881Speter/*** Roots. ***/
644251881Speter
645251881Spetersvn_error_t *
646251881Spetersvn_fs_fs__dag_revision_root(dag_node_t **node_p,
647251881Speter                             svn_fs_t *fs,
648251881Speter                             svn_revnum_t rev,
649251881Speter                             apr_pool_t *pool)
650251881Speter{
651299742Sdim  dag_node_t *new_node;
652251881Speter
653299742Sdim  /* Construct the node. */
654299742Sdim  new_node = apr_pcalloc(pool, sizeof(*new_node));
655299742Sdim  new_node->fs = fs;
656299742Sdim  SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool));
657299742Sdim
658299742Sdim  /* Grab the contents so we can inspect the node's kind and created path. */
659299742Sdim  new_node->node_pool = pool;
660299742Sdim
661299742Sdim  /* Initialize the KIND and CREATED_PATH attributes */
662299742Sdim  new_node->kind = svn_node_dir;
663299742Sdim  new_node->created_path = "/";
664299742Sdim  new_node->fresh_root_predecessor_id = NULL;
665299742Sdim
666299742Sdim  /* Return a fresh new node */
667299742Sdim  *node_p = new_node;
668299742Sdim  return SVN_NO_ERROR;
669251881Speter}
670251881Speter
671251881Speter
672251881Spetersvn_error_t *
673251881Spetersvn_fs_fs__dag_txn_root(dag_node_t **node_p,
674251881Speter                        svn_fs_t *fs,
675299742Sdim                        const svn_fs_fs__id_part_t *txn_id,
676251881Speter                        apr_pool_t *pool)
677251881Speter{
678251881Speter  const svn_fs_id_t *root_id, *ignored;
679251881Speter
680251881Speter  SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool));
681251881Speter  return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
682251881Speter}
683251881Speter
684251881Speter
685251881Spetersvn_error_t *
686251881Spetersvn_fs_fs__dag_txn_base_root(dag_node_t **node_p,
687251881Speter                             svn_fs_t *fs,
688299742Sdim                             const svn_fs_fs__id_part_t *txn_id,
689251881Speter                             apr_pool_t *pool)
690251881Speter{
691251881Speter  const svn_fs_id_t *base_root_id, *ignored;
692251881Speter
693251881Speter  SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool));
694251881Speter  return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool);
695251881Speter}
696251881Speter
697251881Speter
698251881Spetersvn_error_t *
699251881Spetersvn_fs_fs__dag_clone_child(dag_node_t **child_p,
700251881Speter                           dag_node_t *parent,
701251881Speter                           const char *parent_path,
702251881Speter                           const char *name,
703299742Sdim                           const svn_fs_fs__id_part_t *copy_id,
704299742Sdim                           const svn_fs_fs__id_part_t *txn_id,
705251881Speter                           svn_boolean_t is_parent_copyroot,
706251881Speter                           apr_pool_t *pool)
707251881Speter{
708251881Speter  dag_node_t *cur_entry; /* parent's current entry named NAME */
709251881Speter  const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
710251881Speter  svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent);
711251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
712251881Speter
713251881Speter  /* First check that the parent is mutable. */
714251881Speter  if (! svn_fs_fs__dag_check_mutable(parent))
715251881Speter    return svn_error_createf
716251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
717251881Speter       "Attempted to clone child of non-mutable node");
718251881Speter
719251881Speter  /* Make sure that NAME is a single path component. */
720251881Speter  if (! svn_path_is_single_path_component(name))
721251881Speter    return svn_error_createf
722251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
723251881Speter       "Attempted to make a child clone with an illegal name '%s'", name);
724251881Speter
725251881Speter  /* Find the node named NAME in PARENT's entries list if it exists. */
726251881Speter  SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool));
727299742Sdim  if (! cur_entry)
728299742Sdim    return svn_error_createf
729299742Sdim      (SVN_ERR_FS_NOT_FOUND, NULL,
730299742Sdim       "Attempted to open non-existent child node '%s'", name);
731251881Speter
732251881Speter  /* Check for mutability in the node we found.  If it's mutable, we
733251881Speter     don't need to clone it. */
734251881Speter  if (svn_fs_fs__dag_check_mutable(cur_entry))
735251881Speter    {
736251881Speter      /* This has already been cloned */
737251881Speter      new_node_id = cur_entry->id;
738251881Speter    }
739251881Speter  else
740251881Speter    {
741251881Speter      node_revision_t *noderev, *parent_noderev;
742251881Speter
743251881Speter      /* Go get a fresh NODE-REVISION for current child node. */
744251881Speter      SVN_ERR(get_node_revision(&noderev, cur_entry));
745251881Speter
746251881Speter      if (is_parent_copyroot)
747251881Speter        {
748251881Speter          SVN_ERR(get_node_revision(&parent_noderev, parent));
749251881Speter          noderev->copyroot_rev = parent_noderev->copyroot_rev;
750251881Speter          noderev->copyroot_path = apr_pstrdup(pool,
751251881Speter                                               parent_noderev->copyroot_path);
752251881Speter        }
753251881Speter
754251881Speter      noderev->copyfrom_path = NULL;
755251881Speter      noderev->copyfrom_rev = SVN_INVALID_REVNUM;
756251881Speter
757251881Speter      noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
758251881Speter      if (noderev->predecessor_count != -1)
759251881Speter        noderev->predecessor_count++;
760251881Speter      noderev->created_path = svn_fspath__join(parent_path, name, pool);
761251881Speter
762251881Speter      SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
763251881Speter                                          noderev, copy_id, txn_id, pool));
764251881Speter
765251881Speter      /* Replace the ID in the parent's ENTRY list with the ID which
766251881Speter         refers to the mutable clone of this child. */
767251881Speter      SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
768251881Speter                        pool));
769251881Speter    }
770251881Speter
771251881Speter  /* Initialize the youngster. */
772251881Speter  svn_pool_destroy(subpool);
773251881Speter  return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool);
774251881Speter}
775251881Speter
776251881Speter
777251881Speter
778251881Spetersvn_error_t *
779251881Spetersvn_fs_fs__dag_clone_root(dag_node_t **root_p,
780251881Speter                          svn_fs_t *fs,
781299742Sdim                          const svn_fs_fs__id_part_t *txn_id,
782251881Speter                          apr_pool_t *pool)
783251881Speter{
784251881Speter  const svn_fs_id_t *base_root_id, *root_id;
785251881Speter
786251881Speter  /* Get the node ID's of the root directories of the transaction and
787251881Speter     its base revision.  */
788251881Speter  SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool));
789251881Speter
790251881Speter  /* Oh, give me a clone...
791251881Speter     (If they're the same, we haven't cloned the transaction's root
792251881Speter     directory yet.)  */
793251881Speter  SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id));
794251881Speter
795251881Speter  /*
796251881Speter   * (Sung to the tune of "Home, Home on the Range", with thanks to
797251881Speter   * Randall Garrett and Isaac Asimov.)
798251881Speter   */
799251881Speter
800251881Speter  /* One way or another, root_id now identifies a cloned root node. */
801251881Speter  return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool);
802251881Speter}
803251881Speter
804251881Speter
805251881Spetersvn_error_t *
806251881Spetersvn_fs_fs__dag_delete(dag_node_t *parent,
807251881Speter                      const char *name,
808299742Sdim                      const svn_fs_fs__id_part_t *txn_id,
809251881Speter                      apr_pool_t *pool)
810251881Speter{
811251881Speter  node_revision_t *parent_noderev;
812251881Speter  svn_fs_t *fs = parent->fs;
813251881Speter  svn_fs_dirent_t *dirent;
814251881Speter  svn_fs_id_t *id;
815251881Speter  apr_pool_t *subpool;
816251881Speter
817251881Speter  /* Make sure parent is a directory. */
818251881Speter  if (parent->kind != svn_node_dir)
819251881Speter    return svn_error_createf
820251881Speter      (SVN_ERR_FS_NOT_DIRECTORY, NULL,
821251881Speter       "Attempted to delete entry '%s' from *non*-directory node", name);
822251881Speter
823251881Speter  /* Make sure parent is mutable. */
824251881Speter  if (! svn_fs_fs__dag_check_mutable(parent))
825251881Speter    return svn_error_createf
826251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
827251881Speter       "Attempted to delete entry '%s' from immutable directory node", name);
828251881Speter
829251881Speter  /* Make sure that NAME is a single path component. */
830251881Speter  if (! svn_path_is_single_path_component(name))
831251881Speter    return svn_error_createf
832251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
833251881Speter       "Attempted to delete a node with an illegal name '%s'", name);
834251881Speter
835251881Speter  /* Get a fresh NODE-REVISION for the parent node. */
836251881Speter  SVN_ERR(get_node_revision(&parent_noderev, parent));
837251881Speter
838251881Speter  subpool = svn_pool_create(pool);
839251881Speter
840251881Speter  /* Search this directory for a dirent with that NAME. */
841251881Speter  SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev,
842251881Speter                                            name, subpool, subpool));
843251881Speter
844251881Speter  /* If we never found ID in ENTRIES (perhaps because there are no
845251881Speter     ENTRIES, perhaps because ID just isn't in the existing ENTRIES
846251881Speter     ... it doesn't matter), return an error.  */
847251881Speter  if (! dirent)
848251881Speter    return svn_error_createf
849251881Speter      (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
850251881Speter       "Delete failed--directory has no entry '%s'", name);
851251881Speter
852251881Speter  /* Copy the ID out of the subpool and release the rest of the
853251881Speter     directory listing. */
854251881Speter  id = svn_fs_fs__id_copy(dirent->id, pool);
855251881Speter  svn_pool_destroy(subpool);
856251881Speter
857251881Speter  /* If mutable, remove it and any mutable children from db. */
858251881Speter  SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool));
859251881Speter
860251881Speter  /* Remove this entry from its parent's entries list. */
861251881Speter  return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name,
862251881Speter                              NULL, svn_node_unknown, pool);
863251881Speter}
864251881Speter
865251881Speter
866251881Spetersvn_error_t *
867251881Spetersvn_fs_fs__dag_remove_node(svn_fs_t *fs,
868251881Speter                           const svn_fs_id_t *id,
869251881Speter                           apr_pool_t *pool)
870251881Speter{
871251881Speter  dag_node_t *node;
872251881Speter
873251881Speter  /* Fetch the node. */
874251881Speter  SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
875251881Speter
876251881Speter  /* If immutable, do nothing and return immediately. */
877251881Speter  if (! svn_fs_fs__dag_check_mutable(node))
878251881Speter    return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
879251881Speter                             "Attempted removal of immutable node");
880251881Speter
881251881Speter  /* Delete the node revision. */
882251881Speter  return svn_fs_fs__delete_node_revision(fs, id, pool);
883251881Speter}
884251881Speter
885251881Speter
886251881Spetersvn_error_t *
887251881Spetersvn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs,
888251881Speter                                 const svn_fs_id_t *id,
889251881Speter                                 apr_pool_t *pool)
890251881Speter{
891251881Speter  dag_node_t *node;
892251881Speter
893251881Speter  /* Get the node. */
894251881Speter  SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
895251881Speter
896251881Speter  /* If immutable, do nothing and return immediately. */
897251881Speter  if (! svn_fs_fs__dag_check_mutable(node))
898251881Speter    return SVN_NO_ERROR;
899251881Speter
900251881Speter  /* Else it's mutable.  Recurse on directories... */
901251881Speter  if (node->kind == svn_node_dir)
902251881Speter    {
903299742Sdim      apr_array_header_t *entries;
904299742Sdim      int i;
905299742Sdim      apr_pool_t *iterpool = svn_pool_create(pool);
906251881Speter
907299742Sdim      /* Loop over directory entries */
908251881Speter      SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
909251881Speter      if (entries)
910299742Sdim        for (i = 0; i < entries->nelts; ++i)
911299742Sdim          {
912299742Sdim            svn_pool_clear(iterpool);
913299742Sdim            SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs,
914299742Sdim                          APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id,
915299742Sdim                          iterpool));
916299742Sdim          }
917251881Speter
918299742Sdim      svn_pool_destroy(iterpool);
919251881Speter    }
920251881Speter
921251881Speter  /* ... then delete the node itself, after deleting any mutable
922251881Speter     representations and strings it points to. */
923251881Speter  return svn_fs_fs__dag_remove_node(fs, id, pool);
924251881Speter}
925251881Speter
926251881Spetersvn_error_t *
927251881Spetersvn_fs_fs__dag_make_file(dag_node_t **child_p,
928251881Speter                         dag_node_t *parent,
929251881Speter                         const char *parent_path,
930251881Speter                         const char *name,
931299742Sdim                         const svn_fs_fs__id_part_t *txn_id,
932251881Speter                         apr_pool_t *pool)
933251881Speter{
934251881Speter  /* Call our little helper function */
935251881Speter  return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool);
936251881Speter}
937251881Speter
938251881Speter
939251881Spetersvn_error_t *
940251881Spetersvn_fs_fs__dag_make_dir(dag_node_t **child_p,
941251881Speter                        dag_node_t *parent,
942251881Speter                        const char *parent_path,
943251881Speter                        const char *name,
944299742Sdim                        const svn_fs_fs__id_part_t *txn_id,
945251881Speter                        apr_pool_t *pool)
946251881Speter{
947251881Speter  /* Call our little helper function */
948251881Speter  return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool);
949251881Speter}
950251881Speter
951251881Speter
952251881Spetersvn_error_t *
953251881Spetersvn_fs_fs__dag_get_contents(svn_stream_t **contents_p,
954251881Speter                            dag_node_t *file,
955251881Speter                            apr_pool_t *pool)
956251881Speter{
957251881Speter  node_revision_t *noderev;
958251881Speter  svn_stream_t *contents;
959251881Speter
960251881Speter  /* Make sure our node is a file. */
961251881Speter  if (file->kind != svn_node_file)
962251881Speter    return svn_error_createf
963251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
964251881Speter       "Attempted to get textual contents of a *non*-file node");
965251881Speter
966251881Speter  /* Go get a fresh node-revision for FILE. */
967251881Speter  SVN_ERR(get_node_revision(&noderev, file));
968251881Speter
969251881Speter  /* Get a stream to the contents. */
970251881Speter  SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs,
971299742Sdim                                  noderev->data_rep, TRUE, pool));
972251881Speter
973251881Speter  *contents_p = contents;
974251881Speter
975251881Speter  return SVN_NO_ERROR;
976251881Speter}
977251881Speter
978251881Speter
979251881Spetersvn_error_t *
980251881Spetersvn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
981251881Speter                                     dag_node_t *source,
982251881Speter                                     dag_node_t *target,
983251881Speter                                     apr_pool_t *pool)
984251881Speter{
985251881Speter  node_revision_t *src_noderev;
986251881Speter  node_revision_t *tgt_noderev;
987251881Speter
988251881Speter  /* Make sure our nodes are files. */
989251881Speter  if ((source && source->kind != svn_node_file)
990251881Speter      || target->kind != svn_node_file)
991251881Speter    return svn_error_createf
992251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
993251881Speter       "Attempted to get textual contents of a *non*-file node");
994251881Speter
995251881Speter  /* Go get fresh node-revisions for the nodes. */
996251881Speter  if (source)
997251881Speter    SVN_ERR(get_node_revision(&src_noderev, source));
998251881Speter  else
999251881Speter    src_noderev = NULL;
1000251881Speter  SVN_ERR(get_node_revision(&tgt_noderev, target));
1001251881Speter
1002251881Speter  /* Get the delta stream. */
1003251881Speter  return svn_fs_fs__get_file_delta_stream(stream_p, target->fs,
1004251881Speter                                          src_noderev, tgt_noderev, pool);
1005251881Speter}
1006251881Speter
1007251881Speter
1008251881Spetersvn_error_t *
1009251881Spetersvn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success,
1010251881Speter                                         dag_node_t *node,
1011251881Speter                                         svn_fs_process_contents_func_t processor,
1012251881Speter                                         void* baton,
1013251881Speter                                         apr_pool_t *pool)
1014251881Speter{
1015251881Speter  node_revision_t *noderev;
1016251881Speter
1017251881Speter  /* Go get fresh node-revisions for the nodes. */
1018251881Speter  SVN_ERR(get_node_revision(&noderev, node));
1019251881Speter
1020251881Speter  return svn_fs_fs__try_process_file_contents(success, node->fs,
1021251881Speter                                              noderev,
1022251881Speter                                              processor, baton, pool);
1023251881Speter}
1024251881Speter
1025251881Speter
1026251881Spetersvn_error_t *
1027251881Spetersvn_fs_fs__dag_file_length(svn_filesize_t *length,
1028251881Speter                           dag_node_t *file,
1029251881Speter                           apr_pool_t *pool)
1030251881Speter{
1031251881Speter  node_revision_t *noderev;
1032251881Speter
1033251881Speter  /* Make sure our node is a file. */
1034251881Speter  if (file->kind != svn_node_file)
1035251881Speter    return svn_error_createf
1036251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1037251881Speter       "Attempted to get length of a *non*-file node");
1038251881Speter
1039251881Speter  /* Go get a fresh node-revision for FILE, and . */
1040251881Speter  SVN_ERR(get_node_revision(&noderev, file));
1041251881Speter
1042251881Speter  return svn_fs_fs__file_length(length, noderev, pool);
1043251881Speter}
1044251881Speter
1045251881Speter
1046251881Spetersvn_error_t *
1047251881Spetersvn_fs_fs__dag_file_checksum(svn_checksum_t **checksum,
1048251881Speter                             dag_node_t *file,
1049251881Speter                             svn_checksum_kind_t kind,
1050251881Speter                             apr_pool_t *pool)
1051251881Speter{
1052251881Speter  node_revision_t *noderev;
1053251881Speter
1054251881Speter  if (file->kind != svn_node_file)
1055251881Speter    return svn_error_createf
1056251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1057251881Speter       "Attempted to get checksum of a *non*-file node");
1058251881Speter
1059251881Speter  SVN_ERR(get_node_revision(&noderev, file));
1060251881Speter
1061251881Speter  return svn_fs_fs__file_checksum(checksum, noderev, kind, pool);
1062251881Speter}
1063251881Speter
1064251881Speter
1065251881Spetersvn_error_t *
1066251881Spetersvn_fs_fs__dag_get_edit_stream(svn_stream_t **contents,
1067251881Speter                               dag_node_t *file,
1068251881Speter                               apr_pool_t *pool)
1069251881Speter{
1070251881Speter  node_revision_t *noderev;
1071251881Speter  svn_stream_t *ws;
1072251881Speter
1073251881Speter  /* Make sure our node is a file. */
1074251881Speter  if (file->kind != svn_node_file)
1075251881Speter    return svn_error_createf
1076251881Speter      (SVN_ERR_FS_NOT_FILE, NULL,
1077251881Speter       "Attempted to set textual contents of a *non*-file node");
1078251881Speter
1079251881Speter  /* Make sure our node is mutable. */
1080251881Speter  if (! svn_fs_fs__dag_check_mutable(file))
1081251881Speter    return svn_error_createf
1082251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
1083251881Speter       "Attempted to set textual contents of an immutable node");
1084251881Speter
1085251881Speter  /* Get the node revision. */
1086251881Speter  SVN_ERR(get_node_revision(&noderev, file));
1087251881Speter
1088251881Speter  SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool));
1089251881Speter
1090251881Speter  *contents = ws;
1091251881Speter
1092251881Speter  return SVN_NO_ERROR;
1093251881Speter}
1094251881Speter
1095251881Speter
1096251881Speter
1097251881Spetersvn_error_t *
1098251881Spetersvn_fs_fs__dag_finalize_edits(dag_node_t *file,
1099251881Speter                              const svn_checksum_t *checksum,
1100251881Speter                              apr_pool_t *pool)
1101251881Speter{
1102251881Speter  if (checksum)
1103251881Speter    {
1104251881Speter      svn_checksum_t *file_checksum;
1105251881Speter
1106251881Speter      SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
1107251881Speter                                           checksum->kind, pool));
1108251881Speter      if (!svn_checksum_match(checksum, file_checksum))
1109251881Speter        return svn_checksum_mismatch_err(checksum, file_checksum, pool,
1110251881Speter                                         _("Checksum mismatch for '%s'"),
1111251881Speter                                         file->created_path);
1112251881Speter    }
1113251881Speter
1114251881Speter  return SVN_NO_ERROR;
1115251881Speter}
1116251881Speter
1117251881Speter
1118251881Speterdag_node_t *
1119251881Spetersvn_fs_fs__dag_dup(const dag_node_t *node,
1120251881Speter                   apr_pool_t *pool)
1121251881Speter{
1122251881Speter  /* Allocate our new node. */
1123251881Speter  dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
1124251881Speter
1125251881Speter  new_node->fs = node->fs;
1126251881Speter  new_node->id = svn_fs_fs__id_copy(node->id, pool);
1127251881Speter  new_node->kind = node->kind;
1128251881Speter  new_node->created_path = apr_pstrdup(pool, node->created_path);
1129251881Speter
1130251881Speter  /* Only copy cached node_revision_t for immutable nodes. */
1131251881Speter  if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1132251881Speter    {
1133251881Speter      new_node->node_revision = copy_node_revision(node->node_revision, pool);
1134251881Speter      new_node->node_revision->id =
1135251881Speter          svn_fs_fs__id_copy(node->node_revision->id, pool);
1136251881Speter      new_node->node_revision->is_fresh_txn_root =
1137251881Speter          node->node_revision->is_fresh_txn_root;
1138251881Speter    }
1139251881Speter  new_node->node_pool = pool;
1140251881Speter
1141251881Speter  return new_node;
1142251881Speter}
1143251881Speter
1144251881Spetersvn_error_t *
1145251881Spetersvn_fs_fs__dag_serialize(void **data,
1146251881Speter                         apr_size_t *data_len,
1147251881Speter                         void *in,
1148251881Speter                         apr_pool_t *pool)
1149251881Speter{
1150251881Speter  dag_node_t *node = in;
1151251881Speter  svn_stringbuf_t *serialized;
1152251881Speter
1153251881Speter  /* create an serialization context and serialize the dag node as root */
1154251881Speter  svn_temp_serializer__context_t *context =
1155251881Speter      svn_temp_serializer__init(node,
1156251881Speter                                sizeof(*node),
1157251881Speter                                1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1158251881Speter                                pool);
1159251881Speter
1160251881Speter  /* for mutable nodes, we will _never_ cache the noderev */
1161251881Speter  if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1162251881Speter    svn_fs_fs__noderev_serialize(context, &node->node_revision);
1163251881Speter  else
1164251881Speter    svn_temp_serializer__set_null(context,
1165251881Speter                                  (const void * const *)&node->node_revision);
1166251881Speter
1167251881Speter  /* The deserializer will use its own pool. */
1168251881Speter  svn_temp_serializer__set_null(context,
1169299742Sdim                                (const void * const *)&node->node_pool);
1170251881Speter
1171251881Speter  /* serialize other sub-structures */
1172251881Speter  svn_fs_fs__id_serialize(context, (const svn_fs_id_t **)&node->id);
1173251881Speter  svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id);
1174251881Speter  svn_temp_serializer__add_string(context, &node->created_path);
1175251881Speter
1176251881Speter  /* return serialized data */
1177251881Speter  serialized = svn_temp_serializer__get(context);
1178251881Speter  *data = serialized->data;
1179251881Speter  *data_len = serialized->len;
1180251881Speter
1181251881Speter  return SVN_NO_ERROR;
1182251881Speter}
1183251881Speter
1184251881Spetersvn_error_t *
1185251881Spetersvn_fs_fs__dag_deserialize(void **out,
1186251881Speter                           void *data,
1187251881Speter                           apr_size_t data_len,
1188251881Speter                           apr_pool_t *pool)
1189251881Speter{
1190251881Speter  dag_node_t *node = (dag_node_t *)data;
1191251881Speter  if (data_len == 0)
1192251881Speter    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1193251881Speter                            _("Empty noderev in cache"));
1194251881Speter
1195251881Speter  /* Copy the _full_ buffer as it also contains the sub-structures. */
1196251881Speter  node->fs = NULL;
1197251881Speter
1198251881Speter  /* fixup all references to sub-structures */
1199251881Speter  svn_fs_fs__id_deserialize(node, &node->id);
1200251881Speter  svn_fs_fs__id_deserialize(node,
1201251881Speter                            (svn_fs_id_t **)&node->fresh_root_predecessor_id);
1202251881Speter  svn_fs_fs__noderev_deserialize(node, &node->node_revision);
1203251881Speter  node->node_pool = pool;
1204251881Speter
1205251881Speter  svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1206251881Speter
1207251881Speter  /* return result */
1208251881Speter  *out = node;
1209251881Speter
1210251881Speter  return SVN_NO_ERROR;
1211251881Speter}
1212251881Speter
1213251881Spetersvn_error_t *
1214251881Spetersvn_fs_fs__dag_open(dag_node_t **child_p,
1215251881Speter                    dag_node_t *parent,
1216251881Speter                    const char *name,
1217251881Speter                    apr_pool_t *result_pool,
1218251881Speter                    apr_pool_t *scratch_pool)
1219251881Speter{
1220251881Speter  const svn_fs_id_t *node_id;
1221251881Speter
1222251881Speter  /* Ensure that NAME exists in PARENT's entry list. */
1223251881Speter  SVN_ERR(dir_entry_id_from_node(&node_id, parent, name,
1224251881Speter                                 scratch_pool, scratch_pool));
1225251881Speter  if (! node_id)
1226299742Sdim    {
1227299742Sdim      *child_p = NULL;
1228299742Sdim      return SVN_NO_ERROR;
1229299742Sdim    }
1230251881Speter
1231251881Speter  /* Make sure that NAME is a single path component. */
1232251881Speter  if (! svn_path_is_single_path_component(name))
1233251881Speter    return svn_error_createf
1234251881Speter      (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
1235251881Speter       "Attempted to open node with an illegal name '%s'", name);
1236251881Speter
1237251881Speter  /* Now get the node that was requested. */
1238251881Speter  return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
1239251881Speter                                 node_id, result_pool);
1240251881Speter}
1241251881Speter
1242251881Speter
1243251881Spetersvn_error_t *
1244251881Spetersvn_fs_fs__dag_copy(dag_node_t *to_node,
1245251881Speter                    const char *entry,
1246251881Speter                    dag_node_t *from_node,
1247251881Speter                    svn_boolean_t preserve_history,
1248251881Speter                    svn_revnum_t from_rev,
1249251881Speter                    const char *from_path,
1250299742Sdim                    const svn_fs_fs__id_part_t *txn_id,
1251251881Speter                    apr_pool_t *pool)
1252251881Speter{
1253251881Speter  const svn_fs_id_t *id;
1254251881Speter
1255251881Speter  if (preserve_history)
1256251881Speter    {
1257251881Speter      node_revision_t *from_noderev, *to_noderev;
1258299742Sdim      svn_fs_fs__id_part_t copy_id;
1259251881Speter      const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node);
1260251881Speter      svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node);
1261251881Speter
1262251881Speter      /* Make a copy of the original node revision. */
1263251881Speter      SVN_ERR(get_node_revision(&from_noderev, from_node));
1264251881Speter      to_noderev = copy_node_revision(from_noderev, pool);
1265251881Speter
1266251881Speter      /* Reserve a copy ID for this new copy. */
1267251881Speter      SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, fs, txn_id, pool));
1268251881Speter
1269251881Speter      /* Create a successor with its predecessor pointing at the copy
1270251881Speter         source. */
1271251881Speter      to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
1272251881Speter      if (to_noderev->predecessor_count != -1)
1273251881Speter        to_noderev->predecessor_count++;
1274251881Speter      to_noderev->created_path =
1275251881Speter        svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry,
1276251881Speter                     pool);
1277251881Speter      to_noderev->copyfrom_path = apr_pstrdup(pool, from_path);
1278251881Speter      to_noderev->copyfrom_rev = from_rev;
1279251881Speter
1280251881Speter      /* Set the copyroot equal to our own id. */
1281251881Speter      to_noderev->copyroot_path = NULL;
1282251881Speter
1283251881Speter      SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev,
1284299742Sdim                                          &copy_id, txn_id, pool));
1285251881Speter
1286251881Speter    }
1287251881Speter  else  /* don't preserve history */
1288251881Speter    {
1289251881Speter      id = svn_fs_fs__dag_get_id(from_node);
1290251881Speter    }
1291251881Speter
1292251881Speter  /* Set the entry in to_node to the new id. */
1293251881Speter  return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind,
1294251881Speter                                  txn_id, pool);
1295251881Speter}
1296251881Speter
1297251881Speter
1298251881Speter
1299251881Speter/*** Comparison. ***/
1300251881Speter
1301251881Spetersvn_error_t *
1302251881Spetersvn_fs_fs__dag_things_different(svn_boolean_t *props_changed,
1303251881Speter                                svn_boolean_t *contents_changed,
1304251881Speter                                dag_node_t *node1,
1305299742Sdim                                dag_node_t *node2,
1306299742Sdim                                svn_boolean_t strict,
1307299742Sdim                                apr_pool_t *pool)
1308251881Speter{
1309251881Speter  node_revision_t *noderev1, *noderev2;
1310251881Speter
1311251881Speter  /* If we have no place to store our results, don't bother doing
1312251881Speter     anything. */
1313251881Speter  if (! props_changed && ! contents_changed)
1314251881Speter    return SVN_NO_ERROR;
1315251881Speter
1316251881Speter  /* The node revision skels for these two nodes. */
1317251881Speter  SVN_ERR(get_node_revision(&noderev1, node1));
1318251881Speter  SVN_ERR(get_node_revision(&noderev2, node2));
1319251881Speter
1320299742Sdim  if (strict)
1321299742Sdim    {
1322299742Sdim      /* In strict mode, compare text and property representations in the
1323299742Sdim         svn_fs_contents_different() / svn_fs_props_different() manner.
1324251881Speter
1325299742Sdim         See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9"
1326299742Sdim         discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and
1327299742Sdim         issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */
1328299742Sdim      svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1);
1329299742Sdim      svn_boolean_t same;
1330251881Speter
1331299742Sdim      /* Compare property keys. */
1332299742Sdim      if (props_changed != NULL)
1333299742Sdim        {
1334299742Sdim          SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1,
1335299742Sdim                                            noderev2, pool));
1336299742Sdim          *props_changed = !same;
1337299742Sdim        }
1338299742Sdim
1339299742Sdim      /* Compare contents keys. */
1340299742Sdim      if (contents_changed != NULL)
1341299742Sdim        {
1342299742Sdim          SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1,
1343299742Sdim                                                 noderev2, pool));
1344299742Sdim          *contents_changed = !same;
1345299742Sdim        }
1346299742Sdim    }
1347299742Sdim  else
1348299742Sdim    {
1349299742Sdim      /* Otherwise, compare representation keys -- as in Subversion 1.8. */
1350299742Sdim
1351299742Sdim      /* Compare property keys. */
1352299742Sdim      if (props_changed != NULL)
1353299742Sdim        *props_changed =
1354299742Sdim          !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep,
1355299742Sdim                                           noderev2->prop_rep);
1356299742Sdim
1357299742Sdim      /* Compare contents keys. */
1358299742Sdim      if (contents_changed != NULL)
1359299742Sdim        *contents_changed =
1360299742Sdim          !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep,
1361299742Sdim                                           noderev2->data_rep);
1362299742Sdim    }
1363299742Sdim
1364251881Speter  return SVN_NO_ERROR;
1365251881Speter}
1366251881Speter
1367251881Spetersvn_error_t *
1368251881Spetersvn_fs_fs__dag_get_copyroot(svn_revnum_t *rev,
1369251881Speter                            const char **path,
1370251881Speter                            dag_node_t *node)
1371251881Speter{
1372251881Speter  node_revision_t *noderev;
1373251881Speter
1374251881Speter  /* Go get a fresh node-revision for NODE. */
1375251881Speter  SVN_ERR(get_node_revision(&noderev, node));
1376251881Speter
1377251881Speter  *rev = noderev->copyroot_rev;
1378251881Speter  *path = noderev->copyroot_path;
1379251881Speter
1380251881Speter  return SVN_NO_ERROR;
1381251881Speter}
1382251881Speter
1383251881Spetersvn_error_t *
1384251881Spetersvn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev,
1385251881Speter                                dag_node_t *node)
1386251881Speter{
1387251881Speter  node_revision_t *noderev;
1388251881Speter
1389251881Speter  /* Go get a fresh node-revision for NODE. */
1390251881Speter  SVN_ERR(get_node_revision(&noderev, node));
1391251881Speter
1392251881Speter  *rev = noderev->copyfrom_rev;
1393251881Speter
1394251881Speter  return SVN_NO_ERROR;
1395251881Speter}
1396251881Speter
1397251881Spetersvn_error_t *
1398251881Spetersvn_fs_fs__dag_get_copyfrom_path(const char **path,
1399251881Speter                                 dag_node_t *node)
1400251881Speter{
1401251881Speter  node_revision_t *noderev;
1402251881Speter
1403251881Speter  /* Go get a fresh node-revision for NODE. */
1404251881Speter  SVN_ERR(get_node_revision(&noderev, node));
1405251881Speter
1406251881Speter  *path = noderev->copyfrom_path;
1407251881Speter
1408251881Speter  return SVN_NO_ERROR;
1409251881Speter}
1410251881Speter
1411251881Spetersvn_error_t *
1412251881Spetersvn_fs_fs__dag_update_ancestry(dag_node_t *target,
1413251881Speter                               dag_node_t *source,
1414251881Speter                               apr_pool_t *pool)
1415251881Speter{
1416251881Speter  node_revision_t *source_noderev, *target_noderev;
1417251881Speter
1418251881Speter  if (! svn_fs_fs__dag_check_mutable(target))
1419251881Speter    return svn_error_createf
1420251881Speter      (SVN_ERR_FS_NOT_MUTABLE, NULL,
1421251881Speter       _("Attempted to update ancestry of non-mutable node"));
1422251881Speter
1423251881Speter  SVN_ERR(get_node_revision(&source_noderev, source));
1424251881Speter  SVN_ERR(get_node_revision(&target_noderev, target));
1425251881Speter
1426251881Speter  target_noderev->predecessor_id = source->id;
1427251881Speter  target_noderev->predecessor_count = source_noderev->predecessor_count;
1428251881Speter  if (target_noderev->predecessor_count != -1)
1429251881Speter    target_noderev->predecessor_count++;
1430251881Speter
1431251881Speter  return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev,
1432251881Speter                                      FALSE, pool);
1433251881Speter}
1434