1251881Speter/*
2251881Speter * editor.c:  Editor for modifying FS transactions
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <apr_pools.h>
25251881Speter
26251881Speter#include "svn_types.h"
27251881Speter#include "svn_error.h"
28251881Speter#include "svn_pools.h"
29251881Speter#include "svn_fs.h"
30251881Speter#include "svn_props.h"
31251881Speter#include "svn_path.h"
32251881Speter
33251881Speter#include "svn_private_config.h"
34251881Speter
35251881Speter#include "fs-loader.h"
36251881Speter
37251881Speter#include "private/svn_fspath.h"
38251881Speter#include "private/svn_fs_private.h"
39251881Speter#include "private/svn_editor.h"
40251881Speter
41251881Speter
42251881Speterstruct edit_baton {
43251881Speter  /* The transaction associated with this editor.  */
44251881Speter  svn_fs_txn_t *txn;
45251881Speter
46251881Speter  /* Has this editor been completed?  */
47251881Speter  svn_boolean_t completed;
48251881Speter
49251881Speter  /* We sometimes need the cancellation beyond what svn_editor_t provides  */
50251881Speter  svn_cancel_func_t cancel_func;
51251881Speter  void *cancel_baton;
52251881Speter
53251881Speter  /* The pool that the txn lives within. When we create a ROOT, it will
54251881Speter     be allocated within a subpool of this. The root will be closed in
55251881Speter     complete/abort and that subpool will be destroyed.
56251881Speter
57251881Speter     This pool SHOULD NOT be used for any allocations.  */
58251881Speter  apr_pool_t *txn_pool;
59251881Speter
60251881Speter  /* This is the root from the txn. Use get_root() to fetch/create this
61251881Speter     member as appropriate.  */
62251881Speter  svn_fs_root_t *root;
63251881Speter};
64251881Speter
65251881Speter#define FSPATH(relpath, pool) apr_pstrcat(pool, "/", relpath, NULL)
66251881Speter#define UNUSED(x) ((void)(x))
67251881Speter
68251881Speter
69251881Speterstatic svn_error_t *
70251881Speterget_root(svn_fs_root_t **root,
71251881Speter         struct edit_baton *eb)
72251881Speter{
73251881Speter  if (eb->root == NULL)
74251881Speter    SVN_ERR(svn_fs_txn_root(&eb->root, eb->txn, eb->txn_pool));
75251881Speter  *root = eb->root;
76251881Speter  return SVN_NO_ERROR;
77251881Speter}
78251881Speter
79251881Speter
80251881Speter/* Apply each property in PROPS to the node at FSPATH in ROOT.  */
81251881Speterstatic svn_error_t *
82251881Speteradd_new_props(svn_fs_root_t *root,
83251881Speter              const char *fspath,
84251881Speter              apr_hash_t *props,
85251881Speter              apr_pool_t *scratch_pool)
86251881Speter{
87251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
88251881Speter  apr_hash_index_t *hi;
89251881Speter
90251881Speter  /* ### it would be nice to have svn_fs_set_node_props(). but since we
91251881Speter     ### don't... add each property to the node. this is a new node, so
92251881Speter     ### we don't need to worry about deleting props. just adding.  */
93251881Speter
94251881Speter  for (hi = apr_hash_first(scratch_pool, props); hi;
95251881Speter       hi = apr_hash_next(hi))
96251881Speter    {
97251881Speter      const char *name = svn__apr_hash_index_key(hi);
98251881Speter      const svn_string_t *value = svn__apr_hash_index_val(hi);
99251881Speter
100251881Speter      svn_pool_clear(iterpool);
101251881Speter
102251881Speter      SVN_ERR(svn_fs_change_node_prop(root, fspath, name, value, iterpool));
103251881Speter    }
104251881Speter
105251881Speter  svn_pool_destroy(iterpool);
106251881Speter  return SVN_NO_ERROR;
107251881Speter}
108251881Speter
109251881Speter
110251881Speterstatic svn_error_t *
111251881Speteralter_props(svn_fs_root_t *root,
112251881Speter            const char *fspath,
113251881Speter            apr_hash_t *props,
114251881Speter            apr_pool_t *scratch_pool)
115251881Speter{
116251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
117251881Speter  apr_hash_t *old_props;
118251881Speter  apr_array_header_t *propdiffs;
119251881Speter  int i;
120251881Speter
121251881Speter  SVN_ERR(svn_fs_node_proplist(&old_props, root, fspath, scratch_pool));
122251881Speter
123251881Speter  SVN_ERR(svn_prop_diffs(&propdiffs, props, old_props, scratch_pool));
124251881Speter
125251881Speter  for (i = 0; i < propdiffs->nelts; ++i)
126251881Speter    {
127251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
128251881Speter
129251881Speter      svn_pool_clear(iterpool);
130251881Speter
131251881Speter      /* Add, change, or delete properties.  */
132251881Speter      SVN_ERR(svn_fs_change_node_prop(root, fspath, prop->name, prop->value,
133251881Speter                                      iterpool));
134251881Speter    }
135251881Speter
136251881Speter  svn_pool_destroy(iterpool);
137251881Speter  return SVN_NO_ERROR;
138251881Speter}
139251881Speter
140251881Speter
141251881Speterstatic svn_error_t *
142251881Speterset_text(svn_fs_root_t *root,
143251881Speter         const char *fspath,
144251881Speter         const svn_checksum_t *checksum,
145251881Speter         svn_stream_t *contents,
146251881Speter         svn_cancel_func_t cancel_func,
147251881Speter         void *cancel_baton,
148251881Speter         apr_pool_t *scratch_pool)
149251881Speter{
150251881Speter  svn_stream_t *fs_contents;
151251881Speter
152251881Speter  /* ### We probably don't have an MD5 checksum, so no digest is available
153251881Speter     ### for svn_fs_apply_text() to validate. It would be nice to have an
154251881Speter     ### FS API that takes our CHECKSUM/CONTENTS pair (and PROPS!).  */
155251881Speter  SVN_ERR(svn_fs_apply_text(&fs_contents, root, fspath,
156251881Speter                            NULL /* result_checksum */,
157251881Speter                            scratch_pool));
158251881Speter  SVN_ERR(svn_stream_copy3(contents, fs_contents,
159251881Speter                           cancel_func, cancel_baton,
160251881Speter                           scratch_pool));
161251881Speter
162251881Speter  return SVN_NO_ERROR;
163251881Speter}
164251881Speter
165251881Speter
166251881Speter/* The caller wants to modify REVISION of FSPATH. Is that allowed?  */
167251881Speterstatic svn_error_t *
168251881Spetercan_modify(svn_fs_root_t *txn_root,
169251881Speter           const char *fspath,
170251881Speter           svn_revnum_t revision,
171251881Speter           apr_pool_t *scratch_pool)
172251881Speter{
173251881Speter  svn_revnum_t created_rev;
174251881Speter
175251881Speter  /* Out-of-dateness check:  compare the created-rev of the node
176251881Speter     in the txn against the created-rev of FSPATH.  */
177251881Speter  SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath,
178251881Speter                                  scratch_pool));
179251881Speter
180251881Speter  /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination)
181251881Speter     have no (committed) revision number. Let the caller go ahead and
182251881Speter     modify these nodes.
183251881Speter
184251881Speter     Note: strictly speaking, they might be performing an "illegal" edit
185251881Speter     in certain cases, but let's just assume they're Good Little Boys.
186251881Speter
187251881Speter     If CREATED_REV is invalid, that means it's already mutable in the
188251881Speter     txn, which means it has already passed this out-of-dateness check.
189251881Speter     (Usually, this happens when looking at a parent directory of an
190251881Speter     already-modified node)  */
191251881Speter  if (!SVN_IS_VALID_REVNUM(created_rev))
192251881Speter    return SVN_NO_ERROR;
193251881Speter
194251881Speter  /* If the node is immutable (has a revision), then the caller should
195251881Speter     have supplied a valid revision number [that they expect to change].
196251881Speter     The checks further below will determine the out-of-dateness of the
197251881Speter     specified revision.  */
198251881Speter  /* ### ugh. descendents of copy/move/rotate destinations carry along
199251881Speter     ### their original immutable state and (thus) a valid CREATED_REV.
200251881Speter     ### but they are logically uncommitted, so the caller will pass
201251881Speter     ### SVN_INVALID_REVNUM. (technically, the caller could provide
202251881Speter     ### ORIGINAL_REV, but that is semantically incorrect for the Ev2
203251881Speter     ### API).
204251881Speter     ###
205251881Speter     ### for now, we will assume the caller knows what they are doing
206251881Speter     ### and an invalid revision implies such a descendent. in the
207251881Speter     ### future, we could examine the ancestor chain looking for a
208251881Speter     ### copy/move/rotate-here node and allow the modification (and the
209251881Speter     ### converse: if no such ancestor, the caller must specify the
210251881Speter     ### correct/intended revision to modify).
211251881Speter  */
212251881Speter#if 1
213251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
214251881Speter    return SVN_NO_ERROR;
215251881Speter#else
216251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
217251881Speter    /* ### use a custom error code?  */
218251881Speter    return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
219251881Speter                             _("Revision for modifying '%s' is required"),
220251881Speter                             fspath);
221251881Speter#endif
222251881Speter
223251881Speter  if (revision < created_rev)
224251881Speter    {
225251881Speter      /* We asked to change a node that is *older* than what we found
226251881Speter         in the transaction. The client is out of date.  */
227251881Speter      return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
228251881Speter                               _("'%s' is out of date; try updating"),
229251881Speter                               fspath);
230251881Speter    }
231251881Speter
232251881Speter  if (revision > created_rev)
233251881Speter    {
234251881Speter      /* We asked to change a node that is *newer* than what we found
235251881Speter         in the transaction. Given that the transaction was based off
236251881Speter         of 'youngest', then either:
237251881Speter         - the caller asked to modify a future node
238251881Speter         - the caller has committed more revisions since this txn
239251881Speter         was constructed, and is asking to modify a node in one
240251881Speter         of those new revisions.
241251881Speter         In either case, the node may not have changed in those new
242251881Speter         revisions; use the node's ID to determine this case.  */
243251881Speter      const svn_fs_id_t *txn_noderev_id;
244251881Speter      svn_fs_root_t *rev_root;
245251881Speter      const svn_fs_id_t *new_noderev_id;
246251881Speter
247251881Speter      /* The ID of the node that we would be modifying in the txn  */
248251881Speter      SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath,
249251881Speter                             scratch_pool));
250251881Speter
251251881Speter      /* Get the ID from the future/new revision.  */
252251881Speter      SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root),
253251881Speter                                   revision, scratch_pool));
254251881Speter      SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath,
255251881Speter                             scratch_pool));
256251881Speter      svn_fs_close_root(rev_root);
257251881Speter
258251881Speter      /* Has the target node changed in the future?  */
259251881Speter      if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0)
260251881Speter        {
261251881Speter          /* Restarting the commit will base the txn on the future/new
262251881Speter             revision, allowing the modification at REVISION.  */
263251881Speter          /* ### use a custom error code  */
264251881Speter          return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
265251881Speter                                   _("'%s' has been modified since the "
266251881Speter                                     "commit began (restart the commit)"),
267251881Speter                                   fspath);
268251881Speter        }
269251881Speter    }
270251881Speter
271251881Speter  return SVN_NO_ERROR;
272251881Speter}
273251881Speter
274251881Speter
275251881Speter/* Can we create a node at FSPATH in TXN_ROOT? If something already exists
276251881Speter   at that path, then the client MAY be out of date. We then have to see if
277251881Speter   the path was created/modified in this transaction. IOW, it is new and
278251881Speter   can be replaced without problem.
279251881Speter
280251881Speter   Note: the editor protocol disallows double-modifications. This is to
281251881Speter   ensure somebody does not accidentally overwrite another file due to
282251881Speter   being out-of-date.  */
283251881Speterstatic svn_error_t *
284251881Spetercan_create(svn_fs_root_t *txn_root,
285251881Speter           const char *fspath,
286251881Speter           apr_pool_t *scratch_pool)
287251881Speter{
288251881Speter  svn_node_kind_t kind;
289251881Speter  const char *cur_fspath;
290251881Speter
291251881Speter  SVN_ERR(svn_fs_check_path(&kind, txn_root, fspath, scratch_pool));
292251881Speter  if (kind == svn_node_none)
293251881Speter    return SVN_NO_ERROR;
294251881Speter
295251881Speter  /* ### I'm not sure if this works perfectly. We might have an ancestor
296251881Speter     ### that was modified as a result of a change on a cousin. We might
297251881Speter     ### misinterpret that as a *-here node which brought along this
298251881Speter     ### child. Need to write a test to verify. We may also be able to
299251881Speter     ### test the ancestor to determine if it has been *-here in this
300251881Speter     ### txn, or just a simple modification.  */
301251881Speter
302251881Speter  /* Are any of the parents copied/moved/rotated-here?  */
303251881Speter  for (cur_fspath = fspath;
304251881Speter       strlen(cur_fspath) > 1;  /* not the root  */
305251881Speter       cur_fspath = svn_fspath__dirname(cur_fspath, scratch_pool))
306251881Speter    {
307251881Speter      svn_revnum_t created_rev;
308251881Speter
309251881Speter      SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, cur_fspath,
310251881Speter                                      scratch_pool));
311251881Speter      if (!SVN_IS_VALID_REVNUM(created_rev))
312251881Speter        {
313251881Speter          /* The node has no created revision, meaning it is uncommitted.
314251881Speter             Thus, it was created in this transaction, or it has already
315251881Speter             been modified in some way (implying it has already passed a
316251881Speter             modification check.  */
317251881Speter          /* ### verify the node has been *-here ??  */
318251881Speter          return SVN_NO_ERROR;
319251881Speter        }
320251881Speter    }
321251881Speter
322251881Speter  return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
323251881Speter                           _("'%s' already exists, so may be out"
324251881Speter                             " of date; try updating"),
325251881Speter                           fspath);
326251881Speter}
327251881Speter
328251881Speter
329251881Speter/* This implements svn_editor_cb_add_directory_t */
330251881Speterstatic svn_error_t *
331251881Speteradd_directory_cb(void *baton,
332251881Speter                 const char *relpath,
333251881Speter                 const apr_array_header_t *children,
334251881Speter                 apr_hash_t *props,
335251881Speter                 svn_revnum_t replaces_rev,
336251881Speter                 apr_pool_t *scratch_pool)
337251881Speter{
338251881Speter  struct edit_baton *eb = baton;
339251881Speter  const char *fspath = FSPATH(relpath, scratch_pool);
340251881Speter  svn_fs_root_t *root;
341251881Speter
342251881Speter  /* Note: we ignore CHILDREN. We have no "incomplete" state to worry about,
343251881Speter     so we don't need to be aware of what children will be created.  */
344251881Speter
345251881Speter  SVN_ERR(get_root(&root, eb));
346251881Speter
347251881Speter  if (SVN_IS_VALID_REVNUM(replaces_rev))
348251881Speter    {
349251881Speter      SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool));
350251881Speter      SVN_ERR(svn_fs_delete(root, fspath, scratch_pool));
351251881Speter    }
352251881Speter  else
353251881Speter    {
354251881Speter      SVN_ERR(can_create(root, fspath, scratch_pool));
355251881Speter    }
356251881Speter
357251881Speter  SVN_ERR(svn_fs_make_dir(root, fspath, scratch_pool));
358251881Speter  SVN_ERR(add_new_props(root, fspath, props, scratch_pool));
359251881Speter
360251881Speter  return SVN_NO_ERROR;
361251881Speter}
362251881Speter
363251881Speter
364251881Speter/* This implements svn_editor_cb_add_file_t */
365251881Speterstatic svn_error_t *
366251881Speteradd_file_cb(void *baton,
367251881Speter            const char *relpath,
368251881Speter            const svn_checksum_t *checksum,
369251881Speter            svn_stream_t *contents,
370251881Speter            apr_hash_t *props,
371251881Speter            svn_revnum_t replaces_rev,
372251881Speter            apr_pool_t *scratch_pool)
373251881Speter{
374251881Speter  struct edit_baton *eb = baton;
375251881Speter  const char *fspath = FSPATH(relpath, scratch_pool);
376251881Speter  svn_fs_root_t *root;
377251881Speter
378251881Speter  SVN_ERR(get_root(&root, eb));
379251881Speter
380251881Speter  if (SVN_IS_VALID_REVNUM(replaces_rev))
381251881Speter    {
382251881Speter      SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool));
383251881Speter      SVN_ERR(svn_fs_delete(root, fspath, scratch_pool));
384251881Speter    }
385251881Speter  else
386251881Speter    {
387251881Speter      SVN_ERR(can_create(root, fspath, scratch_pool));
388251881Speter    }
389251881Speter
390251881Speter  SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool));
391251881Speter
392251881Speter  SVN_ERR(set_text(root, fspath, checksum, contents,
393251881Speter                   eb->cancel_func, eb->cancel_baton, scratch_pool));
394251881Speter  SVN_ERR(add_new_props(root, fspath, props, scratch_pool));
395251881Speter
396251881Speter  return SVN_NO_ERROR;
397251881Speter}
398251881Speter
399251881Speter
400251881Speter/* This implements svn_editor_cb_add_symlink_t */
401251881Speterstatic svn_error_t *
402251881Speteradd_symlink_cb(void *baton,
403251881Speter               const char *relpath,
404251881Speter               const char *target,
405251881Speter               apr_hash_t *props,
406251881Speter               svn_revnum_t replaces_rev,
407251881Speter               apr_pool_t *scratch_pool)
408251881Speter{
409251881Speter  struct edit_baton *eb = baton;
410251881Speter  const char *fspath = FSPATH(relpath, scratch_pool);
411251881Speter  svn_fs_root_t *root;
412251881Speter
413251881Speter  SVN_ERR(get_root(&root, eb));
414251881Speter
415251881Speter  if (SVN_IS_VALID_REVNUM(replaces_rev))
416251881Speter    {
417251881Speter      SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool));
418251881Speter      SVN_ERR(svn_fs_delete(root, fspath, scratch_pool));
419251881Speter    }
420251881Speter  else
421251881Speter    {
422251881Speter      SVN_ERR(can_create(root, fspath, scratch_pool));
423251881Speter    }
424251881Speter
425251881Speter  /* ### we probably need to construct a file with specific contents
426251881Speter     ### (until the FS grows some symlink APIs)  */
427251881Speter#if 0
428251881Speter  SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool));
429251881Speter  SVN_ERR(svn_fs_apply_text(&fs_contents, root, fspath,
430251881Speter                            NULL /* result_checksum */,
431251881Speter                            scratch_pool));
432251881Speter  /* ### SVN_ERR(svn_stream_printf(fs_contents, ..., scratch_pool));  */
433251881Speter  apr_hash_set(props, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING,
434251881Speter               SVN_PROP_SPECIAL_VALUE);
435251881Speter
436251881Speter  SVN_ERR(add_new_props(root, fspath, props, scratch_pool));
437251881Speter#endif
438251881Speter
439251881Speter  SVN__NOT_IMPLEMENTED();
440251881Speter}
441251881Speter
442251881Speter
443251881Speter/* This implements svn_editor_cb_add_absent_t */
444251881Speterstatic svn_error_t *
445251881Speteradd_absent_cb(void *baton,
446251881Speter              const char *relpath,
447251881Speter              svn_node_kind_t kind,
448251881Speter              svn_revnum_t replaces_rev,
449251881Speter              apr_pool_t *scratch_pool)
450251881Speter{
451251881Speter  /* This is a programming error. Code should not attempt to create these
452251881Speter     kinds of nodes within the FS.  */
453251881Speter  /* ### use a custom error code  */
454251881Speter  return svn_error_create(
455251881Speter           SVN_ERR_UNSUPPORTED_FEATURE, NULL,
456251881Speter           _("The filesystem does not support 'absent' nodes"));
457251881Speter}
458251881Speter
459251881Speter
460251881Speter/* This implements svn_editor_cb_alter_directory_t */
461251881Speterstatic svn_error_t *
462251881Speteralter_directory_cb(void *baton,
463251881Speter                   const char *relpath,
464251881Speter                   svn_revnum_t revision,
465251881Speter                   const apr_array_header_t *children,
466251881Speter                   apr_hash_t *props,
467251881Speter                   apr_pool_t *scratch_pool)
468251881Speter{
469251881Speter  struct edit_baton *eb = baton;
470251881Speter  const char *fspath = FSPATH(relpath, scratch_pool);
471251881Speter  svn_fs_root_t *root;
472251881Speter
473251881Speter  /* Note: we ignore CHILDREN. We have no "incomplete" state to worry about,
474251881Speter     so we don't need to be aware of what children will be created.  */
475251881Speter
476251881Speter  SVN_ERR(get_root(&root, eb));
477251881Speter  SVN_ERR(can_modify(root, fspath, revision, scratch_pool));
478251881Speter
479251881Speter  if (props)
480251881Speter    SVN_ERR(alter_props(root, fspath, props, scratch_pool));
481251881Speter
482251881Speter  return SVN_NO_ERROR;
483251881Speter}
484251881Speter
485251881Speter
486251881Speter/* This implements svn_editor_cb_alter_file_t */
487251881Speterstatic svn_error_t *
488251881Speteralter_file_cb(void *baton,
489251881Speter              const char *relpath,
490251881Speter              svn_revnum_t revision,
491251881Speter              apr_hash_t *props,
492251881Speter              const svn_checksum_t *checksum,
493251881Speter              svn_stream_t *contents,
494251881Speter              apr_pool_t *scratch_pool)
495251881Speter{
496251881Speter  struct edit_baton *eb = baton;
497251881Speter  const char *fspath = FSPATH(relpath, scratch_pool);
498251881Speter  svn_fs_root_t *root;
499251881Speter
500251881Speter  SVN_ERR(get_root(&root, eb));
501251881Speter  SVN_ERR(can_modify(root, fspath, revision, scratch_pool));
502251881Speter
503251881Speter  if (contents != NULL)
504251881Speter    {
505251881Speter      SVN_ERR_ASSERT(checksum != NULL);
506251881Speter      SVN_ERR(set_text(root, fspath, checksum, contents,
507251881Speter                       eb->cancel_func, eb->cancel_baton, scratch_pool));
508251881Speter    }
509251881Speter
510251881Speter  if (props != NULL)
511251881Speter    {
512251881Speter      SVN_ERR(alter_props(root, fspath, props, scratch_pool));
513251881Speter    }
514251881Speter
515251881Speter  return SVN_NO_ERROR;
516251881Speter}
517251881Speter
518251881Speter
519251881Speter/* This implements svn_editor_cb_alter_symlink_t */
520251881Speterstatic svn_error_t *
521251881Speteralter_symlink_cb(void *baton,
522251881Speter                 const char *relpath,
523251881Speter                 svn_revnum_t revision,
524251881Speter                 apr_hash_t *props,
525251881Speter                 const char *target,
526251881Speter                 apr_pool_t *scratch_pool)
527251881Speter{
528251881Speter  struct edit_baton *eb = baton;
529251881Speter
530251881Speter  UNUSED(eb); SVN__NOT_IMPLEMENTED();
531251881Speter}
532251881Speter
533251881Speter
534251881Speter/* This implements svn_editor_cb_delete_t */
535251881Speterstatic svn_error_t *
536251881Speterdelete_cb(void *baton,
537251881Speter          const char *relpath,
538251881Speter          svn_revnum_t revision,
539251881Speter          apr_pool_t *scratch_pool)
540251881Speter{
541251881Speter  struct edit_baton *eb = baton;
542251881Speter  const char *fspath = FSPATH(relpath, scratch_pool);
543251881Speter  svn_fs_root_t *root;
544251881Speter
545251881Speter  SVN_ERR(get_root(&root, eb));
546251881Speter  SVN_ERR(can_modify(root, fspath, revision, scratch_pool));
547251881Speter
548251881Speter  SVN_ERR(svn_fs_delete(root, fspath, scratch_pool));
549251881Speter
550251881Speter  return SVN_NO_ERROR;
551251881Speter}
552251881Speter
553251881Speter
554251881Speter/* This implements svn_editor_cb_copy_t */
555251881Speterstatic svn_error_t *
556251881Spetercopy_cb(void *baton,
557251881Speter        const char *src_relpath,
558251881Speter        svn_revnum_t src_revision,
559251881Speter        const char *dst_relpath,
560251881Speter        svn_revnum_t replaces_rev,
561251881Speter        apr_pool_t *scratch_pool)
562251881Speter{
563251881Speter  struct edit_baton *eb = baton;
564251881Speter  const char *src_fspath = FSPATH(src_relpath, scratch_pool);
565251881Speter  const char *dst_fspath = FSPATH(dst_relpath, scratch_pool);
566251881Speter  svn_fs_root_t *root;
567251881Speter  svn_fs_root_t *src_root;
568251881Speter
569251881Speter  SVN_ERR(get_root(&root, eb));
570251881Speter
571251881Speter  /* Check if we can we replace the maybe-specified destination (revision).  */
572251881Speter  if (SVN_IS_VALID_REVNUM(replaces_rev))
573251881Speter    {
574251881Speter      SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool));
575251881Speter      SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool));
576251881Speter    }
577251881Speter  else
578251881Speter    {
579251881Speter      SVN_ERR(can_create(root, dst_fspath, scratch_pool));
580251881Speter    }
581251881Speter
582251881Speter  SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision,
583251881Speter                               scratch_pool));
584251881Speter  SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool));
585251881Speter  svn_fs_close_root(src_root);
586251881Speter
587251881Speter  return SVN_NO_ERROR;
588251881Speter}
589251881Speter
590251881Speter
591251881Speter/* This implements svn_editor_cb_move_t */
592251881Speterstatic svn_error_t *
593251881Spetermove_cb(void *baton,
594251881Speter        const char *src_relpath,
595251881Speter        svn_revnum_t src_revision,
596251881Speter        const char *dst_relpath,
597251881Speter        svn_revnum_t replaces_rev,
598251881Speter        apr_pool_t *scratch_pool)
599251881Speter{
600251881Speter  struct edit_baton *eb = baton;
601251881Speter  const char *src_fspath = FSPATH(src_relpath, scratch_pool);
602251881Speter  const char *dst_fspath = FSPATH(dst_relpath, scratch_pool);
603251881Speter  svn_fs_root_t *root;
604251881Speter  svn_fs_root_t *src_root;
605251881Speter
606251881Speter  SVN_ERR(get_root(&root, eb));
607251881Speter
608251881Speter  /* Check if we delete the specified source (revision), and can we replace
609251881Speter     the maybe-specified destination (revision).  */
610251881Speter  SVN_ERR(can_modify(root, src_fspath, src_revision, scratch_pool));
611251881Speter  if (SVN_IS_VALID_REVNUM(replaces_rev))
612251881Speter    {
613251881Speter      SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool));
614251881Speter      SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool));
615251881Speter    }
616251881Speter  else
617251881Speter    {
618251881Speter      SVN_ERR(can_create(root, dst_fspath, scratch_pool));
619251881Speter    }
620251881Speter
621251881Speter  /* ### would be nice to have svn_fs_move()  */
622251881Speter
623251881Speter  /* Copy the src to the dst. */
624251881Speter  SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision,
625251881Speter                               scratch_pool));
626251881Speter  SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool));
627251881Speter  svn_fs_close_root(src_root);
628251881Speter
629251881Speter  /* Notice: we're deleting the src repos path from the dst root. */
630251881Speter  SVN_ERR(svn_fs_delete(root, src_fspath, scratch_pool));
631251881Speter
632251881Speter  return SVN_NO_ERROR;
633251881Speter}
634251881Speter
635251881Speter
636251881Speter/* This implements svn_editor_cb_rotate_t */
637251881Speterstatic svn_error_t *
638251881Speterrotate_cb(void *baton,
639251881Speter          const apr_array_header_t *relpaths,
640251881Speter          const apr_array_header_t *revisions,
641251881Speter          apr_pool_t *scratch_pool)
642251881Speter{
643251881Speter  struct edit_baton *eb = baton;
644251881Speter
645251881Speter  UNUSED(eb); SVN__NOT_IMPLEMENTED();
646251881Speter}
647251881Speter
648251881Speter
649251881Speter/* This implements svn_editor_cb_complete_t */
650251881Speterstatic svn_error_t *
651251881Spetercomplete_cb(void *baton,
652251881Speter            apr_pool_t *scratch_pool)
653251881Speter{
654251881Speter  struct edit_baton *eb = baton;
655251881Speter
656251881Speter  /* Watch out for a following call to svn_fs_editor_commit(). Note that
657251881Speter     we are likely here because svn_fs_editor_commit() was called, and it
658251881Speter     invoked svn_editor_complete().  */
659251881Speter  eb->completed = TRUE;
660251881Speter
661251881Speter  if (eb->root != NULL)
662251881Speter    {
663251881Speter      svn_fs_close_root(eb->root);
664251881Speter      eb->root = NULL;
665251881Speter    }
666251881Speter
667251881Speter  return SVN_NO_ERROR;
668251881Speter}
669251881Speter
670251881Speter
671251881Speter/* This implements svn_editor_cb_abort_t */
672251881Speterstatic svn_error_t *
673251881Speterabort_cb(void *baton,
674251881Speter         apr_pool_t *scratch_pool)
675251881Speter{
676251881Speter  struct edit_baton *eb = baton;
677251881Speter  svn_error_t *err;
678251881Speter
679251881Speter  /* Don't allow a following call to svn_fs_editor_commit().  */
680251881Speter  eb->completed = TRUE;
681251881Speter
682251881Speter  if (eb->root != NULL)
683251881Speter    {
684251881Speter      svn_fs_close_root(eb->root);
685251881Speter      eb->root = NULL;
686251881Speter    }
687251881Speter
688251881Speter  /* ### should we examine the error and attempt svn_fs_purge_txn() ?  */
689251881Speter  err = svn_fs_abort_txn(eb->txn, scratch_pool);
690251881Speter
691251881Speter  /* For safety, clear the now-useless txn.  */
692251881Speter  eb->txn = NULL;
693251881Speter
694251881Speter  return svn_error_trace(err);
695251881Speter}
696251881Speter
697251881Speter
698251881Speterstatic svn_error_t *
699251881Spetermake_editor(svn_editor_t **editor,
700251881Speter            svn_fs_txn_t *txn,
701251881Speter            svn_cancel_func_t cancel_func,
702251881Speter            void *cancel_baton,
703251881Speter            apr_pool_t *result_pool,
704251881Speter            apr_pool_t *scratch_pool)
705251881Speter{
706251881Speter  static const svn_editor_cb_many_t editor_cbs = {
707251881Speter    add_directory_cb,
708251881Speter    add_file_cb,
709251881Speter    add_symlink_cb,
710251881Speter    add_absent_cb,
711251881Speter    alter_directory_cb,
712251881Speter    alter_file_cb,
713251881Speter    alter_symlink_cb,
714251881Speter    delete_cb,
715251881Speter    copy_cb,
716251881Speter    move_cb,
717251881Speter    rotate_cb,
718251881Speter    complete_cb,
719251881Speter    abort_cb
720251881Speter  };
721251881Speter  struct edit_baton *eb = apr_pcalloc(result_pool, sizeof(*eb));
722251881Speter
723251881Speter  eb->txn = txn;
724251881Speter  eb->cancel_func = cancel_func;
725251881Speter  eb->cancel_baton = cancel_baton;
726251881Speter  eb->txn_pool = result_pool;
727251881Speter
728251881Speter  SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton,
729251881Speter                            result_pool, scratch_pool));
730251881Speter  SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool));
731251881Speter
732251881Speter  return SVN_NO_ERROR;
733251881Speter}
734251881Speter
735251881Speter
736251881Spetersvn_error_t *
737251881Spetersvn_fs__editor_create(svn_editor_t **editor,
738251881Speter                      const char **txn_name,
739251881Speter                      svn_fs_t *fs,
740251881Speter                      apr_uint32_t flags,
741251881Speter                      svn_cancel_func_t cancel_func,
742251881Speter                      void *cancel_baton,
743251881Speter                      apr_pool_t *result_pool,
744251881Speter                      apr_pool_t *scratch_pool)
745251881Speter{
746251881Speter  svn_revnum_t revision;
747251881Speter  svn_fs_txn_t *txn;
748251881Speter
749251881Speter  SVN_ERR(svn_fs_youngest_rev(&revision, fs, scratch_pool));
750251881Speter  SVN_ERR(svn_fs_begin_txn2(&txn, fs, revision, flags, result_pool));
751251881Speter  SVN_ERR(svn_fs_txn_name(txn_name, txn, result_pool));
752251881Speter  return svn_error_trace(make_editor(editor, txn,
753251881Speter                                     cancel_func, cancel_baton,
754251881Speter                                     result_pool, scratch_pool));
755251881Speter}
756251881Speter
757251881Speter
758251881Spetersvn_error_t *
759251881Spetersvn_fs__editor_create_for(svn_editor_t **editor,
760251881Speter                          svn_fs_t *fs,
761251881Speter                          const char *txn_name,
762251881Speter                          svn_cancel_func_t cancel_func,
763251881Speter                          void *cancel_baton,
764251881Speter                          apr_pool_t *result_pool,
765251881Speter                          apr_pool_t *scratch_pool)
766251881Speter{
767251881Speter  svn_fs_txn_t *txn;
768251881Speter
769251881Speter  SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, result_pool));
770251881Speter  return svn_error_trace(make_editor(editor, txn,
771251881Speter                                     cancel_func, cancel_baton,
772251881Speter                                     result_pool, scratch_pool));
773251881Speter}
774251881Speter
775251881Speter
776251881Spetersvn_error_t *
777251881Spetersvn_fs__editor_commit(svn_revnum_t *revision,
778251881Speter                      svn_error_t **post_commit_err,
779251881Speter                      const char **conflict_path,
780251881Speter                      svn_editor_t *editor,
781251881Speter                      apr_pool_t *result_pool,
782251881Speter                      apr_pool_t *scratch_pool)
783251881Speter{
784251881Speter  struct edit_baton *eb = svn_editor_get_baton(editor);
785251881Speter  const char *inner_conflict_path;
786251881Speter  svn_error_t *err = NULL;
787251881Speter
788251881Speter  /* make sure people are using the correct sequencing.  */
789251881Speter  if (eb->completed)
790251881Speter    return svn_error_create(SVN_ERR_FS_INCORRECT_EDITOR_COMPLETION,
791251881Speter                            NULL, NULL);
792251881Speter
793251881Speter  *revision = SVN_INVALID_REVNUM;
794251881Speter  *post_commit_err = NULL;
795251881Speter  *conflict_path = NULL;
796251881Speter
797251881Speter  /* Clean up internal resources (eg. eb->root). This also allows the
798251881Speter     editor infrastructure to know this editor is "complete".  */
799251881Speter  err = svn_editor_complete(editor);
800251881Speter
801251881Speter  /* Note: docco for svn_fs_commit_txn() states that CONFLICT_PATH will
802251881Speter     be allocated in the txn's pool. But it lies. Regardless, we want
803251881Speter     it placed into RESULT_POOL.  */
804251881Speter
805251881Speter  if (!err)
806251881Speter    err = svn_fs_commit_txn(&inner_conflict_path,
807251881Speter                            revision,
808251881Speter                            eb->txn,
809251881Speter                            scratch_pool);
810251881Speter  if (SVN_IS_VALID_REVNUM(*revision))
811251881Speter    {
812251881Speter      if (err)
813251881Speter        {
814251881Speter          /* Case 3. ERR is a post-commit (cleanup) error.  */
815251881Speter
816251881Speter          /* Pass responsibility via POST_COMMIT_ERR.  */
817251881Speter          *post_commit_err = err;
818251881Speter          err = SVN_NO_ERROR;
819251881Speter        }
820251881Speter      /* else: Case 1.  */
821251881Speter    }
822251881Speter  else
823251881Speter    {
824251881Speter      SVN_ERR_ASSERT(err != NULL);
825251881Speter      if (err->apr_err == SVN_ERR_FS_CONFLICT)
826251881Speter        {
827251881Speter          /* Case 2.  */
828251881Speter
829251881Speter          /* Copy this into the correct pool (see note above).  */
830251881Speter          *conflict_path = apr_pstrdup(result_pool, inner_conflict_path);
831251881Speter
832251881Speter          /* Return sucess. The caller should inspect CONFLICT_PATH to
833251881Speter             determine this particular case.  */
834251881Speter          svn_error_clear(err);
835251881Speter          err = SVN_NO_ERROR;
836251881Speter        }
837251881Speter      /* else: Case 4.  */
838251881Speter
839251881Speter      /* Abort the TXN. Nobody wants to use it.  */
840251881Speter      /* ### should we examine the error and attempt svn_fs_purge_txn() ?  */
841251881Speter      err = svn_error_compose_create(
842251881Speter        err,
843251881Speter        svn_fs_abort_txn(eb->txn, scratch_pool));
844251881Speter    }
845251881Speter
846251881Speter  /* For safety, clear the now-useless txn.  */
847251881Speter  eb->txn = NULL;
848251881Speter
849251881Speter  return svn_error_trace(err);
850251881Speter}
851