adm_files.c revision 299742
1/* 2 * adm_files.c: helper routines for handling files & dirs in the 3 * working copy administrative area (creating, 4 * deleting, opening, and closing). This is the only 5 * code that actually knows where administrative 6 * information is kept. 7 * 8 * ==================================================================== 9 * Licensed to the Apache Software Foundation (ASF) under one 10 * or more contributor license agreements. See the NOTICE file 11 * distributed with this work for additional information 12 * regarding copyright ownership. The ASF licenses this file 13 * to you under the Apache License, Version 2.0 (the 14 * "License"); you may not use this file except in compliance 15 * with the License. You may obtain a copy of the License at 16 * 17 * http://www.apache.org/licenses/LICENSE-2.0 18 * 19 * Unless required by applicable law or agreed to in writing, 20 * software distributed under the License is distributed on an 21 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22 * KIND, either express or implied. See the License for the 23 * specific language governing permissions and limitations 24 * under the License. 25 * ==================================================================== 26 */ 27 28 29 30#include <stdarg.h> 31#include <apr_pools.h> 32#include <apr_file_io.h> 33#include <apr_strings.h> 34 35#include "svn_types.h" 36#include "svn_error.h" 37#include "svn_io.h" 38#include "svn_dirent_uri.h" 39#include "svn_path.h" 40#include "svn_hash.h" 41 42#include "wc.h" 43#include "adm_files.h" 44#include "entries.h" 45#include "lock.h" 46 47#include "svn_private_config.h" 48#include "private/svn_wc_private.h" 49 50 51/*** File names in the adm area. ***/ 52 53/* The default name of the WC admin directory. This name is always 54 checked by svn_wc_is_adm_dir. */ 55static const char default_adm_dir_name[] = ".svn"; 56 57/* The name that is actually used for the WC admin directory. The 58 commonest case where this won't be the default is in Windows 59 ASP.NET development environments, which used to choke on ".svn". */ 60static const char *adm_dir_name = default_adm_dir_name; 61 62 63svn_boolean_t 64svn_wc_is_adm_dir(const char *name, apr_pool_t *pool) 65{ 66 return (0 == strcmp(name, adm_dir_name) 67 || 0 == strcmp(name, default_adm_dir_name)); 68} 69 70 71const char * 72svn_wc_get_adm_dir(apr_pool_t *pool) 73{ 74 return adm_dir_name; 75} 76 77 78svn_error_t * 79svn_wc_set_adm_dir(const char *name, apr_pool_t *pool) 80{ 81 /* This is the canonical list of administrative directory names. 82 83 FIXME: 84 An identical list is used in 85 libsvn_subr/opt.c:svn_opt__args_to_target_array(), 86 but that function can't use this list, because that use would 87 create a circular dependency between libsvn_wc and libsvn_subr. 88 Make sure changes to the lists are always synchronized! */ 89 static const char *valid_dir_names[] = { 90 default_adm_dir_name, 91 "_svn", 92 NULL 93 }; 94 95 const char **dir_name; 96 for (dir_name = valid_dir_names; *dir_name; ++dir_name) 97 if (0 == strcmp(name, *dir_name)) 98 { 99 /* Use the pointer to the statically allocated string 100 constant, to avoid potential pool lifetime issues. */ 101 adm_dir_name = *dir_name; 102 return SVN_NO_ERROR; 103 } 104 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 105 _("'%s' is not a valid administrative " 106 "directory name"), 107 svn_dirent_local_style(name, pool)); 108} 109 110 111const char * 112svn_wc__adm_child(const char *path, 113 const char *child, 114 apr_pool_t *result_pool) 115{ 116 return svn_dirent_join_many(result_pool, 117 path, 118 adm_dir_name, 119 child, 120 SVN_VA_NULL); 121} 122 123 124svn_boolean_t 125svn_wc__adm_area_exists(const char *adm_abspath, 126 apr_pool_t *pool) 127{ 128 const char *path = svn_wc__adm_child(adm_abspath, NULL, pool); 129 svn_node_kind_t kind; 130 svn_error_t *err; 131 132 err = svn_io_check_path(path, &kind, pool); 133 if (err) 134 { 135 svn_error_clear(err); 136 /* Return early, since kind is undefined in this case. */ 137 return FALSE; 138 } 139 140 return kind != svn_node_none; 141} 142 143 144 145/*** Making and using files in the adm area. ***/ 146 147 148/* */ 149static svn_error_t * 150make_adm_subdir(const char *path, 151 const char *subdir, 152 apr_pool_t *pool) 153{ 154 const char *fullpath; 155 156 fullpath = svn_wc__adm_child(path, subdir, pool); 157 158 return svn_io_dir_make(fullpath, APR_OS_DEFAULT, pool); 159} 160 161 162 163/*** Syncing files in the adm area. ***/ 164 165 166svn_error_t * 167svn_wc__text_base_path_to_read(const char **result_abspath, 168 svn_wc__db_t *db, 169 const char *local_abspath, 170 apr_pool_t *result_pool, 171 apr_pool_t *scratch_pool) 172{ 173 svn_wc__db_status_t status; 174 svn_node_kind_t kind; 175 const svn_checksum_t *checksum; 176 177 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, NULL, 178 &checksum, NULL, NULL, NULL, 179 db, local_abspath, 180 scratch_pool, scratch_pool)); 181 182 /* Sanity */ 183 if (kind != svn_node_file) 184 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 185 _("Can only get the pristine contents of files; " 186 "'%s' is not a file"), 187 svn_dirent_local_style(local_abspath, 188 scratch_pool)); 189 190 if (status == svn_wc__db_status_not_present) 191 /* We know that the delete of this node has been committed. 192 This should be the same as if called on an unknown path. */ 193 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 194 _("Cannot get the pristine contents of '%s' " 195 "because its delete is already committed"), 196 svn_dirent_local_style(local_abspath, 197 scratch_pool)); 198 else if (status == svn_wc__db_status_server_excluded 199 || status == svn_wc__db_status_excluded 200 || status == svn_wc__db_status_incomplete) 201 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 202 _("Cannot get the pristine contents of '%s' " 203 "because it has an unexpected status"), 204 svn_dirent_local_style(local_abspath, 205 scratch_pool)); 206 207 if (checksum == NULL) 208 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 209 _("Node '%s' has no pristine text"), 210 svn_dirent_local_style(local_abspath, 211 scratch_pool)); 212 SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, local_abspath, 213 checksum, 214 result_pool, scratch_pool)); 215 return SVN_NO_ERROR; 216} 217 218svn_error_t * 219svn_wc__get_pristine_contents(svn_stream_t **contents, 220 svn_filesize_t *size, 221 svn_wc__db_t *db, 222 const char *local_abspath, 223 apr_pool_t *result_pool, 224 apr_pool_t *scratch_pool) 225{ 226 svn_wc__db_status_t status; 227 svn_node_kind_t kind; 228 const svn_checksum_t *sha1_checksum; 229 230 if (size) 231 *size = SVN_INVALID_FILESIZE; 232 233 SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, NULL, 234 &sha1_checksum, NULL, NULL, NULL, 235 db, local_abspath, 236 scratch_pool, scratch_pool)); 237 238 /* Sanity */ 239 if (kind != svn_node_file) 240 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 241 _("Can only get the pristine contents of files; " 242 "'%s' is not a file"), 243 svn_dirent_local_style(local_abspath, 244 scratch_pool)); 245 246 if (status == svn_wc__db_status_added && !sha1_checksum) 247 { 248 /* Simply added. The pristine base does not exist. */ 249 *contents = NULL; 250 return SVN_NO_ERROR; 251 } 252 else if (status == svn_wc__db_status_not_present) 253 /* We know that the delete of this node has been committed. 254 This should be the same as if called on an unknown path. */ 255 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 256 _("Cannot get the pristine contents of '%s' " 257 "because its delete is already committed"), 258 svn_dirent_local_style(local_abspath, 259 scratch_pool)); 260 else if (status == svn_wc__db_status_server_excluded 261 || status == svn_wc__db_status_excluded 262 || status == svn_wc__db_status_incomplete) 263 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 264 _("Cannot get the pristine contents of '%s' " 265 "because it has an unexpected status"), 266 svn_dirent_local_style(local_abspath, 267 scratch_pool)); 268 if (sha1_checksum) 269 SVN_ERR(svn_wc__db_pristine_read(contents, size, db, local_abspath, 270 sha1_checksum, 271 result_pool, scratch_pool)); 272 else 273 *contents = NULL; 274 275 return SVN_NO_ERROR; 276} 277 278 279/*** Opening and closing files in the adm area. ***/ 280 281svn_error_t * 282svn_wc__open_adm_stream(svn_stream_t **stream, 283 const char *dir_abspath, 284 const char *fname, 285 apr_pool_t *result_pool, 286 apr_pool_t *scratch_pool) 287{ 288 const char *local_abspath; 289 290 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); 291 292 local_abspath = svn_wc__adm_child(dir_abspath, fname, scratch_pool); 293 return svn_error_trace(svn_stream_open_readonly(stream, local_abspath, 294 result_pool, scratch_pool)); 295} 296 297 298/*** Checking for and creating administrative subdirs. ***/ 299 300 301/* */ 302static svn_error_t * 303init_adm_tmp_area(const char *path, apr_pool_t *pool) 304{ 305 /* SVN_WC__ADM_TMP */ 306 SVN_ERR(make_adm_subdir(path, SVN_WC__ADM_TMP, pool)); 307 308 return SVN_NO_ERROR; 309} 310 311 312/* Set up a new adm area for PATH, with REPOS_* as the repos info, and 313 INITIAL_REV as the starting revision. The entries file starts out 314 marked as 'incomplete. The adm area starts out locked; remember to 315 unlock it when done. */ 316static svn_error_t * 317init_adm(svn_wc__db_t *db, 318 const char *local_abspath, 319 const char *repos_relpath, 320 const char *repos_root_url, 321 const char *repos_uuid, 322 svn_revnum_t initial_rev, 323 svn_depth_t depth, 324 apr_pool_t *pool) 325{ 326 /* First, make an empty administrative area. */ 327 SVN_ERR(svn_io_dir_make_hidden(svn_wc__adm_child(local_abspath, NULL, pool), 328 APR_OS_DEFAULT, pool)); 329 330 /** Make subdirectories. ***/ 331 332 /* SVN_WC__ADM_PRISTINE */ 333 SVN_ERR(make_adm_subdir(local_abspath, SVN_WC__ADM_PRISTINE, pool)); 334 335 /* ### want to add another directory? do a format bump to ensure that 336 ### all existing working copies get the new directories. or maybe 337 ### create-on-demand (more expensive) */ 338 339 /** Init the tmp area. ***/ 340 SVN_ERR(init_adm_tmp_area(local_abspath, pool)); 341 342 /* Create the SDB. */ 343 SVN_ERR(svn_wc__db_init(db, local_abspath, 344 repos_relpath, repos_root_url, repos_uuid, 345 initial_rev, depth, 346 pool)); 347 348 /* Stamp ENTRIES and FORMAT files for old clients. */ 349 SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath, 350 SVN_WC__ADM_ENTRIES, 351 pool), 352 SVN_WC__NON_ENTRIES_STRING, 353 pool)); 354 SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath, 355 SVN_WC__ADM_FORMAT, 356 pool), 357 SVN_WC__NON_ENTRIES_STRING, 358 pool)); 359 360 return SVN_NO_ERROR; 361} 362 363svn_error_t * 364svn_wc__internal_ensure_adm(svn_wc__db_t *db, 365 const char *local_abspath, 366 const char *url, 367 const char *repos_root_url, 368 const char *repos_uuid, 369 svn_revnum_t revision, 370 svn_depth_t depth, 371 apr_pool_t *scratch_pool) 372{ 373 int format; 374 const char *original_repos_relpath; 375 const char *original_root_url; 376 svn_boolean_t is_op_root; 377 const char *repos_relpath = svn_uri_skip_ancestor(repos_root_url, url, 378 scratch_pool); 379 svn_wc__db_status_t status; 380 const char *db_repos_relpath, *db_repos_root_url, *db_repos_uuid; 381 svn_revnum_t db_revision; 382 383 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 384 SVN_ERR_ASSERT(url != NULL); 385 SVN_ERR_ASSERT(repos_root_url != NULL); 386 SVN_ERR_ASSERT(repos_uuid != NULL); 387 SVN_ERR_ASSERT(repos_relpath != NULL); 388 389 SVN_ERR(svn_wc__internal_check_wc(&format, db, local_abspath, TRUE, 390 scratch_pool)); 391 392 /* Early out: we know we're not dealing with an existing wc, so 393 just create one. */ 394 if (format == 0) 395 return svn_error_trace(init_adm(db, local_abspath, 396 repos_relpath, repos_root_url, repos_uuid, 397 revision, depth, scratch_pool)); 398 399 SVN_ERR(svn_wc__db_read_info(&status, NULL, 400 &db_revision, &db_repos_relpath, 401 &db_repos_root_url, &db_repos_uuid, 402 NULL, NULL, NULL, NULL, NULL, NULL, 403 &original_repos_relpath, &original_root_url, 404 NULL, NULL, NULL, NULL, NULL, NULL, 405 NULL, &is_op_root, NULL, NULL, 406 NULL, NULL, NULL, 407 db, local_abspath, scratch_pool, scratch_pool)); 408 409 /* When the directory exists and is scheduled for deletion or is not-present 410 * do not check the revision or the URL. The revision can be any 411 * arbitrary revision and the URL may differ if the add is 412 * being driven from a merge which will have a different URL. */ 413 if (status != svn_wc__db_status_deleted 414 && status != svn_wc__db_status_not_present) 415 { 416 /* ### Should we match copyfrom_revision? */ 417 if (db_revision != revision) 418 return 419 svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 420 _("Revision %ld doesn't match existing " 421 "revision %ld in '%s'"), 422 revision, db_revision, local_abspath); 423 424 if (!db_repos_root_url) 425 { 426 if (status == svn_wc__db_status_added) 427 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, 428 &db_repos_relpath, 429 &db_repos_root_url, 430 &db_repos_uuid, 431 NULL, NULL, NULL, NULL, 432 db, local_abspath, 433 scratch_pool, scratch_pool)); 434 else 435 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, 436 &db_repos_relpath, 437 &db_repos_root_url, 438 &db_repos_uuid, NULL, NULL, NULL, 439 NULL, NULL, NULL, NULL, NULL, 440 NULL, NULL, 441 db, local_abspath, 442 scratch_pool, scratch_pool)); 443 } 444 445 /* The caller gives us a URL which should match the entry. However, 446 some callers compensate for an old problem in entry->url and pass 447 the copyfrom_url instead. See ^/notes/api-errata/1.7/wc002.txt. As 448 a result, we allow the passed URL to match copyfrom_url if it 449 does not match the entry's primary URL. */ 450 if (strcmp(db_repos_uuid, repos_uuid) 451 || strcmp(db_repos_root_url, repos_root_url) 452 || !svn_relpath_skip_ancestor(db_repos_relpath, repos_relpath)) 453 { 454 if (!is_op_root /* copy_from was set on op-roots only */ 455 || original_root_url == NULL 456 || strcmp(original_root_url, repos_root_url) 457 || strcmp(original_repos_relpath, repos_relpath)) 458 return 459 svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 460 _("URL '%s' (uuid: '%s') doesn't match existing " 461 "URL '%s' (uuid: '%s') in '%s'"), 462 url, 463 db_repos_uuid, 464 svn_path_url_add_component2(db_repos_root_url, 465 db_repos_relpath, 466 scratch_pool), 467 repos_uuid, 468 local_abspath); 469 } 470 } 471 472 return SVN_NO_ERROR; 473} 474 475svn_error_t * 476svn_wc_ensure_adm4(svn_wc_context_t *wc_ctx, 477 const char *local_abspath, 478 const char *url, 479 const char *repos_root_url, 480 const char *repos_uuid, 481 svn_revnum_t revision, 482 svn_depth_t depth, 483 apr_pool_t *scratch_pool) 484{ 485 return svn_error_trace( 486 svn_wc__internal_ensure_adm(wc_ctx->db, local_abspath, url, repos_root_url, 487 repos_uuid, revision, depth, scratch_pool)); 488} 489 490svn_error_t * 491svn_wc__adm_destroy(svn_wc__db_t *db, 492 const char *dir_abspath, 493 svn_cancel_func_t cancel_func, 494 void *cancel_baton, 495 apr_pool_t *scratch_pool) 496{ 497 svn_boolean_t is_wcroot; 498 499 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); 500 501 SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool)); 502 503 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, dir_abspath, scratch_pool)); 504 505 /* Well, the coast is clear for blowing away the administrative 506 directory, which also removes remaining locks */ 507 508 /* Now close the DB, and we can delete the working copy */ 509 if (is_wcroot) 510 { 511 SVN_ERR(svn_wc__db_drop_root(db, dir_abspath, scratch_pool)); 512 SVN_ERR(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, NULL, 513 scratch_pool), 514 FALSE, 515 cancel_func, cancel_baton, 516 scratch_pool)); 517 } 518 519 return SVN_NO_ERROR; 520} 521 522 523svn_error_t * 524svn_wc__adm_cleanup_tmp_area(svn_wc__db_t *db, 525 const char *adm_abspath, 526 apr_pool_t *scratch_pool) 527{ 528 const char *tmp_path; 529 530 SVN_ERR_ASSERT(svn_dirent_is_absolute(adm_abspath)); 531 532 SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool)); 533 534 /* Get the path to the tmp area, and blow it away. */ 535 tmp_path = svn_wc__adm_child(adm_abspath, SVN_WC__ADM_TMP, scratch_pool); 536 537 SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, scratch_pool)); 538 539 /* Now, rebuild the tmp area. */ 540 return svn_error_trace(init_adm_tmp_area(adm_abspath, scratch_pool)); 541} 542 543 544svn_error_t * 545svn_wc__get_tmpdir(const char **tmpdir_abspath, 546 svn_wc_context_t *wc_ctx, 547 const char *wri_abspath, 548 apr_pool_t *result_pool, 549 apr_pool_t *scratch_pool) 550{ 551 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(tmpdir_abspath, 552 wc_ctx->db, wri_abspath, 553 result_pool, scratch_pool)); 554 return SVN_NO_ERROR; 555} 556