]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_wc/delete.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_wc / delete.c
1 /*
2  * delete.c: Handling of the in-wc side of the delete operation
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
25 \f
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include <apr_pools.h>
30
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"
38
39 #include "wc.h"
40 #include "adm_files.h"
41 #include "conflicts.h"
42 #include "workqueue.h"
43
44 #include "svn_private_config.h"
45 #include "private/svn_wc_private.h"
46
47
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.
51  *
52  * If ignore_enoent is TRUE, ignore missing targets.
53  *
54  * If CANCEL_FUNC is non-null, invoke it with CANCEL_BATON at various
55  * points, return any error immediately.
56  */
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)
63 {
64   svn_error_t *err;
65
66   /* Optimize the common case: try to delete the file */
67   err = svn_io_remove_file2(path, ignore_enoent, scratch_pool);
68   if (err)
69     {
70       /* Then maybe it was a directory? */
71       svn_error_clear(err);
72
73       err = svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton,
74                                scratch_pool);
75
76       if (err)
77         {
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;
82
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));
101
102         }
103     }
104
105   return SVN_NO_ERROR;
106 }
107
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)
117 {
118   *work_items = NULL;
119
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));
130
131   /* Read conflicts, to allow deleting the markers after updating the DB */
132   if (conflicted)
133     {
134       svn_skel_t *conflict;
135       const apr_array_header_t *markers;
136       int i;
137
138       SVN_ERR(svn_wc__db_read_conflict(&conflict, db, local_abspath,
139                                        scratch_pool, scratch_pool));
140
141       SVN_ERR(svn_wc__conflict_read_markers(&markers, db, local_abspath,
142                                             conflict,
143                                             scratch_pool, scratch_pool));
144
145       /* Maximum number of markers is 4, so no iterpool */
146       for (i = 0; markers && i < markers->nelts; i++)
147         {
148           const char *marker_abspath;
149           svn_node_kind_t marker_kind;
150
151           marker_abspath = APR_ARRAY_IDX(markers, i, const char *);
152           SVN_ERR(svn_io_check_path(marker_abspath, &marker_kind,
153                                     scratch_pool));
154
155           if (marker_kind == svn_node_file)
156             {
157               svn_skel_t *work_item;
158
159               SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db,
160                                                    local_abspath,
161                                                    marker_abspath,
162                                                    result_pool,
163                                                    scratch_pool));
164
165               *work_items = svn_wc__wq_merge(*work_items, work_item,
166                                              result_pool);
167             }
168         }
169     }
170
171   return SVN_NO_ERROR;
172 }
173
174 svn_error_t *
175 svn_wc__delete_many(svn_wc_context_t *wc_ctx,
176                     const apr_array_header_t *targets,
177                     svn_boolean_t keep_local,
178                     svn_boolean_t delete_unversioned_target,
179                     svn_cancel_func_t cancel_func,
180                     void *cancel_baton,
181                     svn_wc_notify_func2_t notify_func,
182                     void *notify_baton,
183                     apr_pool_t *scratch_pool)
184 {
185   svn_wc__db_t *db = wc_ctx->db;
186   svn_error_t *err;
187   svn_wc__db_status_t status;
188   svn_node_kind_t kind;
189   svn_skel_t *work_items = NULL;
190   apr_array_header_t *versioned_targets;
191   const char *local_abspath;
192   int i;
193   apr_pool_t *iterpool;
194
195   iterpool = svn_pool_create(scratch_pool);
196   versioned_targets = apr_array_make(scratch_pool, targets->nelts,
197                                      sizeof(const char *));
198   for (i = 0; i < targets->nelts; i++)
199     {
200       svn_boolean_t conflicted = FALSE;
201       const char *repos_relpath;
202
203       svn_pool_clear(iterpool);
204
205       local_abspath = APR_ARRAY_IDX(targets, i, const char *);
206       err = svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL,
207                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
208                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
209                                  NULL, &conflicted,
210                                  NULL, NULL, NULL, NULL, NULL, NULL,
211                                  db, local_abspath, iterpool, iterpool);
212
213       if (err)
214         {
215           if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
216             {
217               svn_error_clear(err);
218               if (delete_unversioned_target && !keep_local)
219                 SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE,
220                                                   cancel_func, cancel_baton,
221                                                   iterpool));
222               continue;
223             }
224          else
225           return svn_error_trace(err);
226         }
227
228       APR_ARRAY_PUSH(versioned_targets, const char *) = local_abspath;
229
230       switch (status)
231         {
232           /* svn_wc__db_status_server_excluded handled by
233            * svn_wc__db_op_delete_many */
234           case svn_wc__db_status_excluded:
235           case svn_wc__db_status_not_present:
236             return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
237                                      _("'%s' cannot be deleted"),
238                                      svn_dirent_local_style(local_abspath,
239                                                             iterpool));
240
241           /* Explicitly ignore other statii */
242           default:
243             break;
244         }
245
246       if (status == svn_wc__db_status_normal
247           && kind == svn_node_dir)
248         {
249           svn_boolean_t is_wcroot;
250           SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath,
251                                        iterpool));
252
253           if (is_wcroot)
254             return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
255                                      _("'%s' is the root of a working copy and "
256                                        "cannot be deleted"),
257                                      svn_dirent_local_style(local_abspath,
258                                                             iterpool));
259         }
260       if (repos_relpath && !repos_relpath[0])
261         return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
262                                      _("'%s' represents the repository root "
263                                        "and cannot be deleted"),
264                                      svn_dirent_local_style(local_abspath,
265                                                             iterpool));
266
267       /* Verify if we have a write lock on the parent of this node as we might
268          be changing the childlist of that directory. */
269       SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath,
270                                                          iterpool),
271                                   iterpool));
272
273       /* Prepare the on-disk delete */
274       if (!keep_local)
275         {
276           svn_skel_t *work_item;
277
278           SVN_ERR(create_delete_wq_items(&work_item, db, local_abspath, kind,
279                                          conflicted,
280                                          scratch_pool, iterpool));
281
282           work_items = svn_wc__wq_merge(work_items, work_item,
283                                         scratch_pool);
284         }
285     }
286
287   if (versioned_targets->nelts == 0)
288     return SVN_NO_ERROR;
289
290   SVN_ERR(svn_wc__db_op_delete_many(db, versioned_targets,
291                                     !keep_local /* delete_dir_externals */,
292                                     work_items,
293                                     cancel_func, cancel_baton,
294                                     notify_func, notify_baton,
295                                     iterpool));
296
297   if (work_items != NULL)
298     {
299       /* Our only caller locked the wc, so for now assume it only passed
300          nodes from a single wc (asserted in svn_wc__db_op_delete_many) */
301       local_abspath = APR_ARRAY_IDX(versioned_targets, 0, const char *);
302
303       SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
304                              iterpool));
305     }
306   svn_pool_destroy(iterpool);
307
308   return SVN_NO_ERROR;
309 }
310
311 svn_error_t *
312 svn_wc_delete4(svn_wc_context_t *wc_ctx,
313                const char *local_abspath,
314                svn_boolean_t keep_local,
315                svn_boolean_t delete_unversioned_target,
316                svn_cancel_func_t cancel_func,
317                void *cancel_baton,
318                svn_wc_notify_func2_t notify_func,
319                void *notify_baton,
320                apr_pool_t *scratch_pool)
321 {
322   apr_pool_t *pool = scratch_pool;
323   svn_wc__db_t *db = wc_ctx->db;
324   svn_error_t *err;
325   svn_wc__db_status_t status;
326   svn_node_kind_t kind;
327   svn_boolean_t conflicted;
328   svn_skel_t *work_items = NULL;
329   const char *repos_relpath;
330
331   err = svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL, NULL,
332                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
333                              NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
334                              NULL, NULL, NULL, NULL, NULL, NULL,
335                              db, local_abspath, pool, pool);
336
337   if (delete_unversioned_target &&
338       err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
339     {
340       svn_error_clear(err);
341
342       if (!keep_local)
343         SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE,
344                                           cancel_func, cancel_baton,
345                                           pool));
346       return SVN_NO_ERROR;
347     }
348   else
349     SVN_ERR(err);
350
351   switch (status)
352     {
353       /* svn_wc__db_status_server_excluded handled by svn_wc__db_op_delete */
354       case svn_wc__db_status_excluded:
355       case svn_wc__db_status_not_present:
356         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
357                                  _("'%s' cannot be deleted"),
358                                  svn_dirent_local_style(local_abspath, pool));
359
360       /* Explicitly ignore other statii */
361       default:
362         break;
363     }
364
365   if (status == svn_wc__db_status_normal
366       && kind == svn_node_dir)
367     {
368       svn_boolean_t is_wcroot;
369       SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, pool));
370
371       if (is_wcroot)
372         return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
373                                  _("'%s' is the root of a working copy and "
374                                    "cannot be deleted"),
375                                  svn_dirent_local_style(local_abspath, pool));
376     }
377   if (repos_relpath && !repos_relpath[0])
378     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
379                              _("'%s' represents the repository root "
380                                "and cannot be deleted"),
381                                svn_dirent_local_style(local_abspath, pool));
382
383   /* Verify if we have a write lock on the parent of this node as we might
384      be changing the childlist of that directory. */
385   SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath, pool),
386                               pool));
387
388   /* Prepare the on-disk delete */
389       if (!keep_local)
390         {
391           SVN_ERR(create_delete_wq_items(&work_items, db, local_abspath, kind,
392                                          conflicted,
393                                          scratch_pool, scratch_pool));
394         }
395
396   SVN_ERR(svn_wc__db_op_delete(db, local_abspath,
397                                NULL /*moved_to_abspath */,
398                                !keep_local /* delete_dir_externals */,
399                                NULL, work_items,
400                                cancel_func, cancel_baton,
401                                notify_func, notify_baton,
402                                pool));
403
404   if (work_items)
405     SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
406                            scratch_pool));
407
408   return SVN_NO_ERROR;
409 }
410
411 svn_error_t *
412 svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db,
413                                               const char *local_abspath,
414                                               svn_boolean_t destroy_wf,
415                                               svn_cancel_func_t cancel_func,
416                                               void *cancel_baton,
417                                               apr_pool_t *scratch_pool)
418 {
419   svn_boolean_t left_something = FALSE;
420   svn_boolean_t is_root;
421   svn_error_t *err = NULL;
422
423   SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
424
425   SVN_ERR(svn_wc__write_check(db, is_root ? local_abspath
426                                           : svn_dirent_dirname(local_abspath,
427                                                                scratch_pool),
428                               scratch_pool));
429
430   SVN_ERR(svn_wc__db_op_remove_node(&left_something,
431                                     db, local_abspath,
432                                     destroy_wf /* destroy_wc */,
433                                     destroy_wf /* destroy_changes */,
434                                     SVN_INVALID_REVNUM,
435                                     svn_wc__db_status_not_present,
436                                     svn_node_none,
437                                     NULL, NULL,
438                                     cancel_func, cancel_baton,
439                                     scratch_pool));
440
441   SVN_ERR(svn_wc__wq_run(db, local_abspath,
442                          cancel_func, cancel_baton,
443                          scratch_pool));
444
445   if (is_root)
446     {
447       /* Destroy the administrative area */
448       SVN_ERR(svn_wc__adm_destroy(db, local_abspath, cancel_func, cancel_baton,
449                                   scratch_pool));
450
451       /* And if we didn't leave something interesting, remove the directory */
452       if (!left_something && destroy_wf)
453         err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
454     }
455
456   if (left_something || err)
457     return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, err, NULL);
458
459   return SVN_NO_ERROR;
460 }
461
462 /* Implements svn_wc_status_func4_t for svn_wc_remove_from_revision_control2 */
463 static svn_error_t *
464 remove_from_revision_status_callback(void *baton,
465                                      const char *local_abspath,
466                                      const svn_wc_status3_t *status,
467                                      apr_pool_t *scratch_pool)
468 {
469   /* For legacy reasons we only check the file contents for changes */
470   if (status->versioned
471       && status->kind == svn_node_file
472       && (status->text_status == svn_wc_status_modified
473           || status->text_status == svn_wc_status_conflicted))
474     {
475       return svn_error_createf(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL,
476                                _("File '%s' has local modifications"),
477                                svn_dirent_local_style(local_abspath,
478                                                       scratch_pool));
479     }
480   return SVN_NO_ERROR;
481 }
482
483 svn_error_t *
484 svn_wc_remove_from_revision_control2(svn_wc_context_t *wc_ctx,
485                                     const char *local_abspath,
486                                     svn_boolean_t destroy_wf,
487                                     svn_boolean_t instant_error,
488                                     svn_cancel_func_t cancel_func,
489                                     void *cancel_baton,
490                                     apr_pool_t *scratch_pool)
491 {
492   if (instant_error)
493     {
494       SVN_ERR(svn_wc_walk_status(wc_ctx, local_abspath, svn_depth_infinity,
495                                  FALSE, FALSE, FALSE, NULL,
496                                  remove_from_revision_status_callback, NULL,
497                                  cancel_func, cancel_baton,
498                                  scratch_pool));
499     }
500   return svn_error_trace(
501       svn_wc__internal_remove_from_revision_control(wc_ctx->db,
502                                                     local_abspath,
503                                                     destroy_wf,
504                                                     cancel_func,
505                                                     cancel_baton,
506                                                     scratch_pool));
507 }
508