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_rotate(svn_editor_t * editor,svn_editor_cb_rotate_t callback,apr_pool_t * scratch_pool)394 svn_editor_setcb_rotate(svn_editor_t *editor,
395 svn_editor_cb_rotate_t callback,
396 apr_pool_t *scratch_pool)
397 {
398 editor->funcs.cb_rotate = callback;
399 return SVN_NO_ERROR;
400 }
401
402
403 svn_error_t *
svn_editor_setcb_complete(svn_editor_t * editor,svn_editor_cb_complete_t callback,apr_pool_t * scratch_pool)404 svn_editor_setcb_complete(svn_editor_t *editor,
405 svn_editor_cb_complete_t callback,
406 apr_pool_t *scratch_pool)
407 {
408 editor->funcs.cb_complete = callback;
409 return SVN_NO_ERROR;
410 }
411
412
413 svn_error_t *
svn_editor_setcb_abort(svn_editor_t * editor,svn_editor_cb_abort_t callback,apr_pool_t * scratch_pool)414 svn_editor_setcb_abort(svn_editor_t *editor,
415 svn_editor_cb_abort_t callback,
416 apr_pool_t *scratch_pool)
417 {
418 editor->funcs.cb_abort = callback;
419 return SVN_NO_ERROR;
420 }
421
422
423 svn_error_t *
svn_editor_setcb_many(svn_editor_t * editor,const svn_editor_cb_many_t * many,apr_pool_t * scratch_pool)424 svn_editor_setcb_many(svn_editor_t *editor,
425 const svn_editor_cb_many_t *many,
426 apr_pool_t *scratch_pool)
427 {
428 #define COPY_CALLBACK(NAME) if (many->NAME) editor->funcs.NAME = many->NAME
429
430 COPY_CALLBACK(cb_add_directory);
431 COPY_CALLBACK(cb_add_file);
432 COPY_CALLBACK(cb_add_symlink);
433 COPY_CALLBACK(cb_add_absent);
434 COPY_CALLBACK(cb_alter_directory);
435 COPY_CALLBACK(cb_alter_file);
436 COPY_CALLBACK(cb_alter_symlink);
437 COPY_CALLBACK(cb_delete);
438 COPY_CALLBACK(cb_copy);
439 COPY_CALLBACK(cb_move);
440 COPY_CALLBACK(cb_rotate);
441 COPY_CALLBACK(cb_complete);
442 COPY_CALLBACK(cb_abort);
443
444 #undef COPY_CALLBACK
445
446 return SVN_NO_ERROR;
447 }
448
449
450 static svn_error_t *
check_cancel(svn_editor_t * editor)451 check_cancel(svn_editor_t *editor)
452 {
453 svn_error_t *err = NULL;
454
455 if (editor->cancel_func)
456 {
457 START_CALLBACK(editor);
458 err = editor->cancel_func(editor->cancel_baton);
459 END_CALLBACK(editor);
460 }
461
462 return svn_error_trace(err);
463 }
464
465
466 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)467 svn_editor_add_directory(svn_editor_t *editor,
468 const char *relpath,
469 const apr_array_header_t *children,
470 apr_hash_t *props,
471 svn_revnum_t replaces_rev)
472 {
473 svn_error_t *err = SVN_NO_ERROR;
474
475 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
476 SVN_ERR_ASSERT(children != NULL);
477 SVN_ERR_ASSERT(props != NULL);
478 /* ### validate children are just basenames? */
479 SHOULD_NOT_BE_FINISHED(editor);
480 SHOULD_ALLOW_ADD(editor, relpath);
481 VERIFY_PARENT_MAY_EXIST(editor, relpath);
482 CHECK_UNKNOWN_CHILD(editor, relpath);
483
484 SVN_ERR(check_cancel(editor));
485
486 if (editor->funcs.cb_add_directory)
487 {
488 START_CALLBACK(editor);
489 err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
490 props, replaces_rev,
491 editor->scratch_pool);
492 END_CALLBACK(editor);
493 }
494
495 MARK_ADDED_DIR(editor, relpath);
496 MARK_PARENT_STABLE(editor, relpath);
497 CLEAR_INCOMPLETE(editor, relpath);
498
499 #ifdef ENABLE_ORDERING_CHECK
500 {
501 int i;
502 for (i = 0; i < children->nelts; i++)
503 {
504 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
505 const char *child = svn_relpath_join(relpath, child_basename,
506 editor->state_pool);
507
508 svn_hash_sets(editor->pending_incomplete_children, child, "");
509 }
510 }
511 #endif
512
513 svn_pool_clear(editor->scratch_pool);
514 return svn_error_trace(err);
515 }
516
517
518 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)519 svn_editor_add_file(svn_editor_t *editor,
520 const char *relpath,
521 const svn_checksum_t *checksum,
522 svn_stream_t *contents,
523 apr_hash_t *props,
524 svn_revnum_t replaces_rev)
525 {
526 svn_error_t *err = SVN_NO_ERROR;
527
528 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
529 SVN_ERR_ASSERT(checksum != NULL
530 && checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
531 SVN_ERR_ASSERT(contents != NULL);
532 SVN_ERR_ASSERT(props != NULL);
533 SHOULD_NOT_BE_FINISHED(editor);
534 SHOULD_ALLOW_ADD(editor, relpath);
535 VERIFY_PARENT_MAY_EXIST(editor, relpath);
536 CHECK_UNKNOWN_CHILD(editor, relpath);
537
538 SVN_ERR(check_cancel(editor));
539
540 if (editor->funcs.cb_add_file)
541 {
542 START_CALLBACK(editor);
543 err = editor->funcs.cb_add_file(editor->baton, relpath,
544 checksum, contents, props,
545 replaces_rev, editor->scratch_pool);
546 END_CALLBACK(editor);
547 }
548
549 MARK_COMPLETED(editor, relpath);
550 MARK_PARENT_STABLE(editor, relpath);
551 CLEAR_INCOMPLETE(editor, relpath);
552
553 svn_pool_clear(editor->scratch_pool);
554 return svn_error_trace(err);
555 }
556
557
558 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)559 svn_editor_add_symlink(svn_editor_t *editor,
560 const char *relpath,
561 const char *target,
562 apr_hash_t *props,
563 svn_revnum_t replaces_rev)
564 {
565 svn_error_t *err = SVN_NO_ERROR;
566
567 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
568 SVN_ERR_ASSERT(props != NULL);
569 SHOULD_NOT_BE_FINISHED(editor);
570 SHOULD_ALLOW_ADD(editor, relpath);
571 VERIFY_PARENT_MAY_EXIST(editor, relpath);
572 CHECK_UNKNOWN_CHILD(editor, relpath);
573
574 SVN_ERR(check_cancel(editor));
575
576 if (editor->funcs.cb_add_symlink)
577 {
578 START_CALLBACK(editor);
579 err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
580 replaces_rev, editor->scratch_pool);
581 END_CALLBACK(editor);
582 }
583
584 MARK_COMPLETED(editor, relpath);
585 MARK_PARENT_STABLE(editor, relpath);
586 CLEAR_INCOMPLETE(editor, relpath);
587
588 svn_pool_clear(editor->scratch_pool);
589 return svn_error_trace(err);
590 }
591
592
593 svn_error_t *
svn_editor_add_absent(svn_editor_t * editor,const char * relpath,svn_node_kind_t kind,svn_revnum_t replaces_rev)594 svn_editor_add_absent(svn_editor_t *editor,
595 const char *relpath,
596 svn_node_kind_t kind,
597 svn_revnum_t replaces_rev)
598 {
599 svn_error_t *err = SVN_NO_ERROR;
600
601 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
602 SHOULD_NOT_BE_FINISHED(editor);
603 SHOULD_ALLOW_ADD(editor, relpath);
604 VERIFY_PARENT_MAY_EXIST(editor, relpath);
605 CHECK_UNKNOWN_CHILD(editor, relpath);
606
607 SVN_ERR(check_cancel(editor));
608
609 if (editor->funcs.cb_add_absent)
610 {
611 START_CALLBACK(editor);
612 err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
613 replaces_rev, editor->scratch_pool);
614 END_CALLBACK(editor);
615 }
616
617 MARK_COMPLETED(editor, relpath);
618 MARK_PARENT_STABLE(editor, relpath);
619 CLEAR_INCOMPLETE(editor, relpath);
620
621 svn_pool_clear(editor->scratch_pool);
622 return svn_error_trace(err);
623 }
624
625
626 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)627 svn_editor_alter_directory(svn_editor_t *editor,
628 const char *relpath,
629 svn_revnum_t revision,
630 const apr_array_header_t *children,
631 apr_hash_t *props)
632 {
633 svn_error_t *err = SVN_NO_ERROR;
634
635 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
636 SVN_ERR_ASSERT(children != NULL || props != NULL);
637 /* ### validate children are just basenames? */
638 SHOULD_NOT_BE_FINISHED(editor);
639 SHOULD_ALLOW_ALTER(editor, relpath);
640 VERIFY_PARENT_MAY_EXIST(editor, relpath);
641
642 SVN_ERR(check_cancel(editor));
643
644 if (editor->funcs.cb_alter_directory)
645 {
646 START_CALLBACK(editor);
647 err = editor->funcs.cb_alter_directory(editor->baton,
648 relpath, revision,
649 children, props,
650 editor->scratch_pool);
651 END_CALLBACK(editor);
652 }
653
654 MARK_COMPLETED(editor, relpath);
655 MARK_PARENT_STABLE(editor, relpath);
656
657 #ifdef ENABLE_ORDERING_CHECK
658 /* ### this is not entirely correct. we probably need to adjust the
659 ### check_unknown_child() function for this scenario. */
660 #if 0
661 {
662 int i;
663 for (i = 0; i < children->nelts; i++)
664 {
665 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
666 const char *child = svn_relpath_join(relpath, child_basename,
667 editor->state_pool);
668
669 apr_hash_set(editor->pending_incomplete_children, child,
670 APR_HASH_KEY_STRING, "");
671 /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
672 }
673 }
674 #endif
675 #endif
676
677 svn_pool_clear(editor->scratch_pool);
678 return svn_error_trace(err);
679 }
680
681
682 svn_error_t *
svn_editor_alter_file(svn_editor_t * editor,const char * relpath,svn_revnum_t revision,apr_hash_t * props,const svn_checksum_t * checksum,svn_stream_t * contents)683 svn_editor_alter_file(svn_editor_t *editor,
684 const char *relpath,
685 svn_revnum_t revision,
686 apr_hash_t *props,
687 const svn_checksum_t *checksum,
688 svn_stream_t *contents)
689 {
690 svn_error_t *err = SVN_NO_ERROR;
691
692 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
693 SVN_ERR_ASSERT((checksum != NULL && contents != NULL)
694 || (checksum == NULL && contents == NULL));
695 SVN_ERR_ASSERT(props != NULL || checksum != NULL);
696 if (checksum)
697 SVN_ERR_ASSERT(checksum->kind == SVN_EDITOR_CHECKSUM_KIND);
698 SHOULD_NOT_BE_FINISHED(editor);
699 SHOULD_ALLOW_ALTER(editor, relpath);
700 VERIFY_PARENT_MAY_EXIST(editor, relpath);
701
702 SVN_ERR(check_cancel(editor));
703
704 if (editor->funcs.cb_alter_file)
705 {
706 START_CALLBACK(editor);
707 err = editor->funcs.cb_alter_file(editor->baton,
708 relpath, revision, props,
709 checksum, contents,
710 editor->scratch_pool);
711 END_CALLBACK(editor);
712 }
713
714 MARK_COMPLETED(editor, relpath);
715 MARK_PARENT_STABLE(editor, relpath);
716
717 svn_pool_clear(editor->scratch_pool);
718 return svn_error_trace(err);
719 }
720
721
722 svn_error_t *
svn_editor_alter_symlink(svn_editor_t * editor,const char * relpath,svn_revnum_t revision,apr_hash_t * props,const char * target)723 svn_editor_alter_symlink(svn_editor_t *editor,
724 const char *relpath,
725 svn_revnum_t revision,
726 apr_hash_t *props,
727 const char *target)
728 {
729 svn_error_t *err = SVN_NO_ERROR;
730
731 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
732 SVN_ERR_ASSERT(props != NULL || target != NULL);
733 SHOULD_NOT_BE_FINISHED(editor);
734 SHOULD_ALLOW_ALTER(editor, relpath);
735 VERIFY_PARENT_MAY_EXIST(editor, relpath);
736
737 SVN_ERR(check_cancel(editor));
738
739 if (editor->funcs.cb_alter_symlink)
740 {
741 START_CALLBACK(editor);
742 err = editor->funcs.cb_alter_symlink(editor->baton,
743 relpath, revision, props,
744 target,
745 editor->scratch_pool);
746 END_CALLBACK(editor);
747 }
748
749 MARK_COMPLETED(editor, relpath);
750 MARK_PARENT_STABLE(editor, relpath);
751
752 svn_pool_clear(editor->scratch_pool);
753 return svn_error_trace(err);
754 }
755
756
757 svn_error_t *
svn_editor_delete(svn_editor_t * editor,const char * relpath,svn_revnum_t revision)758 svn_editor_delete(svn_editor_t *editor,
759 const char *relpath,
760 svn_revnum_t revision)
761 {
762 svn_error_t *err = SVN_NO_ERROR;
763
764 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
765 SHOULD_NOT_BE_FINISHED(editor);
766 SHOULD_NOT_BE_COMPLETED(editor, relpath);
767 VERIFY_PARENT_MAY_EXIST(editor, relpath);
768 CHILD_DELETIONS_ALLOWED(editor, relpath);
769
770 SVN_ERR(check_cancel(editor));
771
772 if (editor->funcs.cb_delete)
773 {
774 START_CALLBACK(editor);
775 err = editor->funcs.cb_delete(editor->baton, relpath, revision,
776 editor->scratch_pool);
777 END_CALLBACK(editor);
778 }
779
780 MARK_COMPLETED(editor, relpath);
781 MARK_PARENT_STABLE(editor, relpath);
782
783 svn_pool_clear(editor->scratch_pool);
784 return svn_error_trace(err);
785 }
786
787
788 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)789 svn_editor_copy(svn_editor_t *editor,
790 const char *src_relpath,
791 svn_revnum_t src_revision,
792 const char *dst_relpath,
793 svn_revnum_t replaces_rev)
794 {
795 svn_error_t *err = SVN_NO_ERROR;
796
797 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
798 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
799 SHOULD_NOT_BE_FINISHED(editor);
800 SHOULD_ALLOW_ADD(editor, dst_relpath);
801 VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
802 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
803
804 SVN_ERR(check_cancel(editor));
805
806 if (editor->funcs.cb_copy)
807 {
808 START_CALLBACK(editor);
809 err = editor->funcs.cb_copy(editor->baton, src_relpath, src_revision,
810 dst_relpath, replaces_rev,
811 editor->scratch_pool);
812 END_CALLBACK(editor);
813 }
814
815 MARK_ALLOW_ALTER(editor, dst_relpath);
816 MARK_PARENT_STABLE(editor, dst_relpath);
817 CLEAR_INCOMPLETE(editor, dst_relpath);
818
819 svn_pool_clear(editor->scratch_pool);
820 return svn_error_trace(err);
821 }
822
823
824 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)825 svn_editor_move(svn_editor_t *editor,
826 const char *src_relpath,
827 svn_revnum_t src_revision,
828 const char *dst_relpath,
829 svn_revnum_t replaces_rev)
830 {
831 svn_error_t *err = SVN_NO_ERROR;
832
833 SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath));
834 SVN_ERR_ASSERT(svn_relpath_is_canonical(dst_relpath));
835 SHOULD_NOT_BE_FINISHED(editor);
836 SHOULD_NOT_BE_COMPLETED(editor, src_relpath);
837 SHOULD_ALLOW_ADD(editor, dst_relpath);
838 VERIFY_PARENT_MAY_EXIST(editor, src_relpath);
839 CHILD_DELETIONS_ALLOWED(editor, src_relpath);
840 VERIFY_PARENT_MAY_EXIST(editor, dst_relpath);
841
842 SVN_ERR(check_cancel(editor));
843
844 if (editor->funcs.cb_move)
845 {
846 START_CALLBACK(editor);
847 err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
848 dst_relpath, replaces_rev,
849 editor->scratch_pool);
850 END_CALLBACK(editor);
851 }
852
853 MARK_ALLOW_ADD(editor, src_relpath);
854 MARK_PARENT_STABLE(editor, src_relpath);
855 MARK_ALLOW_ALTER(editor, dst_relpath);
856 MARK_PARENT_STABLE(editor, dst_relpath);
857 CLEAR_INCOMPLETE(editor, dst_relpath);
858
859 svn_pool_clear(editor->scratch_pool);
860 return svn_error_trace(err);
861 }
862
863
864 svn_error_t *
svn_editor_rotate(svn_editor_t * editor,const apr_array_header_t * relpaths,const apr_array_header_t * revisions)865 svn_editor_rotate(svn_editor_t *editor,
866 const apr_array_header_t *relpaths,
867 const apr_array_header_t *revisions)
868 {
869 svn_error_t *err = SVN_NO_ERROR;
870
871 SHOULD_NOT_BE_FINISHED(editor);
872 #ifdef ENABLE_ORDERING_CHECK
873 {
874 int i;
875 for (i = 0; i < relpaths->nelts; i++)
876 {
877 const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
878
879 SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
880 SHOULD_NOT_BE_COMPLETED(editor, relpath);
881 VERIFY_PARENT_MAY_EXIST(editor, relpath);
882 CHILD_DELETIONS_ALLOWED(editor, relpath);
883 }
884 }
885 #endif
886
887 SVN_ERR(check_cancel(editor));
888
889 if (editor->funcs.cb_rotate)
890 {
891 START_CALLBACK(editor);
892 err = editor->funcs.cb_rotate(editor->baton, relpaths, revisions,
893 editor->scratch_pool);
894 END_CALLBACK(editor);
895 }
896
897 #ifdef ENABLE_ORDERING_CHECK
898 {
899 int i;
900 for (i = 0; i < relpaths->nelts; i++)
901 {
902 const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *);
903 MARK_ALLOW_ALTER(editor, relpath);
904 MARK_PARENT_STABLE(editor, relpath);
905 }
906 }
907 #endif
908
909 svn_pool_clear(editor->scratch_pool);
910 return svn_error_trace(err);
911 }
912
913
914 svn_error_t *
svn_editor_complete(svn_editor_t * editor)915 svn_editor_complete(svn_editor_t *editor)
916 {
917 svn_error_t *err = SVN_NO_ERROR;
918
919 SHOULD_NOT_BE_FINISHED(editor);
920 #ifdef ENABLE_ORDERING_CHECK
921 SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
922 #endif
923
924 if (editor->funcs.cb_complete)
925 {
926 START_CALLBACK(editor);
927 err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
928 END_CALLBACK(editor);
929 }
930
931 MARK_FINISHED(editor);
932
933 svn_pool_clear(editor->scratch_pool);
934 return svn_error_trace(err);
935 }
936
937
938 svn_error_t *
svn_editor_abort(svn_editor_t * editor)939 svn_editor_abort(svn_editor_t *editor)
940 {
941 svn_error_t *err = SVN_NO_ERROR;
942
943 SHOULD_NOT_BE_FINISHED(editor);
944
945 if (editor->funcs.cb_abort)
946 {
947 START_CALLBACK(editor);
948 err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
949 END_CALLBACK(editor);
950 }
951
952 MARK_FINISHED(editor);
953
954 svn_pool_clear(editor->scratch_pool);
955 return svn_error_trace(err);
956 }
957