]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_wc/delete.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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, NULL, NULL,
139                                        db, local_abspath,
140                                        scratch_pool, scratch_pool));
141
142       SVN_ERR(svn_wc__conflict_read_markers(&markers, db, local_abspath,
143                                             conflict,
144                                             scratch_pool, scratch_pool));
145
146       /* Maximum number of markers is 4, so no iterpool */
147       for (i = 0; markers && i < markers->nelts; i++)
148         {
149           const char *marker_abspath;
150           svn_node_kind_t marker_kind;
151
152           marker_abspath = APR_ARRAY_IDX(markers, i, const char *);
153           SVN_ERR(svn_io_check_path(marker_abspath, &marker_kind,
154                                     scratch_pool));
155
156           if (marker_kind == svn_node_file)
157             {
158               svn_skel_t *work_item;
159
160               SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db,
161                                                    local_abspath,
162                                                    marker_abspath,
163                                                    result_pool,
164                                                    scratch_pool));
165
166               *work_items = svn_wc__wq_merge(*work_items, work_item,
167                                              result_pool);
168             }
169         }
170     }
171
172   return SVN_NO_ERROR;
173 }
174
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)
185 {
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;
195
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++)
200     {
201       svn_boolean_t conflicted = FALSE;
202       const char *repos_relpath;
203
204       svn_pool_clear(iterpool);
205
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);
213
214       if (err)
215         {
216           if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
217             {
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;
224             }
225          else
226           return svn_error_trace(err);
227         }
228
229       APR_ARRAY_PUSH(versioned_targets, const char *) = local_abspath;
230
231       switch (status)
232         {
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));
241
242           /* Explicitly ignore other statii */
243           default:
244             break;
245         }
246
247       if (status == svn_wc__db_status_normal
248           && kind == svn_node_dir)
249         {
250           svn_boolean_t is_wcroot;
251           SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath,
252                                        iterpool));
253
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));
260         }
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));
267
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));
273
274       /* Prepare the on-disk delete */
275       if (!keep_local)
276         {
277           svn_skel_t *work_item;
278
279           SVN_ERR(create_delete_wq_items(&work_item, db, local_abspath, kind,
280                                          conflicted,
281                                          scratch_pool, iterpool));
282
283           work_items = svn_wc__wq_merge(work_items, work_item,
284                                         scratch_pool);
285         }
286     }
287
288   if (versioned_targets->nelts == 0)
289     return SVN_NO_ERROR;
290
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));
297
298   if (work_items != NULL)
299     {
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 *);
303
304       SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
305                              iterpool));
306     }
307   svn_pool_destroy(iterpool);
308
309   return SVN_NO_ERROR;
310 }
311
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)
322 {
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;
331
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);
337
338   if (delete_unversioned_target &&
339       err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
340     {
341       svn_error_clear(err);
342
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;
348     }
349   else
350     SVN_ERR(err);
351
352   switch (status)
353     {
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));
360
361       /* Explicitly ignore other statii */
362       default:
363         break;
364     }
365
366   if (status == svn_wc__db_status_normal
367       && kind == svn_node_dir)
368     {
369       svn_boolean_t is_wcroot;
370       SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, pool));
371
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));
377     }
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));
383
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));
388
389   /* Prepare the on-disk delete */
390       if (!keep_local)
391         {
392           SVN_ERR(create_delete_wq_items(&work_items, db, local_abspath, kind,
393                                          conflicted,
394                                          scratch_pool, scratch_pool));
395         }
396
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));
404
405   if (work_items)
406     SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
407                            scratch_pool));
408
409   return SVN_NO_ERROR;
410 }
411
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)
419 {
420   svn_boolean_t left_something = FALSE;
421   svn_boolean_t is_root;
422   svn_error_t *err = NULL;
423
424   SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
425
426   SVN_ERR(svn_wc__write_check(db, is_root ? local_abspath
427                                           : svn_dirent_dirname(local_abspath,
428                                                                scratch_pool),
429                               scratch_pool));
430
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));
438
439   SVN_ERR(svn_wc__wq_run(db, local_abspath,
440                          cancel_func, cancel_baton,
441                          scratch_pool));
442
443   if (is_root)
444     {
445       /* Destroy the administrative area */
446       SVN_ERR(svn_wc__adm_destroy(db, local_abspath, cancel_func, cancel_baton,
447                                   scratch_pool));
448
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);
452     }
453
454   if (left_something || err)
455     return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, err, NULL);
456
457   return SVN_NO_ERROR;
458 }
459
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)
466 {
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))
472     {
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));
477     }
478   return SVN_NO_ERROR;
479 }
480
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)
489 {
490   if (instant_error)
491     {
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));
497     }
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));
505 }
506