Win32: fix an incorrect error status being propagated to the caller in case
[svn/apache.git] / subversion / libsvn_wc / delete.c
blob82ae9382d6dcd5bf3ce0a01313d9126610573ba5
1 /*
2 * delete.c: Handling of the in-wc side of the delete operation
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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 * ====================================================================
26 #include <string.h>
27 #include <stdlib.h>
29 #include <apr_pools.h>
31 #include "svn_types.h"
32 #include "svn_pools.h"
33 #include "svn_string.h"
34 #include "svn_error.h"
35 #include "svn_dirent_uri.h"
36 #include "svn_wc.h"
37 #include "svn_io.h"
39 #include "wc.h"
40 #include "adm_files.h"
41 #include "conflicts.h"
42 #include "workqueue.h"
44 #include "svn_private_config.h"
45 #include "private/svn_wc_private.h"
48 /* Remove/erase PATH from the working copy. This involves deleting PATH
49 * from the physical filesystem. PATH is assumed to be an unversioned file
50 * or directory.
52 * If ignore_enoent is TRUE, ignore missing targets.
54 * If CANCEL_FUNC is non-null, invoke it with CANCEL_BATON at various
55 * points, return any error immediately.
57 static svn_error_t *
58 erase_unversioned_from_wc(const char *path,
59 svn_boolean_t ignore_enoent,
60 svn_cancel_func_t cancel_func,
61 void *cancel_baton,
62 apr_pool_t *scratch_pool)
64 svn_error_t *err;
66 /* Optimize the common case: try to delete the file */
67 err = svn_io_remove_file2(path, ignore_enoent, scratch_pool);
68 if (err)
70 /* Then maybe it was a directory? */
71 svn_error_clear(err);
73 err = svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton,
74 scratch_pool);
76 if (err)
78 /* We're unlikely to end up here. But we need this fallback
79 to make sure we report the right error *and* try the
80 correct deletion at least once. */
81 svn_node_kind_t kind;
83 svn_error_clear(err);
84 SVN_ERR(svn_io_check_path(path, &kind, scratch_pool));
85 if (kind == svn_node_file)
86 SVN_ERR(svn_io_remove_file2(path, ignore_enoent, scratch_pool));
87 else if (kind == svn_node_dir)
88 SVN_ERR(svn_io_remove_dir2(path, ignore_enoent,
89 cancel_func, cancel_baton,
90 scratch_pool));
91 else if (kind == svn_node_none)
92 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
93 _("'%s' does not exist"),
94 svn_dirent_local_style(path,
95 scratch_pool));
96 else
97 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
98 _("Unsupported node kind for path '%s'"),
99 svn_dirent_local_style(path,
100 scratch_pool));
105 return SVN_NO_ERROR;
108 /* Helper for svn_wc__delete and svn_wc__delete_many */
109 static svn_error_t *
110 create_delete_wq_items(svn_skel_t **work_items,
111 svn_wc__db_t *db,
112 const char *local_abspath,
113 svn_node_kind_t kind,
114 svn_boolean_t conflicted,
115 apr_pool_t *result_pool,
116 apr_pool_t *scratch_pool)
118 *work_items = NULL;
120 /* Schedule the on-disk delete */
121 if (kind == svn_node_dir)
122 SVN_ERR(svn_wc__wq_build_dir_remove(work_items, db, local_abspath,
123 local_abspath,
124 TRUE /* recursive */,
125 result_pool, scratch_pool));
126 else
127 SVN_ERR(svn_wc__wq_build_file_remove(work_items, db, local_abspath,
128 local_abspath,
129 result_pool, scratch_pool));
131 /* Read conflicts, to allow deleting the markers after updating the DB */
132 if (conflicted)
134 svn_skel_t *conflict;
135 const apr_array_header_t *markers;
136 int i;
138 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
139 db, local_abspath,
140 scratch_pool, scratch_pool));
142 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, local_abspath,
143 conflict,
144 scratch_pool, scratch_pool));
146 /* Maximum number of markers is 4, so no iterpool */
147 for (i = 0; markers && i < markers->nelts; i++)
149 const char *marker_abspath;
150 svn_node_kind_t marker_kind;
152 marker_abspath = APR_ARRAY_IDX(markers, i, const char *);
153 SVN_ERR(svn_io_check_path(marker_abspath, &marker_kind,
154 scratch_pool));
156 if (marker_kind == svn_node_file)
158 svn_skel_t *work_item;
160 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db,
161 local_abspath,
162 marker_abspath,
163 result_pool,
164 scratch_pool));
166 *work_items = svn_wc__wq_merge(*work_items, work_item,
167 result_pool);
172 return SVN_NO_ERROR;
175 svn_error_t *
176 svn_wc__delete_many(svn_wc_context_t *wc_ctx,
177 const apr_array_header_t *targets,
178 svn_boolean_t keep_local,
179 svn_boolean_t delete_unversioned_target,
180 svn_cancel_func_t cancel_func,
181 void *cancel_baton,
182 svn_wc_notify_func2_t notify_func,
183 void *notify_baton,
184 apr_pool_t *scratch_pool)
186 svn_wc__db_t *db = wc_ctx->db;
187 svn_error_t *err;
188 svn_wc__db_status_t status;
189 svn_node_kind_t kind;
190 svn_skel_t *work_items = NULL;
191 apr_array_header_t *versioned_targets;
192 const char *local_abspath;
193 int i;
194 apr_pool_t *iterpool;
196 iterpool = svn_pool_create(scratch_pool);
197 versioned_targets = apr_array_make(scratch_pool, targets->nelts,
198 sizeof(const char *));
199 for (i = 0; i < targets->nelts; i++)
201 svn_boolean_t conflicted = FALSE;
202 const char *repos_relpath;
204 svn_pool_clear(iterpool);
206 local_abspath = APR_ARRAY_IDX(targets, i, const char *);
207 err = svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL,
208 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
209 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
210 NULL, &conflicted,
211 NULL, NULL, NULL, NULL, NULL, NULL,
212 db, local_abspath, iterpool, iterpool);
214 if (err)
216 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
218 svn_error_clear(err);
219 if (delete_unversioned_target && !keep_local)
220 SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE,
221 cancel_func, cancel_baton,
222 iterpool));
223 continue;
225 else
226 return svn_error_trace(err);
229 APR_ARRAY_PUSH(versioned_targets, const char *) = local_abspath;
231 switch (status)
233 /* svn_wc__db_status_server_excluded handled by
234 * svn_wc__db_op_delete_many */
235 case svn_wc__db_status_excluded:
236 case svn_wc__db_status_not_present:
237 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
238 _("'%s' cannot be deleted"),
239 svn_dirent_local_style(local_abspath,
240 iterpool));
242 /* Explicitly ignore other statii */
243 default:
244 break;
247 if (status == svn_wc__db_status_normal
248 && kind == svn_node_dir)
250 svn_boolean_t is_wcroot;
251 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath,
252 iterpool));
254 if (is_wcroot)
255 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
256 _("'%s' is the root of a working copy and "
257 "cannot be deleted"),
258 svn_dirent_local_style(local_abspath,
259 iterpool));
261 if (repos_relpath && !repos_relpath[0])
262 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
263 _("'%s' represents the repository root "
264 "and cannot be deleted"),
265 svn_dirent_local_style(local_abspath,
266 iterpool));
268 /* Verify if we have a write lock on the parent of this node as we might
269 be changing the childlist of that directory. */
270 SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath,
271 iterpool),
272 iterpool));
274 /* Prepare the on-disk delete */
275 if (!keep_local)
277 svn_skel_t *work_item;
279 SVN_ERR(create_delete_wq_items(&work_item, db, local_abspath, kind,
280 conflicted,
281 scratch_pool, iterpool));
283 work_items = svn_wc__wq_merge(work_items, work_item,
284 scratch_pool);
288 if (versioned_targets->nelts == 0)
289 return SVN_NO_ERROR;
291 SVN_ERR(svn_wc__db_op_delete_many(db, versioned_targets,
292 !keep_local /* delete_dir_externals */,
293 work_items,
294 cancel_func, cancel_baton,
295 notify_func, notify_baton,
296 iterpool));
298 if (work_items != NULL)
300 /* Our only caller locked the wc, so for now assume it only passed
301 nodes from a single wc (asserted in svn_wc__db_op_delete_many) */
302 local_abspath = APR_ARRAY_IDX(versioned_targets, 0, const char *);
304 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
305 iterpool));
307 svn_pool_destroy(iterpool);
309 return SVN_NO_ERROR;
312 svn_error_t *
313 svn_wc_delete4(svn_wc_context_t *wc_ctx,
314 const char *local_abspath,
315 svn_boolean_t keep_local,
316 svn_boolean_t delete_unversioned_target,
317 svn_cancel_func_t cancel_func,
318 void *cancel_baton,
319 svn_wc_notify_func2_t notify_func,
320 void *notify_baton,
321 apr_pool_t *scratch_pool)
323 apr_pool_t *pool = scratch_pool;
324 svn_wc__db_t *db = wc_ctx->db;
325 svn_error_t *err;
326 svn_wc__db_status_t status;
327 svn_node_kind_t kind;
328 svn_boolean_t conflicted;
329 svn_skel_t *work_items = NULL;
330 const char *repos_relpath;
332 err = svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL, NULL,
333 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
334 NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
335 NULL, NULL, NULL, NULL, NULL, NULL,
336 db, local_abspath, pool, pool);
338 if (delete_unversioned_target &&
339 err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
341 svn_error_clear(err);
343 if (!keep_local)
344 SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE,
345 cancel_func, cancel_baton,
346 pool));
347 return SVN_NO_ERROR;
349 else
350 SVN_ERR(err);
352 switch (status)
354 /* svn_wc__db_status_server_excluded handled by svn_wc__db_op_delete */
355 case svn_wc__db_status_excluded:
356 case svn_wc__db_status_not_present:
357 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
358 _("'%s' cannot be deleted"),
359 svn_dirent_local_style(local_abspath, pool));
361 /* Explicitly ignore other statii */
362 default:
363 break;
366 if (status == svn_wc__db_status_normal
367 && kind == svn_node_dir)
369 svn_boolean_t is_wcroot;
370 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, pool));
372 if (is_wcroot)
373 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
374 _("'%s' is the root of a working copy and "
375 "cannot be deleted"),
376 svn_dirent_local_style(local_abspath, pool));
378 if (repos_relpath && !repos_relpath[0])
379 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
380 _("'%s' represents the repository root "
381 "and cannot be deleted"),
382 svn_dirent_local_style(local_abspath, pool));
384 /* Verify if we have a write lock on the parent of this node as we might
385 be changing the childlist of that directory. */
386 SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath, pool),
387 pool));
389 /* Prepare the on-disk delete */
390 if (!keep_local)
392 SVN_ERR(create_delete_wq_items(&work_items, db, local_abspath, kind,
393 conflicted,
394 scratch_pool, scratch_pool));
397 SVN_ERR(svn_wc__db_op_delete(db, local_abspath,
398 NULL /*moved_to_abspath */,
399 !keep_local /* delete_dir_externals */,
400 NULL, work_items,
401 cancel_func, cancel_baton,
402 notify_func, notify_baton,
403 pool));
405 if (work_items)
406 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
407 scratch_pool));
409 return SVN_NO_ERROR;
412 svn_error_t *
413 svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db,
414 const char *local_abspath,
415 svn_boolean_t destroy_wf,
416 svn_cancel_func_t cancel_func,
417 void *cancel_baton,
418 apr_pool_t *scratch_pool)
420 svn_boolean_t left_something = FALSE;
421 svn_boolean_t is_root;
422 svn_error_t *err = NULL;
424 SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
426 SVN_ERR(svn_wc__write_check(db, is_root ? local_abspath
427 : svn_dirent_dirname(local_abspath,
428 scratch_pool),
429 scratch_pool));
431 SVN_ERR(svn_wc__db_op_remove_node(&left_something,
432 db, local_abspath,
433 destroy_wf /* destroy_wc */,
434 destroy_wf /* destroy_changes */,
435 NULL, NULL,
436 cancel_func, cancel_baton,
437 scratch_pool));
439 SVN_ERR(svn_wc__wq_run(db, local_abspath,
440 cancel_func, cancel_baton,
441 scratch_pool));
443 if (is_root)
445 /* Destroy the administrative area */
446 SVN_ERR(svn_wc__adm_destroy(db, local_abspath, cancel_func, cancel_baton,
447 scratch_pool));
449 /* And if we didn't leave something interesting, remove the directory */
450 if (!left_something && destroy_wf)
451 err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
454 if (left_something || err)
455 return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, err, NULL);
457 return SVN_NO_ERROR;
460 /* Implements svn_wc_status_func4_t for svn_wc_remove_from_revision_control2 */
461 static svn_error_t *
462 remove_from_revision_status_callback(void *baton,
463 const char *local_abspath,
464 const svn_wc_status3_t *status,
465 apr_pool_t *scratch_pool)
467 /* For legacy reasons we only check the file contents for changes */
468 if (status->versioned
469 && status->kind == svn_node_file
470 && (status->text_status == svn_wc_status_modified
471 || status->text_status == svn_wc_status_conflicted))
473 return svn_error_createf(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL,
474 _("File '%s' has local modifications"),
475 svn_dirent_local_style(local_abspath,
476 scratch_pool));
478 return SVN_NO_ERROR;
481 svn_error_t *
482 svn_wc_remove_from_revision_control2(svn_wc_context_t *wc_ctx,
483 const char *local_abspath,
484 svn_boolean_t destroy_wf,
485 svn_boolean_t instant_error,
486 svn_cancel_func_t cancel_func,
487 void *cancel_baton,
488 apr_pool_t *scratch_pool)
490 if (instant_error)
492 SVN_ERR(svn_wc_walk_status(wc_ctx, local_abspath, svn_depth_infinity,
493 FALSE, FALSE, FALSE, NULL,
494 remove_from_revision_status_callback, NULL,
495 cancel_func, cancel_baton,
496 scratch_pool));
498 return svn_error_trace(
499 svn_wc__internal_remove_from_revision_control(wc_ctx->db,
500 local_abspath,
501 destroy_wf,
502 cancel_func,
503 cancel_baton,
504 scratch_pool));