1 /*
2 * editor.c : editing trees of versioned resources
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 #include <apr_pools.h>
25
26 #include "svn_types.h"
27 #include "svn_error.h"
28 #include "svn_pools.h"
29 #include "svn_dirent_uri.h"
30
31 #include "private/svn_editor.h"
32
33 #ifdef SVN_DEBUG
34 /* This enables runtime checks of the editor API constraints. This may
35 introduce additional memory and runtime overhead, and should not be used
36 in production builds.
37
38 ### Remove before release?
39
40 ### Disabled for now. If I call svn_editor_alter_directory(A) then
41 svn_editor_add_file(A/f) the latter fails on SHOULD_ALLOW_ADD.
42 If I modify svn_editor_alter_directory to MARK_ALLOW_ADD(child)
43 then if I call svn_editor_alter_directory(A) followed by
44 svn_editor_alter_directory(A/B/C) the latter fails on
45 VERIFY_PARENT_MAY_EXIST. */
46 #if 0
47 #define ENABLE_ORDERING_CHECK
48 #endif
49 #endif
50
51
52 struct svn_editor_t
53 {
54 void *baton;
55
56 /* Standard cancellation function. Called before each callback. */
57 svn_cancel_func_t cancel_func;
58 void *cancel_baton;
59
60 /* Our callback functions match that of the set-many structure, so
61 just use that. */
62 svn_editor_cb_many_t funcs;
63
64 /* This pool is used as the scratch_pool for all callbacks. */
65 apr_pool_t *scratch_pool;
66
67 #ifdef ENABLE_ORDERING_CHECK
68 svn_boolean_t within_callback;
69
70 apr_hash_t *pending_incomplete_children;
71 apr_hash_t *completed_nodes;
72 svn_boolean_t finished;
73
74 apr_pool_t *state_pool;
75 #endif
76 };
77
78
79 #ifdef ENABLE_ORDERING_CHECK
80
81 #define START_CALLBACK(editor) \
82 do { \
83 svn_editor_t *editor__tmp_e = (editor); \
84 SVN_ERR_ASSERT(!editor__tmp_e->within_callback); \
85 editor__tmp_e->within_callback = TRUE; \
86 } while (0)
87 #define END_CALLBACK(editor) ((editor)->within_callback = FALSE)
88
89 /* Marker to indicate no further changes are allowed on this node. */
90 static const int marker_done = 0;
91 #define MARKER_DONE (&marker_done)
92
93 /* Marker indicating that add_* may be called for this path, or that it
94 can be the destination of a copy or move. For copy/move, the path
95 will switch to MARKER_ALLOW_ALTER, to enable further tweaks. */
96 static const int marker_allow_add = 0;
97 #define MARKER_ALLOW_ADD (&marker_allow_add)
98
99 /* Marker indicating that alter_* may be called for this path. */
100 static const int marker_allow_alter = 0;
101 #define MARKER_ALLOW_ALTER (&marker_allow_alter)
102
103 /* Just like MARKER_DONE, but also indicates that the node was created
104 via add_directory(). This allows us to verify that the CHILDREN param
105 was comprehensive. */
106 static const int marker_added_dir = 0;
107 #define MARKER_ADDED_DIR (&marker_added_dir)
108
109 #define MARK_FINISHED(editor) ((editor)->finished = TRUE)
110 #define SHOULD_NOT_BE_FINISHED(editor) SVN_ERR_ASSERT(!(editor)->finished)
111
112 #define CLEAR_INCOMPLETE(editor, relpath) \
113 svn_hash_sets((editor)->pending_incomplete_children, relpath, NULL);
114
115 #define MARK_RELPATH(editor, relpath, value) \
116 svn_hash_sets((editor)->completed_nodes, \
117 apr_pstrdup((editor)->state_pool, relpath), value)
118
119 #define MARK_COMPLETED(editor, relpath) \
120 MARK_RELPATH(editor, relpath, MARKER_DONE)
121 #define SHOULD_NOT_BE_COMPLETED(editor, relpath) \
122 SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, relpath) == NULL)
123
124 #define MARK_ALLOW_ADD(editor, relpath) \
125 MARK_RELPATH(editor, relpath, MARKER_ALLOW_ADD)
126 #define SHOULD_ALLOW_ADD(editor, relpath) \
127 SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ADD, NULL))
128
129 #define MARK_ALLOW_ALTER(editor, relpath) \
130 MARK_RELPATH(editor, relpath, MARKER_ALLOW_ALTER)
131 #define SHOULD_ALLOW_ALTER(editor, relpath) \
132 SVN_ERR_ASSERT(allow_either(editor, relpath, MARKER_ALLOW_ALTER, NULL))
133
134 #define MARK_ADDED_DIR(editor, relpath) \
135 MARK_RELPATH(editor, relpath, MARKER_ADDED_DIR)
136 #define CHECK_UNKNOWN_CHILD(editor, relpath) \
137 SVN_ERR_ASSERT(check_unknown_child(editor, relpath))
138
139 /* When a child is changed in some way, mark the parent directory as needing
140 to be "stable" (no future structural changes). IOW, only allow "alter" on
141 the parent. Prevents parent-add/delete/move after any child operation. */
142 #define MARK_PARENT_STABLE(editor, relpath) \
143 mark_parent_stable(editor, relpath)
144
145 /* If the parent is MARKER_ALLOW_ADD, then it has been moved-away, and we
146 know it does not exist. All other cases: it might exist. */
147 #define VERIFY_PARENT_MAY_EXIST(editor, relpath) \
148 SVN_ERR_ASSERT(svn_hash_gets((editor)->completed_nodes, \
149 svn_relpath_dirname(relpath, \
150 (editor)->scratch_pool)) \
151 != MARKER_ALLOW_ADD)
152
153 /* If the parent is MARKER_ADDED_DIR, then we should not be deleting
154 children(*). If the parent is MARKER_ALLOW_ADD, then it has been
155 moved-away, so children cannot exist. That leaves MARKER_DONE,
156 MARKER_ALLOW_ALTER, and NULL as possible values. Just assert that
157 we didn't get either of the bad ones.
158
159 (*) if the child as added via add_*(), then it would have been marked
160 as completed and delete/move-away already test against completed nodes.
161 This test is to beware of trying to delete "children" that are not
162 actually (and can't possibly be) present. */
163 #define CHILD_DELETIONS_ALLOWED(editor, relpath) \
164 SVN_ERR_ASSERT(!allow_either(editor, \
165 svn_relpath_dirname(relpath, \
166 (editor)->scratch_pool), \
167 MARKER_ADDED_DIR, MARKER_ALLOW_ADD))
168
169 static svn_boolean_t
allow_either(const svn_editor_t * editor,const char * relpath,const void * marker1,const void * marker2)170 allow_either(const svn_editor_t *editor,
171 const char *relpath,
172 const void *marker1,
173 const void *marker2)
174 {
175 void *value = svn_hash_gets(editor->completed_nodes, relpath);
176 return value == marker1 || value == marker2;
177 }
178
179 static svn_boolean_t
check_unknown_child(const svn_editor_t * editor,const char * relpath)180 check_unknown_child(const svn_editor_t *editor,
181 const char *relpath)
182 {
183 const char *parent;
184
185 /* If we already know about the new child, then exit early. */
186 if (svn_hash_gets(editor->pending_incomplete_children, relpath) != NULL)
187 return TRUE;
188
189 parent = svn_relpath_dirname(relpath, editor->scratch_pool);
190
191 /* Was this parent created via svn_editor_add_directory() ? */
192 if (svn_hash_gets(editor->completed_nodes, parent)
193 == MARKER_ADDED_DIR)
194 {
195 /* Whoops. This child should have been listed in that add call,
196 and placed into ->pending_incomplete_children. */
197 return FALSE;
198 }
199
200 /* The parent was not added in this drive. */
201 return TRUE;
202 }
203
204 static void
mark_parent_stable(const svn_editor_t * editor,const char * relpath)205 mark_parent_stable(const svn_editor_t *editor,
206 const char *relpath)
207 {
208 const char *parent = svn_relpath_dirname(relpath, editor->scratch_pool);
209 const void *marker = svn_hash_gets(editor->completed_nodes, parent);
210
211 /* If RELPATH has already been marked (to disallow adds, or that it
212 has been fully-completed), then do nothing. */
213 if (marker == MARKER_ALLOW_ALTER
214 || marker == MARKER_DONE
215 || marker == MARKER_ADDED_DIR)
216 return;
217
218 /* If the marker is MARKER_ALLOW_ADD, then that means the parent was
219 moved away. There is no way to work on a child. That should have
220 been tested before we got here by VERIFY_PARENT_MAY_EXIST(). */
221 SVN_ERR_ASSERT_NO_RETURN(marker != MARKER_ALLOW_ADD);
222
223 /* MARKER is NULL. Upgrade it to MARKER_ALLOW_ALTER. */
224 MARK_RELPATH(editor, parent, MARKER_ALLOW_ALTER);
225 }
226
227 #else
228
229 /* Be wary with the definition of these macros so that we don't
230 end up with "statement with no effect" warnings. Obviously, this
231 depends upon particular usage, which is easy to verify. */
232
233 #define START_CALLBACK(editor) /* empty */
234 #define END_CALLBACK(editor) /* empty */
235
236 #define MARK_FINISHED(editor) /* empty */
237 #define SHOULD_NOT_BE_FINISHED(editor) /* empty */
238
239 #define CLEAR_INCOMPLETE(editor, relpath) /* empty */
240
241 #define MARK_COMPLETED(editor, relpath) /* empty */
242 #define SHOULD_NOT_BE_COMPLETED(editor, relpath) /* empty */
243
244 #define MARK_ALLOW_ADD(editor, relpath) /* empty */
245 #define SHOULD_ALLOW_ADD(editor, relpath) /* empty */
246
247 #define MARK_ALLOW_ALTER(editor, relpath) /* empty */
248 #define SHOULD_ALLOW_ALTER(editor, relpath) /* empty */
249
250 #define MARK_ADDED_DIR(editor, relpath) /* empty */
251 #define CHECK_UNKNOWN_CHILD(editor, relpath) /* empty */
252
253 #define MARK_PARENT_STABLE(editor, relpath) /* empty */
254 #define VERIFY_PARENT_MAY_EXIST(editor, relpath) /* empty */
255 #define CHILD_DELETIONS_ALLOWED(editor, relpath) /* empty */
256
257 #endif /* ENABLE_ORDERING_CHECK */
258
259
260 svn_error_t *
svn_editor_create(svn_editor_t ** editor,void * editor_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)261 svn_editor_create(svn_editor_t **editor,
262 void *editor_baton,
263 svn_cancel_func_t cancel_func,
264 void *cancel_baton,
265 apr_pool_t *result_pool,
266 apr_pool_t *scratch_pool)
267 {
268 *editor = apr_pcalloc(result_pool, sizeof(**editor));
269
270 (*editor)->baton = editor_baton;
271 (*editor)->cancel_func = cancel_func;
272 (*editor)->cancel_baton = cancel_baton;
273 (*editor)->scratch_pool = svn_pool_create(result_pool);
274
275 #ifdef ENABLE_ORDERING_CHECK
276 (*editor)->pending_incomplete_children = apr_hash_make(result_pool);
277 (*editor)->completed_nodes = apr_hash_make(result_pool);
278 (*editor)->finished = FALSE;
279 (*editor)->state_pool = result_pool;
280 #endif
281
282 return SVN_NO_ERROR;
283 }
284
285
286 void *
svn_editor_get_baton(const svn_editor_t * editor)287 svn_editor_get_baton(const svn_editor_t *editor)
288 {
289 return editor->baton;
290 }
291
292
293 svn_error_t *
svn_editor_setcb_add_directory(svn_editor_t * editor,svn_editor_cb_add_directory_t callback,apr_pool_t * scratch_pool)294 svn_editor_setcb_add_directory(svn_editor_t *editor,
295 svn_editor_cb_add_directory_t callback,
296 apr_pool_t *scratch_pool)
297 {
298 editor->funcs.cb_add_directory = callback;
299 return SVN_NO_ERROR;
300 }
301
302
303 svn_error_t *
svn_editor_setcb_add_file(svn_editor_t * editor,svn_editor_cb_add_file_t callback,apr_pool_t * scratch_pool)304 svn_editor_setcb_add_file(svn_editor_t *editor,
305 svn_editor_cb_add_file_t callback,
306 apr_pool_t *scratch_pool)
307 {
308 editor->funcs.cb_add_file = callback;
309 return SVN_NO_ERROR;
310 }
311
312
313 svn_error_t *
svn_editor_setcb_add_symlink(svn_editor_t * editor,svn_editor_cb_add_symlink_t callback,apr_pool_t * scratch_pool)314 svn_editor_setcb_add_symlink(svn_editor_t *editor,
315 svn_editor_cb_add_symlink_t callback,
316 apr_pool_t *scratch_pool)
317 {
318 editor->funcs.cb_add_symlink = callback;
319 return SVN_NO_ERROR;
320 }
321
322
323 svn_error_t *
svn_editor_setcb_add_absent(svn_editor_t * editor,svn_editor_cb_add_absent_t callback,apr_pool_t * scratch_pool)324 svn_editor_setcb_add_absent(svn_editor_t *editor,
325 svn_editor_cb_add_absent_t callback,
326 apr_pool_t *scratch_pool)
327 {
328 editor->funcs.cb_add_absent = callback;
329 return SVN_NO_ERROR;
330 }
331
332
333 svn_error_t *
svn_editor_setcb_alter_directory(svn_editor_t * editor,svn_editor_cb_alter_directory_t callback,apr_pool_t * scratch_pool)334 svn_editor_setcb_alter_directory(svn_editor_t *editor,
335 svn_editor_cb_alter_directory_t callback,
336 apr_pool_t *scratch_pool)
337 {
338 editor->funcs.cb_alter_directory = callback;
339 return SVN_NO_ERROR;
340 }
341
342
343 svn_error_t *
svn_editor_setcb_alter_file(svn_editor_t * editor,svn_editor_cb_alter_file_t callback,apr_pool_t * scratch_pool)344 svn_editor_setcb_alter_file(svn_editor_t *editor,
345 svn_editor_cb_alter_file_t callback,
346 apr_pool_t *scratch_pool)
347 {
348 editor->funcs.cb_alter_file = callback;
349 return SVN_NO_ERROR;
350 }
351
352
353 svn_error_t *
svn_editor_setcb_alter_symlink(svn_editor_t * editor,svn_editor_cb_alter_symlink_t callback,apr_pool_t * scratch_pool)354 svn_editor_setcb_alter_symlink(svn_editor_t *editor,
355 svn_editor_cb_alter_symlink_t callback,
356 apr_pool_t *scratch_pool)
357 {
358 editor->funcs.cb_alter_symlink = callback;
359 return SVN_NO_ERROR;
360 }
361
362
363 svn_error_t *
svn_editor_setcb_delete(svn_editor_t * editor,svn_editor_cb_delete_t callback,apr_pool_t * scratch_pool)364 svn_editor_setcb_delete(svn_editor_t *editor,
365 svn_editor_cb_delete_t callback,
366 apr_pool_t *scratch_pool)
367 {
368 editor->funcs.cb_delete = callback;
369 return SVN_NO_ERROR;
370 }
371
372
373 svn_error_t *
svn_editor_setcb_copy(svn_editor_t * editor,svn_editor_cb_copy_t callback,apr_pool_t * scratch_pool)374 svn_editor_setcb_copy(svn_editor_t *editor,
375 svn_editor_cb_copy_t callback,
376 apr_pool_t *scratch_pool)
377 {
378 editor->funcs.cb_copy = callback;
379 return SVN_NO_ERROR;
380 }
381
382
383 svn_error_t *
svn_editor_setcb_move(svn_editor_t * editor,svn_editor_cb_move_t callback,apr_pool_t * scratch_pool)384 svn_editor_setcb_move(svn_editor_t *editor,
385 svn_editor_cb_move_t callback,
386 apr_pool_t *scratch_pool)
387 {
388 editor->funcs.cb_move = callback;
389 return SVN_NO_ERROR;
390 }
391
392
393 svn_error_t *
svn_editor_setcb_complete(svn_editor_t * editor,svn_editor_cb_complete_t callback,apr_pool_t * scratch_pool)394 svn_editor_setcb_complete(svn_editor_t *editor,
395 svn_editor_cb_complete_t callback,
396 apr_pool_t *scratch_pool)
397 {
398 editor->funcs.cb_complete = callback;
399 return SVN_NO_ERROR;
400 }
401
402
403 svn_error_t *
svn_editor_setcb_abort(svn_editor_t * editor,svn_editor_cb_abort_t callback,apr_pool_t * scratch_pool)404 svn_editor_setcb_abort(svn_editor_t *editor,
405 svn_editor_cb_abort_t callback,
406 apr_pool_t *scratch_pool)
407 {
408 editor->funcs.cb_abort = callback;
409 return SVN_NO_ERROR;
410 }
411
412
413 svn_error_t *
svn_editor_setcb_many(svn_editor_t * editor,const svn_editor_cb_many_t * many,apr_pool_t * scratch_pool)414 svn_editor_setcb_many(svn_editor_t *editor,
415 const svn_editor_cb_many_t *many,
416 apr_pool_t *scratch_pool)
417 {
418 #define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME
419
420 COPY_CALLBACK(cb_add_directory);
421 COPY_CALLBACK(cb_add_file);
422 COPY_CALLBACK(cb_add_symlink);
423 COPY_CALLBACK(cb_add_absent);
424 COPY_CALLBACK(cb_alter_directory);
425 COPY_CALLBACK(cb_alter_file);
426 COPY_CALLBACK(cb_alter_symlink);
427 COPY_CALLBACK(cb_delete);
428 COPY_CALLBACK(cb_copy);
429 COPY_CALLBACK(cb_move);
430 COPY_CALLBACK(cb_complete);
431 COPY_CALLBACK(cb_abort);
432
433 #undef COPY_CALLBACK
434
435 return SVN_NO_ERROR;
436 }
437
438
439 static svn_error_t *
check_cancel(svn_editor_t * editor)440 check_cancel(svn_editor_t *editor)
441 {
442 svn_error_t *err = NULL;
443
444 if (editor->cancel_func)
445 {
446 START_CALLBACK(editor);
447 err = editor->cancel_func(editor->cancel_baton);
448 END_CALLBACK(editor);
449 }
450
451 return svn_error_trace(err);
452 }
453
454
455 svn_error_t *
svn_editor_add_directory(svn_editor_t * editor,const char * relpath,const apr_array_header_t * children,apr_hash_t * props,svn_revnum_t replaces_rev)456 svn_editor_add_directory(svn_editor_t *editor,
457 const char *relpath,
458 const apr_array_header_t *children,
459 apr_hash_t *props,
460 svn_revnum_t replaces_rev)
461 {
462 svn_error_t *err = SVN_NO_ERROR;
463
464 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
465 SVN_ERR_ASSERT(children != NULL);
466 SVN_ERR_ASSERT(props != NULL);
467 /* ### validate children are just basenames? */
468 SHOULD_NOT_BE_FINISHED(editor);
469 SHOULD_ALLOW_ADD(editor, relpath);
470 VERIFY_PARENT_MAY_EXIST(editor, relpath);
471 CHECK_UNKNOWN_CHILD(editor, relpath);
472
473 SVN_ERR(check_cancel(editor));
474
475 if (editor->funcs.cb_add_directory)
476 {
477 START_CALLBACK(editor);
478 err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
479 props, replaces_rev,
480 editor->scratch_pool);
481 END_CALLBACK(editor);
482 }
483
484 MARK_ADDED_DIR(editor, relpath);
485 MARK_PARENT_STABLE(editor, relpath);
486 CLEAR_INCOMPLETE(editor, relpath);
487
488 #ifdef ENABLE_ORDERING_CHECK
489 {
490 int i;
491 for (i = 0; i < children->nelts; i++)
492 {
493 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
494 const char *child = svn_relpath_join(relpath, child_basename,
495 editor->state_pool);
496
497 svn_hash_sets(editor->pending_incomplete_children, child, "");
498 }
499 }
500 #endif
501
502 svn_pool_clear(editor->scratch_pool);
503 return svn_error_trace(err);
504 }
505
506
507 svn_error_t *
svn_editor_add_file(svn_editor_t * editor,const char * relpath,const svn_checksum_t * checksum,svn_stream_t * contents,apr_hash_t * props,svn_revnum_t replaces_rev)508 svn_editor_add_file(svn_editor_t *editor,
509 const char *relpath,
510 const svn_checksum_t *checksum,
511 svn_stream_t *contents,
512 apr_hash_t *props,
513 svn_revnum_t replaces_rev)
514 {
515 svn_error_t *err = SVN_NO_ERROR;
516
517 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
518 SVN_ERR_ASSERT(checksum != NULL
519 && checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
520 SVN_ERR_ASSERT(contents != NULL);
521 SVN_ERR_ASSERT(props != NULL);
522 SHOULD_NOT_BE_FINISHED(editor);
523 SHOULD_ALLOW_ADD(editor, relpath);
524 VERIFY_PARENT_MAY_EXIST(editor, relpath);
525 CHECK_UNKNOWN_CHILD(editor, relpath);
526
527 SVN_ERR(check_cancel(editor));
528
529 if (editor->funcs.cb_add_file)
530 {
531 START_CALLBACK(editor);
532 err = editor->funcs.cb_add_file(editor->baton, relpath,
533 checksum, contents, props,
534 replaces_rev, editor->scratch_pool);
535 END_CALLBACK(editor);
536 }
537
538 MARK_COMPLETED(editor, relpath);
539 MARK_PARENT_STABLE(editor, relpath);
540 CLEAR_INCOMPLETE(editor, relpath);
541
542 svn_pool_clear(editor->scratch_pool);
543 return svn_error_trace(err);
544 }
545
546
547 svn_error_t *
svn_editor_add_symlink(svn_editor_t * editor,const char * relpath,const char * target,apr_hash_t * props,svn_revnum_t replaces_rev)548 svn_editor_add_symlink(svn_editor_t *editor,
549 const char *relpath,
550 const char *target,
551 apr_hash_t *props,
552 svn_revnum_t replaces_rev)
553 {
554 svn_error_t *err = SVN_NO_ERROR;
555
556 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
557 SVN_ERR_ASSERT(props != NULL);
558 SHOULD_NOT_BE_FINISHED(editor);
559 SHOULD_ALLOW_ADD(editor, relpath);
560 VERIFY_PARENT_MAY_EXIST(editor, relpath);
561 CHECK_UNKNOWN_CHILD(editor, relpath);
562
563 SVN_ERR(check_cancel(editor));
564
565 if (editor->funcs.cb_add_symlink)
566 {
567 START_CALLBACK(editor);
568 err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
569 replaces_rev, editor->scratch_pool);
570 END_CALLBACK(editor);
571 }
572
573 MARK_COMPLETED(editor, relpath);
574 MARK_PARENT_STABLE(editor, relpath);
575 CLEAR_INCOMPLETE(editor, relpath);
576
577 svn_pool_clear(editor->scratch_pool);
578 return svn_error_trace(err);
579 }
580
581
582 svn_error_t *
svn_editor_add_absent(svn_editor_t * editor,const char * relpath,svn_node_kind_t kind,svn_revnum_t replaces_rev)583 svn_editor_add_absent(svn_editor_t *editor,
584 const char *relpath,
585 svn_node_kind_t kind,
586 svn_revnum_t replaces_rev)
587 {
588 svn_error_t *err = SVN_NO_ERROR;
589
590 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
591 SHOULD_NOT_BE_FINISHED(editor);
592 SHOULD_ALLOW_ADD(editor, relpath);
593 VERIFY_PARENT_MAY_EXIST(editor, relpath);
594 CHECK_UNKNOWN_CHILD(editor, relpath);
595
596 SVN_ERR(check_cancel(editor));
597
598 if (editor->funcs.cb_add_absent)
599 {
600 START_CALLBACK(editor);
601 err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
602 replaces_rev, editor->scratch_pool);
603 END_CALLBACK(editor);
604 }
605
606 MARK_COMPLETED(editor, relpath);
607 MARK_PARENT_STABLE(editor, relpath);
608 CLEAR_INCOMPLETE(editor, relpath);
609
610 svn_pool_clear(editor->scratch_pool);
611 return svn_error_trace(err);
612 }
613
614
615 svn_error_t *
svn_editor_alter_directory(svn_editor_t * editor,const char * relpath,svn_revnum_t revision,const apr_array_header_t * children,apr_hash_t * props)616 svn_editor_alter_directory(svn_editor_t *editor,
617 const char *relpath,
618 svn_revnum_t revision,
619 const apr_array_header_t *children,
620 apr_hash_t *props)
621 {
622 svn_error_t *err = SVN_NO_ERROR;
623
624 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
625 SVN_ERR_ASSERT(children != NULL || props != NULL);
626 /* ### validate children are just basenames? */
627 SHOULD_NOT_BE_FINISHED(editor);
628 SHOULD_ALLOW_ALTER(editor, relpath);
629 VERIFY_PARENT_MAY_EXIST(editor, relpath);
630
631 SVN_ERR(check_cancel(editor));
632
633 if (editor->funcs.cb_alter_directory)
634 {
635 START_CALLBACK(editor);
636 err = editor->funcs.cb_alter_directory(editor->baton,
637 relpath, revision,
638 children, props,
639 editor->scratch_pool);
640 END_CALLBACK(editor);
641 }
642
643 MARK_COMPLETED(editor, relpath);
644 MARK_PARENT_STABLE(editor, relpath);
645
646 #ifdef ENABLE_ORDERING_CHECK
647 /* ### this is not entirely correct. we probably need to adjust the
648 ### check_unknown_child() function for this scenario. */
649 #if 0
650 {
651 int i;
652 for (i = 0; i < children->nelts; i++)
653 {
654 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
655 const char *child = svn_relpath_join(relpath, child_basename,
656 editor->state_pool);
657
658 apr_hash_set(editor->pending_incomplete_children, child,
659 APR_HASH_KEY_STRING, "");
660 /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
661 }
662 }
663 #endif
664 #endif
665
666 svn_pool_clear(editor->scratch_pool);
667 return svn_error_trace(err);
668 }
669
670
671 svn_error_t *
svn_editor_alter_file(svn_editor_t * editor,const char * relpath,svn_revnum_t revision,const svn_checksum_t * checksum,svn_stream_t * contents,apr_hash_t * props)672 svn_editor_alter_file(svn_editor_t *editor,
673 const char *relpath,
674 svn_revnum_t revision,
675 const svn_checksum_t *checksum,
676 svn_stream_t *contents,
677 apr_hash_t *props)
678 {
679 svn_error_t *err = SVN_NO_ERROR;
680
681 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
682 SVN_ERR_ASSERT((checksum != NULL && contents != NULL)
683 || (checksum == NULL && contents == NULL));
684 SVN_ERR_ASSERT(props != NULL || checksum != NULL);
685 if (checksum)
686 SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
687 SHOULD_NOT_BE_FINISHED(editor);
688 SHOULD_ALLOW_ALTER(editor, relpath);
689 VERIFY_PARENT_MAY_EXIST(editor, relpath);
690
691 SVN_ERR(check_cancel(editor));
692
693 if (editor->funcs.cb_alter_file)
694 {
695 START_CALLBACK(editor);
696 err = editor->funcs.cb_alter_file(editor->baton,
697 relpath, revision,
698 checksum, contents, props,
699 editor->scratch_pool);
700 END_CALLBACK(editor);
701 }
702
703 MARK_COMPLETED(editor, relpath);
704 MARK_PARENT_STABLE(editor, relpath);
705
706 svn_pool_clear(editor->scratch_pool);
707 return svn_error_trace(err);
708 }
709
710
711 svn_error_t *
svn_editor_alter_symlink(svn_editor_t * editor,const char * relpath,svn_revnum_t revision,const char * target,apr_hash_t * props)712 svn_editor_alter_symlink(svn_editor_t *editor,
713 const char *relpath,
714 svn_revnum_t revision,
715 const char *target,
716 apr_hash_t *props)
717 {
718 svn_error_t *err = SVN_NO_ERROR;
719
720 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
721 SVN_ERR_ASSERT(props != NULL || target != NULL);
722 SHOULD_NOT_BE_FINISHED(editor);
723 SHOULD_ALLOW_ALTER(editor, relpath);
724 VERIFY_PARENT_MAY_EXIST(editor, relpath);
725
726 SVN_ERR(check_cancel(editor));
727
728 if (editor->funcs.cb_alter_symlink)
729 {
730 START_CALLBACK(editor);
731 err = editor->funcs.cb_alter_symlink(editor->baton,
732 relpath, revision,
733 target, props,
734 editor->scratch_pool);
735 END_CALLBACK(editor);
736 }
737
738 MARK_COMPLETED(editor, relpath);
739 MARK_PARENT_STABLE(editor, relpath);
740
741 svn_pool_clear(editor->scratch_pool);
742 return svn_error_trace(err);
743 }
744
745
746 svn_error_t *
svn_editor_delete(svn_editor_t * editor,const char * relpath,svn_revnum_t revision)747 svn_editor_delete(svn_editor_t *editor,
748 const char *relpath,
749 svn_revnum_t revision)
750 {
751 svn_error_t *err = SVN_NO_ERROR;
752
753 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
754 SHOULD_NOT_BE_FINISHED(editor);
755 SHOULD_NOT_BE_COMPLETED(editor, relpath);
756 VERIFY_PARENT_MAY_EXIST(editor, relpath);
757 CHILD_DELETIONS_ALLOWED(editor, relpath);
758
759 SVN_ERR(check_cancel(editor));
760
761 if (editor->funcs.cb_delete)
762 {
763 START_CALLBACK(editor);
764 err = editor->funcs.cb_delete(editor->baton, relpath, revision,
765 editor->scratch_pool);
766 END_CALLBACK(editor);
767 }
768
769 MARK_COMPLETED(editor, relpath);
770 MARK_PARENT_STABLE(editor, relpath);
771
772 svn_pool_clear(editor->scratch_pool);
773 return svn_error_trace(err);
774 }
775
776
777 svn_error_t *
svn_editor_copy(svn_editor_t * editor,const char * src_relpath,svn_revnum_t src_revision,const char * dst_relpath,svn_revnum_t replaces_rev)778 svn_editor_copy(svn_editor_t *editor,
779 const char *src_relpath,
780 svn_revnum_t src_revision,
781 const char *dst_relpath,
782 svn_revnum_t replaces_rev)
783 {
784 svn_error_t *err = SVN_NO_ERROR;
785
786 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
787 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
788 SHOULD_NOT_BE_FINISHED(editor);
789 SHOULD_ALLOW_ADD(editor, dst_relpath);
790 VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
791 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
792
793 SVN_ERR(check_cancel(editor));
794
795 if (editor->funcs.cb_copy)
796 {
797 START_CALLBACK(editor);
798 err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision,
799 dst_relpath, replaces_rev,
800 editor->scratch_pool);
801 END_CALLBACK(editor);
802 }
803
804 MARK_ALLOW_ALTER(editor, dst_relpath);
805 MARK_PARENT_STABLE(editor, dst_relpath);
806 CLEAR_INCOMPLETE(editor, dst_relpath);
807
808 svn_pool_clear(editor->scratch_pool);
809 return svn_error_trace(err);
810 }
811
812
813 svn_error_t *
svn_editor_move(svn_editor_t * editor,const char * src_relpath,svn_revnum_t src_revision,const char * dst_relpath,svn_revnum_t replaces_rev)814 svn_editor_move(svn_editor_t *editor,
815 const char *src_relpath,
816 svn_revnum_t src_revision,
817 const char *dst_relpath,
818 svn_revnum_t replaces_rev)
819 {
820 svn_error_t *err = SVN_NO_ERROR;
821
822 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
823 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
824 SHOULD_NOT_BE_FINISHED(editor);
825 SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
826 SHOULD_ALLOW_ADD(editor, dst_relpath);
827 VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
828 CHILD_DELETIONS_ALLOWED(editor, src_relpath);
829 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
830
831 SVN_ERR(check_cancel(editor));
832
833 if (editor->funcs.cb_move)
834 {
835 START_CALLBACK(editor);
836 err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
837 dst_relpath, replaces_rev,
838 editor->scratch_pool);
839 END_CALLBACK(editor);
840 }
841
842 MARK_ALLOW_ADD(editor, src_relpath);
843 MARK_PARENT_STABLE(editor, src_relpath);
844 MARK_ALLOW_ALTER(editor, dst_relpath);
845 MARK_PARENT_STABLE(editor, dst_relpath);
846 CLEAR_INCOMPLETE(editor, dst_relpath);
847
848 svn_pool_clear(editor->scratch_pool);
849 return svn_error_trace(err);
850 }
851
852
853 svn_error_t *
svn_editor_complete(svn_editor_t * editor)854 svn_editor_complete(svn_editor_t *editor)
855 {
856 svn_error_t *err = SVN_NO_ERROR;
857
858 SHOULD_NOT_BE_FINISHED(editor);
859 #ifdef ENABLE_ORDERING_CHECK
860 SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
861 #endif
862
863 if (editor->funcs.cb_complete)
864 {
865 START_CALLBACK(editor);
866 err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
867 END_CALLBACK(editor);
868 }
869
870 MARK_FINISHED(editor);
871
872 svn_pool_clear(editor->scratch_pool);
873 return svn_error_trace(err);
874 }
875
876
877 svn_error_t *
svn_editor_abort(svn_editor_t * editor)878 svn_editor_abort(svn_editor_t *editor)
879 {
880 svn_error_t *err = SVN_NO_ERROR;
881
882 SHOULD_NOT_BE_FINISHED(editor);
883
884 if (editor->funcs.cb_abort)
885 {
886 START_CALLBACK(editor);
887 err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
888 END_CALLBACK(editor);
889 }
890
891 MARK_FINISHED(editor);
892
893 svn_pool_clear(editor->scratch_pool);
894 return svn_error_trace(err);
895 }
896