1251881Speter/* 2251881Speter * delete.c: Handling of the in-wc side of the delete operation 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <string.h> 27251881Speter#include <stdlib.h> 28251881Speter 29251881Speter#include <apr_pools.h> 30251881Speter 31251881Speter#include "svn_types.h" 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_string.h" 34251881Speter#include "svn_error.h" 35251881Speter#include "svn_dirent_uri.h" 36251881Speter#include "svn_wc.h" 37251881Speter#include "svn_io.h" 38251881Speter 39251881Speter#include "wc.h" 40251881Speter#include "adm_files.h" 41251881Speter#include "conflicts.h" 42251881Speter#include "workqueue.h" 43251881Speter 44251881Speter#include "svn_private_config.h" 45251881Speter#include "private/svn_wc_private.h" 46251881Speter 47251881Speter 48251881Speter/* Remove/erase PATH from the working copy. This involves deleting PATH 49251881Speter * from the physical filesystem. PATH is assumed to be an unversioned file 50251881Speter * or directory. 51251881Speter * 52251881Speter * If ignore_enoent is TRUE, ignore missing targets. 53251881Speter * 54251881Speter * If CANCEL_FUNC is non-null, invoke it with CANCEL_BATON at various 55251881Speter * points, return any error immediately. 56251881Speter */ 57251881Speterstatic svn_error_t * 58251881Spetererase_unversioned_from_wc(const char *path, 59251881Speter svn_boolean_t ignore_enoent, 60251881Speter svn_cancel_func_t cancel_func, 61251881Speter void *cancel_baton, 62251881Speter apr_pool_t *scratch_pool) 63251881Speter{ 64251881Speter svn_error_t *err; 65251881Speter 66251881Speter /* Optimize the common case: try to delete the file */ 67251881Speter err = svn_io_remove_file2(path, ignore_enoent, scratch_pool); 68251881Speter if (err) 69251881Speter { 70251881Speter /* Then maybe it was a directory? */ 71251881Speter svn_error_clear(err); 72251881Speter 73251881Speter err = svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton, 74251881Speter scratch_pool); 75251881Speter 76251881Speter if (err) 77251881Speter { 78251881Speter /* We're unlikely to end up here. But we need this fallback 79251881Speter to make sure we report the right error *and* try the 80251881Speter correct deletion at least once. */ 81251881Speter svn_node_kind_t kind; 82251881Speter 83251881Speter svn_error_clear(err); 84251881Speter SVN_ERR(svn_io_check_path(path, &kind, scratch_pool)); 85251881Speter if (kind == svn_node_file) 86251881Speter SVN_ERR(svn_io_remove_file2(path, ignore_enoent, scratch_pool)); 87251881Speter else if (kind == svn_node_dir) 88251881Speter SVN_ERR(svn_io_remove_dir2(path, ignore_enoent, 89251881Speter cancel_func, cancel_baton, 90251881Speter scratch_pool)); 91251881Speter else if (kind == svn_node_none) 92251881Speter return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 93251881Speter _("'%s' does not exist"), 94251881Speter svn_dirent_local_style(path, 95251881Speter scratch_pool)); 96251881Speter else 97251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 98251881Speter _("Unsupported node kind for path '%s'"), 99251881Speter svn_dirent_local_style(path, 100251881Speter scratch_pool)); 101251881Speter 102251881Speter } 103251881Speter } 104251881Speter 105251881Speter return SVN_NO_ERROR; 106251881Speter} 107251881Speter 108251881Speter/* Helper for svn_wc__delete and svn_wc__delete_many */ 109251881Speterstatic svn_error_t * 110251881Spetercreate_delete_wq_items(svn_skel_t **work_items, 111251881Speter svn_wc__db_t *db, 112251881Speter const char *local_abspath, 113251881Speter svn_node_kind_t kind, 114251881Speter svn_boolean_t conflicted, 115251881Speter apr_pool_t *result_pool, 116251881Speter apr_pool_t *scratch_pool) 117251881Speter{ 118251881Speter *work_items = NULL; 119251881Speter 120251881Speter /* Schedule the on-disk delete */ 121251881Speter if (kind == svn_node_dir) 122251881Speter SVN_ERR(svn_wc__wq_build_dir_remove(work_items, db, local_abspath, 123251881Speter local_abspath, 124251881Speter TRUE /* recursive */, 125251881Speter result_pool, scratch_pool)); 126251881Speter else 127251881Speter SVN_ERR(svn_wc__wq_build_file_remove(work_items, db, local_abspath, 128251881Speter local_abspath, 129251881Speter result_pool, scratch_pool)); 130251881Speter 131251881Speter /* Read conflicts, to allow deleting the markers after updating the DB */ 132251881Speter if (conflicted) 133251881Speter { 134251881Speter svn_skel_t *conflict; 135251881Speter const apr_array_header_t *markers; 136251881Speter int i; 137251881Speter 138251881Speter SVN_ERR(svn_wc__db_read_conflict(&conflict, db, local_abspath, 139251881Speter scratch_pool, scratch_pool)); 140251881Speter 141251881Speter SVN_ERR(svn_wc__conflict_read_markers(&markers, db, local_abspath, 142251881Speter conflict, 143251881Speter scratch_pool, scratch_pool)); 144251881Speter 145251881Speter /* Maximum number of markers is 4, so no iterpool */ 146251881Speter for (i = 0; markers && i < markers->nelts; i++) 147251881Speter { 148251881Speter const char *marker_abspath; 149251881Speter svn_node_kind_t marker_kind; 150251881Speter 151251881Speter marker_abspath = APR_ARRAY_IDX(markers, i, const char *); 152251881Speter SVN_ERR(svn_io_check_path(marker_abspath, &marker_kind, 153251881Speter scratch_pool)); 154251881Speter 155251881Speter if (marker_kind == svn_node_file) 156251881Speter { 157251881Speter svn_skel_t *work_item; 158251881Speter 159251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, 160251881Speter local_abspath, 161251881Speter marker_abspath, 162251881Speter result_pool, 163251881Speter scratch_pool)); 164251881Speter 165251881Speter *work_items = svn_wc__wq_merge(*work_items, work_item, 166251881Speter result_pool); 167251881Speter } 168251881Speter } 169251881Speter } 170251881Speter 171251881Speter return SVN_NO_ERROR; 172251881Speter} 173251881Speter 174251881Spetersvn_error_t * 175251881Spetersvn_wc__delete_many(svn_wc_context_t *wc_ctx, 176251881Speter const apr_array_header_t *targets, 177251881Speter svn_boolean_t keep_local, 178251881Speter svn_boolean_t delete_unversioned_target, 179251881Speter svn_cancel_func_t cancel_func, 180251881Speter void *cancel_baton, 181251881Speter svn_wc_notify_func2_t notify_func, 182251881Speter void *notify_baton, 183251881Speter apr_pool_t *scratch_pool) 184251881Speter{ 185251881Speter svn_wc__db_t *db = wc_ctx->db; 186251881Speter svn_error_t *err; 187251881Speter svn_wc__db_status_t status; 188251881Speter svn_node_kind_t kind; 189251881Speter svn_skel_t *work_items = NULL; 190251881Speter apr_array_header_t *versioned_targets; 191251881Speter const char *local_abspath; 192251881Speter int i; 193251881Speter apr_pool_t *iterpool; 194251881Speter 195251881Speter iterpool = svn_pool_create(scratch_pool); 196251881Speter versioned_targets = apr_array_make(scratch_pool, targets->nelts, 197251881Speter sizeof(const char *)); 198251881Speter for (i = 0; i < targets->nelts; i++) 199251881Speter { 200251881Speter svn_boolean_t conflicted = FALSE; 201251881Speter const char *repos_relpath; 202251881Speter 203251881Speter svn_pool_clear(iterpool); 204251881Speter 205251881Speter local_abspath = APR_ARRAY_IDX(targets, i, const char *); 206251881Speter err = svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL, 207251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 208251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 209251881Speter NULL, &conflicted, 210251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 211251881Speter db, local_abspath, iterpool, iterpool); 212251881Speter 213251881Speter if (err) 214251881Speter { 215251881Speter if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 216251881Speter { 217251881Speter svn_error_clear(err); 218251881Speter if (delete_unversioned_target && !keep_local) 219251881Speter SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE, 220251881Speter cancel_func, cancel_baton, 221251881Speter iterpool)); 222251881Speter continue; 223251881Speter } 224251881Speter else 225251881Speter return svn_error_trace(err); 226251881Speter } 227251881Speter 228251881Speter APR_ARRAY_PUSH(versioned_targets, const char *) = local_abspath; 229251881Speter 230251881Speter switch (status) 231251881Speter { 232251881Speter /* svn_wc__db_status_server_excluded handled by 233251881Speter * svn_wc__db_op_delete_many */ 234251881Speter case svn_wc__db_status_excluded: 235251881Speter case svn_wc__db_status_not_present: 236251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 237251881Speter _("'%s' cannot be deleted"), 238251881Speter svn_dirent_local_style(local_abspath, 239251881Speter iterpool)); 240251881Speter 241251881Speter /* Explicitly ignore other statii */ 242251881Speter default: 243251881Speter break; 244251881Speter } 245251881Speter 246251881Speter if (status == svn_wc__db_status_normal 247251881Speter && kind == svn_node_dir) 248251881Speter { 249251881Speter svn_boolean_t is_wcroot; 250251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, 251251881Speter iterpool)); 252251881Speter 253251881Speter if (is_wcroot) 254251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 255251881Speter _("'%s' is the root of a working copy and " 256251881Speter "cannot be deleted"), 257251881Speter svn_dirent_local_style(local_abspath, 258251881Speter iterpool)); 259251881Speter } 260251881Speter if (repos_relpath && !repos_relpath[0]) 261251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 262251881Speter _("'%s' represents the repository root " 263251881Speter "and cannot be deleted"), 264251881Speter svn_dirent_local_style(local_abspath, 265251881Speter iterpool)); 266251881Speter 267251881Speter /* Verify if we have a write lock on the parent of this node as we might 268251881Speter be changing the childlist of that directory. */ 269251881Speter SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath, 270251881Speter iterpool), 271251881Speter iterpool)); 272251881Speter 273251881Speter /* Prepare the on-disk delete */ 274251881Speter if (!keep_local) 275251881Speter { 276251881Speter svn_skel_t *work_item; 277251881Speter 278251881Speter SVN_ERR(create_delete_wq_items(&work_item, db, local_abspath, kind, 279251881Speter conflicted, 280251881Speter scratch_pool, iterpool)); 281251881Speter 282251881Speter work_items = svn_wc__wq_merge(work_items, work_item, 283251881Speter scratch_pool); 284251881Speter } 285251881Speter } 286251881Speter 287251881Speter if (versioned_targets->nelts == 0) 288251881Speter return SVN_NO_ERROR; 289251881Speter 290251881Speter SVN_ERR(svn_wc__db_op_delete_many(db, versioned_targets, 291251881Speter !keep_local /* delete_dir_externals */, 292251881Speter work_items, 293251881Speter cancel_func, cancel_baton, 294251881Speter notify_func, notify_baton, 295251881Speter iterpool)); 296251881Speter 297251881Speter if (work_items != NULL) 298251881Speter { 299251881Speter /* Our only caller locked the wc, so for now assume it only passed 300251881Speter nodes from a single wc (asserted in svn_wc__db_op_delete_many) */ 301251881Speter local_abspath = APR_ARRAY_IDX(versioned_targets, 0, const char *); 302251881Speter 303251881Speter SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 304251881Speter iterpool)); 305251881Speter } 306251881Speter svn_pool_destroy(iterpool); 307251881Speter 308251881Speter return SVN_NO_ERROR; 309251881Speter} 310251881Speter 311251881Spetersvn_error_t * 312251881Spetersvn_wc_delete4(svn_wc_context_t *wc_ctx, 313251881Speter const char *local_abspath, 314251881Speter svn_boolean_t keep_local, 315251881Speter svn_boolean_t delete_unversioned_target, 316251881Speter svn_cancel_func_t cancel_func, 317251881Speter void *cancel_baton, 318251881Speter svn_wc_notify_func2_t notify_func, 319251881Speter void *notify_baton, 320251881Speter apr_pool_t *scratch_pool) 321251881Speter{ 322251881Speter apr_pool_t *pool = scratch_pool; 323251881Speter svn_wc__db_t *db = wc_ctx->db; 324251881Speter svn_error_t *err; 325251881Speter svn_wc__db_status_t status; 326251881Speter svn_node_kind_t kind; 327251881Speter svn_boolean_t conflicted; 328251881Speter svn_skel_t *work_items = NULL; 329251881Speter const char *repos_relpath; 330251881Speter 331251881Speter err = svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL, NULL, 332251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 333251881Speter NULL, NULL, NULL, NULL, NULL, NULL, &conflicted, 334251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 335251881Speter db, local_abspath, pool, pool); 336251881Speter 337251881Speter if (delete_unversioned_target && 338251881Speter err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 339251881Speter { 340251881Speter svn_error_clear(err); 341251881Speter 342251881Speter if (!keep_local) 343251881Speter SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE, 344251881Speter cancel_func, cancel_baton, 345251881Speter pool)); 346251881Speter return SVN_NO_ERROR; 347251881Speter } 348251881Speter else 349251881Speter SVN_ERR(err); 350251881Speter 351251881Speter switch (status) 352251881Speter { 353251881Speter /* svn_wc__db_status_server_excluded handled by svn_wc__db_op_delete */ 354251881Speter case svn_wc__db_status_excluded: 355251881Speter case svn_wc__db_status_not_present: 356251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 357251881Speter _("'%s' cannot be deleted"), 358251881Speter svn_dirent_local_style(local_abspath, pool)); 359251881Speter 360251881Speter /* Explicitly ignore other statii */ 361251881Speter default: 362251881Speter break; 363251881Speter } 364251881Speter 365251881Speter if (status == svn_wc__db_status_normal 366251881Speter && kind == svn_node_dir) 367251881Speter { 368251881Speter svn_boolean_t is_wcroot; 369251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, pool)); 370251881Speter 371251881Speter if (is_wcroot) 372251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 373251881Speter _("'%s' is the root of a working copy and " 374251881Speter "cannot be deleted"), 375251881Speter svn_dirent_local_style(local_abspath, pool)); 376251881Speter } 377251881Speter if (repos_relpath && !repos_relpath[0]) 378251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 379251881Speter _("'%s' represents the repository root " 380251881Speter "and cannot be deleted"), 381251881Speter svn_dirent_local_style(local_abspath, pool)); 382251881Speter 383251881Speter /* Verify if we have a write lock on the parent of this node as we might 384251881Speter be changing the childlist of that directory. */ 385251881Speter SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath, pool), 386251881Speter pool)); 387251881Speter 388251881Speter /* Prepare the on-disk delete */ 389251881Speter if (!keep_local) 390251881Speter { 391251881Speter SVN_ERR(create_delete_wq_items(&work_items, db, local_abspath, kind, 392251881Speter conflicted, 393251881Speter scratch_pool, scratch_pool)); 394251881Speter } 395251881Speter 396251881Speter SVN_ERR(svn_wc__db_op_delete(db, local_abspath, 397251881Speter NULL /*moved_to_abspath */, 398251881Speter !keep_local /* delete_dir_externals */, 399251881Speter NULL, work_items, 400251881Speter cancel_func, cancel_baton, 401251881Speter notify_func, notify_baton, 402251881Speter pool)); 403251881Speter 404251881Speter if (work_items) 405251881Speter SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 406251881Speter scratch_pool)); 407251881Speter 408251881Speter return SVN_NO_ERROR; 409251881Speter} 410251881Speter 411251881Spetersvn_error_t * 412251881Spetersvn_wc__internal_remove_from_revision_control(svn_wc__db_t *db, 413251881Speter const char *local_abspath, 414251881Speter svn_boolean_t destroy_wf, 415251881Speter svn_cancel_func_t cancel_func, 416251881Speter void *cancel_baton, 417251881Speter apr_pool_t *scratch_pool) 418251881Speter{ 419251881Speter svn_boolean_t left_something = FALSE; 420251881Speter svn_boolean_t is_root; 421251881Speter svn_error_t *err = NULL; 422251881Speter 423251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool)); 424251881Speter 425251881Speter SVN_ERR(svn_wc__write_check(db, is_root ? local_abspath 426251881Speter : svn_dirent_dirname(local_abspath, 427251881Speter scratch_pool), 428251881Speter scratch_pool)); 429251881Speter 430251881Speter SVN_ERR(svn_wc__db_op_remove_node(&left_something, 431251881Speter db, local_abspath, 432251881Speter destroy_wf /* destroy_wc */, 433251881Speter destroy_wf /* destroy_changes */, 434251881Speter SVN_INVALID_REVNUM, 435251881Speter svn_wc__db_status_not_present, 436251881Speter svn_node_none, 437251881Speter NULL, NULL, 438251881Speter cancel_func, cancel_baton, 439251881Speter scratch_pool)); 440251881Speter 441251881Speter SVN_ERR(svn_wc__wq_run(db, local_abspath, 442251881Speter cancel_func, cancel_baton, 443251881Speter scratch_pool)); 444251881Speter 445251881Speter if (is_root) 446251881Speter { 447251881Speter /* Destroy the administrative area */ 448251881Speter SVN_ERR(svn_wc__adm_destroy(db, local_abspath, cancel_func, cancel_baton, 449251881Speter scratch_pool)); 450251881Speter 451251881Speter /* And if we didn't leave something interesting, remove the directory */ 452251881Speter if (!left_something && destroy_wf) 453251881Speter err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool); 454251881Speter } 455251881Speter 456251881Speter if (left_something || err) 457251881Speter return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, err, NULL); 458251881Speter 459251881Speter return SVN_NO_ERROR; 460251881Speter} 461251881Speter 462251881Speter/* Implements svn_wc_status_func4_t for svn_wc_remove_from_revision_control2 */ 463251881Speterstatic svn_error_t * 464251881Speterremove_from_revision_status_callback(void *baton, 465251881Speter const char *local_abspath, 466251881Speter const svn_wc_status3_t *status, 467251881Speter apr_pool_t *scratch_pool) 468251881Speter{ 469251881Speter /* For legacy reasons we only check the file contents for changes */ 470251881Speter if (status->versioned 471251881Speter && status->kind == svn_node_file 472251881Speter && (status->text_status == svn_wc_status_modified 473251881Speter || status->text_status == svn_wc_status_conflicted)) 474251881Speter { 475251881Speter return svn_error_createf(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, 476251881Speter _("File '%s' has local modifications"), 477251881Speter svn_dirent_local_style(local_abspath, 478251881Speter scratch_pool)); 479251881Speter } 480251881Speter return SVN_NO_ERROR; 481251881Speter} 482251881Speter 483251881Spetersvn_error_t * 484251881Spetersvn_wc_remove_from_revision_control2(svn_wc_context_t *wc_ctx, 485251881Speter const char *local_abspath, 486251881Speter svn_boolean_t destroy_wf, 487251881Speter svn_boolean_t instant_error, 488251881Speter svn_cancel_func_t cancel_func, 489251881Speter void *cancel_baton, 490251881Speter apr_pool_t *scratch_pool) 491251881Speter{ 492251881Speter if (instant_error) 493251881Speter { 494251881Speter SVN_ERR(svn_wc_walk_status(wc_ctx, local_abspath, svn_depth_infinity, 495251881Speter FALSE, FALSE, FALSE, NULL, 496251881Speter remove_from_revision_status_callback, NULL, 497251881Speter cancel_func, cancel_baton, 498251881Speter scratch_pool)); 499251881Speter } 500251881Speter return svn_error_trace( 501251881Speter svn_wc__internal_remove_from_revision_control(wc_ctx->db, 502251881Speter local_abspath, 503251881Speter destroy_wf, 504251881Speter cancel_func, 505251881Speter cancel_baton, 506251881Speter scratch_pool)); 507251881Speter} 508251881Speter 509