io.c revision 299742
1/* 2 * io.c: shared file reading, writing, and probing code. 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 26#include <stdio.h> 27 28#ifndef WIN32 29#include <unistd.h> 30#endif 31 32#ifndef APR_STATUS_IS_EPERM 33#include <errno.h> 34#ifdef EPERM 35#define APR_STATUS_IS_EPERM(s) ((s) == EPERM) 36#else 37#define APR_STATUS_IS_EPERM(s) (0) 38#endif 39#endif 40 41#include <apr_lib.h> 42#include <apr_pools.h> 43#include <apr_file_io.h> 44#include <apr_file_info.h> 45#include <apr_general.h> 46#include <apr_strings.h> 47#include <apr_portable.h> 48#include <apr_md5.h> 49 50#if APR_HAVE_FCNTL_H 51#include <fcntl.h> 52#endif 53 54#include "svn_hash.h" 55#include "svn_types.h" 56#include "svn_dirent_uri.h" 57#include "svn_path.h" 58#include "svn_string.h" 59#include "svn_error.h" 60#include "svn_io.h" 61#include "svn_pools.h" 62#include "svn_utf.h" 63#include "svn_config.h" 64#include "svn_private_config.h" 65#include "svn_ctype.h" 66 67#include "private/svn_atomic.h" 68#include "private/svn_io_private.h" 69#include "private/svn_utf_private.h" 70#include "private/svn_dep_compat.h" 71 72#define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS" 73 74/* 75 Windows is 'aided' by a number of types of applications that 76 follow other applications around and open up files they have 77 changed for various reasons (the most intrusive are virus 78 scanners). So, if one of these other apps has glommed onto 79 our file we may get an 'access denied' error. 80 81 This retry loop does not completely solve the problem (who 82 knows how long the other app is going to hold onto it for), but 83 goes a long way towards minimizing it. It is not an infinite 84 loop because there might really be an error. 85 86 Another reason for retrying delete operations on Windows 87 is that they are asynchronous -- the file or directory is not 88 actually deleted until the last handle to it is closed. The 89 retry loop cannot completely solve this problem either, but can 90 help mitigate it. 91*/ 92#define RETRY_MAX_ATTEMPTS 100 93#define RETRY_INITIAL_SLEEP 1000 94#define RETRY_MAX_SLEEP 128000 95 96#define RETRY_LOOP(err, expr, retry_test, sleep_test) \ 97 do \ 98 { \ 99 apr_status_t os_err = APR_TO_OS_ERROR(err); \ 100 int sleep_count = RETRY_INITIAL_SLEEP; \ 101 int retries; \ 102 for (retries = 0; \ 103 retries < RETRY_MAX_ATTEMPTS && (retry_test); \ 104 os_err = APR_TO_OS_ERROR(err)) \ 105 { \ 106 if (sleep_test) \ 107 { \ 108 ++retries; \ 109 apr_sleep(sleep_count); \ 110 if (sleep_count < RETRY_MAX_SLEEP) \ 111 sleep_count *= 2; \ 112 } \ 113 (err) = (expr); \ 114 } \ 115 } \ 116 while (0) 117 118#if defined(EDEADLK) && APR_HAS_THREADS 119#define FILE_LOCK_RETRY_LOOP(err, expr) \ 120 RETRY_LOOP(err, \ 121 expr, \ 122 (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \ 123 (!APR_STATUS_IS_EINTR(err))) 124#else 125#define FILE_LOCK_RETRY_LOOP(err, expr) \ 126 RETRY_LOOP(err, \ 127 expr, \ 128 (APR_STATUS_IS_EINTR(err)), \ 129 0) 130#endif 131 132#ifndef WIN32_RETRY_LOOP 133#if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP) 134#define WIN32_RETRY_LOOP(err, expr) \ 135 RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \ 136 || os_err == ERROR_SHARING_VIOLATION \ 137 || os_err == ERROR_DIR_NOT_EMPTY), \ 138 1) 139#else 140#define WIN32_RETRY_LOOP(err, expr) ((void)0) 141#endif 142#endif 143 144#ifdef WIN32 145 146#if _WIN32_WINNT < 0x600 /* Does the SDK assume Windows Vista+? */ 147typedef struct _FILE_RENAME_INFO { 148 BOOL ReplaceIfExists; 149 HANDLE RootDirectory; 150 DWORD FileNameLength; 151 WCHAR FileName[1]; 152} FILE_RENAME_INFO, *PFILE_RENAME_INFO; 153 154typedef struct _FILE_DISPOSITION_INFO { 155 BOOL DeleteFile; 156} FILE_DISPOSITION_INFO, *PFILE_DISPOSITION_INFO; 157 158#define FileRenameInfo 3 159#define FileDispositionInfo 4 160#endif /* WIN32 < Vista */ 161 162/* One-time initialization of the late bound Windows API functions. */ 163static volatile svn_atomic_t win_dynamic_imports_state = 0; 164 165/* Pointer to GetFinalPathNameByHandleW function from kernel32.dll. */ 166typedef DWORD (WINAPI *GETFINALPATHNAMEBYHANDLE)( 167 HANDLE hFile, 168 WCHAR *lpszFilePath, 169 DWORD cchFilePath, 170 DWORD dwFlags); 171 172typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, 173 int FileInformationClass, 174 LPVOID lpFileInformation, 175 DWORD dwBufferSize); 176 177static GETFINALPATHNAMEBYHANDLE get_final_path_name_by_handle_proc = NULL; 178static SetFileInformationByHandle_t set_file_information_by_handle_proc = NULL; 179 180/* Forward declaration. */ 181static svn_error_t * io_win_read_link(svn_string_t **dest, 182 const char *path, 183 apr_pool_t *pool); 184 185#endif 186 187/* Forward declaration */ 188static apr_status_t 189dir_is_empty(const char *dir, apr_pool_t *pool); 190static APR_INLINE svn_error_t * 191do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status, 192 const char *msg, const char *msg_no_name, 193 apr_pool_t *pool); 194 195/* Local wrapper of svn_path_cstring_to_utf8() that does no copying on 196 * operating systems where APR always uses utf-8 as native path format */ 197static svn_error_t * 198cstring_to_utf8(const char **path_utf8, 199 const char *path_apr, 200 apr_pool_t *pool) 201{ 202#if defined(WIN32) || defined(DARWIN) 203 *path_utf8 = path_apr; 204 return SVN_NO_ERROR; 205#else 206 return svn_path_cstring_to_utf8(path_utf8, path_apr, pool); 207#endif 208} 209 210/* Local wrapper of svn_path_cstring_from_utf8() that does no copying on 211 * operating systems where APR always uses utf-8 as native path format */ 212static svn_error_t * 213cstring_from_utf8(const char **path_apr, 214 const char *path_utf8, 215 apr_pool_t *pool) 216{ 217#if defined(WIN32) || defined(DARWIN) 218 *path_apr = path_utf8; 219 return SVN_NO_ERROR; 220#else 221 return svn_path_cstring_from_utf8(path_apr, path_utf8, pool); 222#endif 223} 224 225/* Helper function that allows to convert an APR-level PATH to something 226 * that we can pass the svn_error_wrap_apr. Since we use it in context 227 * of error reporting, having *some* path info may be more useful than 228 * having none. Therefore, we use a best effort approach here. 229 * 230 * This is different from svn_io_file_name_get in that it uses a different 231 * signature style and will never fail. 232 */ 233static const char * 234try_utf8_from_internal_style(const char *path, apr_pool_t *pool) 235{ 236 svn_error_t *error; 237 const char *path_utf8; 238 239 /* Special case. */ 240 if (path == NULL) 241 return "(NULL)"; 242 243 /* (try to) convert PATH to UTF-8. If that fails, continue with the plain 244 * PATH because it is the best we have. It may actually be UTF-8 already. 245 */ 246 error = cstring_to_utf8(&path_utf8, path, pool); 247 if (error) 248 { 249 /* fallback to best representation we have */ 250 251 svn_error_clear(error); 252 path_utf8 = path; 253 } 254 255 /* Toggle (back-)slashes etc. as necessary. 256 */ 257 return svn_dirent_local_style(path_utf8, pool); 258} 259 260 261/* Set *NAME_P to the UTF-8 representation of directory entry NAME. 262 * NAME is in the internal encoding used by APR; PARENT is in 263 * UTF-8 and in internal (not local) style. 264 * 265 * Use PARENT only for generating an error string if the conversion 266 * fails because NAME could not be represented in UTF-8. In that 267 * case, return a two-level error in which the outer error's message 268 * mentions PARENT, but the inner error's message does not mention 269 * NAME (except possibly in hex) since NAME may not be printable. 270 * Such a compound error at least allows the user to go looking in the 271 * right directory for the problem. 272 * 273 * If there is any other error, just return that error directly. 274 * 275 * If there is any error, the effect on *NAME_P is undefined. 276 * 277 * *NAME_P and NAME may refer to the same storage. 278 */ 279static svn_error_t * 280entry_name_to_utf8(const char **name_p, 281 const char *name, 282 const char *parent, 283 apr_pool_t *pool) 284{ 285#if defined(WIN32) || defined(DARWIN) 286 *name_p = apr_pstrdup(pool, name); 287 return SVN_NO_ERROR; 288#else 289 svn_error_t *err = svn_path_cstring_to_utf8(name_p, name, pool); 290 if (err && err->apr_err == APR_EINVAL) 291 { 292 return svn_error_createf(err->apr_err, err, 293 _("Error converting entry " 294 "in directory '%s' to UTF-8"), 295 svn_dirent_local_style(parent, pool)); 296 } 297 return err; 298#endif 299} 300 301 302 303static void 304map_apr_finfo_to_node_kind(svn_node_kind_t *kind, 305 svn_boolean_t *is_special, 306 apr_finfo_t *finfo) 307{ 308 *is_special = FALSE; 309 310 if (finfo->filetype == APR_REG) 311 *kind = svn_node_file; 312 else if (finfo->filetype == APR_DIR) 313 *kind = svn_node_dir; 314 else if (finfo->filetype == APR_LNK) 315 { 316 *is_special = TRUE; 317 *kind = svn_node_file; 318 } 319 else 320 *kind = svn_node_unknown; 321} 322 323/* Helper for svn_io_check_path() and svn_io_check_resolved_path(); 324 essentially the same semantics as those two, with the obvious 325 interpretation for RESOLVE_SYMLINKS. */ 326static svn_error_t * 327io_check_path(const char *path, 328 svn_boolean_t resolve_symlinks, 329 svn_boolean_t *is_special_p, 330 svn_node_kind_t *kind, 331 apr_pool_t *pool) 332{ 333 apr_int32_t flags; 334 apr_finfo_t finfo; 335 apr_status_t apr_err; 336 const char *path_apr; 337 svn_boolean_t is_special = FALSE; 338 339 if (path[0] == '\0') 340 path = "."; 341 342 /* Not using svn_io_stat() here because we want to check the 343 apr_err return explicitly. */ 344 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 345 346 flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK); 347 apr_err = apr_stat(&finfo, path_apr, flags, pool); 348 349 if (APR_STATUS_IS_ENOENT(apr_err)) 350 *kind = svn_node_none; 351 else if (SVN__APR_STATUS_IS_ENOTDIR(apr_err)) 352 *kind = svn_node_none; 353 else if (apr_err) 354 return svn_error_wrap_apr(apr_err, _("Can't check path '%s'"), 355 svn_dirent_local_style(path, pool)); 356 else 357 map_apr_finfo_to_node_kind(kind, &is_special, &finfo); 358 359 *is_special_p = is_special; 360 361 return SVN_NO_ERROR; 362} 363 364 365/* Wrapper for apr_file_open(), taking an APR-encoded filename. */ 366static apr_status_t 367file_open(apr_file_t **f, 368 const char *fname_apr, 369 apr_int32_t flag, 370 apr_fileperms_t perm, 371 svn_boolean_t retry_on_failure, 372 apr_pool_t *pool) 373{ 374 apr_status_t status = apr_file_open(f, fname_apr, flag, perm, pool); 375 376 if (retry_on_failure) 377 { 378#ifdef WIN32 379 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)) 380 { 381 if ((flag & (APR_CREATE | APR_EXCL)) == (APR_CREATE | APR_EXCL)) 382 return status; /* Can't create if there is something */ 383 384 if (flag & (APR_WRITE | APR_CREATE)) 385 { 386 apr_finfo_t finfo; 387 388 if (!apr_stat(&finfo, fname_apr, SVN__APR_FINFO_READONLY, pool)) 389 { 390 if (finfo.protection & APR_FREADONLY) 391 return status; /* Retrying won't fix this */ 392 } 393 } 394 } 395#endif 396 397 WIN32_RETRY_LOOP(status, apr_file_open(f, fname_apr, flag, perm, pool)); 398 } 399 return status; 400} 401 402 403svn_error_t * 404svn_io_check_resolved_path(const char *path, 405 svn_node_kind_t *kind, 406 apr_pool_t *pool) 407{ 408 svn_boolean_t ignored; 409 return io_check_path(path, TRUE, &ignored, kind, pool); 410} 411 412svn_error_t * 413svn_io_check_path(const char *path, 414 svn_node_kind_t *kind, 415 apr_pool_t *pool) 416{ 417 svn_boolean_t ignored; 418 return io_check_path(path, FALSE, &ignored, kind, pool); 419} 420 421svn_error_t * 422svn_io_check_special_path(const char *path, 423 svn_node_kind_t *kind, 424 svn_boolean_t *is_special, 425 apr_pool_t *pool) 426{ 427 return io_check_path(path, FALSE, is_special, kind, pool); 428} 429 430struct temp_file_cleanup_s 431{ 432 apr_pool_t *pool; 433 /* The (APR-encoded) full path of the file to be removed, or NULL if 434 * nothing to do. */ 435 const char *fname_apr; 436}; 437 438 439static apr_status_t 440temp_file_plain_cleanup_handler(void *baton) 441{ 442 struct temp_file_cleanup_s *b = baton; 443 apr_status_t apr_err = APR_SUCCESS; 444 445 if (b->fname_apr) 446 { 447 apr_err = apr_file_remove(b->fname_apr, b->pool); 448 WIN32_RETRY_LOOP(apr_err, apr_file_remove(b->fname_apr, b->pool)); 449 } 450 451 return apr_err; 452} 453 454 455static apr_status_t 456temp_file_child_cleanup_handler(void *baton) 457{ 458 struct temp_file_cleanup_s *b = baton; 459 460 apr_pool_cleanup_kill(b->pool, b, 461 temp_file_plain_cleanup_handler); 462 463 return APR_SUCCESS; 464} 465 466 467svn_error_t * 468svn_io_open_uniquely_named(apr_file_t **file, 469 const char **unique_path, 470 const char *dirpath, 471 const char *filename, 472 const char *suffix, 473 svn_io_file_del_t delete_when, 474 apr_pool_t *result_pool, 475 apr_pool_t *scratch_pool) 476{ 477 const char *path; 478 unsigned int i; 479 struct temp_file_cleanup_s *baton = NULL; 480 481 /* At the beginning, we don't know whether unique_path will need 482 UTF8 conversion */ 483 svn_boolean_t needs_utf8_conversion = TRUE; 484 485 SVN_ERR_ASSERT(file || unique_path); 486 487 if (dirpath == NULL) 488 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool)); 489 if (filename == NULL) 490 filename = "tempfile"; 491 if (suffix == NULL) 492 suffix = ".tmp"; 493 494 path = svn_dirent_join(dirpath, filename, scratch_pool); 495 496 if (delete_when == svn_io_file_del_on_pool_cleanup) 497 { 498 baton = apr_palloc(result_pool, sizeof(*baton)); 499 500 baton->pool = result_pool; 501 baton->fname_apr = NULL; 502 503 /* Because cleanups are run LIFO, we need to make sure to register 504 our cleanup before the apr_file_close cleanup: 505 506 On Windows, you can't remove an open file. 507 */ 508 apr_pool_cleanup_register(result_pool, baton, 509 temp_file_plain_cleanup_handler, 510 temp_file_child_cleanup_handler); 511 } 512 513 for (i = 1; i <= 99999; i++) 514 { 515 const char *unique_name; 516 const char *unique_name_apr; 517 apr_file_t *try_file; 518 apr_status_t apr_err; 519 apr_int32_t flag = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL 520 | APR_BUFFERED | APR_BINARY); 521 522 if (delete_when == svn_io_file_del_on_close) 523 flag |= APR_DELONCLOSE; 524 525 /* Special case the first attempt -- if we can avoid having a 526 generated numeric portion at all, that's best. So first we 527 try with just the suffix; then future tries add a number 528 before the suffix. (A do-while loop could avoid the repeated 529 conditional, but it's not worth the clarity loss.) 530 531 If the first attempt fails, the first number will be "2". 532 This is good, since "1" would misleadingly imply that 533 the second attempt was actually the first... and if someone's 534 got conflicts on their conflicts, we probably don't want to 535 add to their confusion :-). */ 536 if (i == 1) 537 unique_name = apr_psprintf(scratch_pool, "%s%s", path, suffix); 538 else 539 unique_name = apr_psprintf(scratch_pool, "%s.%u%s", path, i, suffix); 540 541 /* Hmmm. Ideally, we would append to a native-encoding buf 542 before starting iteration, then convert back to UTF-8 for 543 return. But I suppose that would make the appending code 544 sensitive to i18n in a way it shouldn't be... Oh well. */ 545 if (needs_utf8_conversion) 546 { 547 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, 548 scratch_pool)); 549 if (i == 1) 550 { 551 /* The variable parts of unique_name will not require UTF8 552 conversion. Therefore, if UTF8 conversion had no effect 553 on it in the first iteration, it won't require conversion 554 in any future iteration. */ 555 needs_utf8_conversion = strcmp(unique_name_apr, unique_name); 556 } 557 } 558 else 559 unique_name_apr = unique_name; 560 561 apr_err = file_open(&try_file, unique_name_apr, flag, 562 APR_OS_DEFAULT, FALSE, result_pool); 563 564 if (APR_STATUS_IS_EEXIST(apr_err)) 565 continue; 566 else if (apr_err) 567 { 568 /* On Win32, CreateFile fails with an "Access Denied" error 569 code, rather than "File Already Exists", if the colliding 570 name belongs to a directory. */ 571 if (APR_STATUS_IS_EACCES(apr_err)) 572 { 573 apr_finfo_t finfo; 574 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr, 575 APR_FINFO_TYPE, scratch_pool); 576 577 if (!apr_err_2 && finfo.filetype == APR_DIR) 578 continue; 579 580#ifdef WIN32 581 apr_err_2 = APR_TO_OS_ERROR(apr_err); 582 583 if (apr_err_2 == ERROR_ACCESS_DENIED || 584 apr_err_2 == ERROR_SHARING_VIOLATION) 585 { 586 /* The file is in use by another process or is hidden; 587 create a new name, but don't do this 99999 times in 588 case the folder is not writable */ 589 i += 797; 590 continue; 591 } 592#endif 593 594 /* Else fall through and return the original error. */ 595 } 596 597 if (file) 598 *file = NULL; 599 if (unique_path) 600 *unique_path = NULL; 601 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"), 602 svn_dirent_local_style(unique_name, 603 scratch_pool)); 604 } 605 else 606 { 607 if (delete_when == svn_io_file_del_on_pool_cleanup) 608 baton->fname_apr = apr_pstrdup(result_pool, unique_name_apr); 609 610 if (file) 611 *file = try_file; 612 else 613 apr_file_close(try_file); 614 if (unique_path) 615 *unique_path = apr_pstrdup(result_pool, unique_name); 616 617 return SVN_NO_ERROR; 618 } 619 } 620 621 if (file) 622 *file = NULL; 623 if (unique_path) 624 *unique_path = NULL; 625 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED, 626 NULL, 627 _("Unable to make name for '%s'"), 628 svn_dirent_local_style(path, scratch_pool)); 629} 630 631svn_error_t * 632svn_io_create_unique_link(const char **unique_name_p, 633 const char *path, 634 const char *dest, 635 const char *suffix, 636 apr_pool_t *pool) 637{ 638#ifdef HAVE_SYMLINK 639 unsigned int i; 640 const char *unique_name; 641 const char *unique_name_apr; 642 const char *dest_apr; 643 int rv; 644 645 SVN_ERR(cstring_from_utf8(&dest_apr, dest, pool)); 646 for (i = 1; i <= 99999; i++) 647 { 648 apr_status_t apr_err; 649 650 /* Special case the first attempt -- if we can avoid having a 651 generated numeric portion at all, that's best. So first we 652 try with just the suffix; then future tries add a number 653 before the suffix. (A do-while loop could avoid the repeated 654 conditional, but it's not worth the clarity loss.) 655 656 If the first attempt fails, the first number will be "2". 657 This is good, since "1" would misleadingly imply that 658 the second attempt was actually the first... and if someone's 659 got conflicts on their conflicts, we probably don't want to 660 add to their confusion :-). */ 661 if (i == 1) 662 unique_name = apr_psprintf(pool, "%s%s", path, suffix); 663 else 664 unique_name = apr_psprintf(pool, "%s.%u%s", path, i, suffix); 665 666 /* Hmmm. Ideally, we would append to a native-encoding buf 667 before starting iteration, then convert back to UTF-8 for 668 return. But I suppose that would make the appending code 669 sensitive to i18n in a way it shouldn't be... Oh well. */ 670 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, pool)); 671 do { 672 rv = symlink(dest_apr, unique_name_apr); 673 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error())); 674 675 apr_err = apr_get_os_error(); 676 677 if (rv == -1 && APR_STATUS_IS_EEXIST(apr_err)) 678 continue; 679 else if (rv == -1 && apr_err) 680 { 681 /* On Win32, CreateFile fails with an "Access Denied" error 682 code, rather than "File Already Exists", if the colliding 683 name belongs to a directory. */ 684 if (APR_STATUS_IS_EACCES(apr_err)) 685 { 686 apr_finfo_t finfo; 687 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr, 688 APR_FINFO_TYPE, pool); 689 690 if (!apr_err_2 691 && (finfo.filetype == APR_DIR)) 692 continue; 693 694 /* Else ignore apr_err_2; better to fall through and 695 return the original error. */ 696 } 697 698 *unique_name_p = NULL; 699 return svn_error_wrap_apr(apr_err, 700 _("Can't create symbolic link '%s'"), 701 svn_dirent_local_style(unique_name, pool)); 702 } 703 else 704 { 705 *unique_name_p = unique_name; 706 return SVN_NO_ERROR; 707 } 708 } 709 710 *unique_name_p = NULL; 711 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED, 712 NULL, 713 _("Unable to make name for '%s'"), 714 svn_dirent_local_style(path, pool)); 715#else 716 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 717 _("Symbolic links are not supported on this " 718 "platform")); 719#endif 720} 721 722svn_error_t * 723svn_io_read_link(svn_string_t **dest, 724 const char *path, 725 apr_pool_t *pool) 726{ 727#if defined(HAVE_READLINK) 728 svn_string_t dest_apr; 729 const char *path_apr; 730 char buf[1025]; 731 ssize_t rv; 732 733 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 734 do { 735 rv = readlink(path_apr, buf, sizeof(buf) - 1); 736 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error())); 737 738 if (rv == -1) 739 return svn_error_wrap_apr(apr_get_os_error(), 740 _("Can't read contents of link")); 741 742 buf[rv] = '\0'; 743 dest_apr.data = buf; 744 dest_apr.len = rv; 745 746 /* ### Cast needed, one of these interfaces is wrong */ 747 return svn_utf_string_to_utf8((const svn_string_t **)dest, &dest_apr, pool); 748#elif defined(WIN32) 749 return io_win_read_link(dest, path, pool); 750#else 751 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 752 _("Symbolic links are not supported on this " 753 "platform")); 754#endif 755} 756 757 758svn_error_t * 759svn_io_copy_link(const char *src, 760 const char *dst, 761 apr_pool_t *pool) 762 763{ 764#ifdef HAVE_READLINK 765 svn_string_t *link_dest; 766 const char *dst_tmp; 767 768 /* Notice what the link is pointing at... */ 769 SVN_ERR(svn_io_read_link(&link_dest, src, pool)); 770 771 /* Make a tmp-link pointing at the same thing. */ 772 SVN_ERR(svn_io_create_unique_link(&dst_tmp, dst, link_dest->data, 773 ".tmp", pool)); 774 775 /* Move the tmp-link to link. */ 776 return svn_io_file_rename(dst_tmp, dst, pool); 777 778#else 779 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 780 _("Symbolic links are not supported on this " 781 "platform")); 782#endif 783} 784 785/* Temporary directory name cache for svn_io_temp_dir() */ 786static volatile svn_atomic_t temp_dir_init_state = 0; 787static const char *temp_dir; 788 789/* Helper function to initialize temp dir. Passed to svn_atomic__init_once */ 790static svn_error_t * 791init_temp_dir(void *baton, apr_pool_t *scratch_pool) 792{ 793 /* Global pool for the temp path */ 794 apr_pool_t *global_pool = svn_pool_create(NULL); 795 const char *dir; 796 797 apr_status_t apr_err = apr_temp_dir_get(&dir, scratch_pool); 798 799 if (apr_err) 800 return svn_error_wrap_apr(apr_err, _("Can't find a temporary directory")); 801 802 SVN_ERR(cstring_to_utf8(&dir, dir, scratch_pool)); 803 804 dir = svn_dirent_internal_style(dir, scratch_pool); 805 806 SVN_ERR(svn_dirent_get_absolute(&temp_dir, dir, global_pool)); 807 808 return SVN_NO_ERROR; 809} 810 811 812svn_error_t * 813svn_io_temp_dir(const char **dir, 814 apr_pool_t *pool) 815{ 816 SVN_ERR(svn_atomic__init_once(&temp_dir_init_state, 817 init_temp_dir, NULL, pool)); 818 819 *dir = apr_pstrdup(pool, temp_dir); 820 821 return SVN_NO_ERROR; 822} 823 824 825 826 827/*** Creating, copying and appending files. ***/ 828 829/* Transfer the contents of FROM_FILE to TO_FILE, using POOL for temporary 830 * allocations. 831 * 832 * NOTE: We don't use apr_copy_file() for this, since it takes filenames 833 * as parameters. Since we want to copy to a temporary file 834 * and rename for atomicity (see below), this would require an extra 835 * close/open pair, which can be expensive, especially on 836 * remote file systems. 837 */ 838static apr_status_t 839copy_contents(apr_file_t *from_file, 840 apr_file_t *to_file, 841 apr_pool_t *pool) 842{ 843 /* Copy bytes till the cows come home. */ 844 while (1) 845 { 846 char buf[SVN__STREAM_CHUNK_SIZE]; 847 apr_size_t bytes_this_time = sizeof(buf); 848 apr_status_t read_err; 849 apr_status_t write_err; 850 851 /* Read 'em. */ 852 read_err = apr_file_read(from_file, buf, &bytes_this_time); 853 if (read_err && !APR_STATUS_IS_EOF(read_err)) 854 { 855 return read_err; 856 } 857 858 /* Write 'em. */ 859 write_err = apr_file_write_full(to_file, buf, bytes_this_time, NULL); 860 if (write_err) 861 { 862 return write_err; 863 } 864 865 if (read_err && APR_STATUS_IS_EOF(read_err)) 866 { 867 /* Return the results of this close: an error, or success. */ 868 return APR_SUCCESS; 869 } 870 } 871 /* NOTREACHED */ 872} 873 874 875svn_error_t * 876svn_io_copy_file(const char *src, 877 const char *dst, 878 svn_boolean_t copy_perms, 879 apr_pool_t *pool) 880{ 881 apr_file_t *from_file, *to_file; 882 apr_status_t apr_err; 883 const char *dst_tmp; 884 svn_error_t *err; 885 886 /* ### NOTE: sometimes src == dst. In this case, because we copy to a 887 ### temporary file, and then rename over the top of the destination, 888 ### the net result is resetting the permissions on src/dst. 889 ### 890 ### Note: specifically, this can happen during a switch when the desired 891 ### permissions for a file change from one branch to another. See 892 ### switch_tests 17. 893 ### 894 ### ... yes, we should avoid copying to the same file, and we should 895 ### make the "reset perms" explicit. The switch *happens* to work 896 ### because of this copy-to-temp-then-rename implementation. If it 897 ### weren't for that, the switch would break. 898 */ 899#ifdef CHECK_FOR_SAME_FILE 900 if (strcmp(src, dst) == 0) 901 return SVN_NO_ERROR; 902#endif 903 904 SVN_ERR(svn_io_file_open(&from_file, src, APR_READ, 905 APR_OS_DEFAULT, pool)); 906 907 /* For atomicity, we copy to a tmp file and then rename the tmp 908 file over the real destination. */ 909 910 SVN_ERR(svn_io_open_unique_file3(&to_file, &dst_tmp, 911 svn_dirent_dirname(dst, pool), 912 svn_io_file_del_none, pool, pool)); 913 914 apr_err = copy_contents(from_file, to_file, pool); 915 916 if (apr_err) 917 { 918 err = svn_error_wrap_apr(apr_err, _("Can't copy '%s' to '%s'"), 919 svn_dirent_local_style(src, pool), 920 svn_dirent_local_style(dst_tmp, pool)); 921 } 922 else 923 err = NULL; 924 925 err = svn_error_compose_create(err, 926 svn_io_file_close(from_file, pool)); 927 928 err = svn_error_compose_create(err, 929 svn_io_file_close(to_file, pool)); 930 931 if (err) 932 { 933 return svn_error_compose_create( 934 err, 935 svn_io_remove_file2(dst_tmp, TRUE, pool)); 936 } 937 938 /* If copying perms, set the perms on dst_tmp now, so they will be 939 atomically inherited in the upcoming rename. But note that we 940 had to wait until now to set perms, because if they say 941 read-only, then we'd have failed filling dst_tmp's contents. */ 942 if (copy_perms) 943 SVN_ERR(svn_io_copy_perms(src, dst_tmp, pool)); 944 945 return svn_error_trace(svn_io_file_rename(dst_tmp, dst, pool)); 946} 947 948#if !defined(WIN32) && !defined(__OS2__) 949/* Wrapper for apr_file_perms_set(), taking a UTF8-encoded filename. */ 950static svn_error_t * 951file_perms_set(const char *fname, apr_fileperms_t perms, 952 apr_pool_t *pool) 953{ 954 const char *fname_apr; 955 apr_status_t status; 956 957 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool)); 958 959 status = apr_file_perms_set(fname_apr, perms); 960 if (status) 961 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"), 962 fname); 963 else 964 return SVN_NO_ERROR; 965} 966 967/* Set permissions PERMS on the FILE. This is a cheaper variant of the 968 * file_perms_set wrapper() function because no locale-dependent string 969 * conversion is required. POOL will be used for allocations. 970 */ 971static svn_error_t * 972file_perms_set2(apr_file_t* file, apr_fileperms_t perms, apr_pool_t *pool) 973{ 974 const char *fname_apr; 975 apr_status_t status; 976 977 status = apr_file_name_get(&fname_apr, file); 978 if (status) 979 return svn_error_wrap_apr(status, _("Can't get file name")); 980 981 status = apr_file_perms_set(fname_apr, perms); 982 if (status) 983 return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"), 984 try_utf8_from_internal_style(fname_apr, pool)); 985 else 986 return SVN_NO_ERROR; 987} 988 989#endif /* !WIN32 && !__OS2__ */ 990 991svn_error_t * 992svn_io_copy_perms(const char *src, 993 const char *dst, 994 apr_pool_t *pool) 995{ 996 /* ### On Windows or OS/2, apr_file_perms_set always returns APR_ENOTIMPL, 997 and the path passed to apr_file_perms_set must be encoded 998 in the platform-specific path encoding; not necessary UTF-8. 999 We need a platform-specific implementation to get the 1000 permissions right. */ 1001 1002#if !defined(WIN32) && !defined(__OS2__) 1003 { 1004 apr_finfo_t finfo; 1005 svn_node_kind_t kind; 1006 svn_boolean_t is_special; 1007 svn_error_t *err; 1008 1009 /* If DST is a symlink, don't bother copying permissions. */ 1010 SVN_ERR(svn_io_check_special_path(dst, &kind, &is_special, pool)); 1011 if (is_special) 1012 return SVN_NO_ERROR; 1013 1014 SVN_ERR(svn_io_stat(&finfo, src, APR_FINFO_PROT, pool)); 1015 err = file_perms_set(dst, finfo.protection, pool); 1016 if (err) 1017 { 1018 /* We shouldn't be able to get APR_INCOMPLETE or APR_ENOTIMPL 1019 here under normal circumstances, because the perms themselves 1020 came from a call to apr_file_info_get(), and we already know 1021 this is the non-Win32 case. But if it does happen, it's not 1022 an error. */ 1023 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) || 1024 APR_STATUS_IS_ENOTIMPL(err->apr_err)) 1025 svn_error_clear(err); 1026 else 1027 { 1028 return svn_error_quick_wrapf( 1029 err, _("Can't set permissions on '%s'"), 1030 svn_dirent_local_style(dst, pool)); 1031 } 1032 } 1033 } 1034#endif /* !WIN32 && !__OS2__ */ 1035 1036 return SVN_NO_ERROR; 1037} 1038 1039 1040svn_error_t * 1041svn_io_append_file(const char *src, const char *dst, apr_pool_t *pool) 1042{ 1043 apr_status_t apr_err; 1044 const char *src_apr, *dst_apr; 1045 1046 SVN_ERR(cstring_from_utf8(&src_apr, src, pool)); 1047 SVN_ERR(cstring_from_utf8(&dst_apr, dst, pool)); 1048 1049 apr_err = apr_file_append(src_apr, dst_apr, APR_OS_DEFAULT, pool); 1050 1051 if (apr_err) 1052 return svn_error_wrap_apr(apr_err, _("Can't append '%s' to '%s'"), 1053 svn_dirent_local_style(src, pool), 1054 svn_dirent_local_style(dst, pool)); 1055 1056 return SVN_NO_ERROR; 1057} 1058 1059 1060svn_error_t *svn_io_copy_dir_recursively(const char *src, 1061 const char *dst_parent, 1062 const char *dst_basename, 1063 svn_boolean_t copy_perms, 1064 svn_cancel_func_t cancel_func, 1065 void *cancel_baton, 1066 apr_pool_t *pool) 1067{ 1068 svn_node_kind_t kind; 1069 apr_status_t status; 1070 const char *dst_path; 1071 apr_dir_t *this_dir; 1072 apr_finfo_t this_entry; 1073 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME; 1074 1075 /* Make a subpool for recursion */ 1076 apr_pool_t *subpool = svn_pool_create(pool); 1077 1078 /* The 'dst_path' is simply dst_parent/dst_basename */ 1079 dst_path = svn_dirent_join(dst_parent, dst_basename, pool); 1080 1081 /* Sanity checks: SRC and DST_PARENT are directories, and 1082 DST_BASENAME doesn't already exist in DST_PARENT. */ 1083 SVN_ERR(svn_io_check_path(src, &kind, subpool)); 1084 if (kind != svn_node_dir) 1085 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 1086 _("Source '%s' is not a directory"), 1087 svn_dirent_local_style(src, pool)); 1088 1089 SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool)); 1090 if (kind != svn_node_dir) 1091 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 1092 _("Destination '%s' is not a directory"), 1093 svn_dirent_local_style(dst_parent, pool)); 1094 1095 SVN_ERR(svn_io_check_path(dst_path, &kind, subpool)); 1096 if (kind != svn_node_none) 1097 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 1098 _("Destination '%s' already exists"), 1099 svn_dirent_local_style(dst_path, pool)); 1100 1101 /* Create the new directory. */ 1102 /* ### TODO: copy permissions (needs apr_file_attrs_get()) */ 1103 SVN_ERR(svn_io_dir_make(dst_path, APR_OS_DEFAULT, pool)); 1104 1105 /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */ 1106 SVN_ERR(svn_io_dir_open(&this_dir, src, subpool)); 1107 1108 for (status = apr_dir_read(&this_entry, flags, this_dir); 1109 status == APR_SUCCESS; 1110 status = apr_dir_read(&this_entry, flags, this_dir)) 1111 { 1112 if ((this_entry.name[0] == '.') 1113 && ((this_entry.name[1] == '\0') 1114 || ((this_entry.name[1] == '.') 1115 && (this_entry.name[2] == '\0')))) 1116 { 1117 continue; 1118 } 1119 else 1120 { 1121 const char *src_target, *entryname_utf8; 1122 1123 if (cancel_func) 1124 SVN_ERR(cancel_func(cancel_baton)); 1125 1126 SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name, 1127 src, subpool)); 1128 src_target = svn_dirent_join(src, entryname_utf8, subpool); 1129 1130 if (this_entry.filetype == APR_REG) /* regular file */ 1131 { 1132 const char *dst_target = svn_dirent_join(dst_path, 1133 entryname_utf8, 1134 subpool); 1135 SVN_ERR(svn_io_copy_file(src_target, dst_target, 1136 copy_perms, subpool)); 1137 } 1138 else if (this_entry.filetype == APR_LNK) /* symlink */ 1139 { 1140 const char *dst_target = svn_dirent_join(dst_path, 1141 entryname_utf8, 1142 subpool); 1143 SVN_ERR(svn_io_copy_link(src_target, dst_target, 1144 subpool)); 1145 } 1146 else if (this_entry.filetype == APR_DIR) /* recurse */ 1147 { 1148 /* Prevent infinite recursion by filtering off our 1149 newly created destination path. */ 1150 if (strcmp(src, dst_parent) == 0 1151 && strcmp(entryname_utf8, dst_basename) == 0) 1152 continue; 1153 1154 SVN_ERR(svn_io_copy_dir_recursively 1155 (src_target, 1156 dst_path, 1157 entryname_utf8, 1158 copy_perms, 1159 cancel_func, 1160 cancel_baton, 1161 subpool)); 1162 } 1163 /* ### support other APR node types someday?? */ 1164 1165 } 1166 } 1167 1168 if (! (APR_STATUS_IS_ENOENT(status))) 1169 return svn_error_wrap_apr(status, _("Can't read directory '%s'"), 1170 svn_dirent_local_style(src, pool)); 1171 1172 status = apr_dir_close(this_dir); 1173 if (status) 1174 return svn_error_wrap_apr(status, _("Error closing directory '%s'"), 1175 svn_dirent_local_style(src, pool)); 1176 1177 /* Free any memory used by recursion */ 1178 svn_pool_destroy(subpool); 1179 1180 return SVN_NO_ERROR; 1181} 1182 1183 1184svn_error_t * 1185svn_io_make_dir_recursively(const char *path, apr_pool_t *pool) 1186{ 1187 const char *path_apr; 1188 apr_status_t apr_err; 1189 1190 if (svn_path_is_empty(path)) 1191 /* Empty path (current dir) is assumed to always exist, 1192 so we do nothing, per docs. */ 1193 return SVN_NO_ERROR; 1194 1195 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 1196 1197 apr_err = apr_dir_make_recursive(path_apr, APR_OS_DEFAULT, pool); 1198#ifdef WIN32 1199 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a 1200 permanent error */ 1201 if (apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) 1202 WIN32_RETRY_LOOP(apr_err, apr_dir_make_recursive(path_apr, 1203 APR_OS_DEFAULT, pool)); 1204#endif 1205 1206 if (apr_err) 1207 return svn_error_wrap_apr(apr_err, _("Can't make directory '%s'"), 1208 svn_dirent_local_style(path, pool)); 1209 1210 return SVN_NO_ERROR; 1211} 1212 1213svn_error_t * 1214svn_io_file_create_bytes(const char *file, 1215 const void *contents, 1216 apr_size_t length, 1217 apr_pool_t *scratch_pool) 1218{ 1219 apr_file_t *f; 1220 apr_size_t written; 1221 svn_error_t *err = SVN_NO_ERROR; 1222 1223 SVN_ERR(svn_io_file_open(&f, file, 1224 (APR_WRITE | APR_CREATE | APR_EXCL), 1225 APR_OS_DEFAULT, 1226 scratch_pool)); 1227 if (length) 1228 err = svn_io_file_write_full(f, contents, length, &written, 1229 scratch_pool); 1230 1231 err = svn_error_compose_create( 1232 err, 1233 svn_io_file_close(f, scratch_pool)); 1234 1235 if (err) 1236 { 1237 /* Our caller doesn't know if we left a file or not if we return 1238 an error. Better to cleanup after ourselves if we created the 1239 file. */ 1240 return svn_error_trace( 1241 svn_error_compose_create( 1242 err, 1243 svn_io_remove_file2(file, TRUE, scratch_pool))); 1244 } 1245 1246 return SVN_NO_ERROR; 1247} 1248 1249svn_error_t * 1250svn_io_file_create(const char *file, 1251 const char *contents, 1252 apr_pool_t *pool) 1253{ 1254 return svn_error_trace(svn_io_file_create_bytes(file, contents, 1255 contents ? strlen(contents) 1256 : 0, 1257 pool)); 1258} 1259 1260svn_error_t * 1261svn_io_file_create_empty(const char *file, 1262 apr_pool_t *scratch_pool) 1263{ 1264 return svn_error_trace(svn_io_file_create_bytes(file, NULL, 0, 1265 scratch_pool)); 1266} 1267 1268svn_error_t * 1269svn_io_dir_file_copy(const char *src_path, 1270 const char *dest_path, 1271 const char *file, 1272 apr_pool_t *pool) 1273{ 1274 const char *file_dest_path = svn_dirent_join(dest_path, file, pool); 1275 const char *file_src_path = svn_dirent_join(src_path, file, pool); 1276 1277 return svn_error_trace( 1278 svn_io_copy_file(file_src_path, file_dest_path, TRUE, pool)); 1279} 1280 1281 1282/*** Modtime checking. ***/ 1283 1284svn_error_t * 1285svn_io_file_affected_time(apr_time_t *apr_time, 1286 const char *path, 1287 apr_pool_t *pool) 1288{ 1289 apr_finfo_t finfo; 1290 1291 SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool)); 1292 1293 *apr_time = finfo.mtime; 1294 1295 return SVN_NO_ERROR; 1296} 1297 1298 1299svn_error_t * 1300svn_io_set_file_affected_time(apr_time_t apr_time, 1301 const char *path, 1302 apr_pool_t *pool) 1303{ 1304 apr_status_t status; 1305 const char *native_path; 1306 1307 SVN_ERR(cstring_from_utf8(&native_path, path, pool)); 1308 status = apr_file_mtime_set(native_path, apr_time, pool); 1309 1310 if (status) 1311 return svn_error_wrap_apr(status, _("Can't set access time of '%s'"), 1312 svn_dirent_local_style(path, pool)); 1313 1314 return SVN_NO_ERROR; 1315} 1316 1317 1318void 1319svn_io_sleep_for_timestamps(const char *path, apr_pool_t *pool) 1320{ 1321 apr_time_t now, then; 1322 svn_error_t *err; 1323 char *sleep_env_var; 1324 1325 sleep_env_var = getenv(SVN_SLEEP_ENV_VAR); 1326 1327 if (sleep_env_var && apr_strnatcasecmp(sleep_env_var, "yes") == 0) 1328 return; /* Allow skipping for testing */ 1329 1330 now = apr_time_now(); 1331 1332 /* Calculate 0.02 seconds after the next second wallclock tick. */ 1333 then = apr_time_make(apr_time_sec(now) + 1, APR_USEC_PER_SEC / 50); 1334 1335 /* Worst case is waiting one second, so we can use that time to determine 1336 if we can sleep shorter than that */ 1337 if (path) 1338 { 1339 apr_finfo_t finfo; 1340 1341 err = svn_io_stat(&finfo, path, APR_FINFO_MTIME | APR_FINFO_LINK, pool); 1342 1343 if (err) 1344 { 1345 svn_error_clear(err); /* Fall back on original behavior */ 1346 } 1347 else if (finfo.mtime % APR_USEC_PER_SEC) 1348 { 1349 /* Very simplistic but safe approach: 1350 If the filesystem has < sec mtime we can be reasonably sure 1351 that the filesystem has some sub-second resolution. On Windows 1352 it is likely to be sub-millisecond; on Linux systems it depends 1353 on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution. 1354 1355 ## Perhaps find a better algorithm here. This will fail once 1356 in every 1000 cases on a millisecond precision filesystem 1357 if the mtime happens to be an exact second. 1358 1359 But better to fail once in every thousand cases than every 1360 time, like we did before. 1361 1362 Note for further research on algorithm: 1363 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime. 1364 1365 Linux/ext4 with CONFIG_HZ=250 has high resolution 1366 apr_time_now and although the filesystem timestamps 1367 have similar high precision they are only updated with 1368 a coarser 4ms resolution. */ 1369 1370 /* 10 milliseconds after now. */ 1371#ifndef SVN_HI_RES_SLEEP_MS 1372#define SVN_HI_RES_SLEEP_MS 10 1373#endif 1374 then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS); 1375 } 1376 1377 /* Remove time taken to do stat() from sleep. */ 1378 now = apr_time_now(); 1379 } 1380 1381 if (now >= then) 1382 return; /* Passing negative values may suspend indefinitely (Windows) */ 1383 1384 /* (t < 1000 will be round to 0 in apr) */ 1385 if (then - now < 1000) 1386 apr_sleep(1000); 1387 else 1388 apr_sleep(then - now); 1389} 1390 1391 1392svn_error_t * 1393svn_io_filesizes_different_p(svn_boolean_t *different_p, 1394 const char *file1, 1395 const char *file2, 1396 apr_pool_t *pool) 1397{ 1398 apr_finfo_t finfo1; 1399 apr_finfo_t finfo2; 1400 apr_status_t status; 1401 const char *file1_apr, *file2_apr; 1402 1403 /* Not using svn_io_stat() because don't want to generate 1404 svn_error_t objects for non-error conditions. */ 1405 1406 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool)); 1407 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool)); 1408 1409 /* Stat both files */ 1410 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool); 1411 if (status) 1412 { 1413 /* If we got an error stat'ing a file, it could be because the 1414 file was removed... or who knows. Whatever the case, we 1415 don't know if the filesizes are definitely different, so 1416 assume that they're not. */ 1417 *different_p = FALSE; 1418 return SVN_NO_ERROR; 1419 } 1420 1421 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool); 1422 if (status) 1423 { 1424 /* See previous comment. */ 1425 *different_p = FALSE; 1426 return SVN_NO_ERROR; 1427 } 1428 1429 /* Examine file sizes */ 1430 if (finfo1.size == finfo2.size) 1431 *different_p = FALSE; 1432 else 1433 *different_p = TRUE; 1434 1435 return SVN_NO_ERROR; 1436} 1437 1438 1439svn_error_t * 1440svn_io_filesizes_three_different_p(svn_boolean_t *different_p12, 1441 svn_boolean_t *different_p23, 1442 svn_boolean_t *different_p13, 1443 const char *file1, 1444 const char *file2, 1445 const char *file3, 1446 apr_pool_t *scratch_pool) 1447{ 1448 apr_finfo_t finfo1, finfo2, finfo3; 1449 apr_status_t status1, status2, status3; 1450 const char *file1_apr, *file2_apr, *file3_apr; 1451 1452 /* Not using svn_io_stat() because don't want to generate 1453 svn_error_t objects for non-error conditions. */ 1454 1455 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool)); 1456 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool)); 1457 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool)); 1458 1459 /* Stat all three files */ 1460 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool); 1461 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool); 1462 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool); 1463 1464 /* If we got an error stat'ing a file, it could be because the 1465 file was removed... or who knows. Whatever the case, we 1466 don't know if the filesizes are definitely different, so 1467 assume that they're not. */ 1468 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size; 1469 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size; 1470 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size; 1471 1472 return SVN_NO_ERROR; 1473} 1474 1475 1476svn_error_t * 1477svn_io_file_checksum2(svn_checksum_t **checksum, 1478 const char *file, 1479 svn_checksum_kind_t kind, 1480 apr_pool_t *pool) 1481{ 1482 svn_stream_t *file_stream; 1483 svn_stream_t *checksum_stream; 1484 apr_file_t* f; 1485 1486 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool)); 1487 file_stream = svn_stream_from_aprfile2(f, FALSE, pool); 1488 checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind, 1489 TRUE, pool); 1490 1491 /* Because the checksummed stream will force the reading (and 1492 checksumming) of all the file's bytes, we can just close the stream 1493 and let its magic work. */ 1494 return svn_stream_close(checksum_stream); 1495} 1496 1497 1498svn_error_t * 1499svn_io_file_checksum(unsigned char digest[], 1500 const char *file, 1501 apr_pool_t *pool) 1502{ 1503 svn_checksum_t *checksum; 1504 1505 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool)); 1506 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE); 1507 1508 return SVN_NO_ERROR; 1509} 1510 1511 1512 1513/*** Permissions and modes. ***/ 1514 1515#if !defined(WIN32) && !defined(__OS2__) 1516/* Given the file specified by PATH, attempt to create an 1517 identical version of it owned by the current user. This is done by 1518 moving it to a temporary location, copying the file back to its old 1519 path, then deleting the temporarily moved version. All temporary 1520 allocations are done in POOL. */ 1521static svn_error_t * 1522reown_file(const char *path, 1523 apr_pool_t *pool) 1524{ 1525 const char *unique_name; 1526 1527 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name, 1528 svn_dirent_dirname(path, pool), 1529 svn_io_file_del_none, pool, pool)); 1530 SVN_ERR(svn_io_file_rename(path, unique_name, pool)); 1531 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool)); 1532 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool)); 1533} 1534 1535/* Determine what the PERMS for a new file should be by looking at the 1536 permissions of a temporary file that we create. 1537 Unfortunately, umask() as defined in POSIX provides no thread-safe way 1538 to get at the current value of the umask, so what we're doing here is 1539 the only way we have to determine which combination of write bits 1540 (User/Group/World) should be set by default. 1541 Make temporary allocations in SCRATCH_POOL. */ 1542static svn_error_t * 1543get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool) 1544{ 1545 /* the default permissions as read from the temp folder */ 1546 static apr_fileperms_t default_perms = 0; 1547 1548 /* Technically, this "racy": Multiple threads may use enter here and 1549 try to figure out the default permission concurrently. That's fine 1550 since they will end up with the same results. Even more technical, 1551 apr_fileperms_t is an atomic type on 32+ bit machines. 1552 */ 1553 if (default_perms == 0) 1554 { 1555 apr_finfo_t finfo; 1556 apr_file_t *fd; 1557 const char *fname_base, *fname; 1558 apr_uint32_t randomish; 1559 svn_error_t *err; 1560 1561 /* Get the perms for a newly created file to find out what bits 1562 should be set. 1563 1564 Explicitly delete the file because we want this file to be as 1565 short-lived as possible since its presence means other 1566 processes may have to try multiple names. 1567 1568 Using svn_io_open_uniquely_named() here because other tempfile 1569 creation functions tweak the permission bits of files they create. 1570 */ 1571 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool 1572 + (apr_uint32_t)apr_time_now()); 1573 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish); 1574 1575 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base, 1576 NULL, svn_io_file_del_none, 1577 scratch_pool, scratch_pool)); 1578 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool); 1579 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool)); 1580 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE, 1581 scratch_pool)); 1582 SVN_ERR(err); 1583 *perms = finfo.protection; 1584 default_perms = finfo.protection; 1585 } 1586 else 1587 *perms = default_perms; 1588 1589 return SVN_NO_ERROR; 1590} 1591 1592/* OR together permission bits of the file FD and the default permissions 1593 of a file as determined by get_default_file_perms(). Do temporary 1594 allocations in SCRATCH_POOL. */ 1595static svn_error_t * 1596merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms, 1597 apr_pool_t *scratch_pool) 1598{ 1599 apr_finfo_t finfo; 1600 apr_fileperms_t default_perms; 1601 1602 SVN_ERR(get_default_file_perms(&default_perms, scratch_pool)); 1603 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool)); 1604 1605 /* Glom the perms together. */ 1606 *perms = default_perms | finfo.protection; 1607 return SVN_NO_ERROR; 1608} 1609 1610/* This is a helper function for the svn_io_set_file_read* functions 1611 that attempts to honor the users umask when dealing with 1612 permission changes. It is a no-op when invoked on a symlink. */ 1613static svn_error_t * 1614io_set_file_perms(const char *path, 1615 svn_boolean_t change_readwrite, 1616 svn_boolean_t enable_write, 1617 svn_boolean_t change_executable, 1618 svn_boolean_t executable, 1619 svn_boolean_t ignore_enoent, 1620 apr_pool_t *pool) 1621{ 1622 apr_status_t status; 1623 const char *path_apr; 1624 apr_finfo_t finfo; 1625 apr_fileperms_t perms_to_set; 1626 1627 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 1628 1629 /* Try to change only a minimal amount of the perms first 1630 by getting the current perms and adding bits 1631 only on where read perms are granted. If this fails 1632 fall through to just setting file attributes. */ 1633 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool); 1634 if (status) 1635 { 1636 if (ignore_enoent && (APR_STATUS_IS_ENOENT(status) 1637 || SVN__APR_STATUS_IS_ENOTDIR(status))) 1638 return SVN_NO_ERROR; 1639 else if (status != APR_ENOTIMPL) 1640 return svn_error_wrap_apr(status, 1641 _("Can't change perms of file '%s'"), 1642 svn_dirent_local_style(path, pool)); 1643 return SVN_NO_ERROR; 1644 } 1645 1646 if (finfo.filetype == APR_LNK) 1647 return SVN_NO_ERROR; 1648 1649 perms_to_set = finfo.protection; 1650 if (change_readwrite) 1651 { 1652 if (enable_write) /* Make read-write. */ 1653 { 1654 /* Tweak the owner bits only. The group/other bits aren't safe to 1655 * touch because we may end up setting them in undesired ways. */ 1656 perms_to_set |= (APR_UREAD|APR_UWRITE); 1657 } 1658 else 1659 { 1660 if (finfo.protection & APR_UREAD) 1661 perms_to_set &= ~APR_UWRITE; 1662 if (finfo.protection & APR_GREAD) 1663 perms_to_set &= ~APR_GWRITE; 1664 if (finfo.protection & APR_WREAD) 1665 perms_to_set &= ~APR_WWRITE; 1666 } 1667 } 1668 1669 if (change_executable) 1670 { 1671 if (executable) 1672 { 1673 if (finfo.protection & APR_UREAD) 1674 perms_to_set |= APR_UEXECUTE; 1675 if (finfo.protection & APR_GREAD) 1676 perms_to_set |= APR_GEXECUTE; 1677 if (finfo.protection & APR_WREAD) 1678 perms_to_set |= APR_WEXECUTE; 1679 } 1680 else 1681 { 1682 if (finfo.protection & APR_UREAD) 1683 perms_to_set &= ~APR_UEXECUTE; 1684 if (finfo.protection & APR_GREAD) 1685 perms_to_set &= ~APR_GEXECUTE; 1686 if (finfo.protection & APR_WREAD) 1687 perms_to_set &= ~APR_WEXECUTE; 1688 } 1689 } 1690 1691 /* If we aren't changing anything then just return, this saves 1692 some system calls and helps with shared working copies */ 1693 if (perms_to_set == finfo.protection) 1694 return SVN_NO_ERROR; 1695 1696 status = apr_file_perms_set(path_apr, perms_to_set); 1697 if (!status) 1698 return SVN_NO_ERROR; 1699 1700 if (APR_STATUS_IS_EPERM(status)) 1701 { 1702 /* We don't have permissions to change the 1703 permissions! Try a move, copy, and delete 1704 workaround to see if we can get the file owned by 1705 us. If these succeed, try the permissions set 1706 again. 1707 1708 Note that we only attempt this in the 1709 stat-available path. This assumes that the 1710 move-copy workaround will only be helpful on 1711 platforms that implement apr_stat. */ 1712 SVN_ERR(reown_file(path, pool)); 1713 status = apr_file_perms_set(path_apr, perms_to_set); 1714 } 1715 1716 if (!status) 1717 return SVN_NO_ERROR; 1718 1719 if (ignore_enoent && APR_STATUS_IS_ENOENT(status)) 1720 return SVN_NO_ERROR; 1721 else if (status == APR_ENOTIMPL) 1722 { 1723 /* At least try to set the attributes. */ 1724 apr_fileattrs_t attrs = 0; 1725 apr_fileattrs_t attrs_values = 0; 1726 1727 if (change_readwrite) 1728 { 1729 attrs = APR_FILE_ATTR_READONLY; 1730 if (!enable_write) 1731 attrs_values = APR_FILE_ATTR_READONLY; 1732 } 1733 if (change_executable) 1734 { 1735 attrs = APR_FILE_ATTR_EXECUTABLE; 1736 if (executable) 1737 attrs_values = APR_FILE_ATTR_EXECUTABLE; 1738 } 1739 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool); 1740 } 1741 1742 return svn_error_wrap_apr(status, 1743 _("Can't change perms of file '%s'"), 1744 svn_dirent_local_style(path, pool)); 1745} 1746#endif /* !WIN32 && !__OS2__ */ 1747 1748#ifdef WIN32 1749/* This is semantically the same as the APR utf8_to_unicode_path 1750 function, but reimplemented here because APR does not export it. */ 1751svn_error_t* 1752svn_io__utf8_to_unicode_longpath(const WCHAR **result, 1753 const char *source, 1754 apr_pool_t *result_pool) 1755{ 1756 /* This is correct, we don't twist the filename if it will 1757 * definitely be shorter than 248 characters. It merits some 1758 * performance testing to see if this has any effect, but there 1759 * seem to be applications that get confused by the resulting 1760 * Unicode \\?\ style file names, especially if they use argv[0] 1761 * or call the Win32 API functions such as GetModuleName, etc. 1762 * Not every application is prepared to handle such names. 1763 * 1764 * Note also this is shorter than MAX_PATH, as directory paths 1765 * are actually limited to 248 characters. 1766 * 1767 * Note that a utf-8 name can never result in more wide chars 1768 * than the original number of utf-8 narrow chars. 1769 */ 1770 const WCHAR *prefix = NULL; 1771 const int srclen = strlen(source); 1772 WCHAR *buffer; 1773 1774 if (srclen > 248) 1775 { 1776 if (svn_ctype_isalpha(source[0]) && source[1] == ':' 1777 && (source[2] == '/' || source[2] == '\\')) 1778 { 1779 /* This is an ordinary absolute path. */ 1780 prefix = L"\\\\?\\"; 1781 } 1782 else if ((source[0] == '/' || source[0] == '\\') 1783 && (source[1] == '/' || source[1] == '\\') 1784 && source[2] != '?') 1785 { 1786 /* This is a UNC path */ 1787 source += 2; /* Skip the leading slashes */ 1788 prefix = L"\\\\?\\UNC\\"; 1789 } 1790 } 1791 1792 SVN_ERR(svn_utf__win32_utf8_to_utf16(&(const WCHAR*)buffer, source, 1793 prefix, result_pool)); 1794 1795 /* Convert slashes to backslashes because the \\?\ path format 1796 does not allow backslashes as path separators. */ 1797 *result = buffer; 1798 for (; *buffer; ++buffer) 1799 { 1800 if (*buffer == '/') 1801 *buffer = '\\'; 1802 } 1803 return SVN_NO_ERROR; 1804} 1805 1806/* This is semantically the same as the APR unicode_to_utf8_path 1807 function, but reimplemented here because APR does not export it. */ 1808static svn_error_t * 1809io_unicode_to_utf8_path(const char **result, 1810 const WCHAR *source, 1811 apr_pool_t *result_pool) 1812{ 1813 const char *utf8_buffer; 1814 char *buffer; 1815 1816 SVN_ERR(svn_utf__win32_utf16_to_utf8(&utf8_buffer, source, 1817 NULL, result_pool)); 1818 if (!*utf8_buffer) 1819 { 1820 *result = utf8_buffer; 1821 return SVN_NO_ERROR; 1822 } 1823 1824 /* We know that the non-empty buffer returned from the UTF-16 to 1825 UTF-8 conversion function is in fact writable. */ 1826 buffer = (char*)utf8_buffer; 1827 1828 /* Skip the leading 4 characters if the path begins \\?\, or substitute 1829 * // for the \\?\UNC\ path prefix, allocating the maximum string 1830 * length based on the remaining string, plus the trailing null. 1831 * then transform \\'s back into /'s since the \\?\ form never 1832 * allows '/' path separators, and APR always uses '/'s. 1833 */ 1834 if (0 == strncmp(buffer, "\\\\?\\", 4)) 1835 { 1836 buffer += 4; 1837 if (0 == strncmp(buffer, "UNC\\", 4)) 1838 { 1839 buffer += 2; 1840 *buffer = '/'; 1841 } 1842 } 1843 1844 *result = buffer; 1845 for (; *buffer; ++buffer) 1846 { 1847 if (*buffer == '\\') 1848 *buffer = '/'; 1849 } 1850 return SVN_NO_ERROR; 1851} 1852 1853static svn_error_t * 1854io_win_file_attrs_set(const char *fname, 1855 DWORD attributes, 1856 DWORD attr_mask, 1857 apr_pool_t *pool) 1858{ 1859 /* this is an implementation of apr_file_attrs_set() but one 1860 that uses the proper Windows attributes instead of the apr 1861 attributes. This way, we can apply any Windows file and 1862 folder attributes even if apr doesn't implement them */ 1863 DWORD flags; 1864 const WCHAR *wfname; 1865 1866 SVN_ERR(svn_io__utf8_to_unicode_longpath(&wfname, fname, pool)); 1867 1868 flags = GetFileAttributesW(wfname); 1869 if (flags == 0xFFFFFFFF) 1870 return svn_error_wrap_apr(apr_get_os_error(), 1871 _("Can't get attributes of file '%s'"), 1872 svn_dirent_local_style(fname, pool)); 1873 1874 flags &= ~attr_mask; 1875 flags |= (attributes & attr_mask); 1876 1877 if (!SetFileAttributesW(wfname, flags)) 1878 return svn_error_wrap_apr(apr_get_os_error(), 1879 _("Can't set attributes of file '%s'"), 1880 svn_dirent_local_style(fname, pool)); 1881 1882 return SVN_NO_ERROR;; 1883} 1884 1885static svn_error_t *win_init_dynamic_imports(void *baton, apr_pool_t *pool) 1886{ 1887 HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); 1888 1889 if (kernel32) 1890 { 1891 get_final_path_name_by_handle_proc = (GETFINALPATHNAMEBYHANDLE) 1892 GetProcAddress(kernel32, "GetFinalPathNameByHandleW"); 1893 1894 set_file_information_by_handle_proc = (SetFileInformationByHandle_t) 1895 GetProcAddress(kernel32, "SetFileInformationByHandle"); 1896 } 1897 1898 return SVN_NO_ERROR; 1899} 1900 1901static svn_error_t * io_win_read_link(svn_string_t **dest, 1902 const char *path, 1903 apr_pool_t *pool) 1904{ 1905 SVN_ERR(svn_atomic__init_once(&win_dynamic_imports_state, 1906 win_init_dynamic_imports, NULL, pool)); 1907 1908 if (get_final_path_name_by_handle_proc) 1909 { 1910 DWORD rv; 1911 apr_status_t status; 1912 apr_file_t *file; 1913 apr_os_file_t filehand; 1914 WCHAR wdest[APR_PATH_MAX]; 1915 const char *data; 1916 1917 /* reserve one char for terminating zero. */ 1918 DWORD wdest_len = sizeof(wdest)/sizeof(wdest[0]) - 1; 1919 1920 status = apr_file_open(&file, path, APR_OPENINFO, APR_OS_DEFAULT, pool); 1921 1922 if (status) 1923 return svn_error_wrap_apr(status, 1924 _("Can't read contents of link")); 1925 1926 apr_os_file_get(&filehand, file); 1927 1928 rv = get_final_path_name_by_handle_proc( 1929 filehand, wdest, wdest_len, 1930 FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); 1931 1932 /* Save error code. */ 1933 status = apr_get_os_error(); 1934 1935 /* Close file/directory handle in any case. */ 1936 apr_file_close(file); 1937 1938 /* GetFinaPathNameByHandleW returns number of characters copied to 1939 * output buffer. Returns zero on error. Returns required buffer size 1940 * if supplied buffer is not enough. */ 1941 if (rv > wdest_len || rv == 0) 1942 { 1943 return svn_error_wrap_apr(status, 1944 _("Can't read contents of link")); 1945 } 1946 1947 /* GetFinaPathNameByHandleW doesn't add terminating NUL. */ 1948 wdest[rv] = 0; 1949 SVN_ERR(io_unicode_to_utf8_path(&data, wdest, pool)); 1950 1951 /* The result is already in the correct pool, so avoid copying 1952 it to create the string. */ 1953 *dest = svn_string_create_empty(pool); 1954 if (*data) 1955 { 1956 (*dest)->data = data; 1957 (*dest)->len = strlen(data); 1958 } 1959 1960 return SVN_NO_ERROR; 1961 } 1962 else 1963 { 1964 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1965 _("Symbolic links are not supported on this " 1966 "platform")); 1967 } 1968} 1969 1970/* Wrapper around Windows API function SetFileInformationByHandle() that 1971 * returns APR status instead of boolean flag. */ 1972static apr_status_t 1973win32_set_file_information_by_handle(HANDLE hFile, 1974 int FileInformationClass, 1975 LPVOID lpFileInformation, 1976 DWORD dwBufferSize) 1977{ 1978 svn_error_clear(svn_atomic__init_once(&win_dynamic_imports_state, 1979 win_init_dynamic_imports, 1980 NULL, NULL)); 1981 1982 if (!set_file_information_by_handle_proc) 1983 { 1984 return SVN_ERR_UNSUPPORTED_FEATURE; 1985 } 1986 1987 if (!set_file_information_by_handle_proc(hFile, FileInformationClass, 1988 lpFileInformation, 1989 dwBufferSize)) 1990 { 1991 return apr_get_os_error(); 1992 } 1993 1994 return APR_SUCCESS; 1995} 1996 1997svn_error_t * 1998svn_io__win_delete_file_on_close(apr_file_t *file, 1999 const char *path, 2000 apr_pool_t *pool) 2001{ 2002 FILE_DISPOSITION_INFO disposition_info; 2003 HANDLE hFile; 2004 apr_status_t status; 2005 2006 apr_os_file_get(&hFile, file); 2007 2008 disposition_info.DeleteFile = TRUE; 2009 2010 status = win32_set_file_information_by_handle(hFile, FileDispositionInfo, 2011 &disposition_info, 2012 sizeof(disposition_info)); 2013 2014 if (status) 2015 { 2016 return svn_error_wrap_apr(status, _("Can't remove file '%s'"), 2017 svn_dirent_local_style(path, pool)); 2018 } 2019 2020 return SVN_NO_ERROR; 2021} 2022 2023svn_error_t * 2024svn_io__win_rename_open_file(apr_file_t *file, 2025 const char *from_path, 2026 const char *to_path, 2027 apr_pool_t *pool) 2028{ 2029 WCHAR *w_final_abspath; 2030 size_t path_len; 2031 size_t rename_size; 2032 FILE_RENAME_INFO *rename_info; 2033 HANDLE hFile; 2034 apr_status_t status; 2035 2036 apr_os_file_get(&hFile, file); 2037 2038 SVN_ERR(svn_io__utf8_to_unicode_longpath( 2039 &w_final_abspath, svn_dirent_local_style(to_path,pool), 2040 pool)); 2041 2042 path_len = wcslen(w_final_abspath); 2043 rename_size = sizeof(*rename_info) + sizeof(WCHAR) * path_len; 2044 2045 /* The rename info struct doesn't need hacks for long paths, 2046 so no ugly escaping calls here */ 2047 rename_info = apr_pcalloc(pool, rename_size); 2048 rename_info->ReplaceIfExists = TRUE; 2049 rename_info->FileNameLength = path_len; 2050 memcpy(rename_info->FileName, w_final_abspath, path_len * sizeof(WCHAR)); 2051 2052 status = win32_set_file_information_by_handle(hFile, FileRenameInfo, 2053 rename_info, 2054 rename_size); 2055 2056 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) 2057 { 2058 /* Set the destination file writable because Windows will not allow 2059 us to rename when final_abspath is read-only. */ 2060 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); 2061 2062 status = win32_set_file_information_by_handle(hFile, 2063 FileRenameInfo, 2064 rename_info, 2065 rename_size); 2066 } 2067 2068 /* Windows returns Vista+ client accessing network share stored on Windows 2069 Server 2003 returns ERROR_ACCESS_DENIED. The same happens when Vista+ 2070 client access Windows Server 2008 with disabled SMBv2 protocol. 2071 2072 So return SVN_ERR_UNSUPPORTED_FEATURE in this case like we do when 2073 SetFileInformationByHandle() is not available and let caller to 2074 handle it. 2075 2076 See "Access denied error on checkout-commit after updating to 1.9.X" 2077 discussion on dev@s.a.o: 2078 http://svn.haxx.se/dev/archive-2015-09/0054.shtml */ 2079 if (status == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)) 2080 { 2081 status = SVN_ERR_UNSUPPORTED_FEATURE; 2082 } 2083 2084 if (status) 2085 { 2086 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"), 2087 svn_dirent_local_style(from_path, pool), 2088 svn_dirent_local_style(to_path, pool)); 2089 } 2090 2091 return SVN_NO_ERROR; 2092} 2093 2094#endif /* WIN32 */ 2095 2096svn_error_t * 2097svn_io_set_file_read_write_carefully(const char *path, 2098 svn_boolean_t enable_write, 2099 svn_boolean_t ignore_enoent, 2100 apr_pool_t *pool) 2101{ 2102 if (enable_write) 2103 return svn_io_set_file_read_write(path, ignore_enoent, pool); 2104 return svn_io_set_file_read_only(path, ignore_enoent, pool); 2105} 2106 2107svn_error_t * 2108svn_io_set_file_read_only(const char *path, 2109 svn_boolean_t ignore_enoent, 2110 apr_pool_t *pool) 2111{ 2112 /* On Windows and OS/2, just set the file attributes -- on unix call 2113 our internal function which attempts to honor the umask. */ 2114#if !defined(WIN32) && !defined(__OS2__) 2115 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE, 2116 ignore_enoent, pool); 2117#else 2118 apr_status_t status; 2119 const char *path_apr; 2120 2121 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 2122 2123 status = apr_file_attrs_set(path_apr, 2124 APR_FILE_ATTR_READONLY, 2125 APR_FILE_ATTR_READONLY, 2126 pool); 2127 2128 if (status && status != APR_ENOTIMPL) 2129 if (!(ignore_enoent && (APR_STATUS_IS_ENOENT(status) 2130 || SVN__APR_STATUS_IS_ENOTDIR(status)))) 2131 return svn_error_wrap_apr(status, 2132 _("Can't set file '%s' read-only"), 2133 svn_dirent_local_style(path, pool)); 2134 2135 return SVN_NO_ERROR; 2136#endif 2137} 2138 2139 2140svn_error_t * 2141svn_io_set_file_read_write(const char *path, 2142 svn_boolean_t ignore_enoent, 2143 apr_pool_t *pool) 2144{ 2145 /* On Windows and OS/2, just set the file attributes -- on unix call 2146 our internal function which attempts to honor the umask. */ 2147#if !defined(WIN32) && !defined(__OS2__) 2148 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE, 2149 ignore_enoent, pool); 2150#else 2151 apr_status_t status; 2152 const char *path_apr; 2153 2154 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 2155 2156 status = apr_file_attrs_set(path_apr, 2157 0, 2158 APR_FILE_ATTR_READONLY, 2159 pool); 2160 2161 if (status && status != APR_ENOTIMPL) 2162 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status)) 2163 return svn_error_wrap_apr(status, 2164 _("Can't set file '%s' read-write"), 2165 svn_dirent_local_style(path, pool)); 2166 2167 return SVN_NO_ERROR; 2168#endif 2169} 2170 2171svn_error_t * 2172svn_io_set_file_executable(const char *path, 2173 svn_boolean_t executable, 2174 svn_boolean_t ignore_enoent, 2175 apr_pool_t *pool) 2176{ 2177 /* On Windows and OS/2, just exit -- on unix call our internal function 2178 which attempts to honor the umask. */ 2179#if (!defined(WIN32) && !defined(__OS2__)) 2180 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable, 2181 ignore_enoent, pool); 2182#else 2183 return SVN_NO_ERROR; 2184#endif 2185} 2186 2187 2188svn_error_t * 2189svn_io__is_finfo_read_only(svn_boolean_t *read_only, 2190 apr_finfo_t *file_info, 2191 apr_pool_t *pool) 2192{ 2193#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__) 2194 apr_status_t apr_err; 2195 apr_uid_t uid; 2196 apr_gid_t gid; 2197 2198 *read_only = FALSE; 2199 2200 apr_err = apr_uid_current(&uid, &gid, pool); 2201 2202 if (apr_err) 2203 return svn_error_wrap_apr(apr_err, _("Error getting UID of process")); 2204 2205 /* Check write bit for current user. */ 2206 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS) 2207 *read_only = !(file_info->protection & APR_UWRITE); 2208 2209 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS) 2210 *read_only = !(file_info->protection & APR_GWRITE); 2211 2212 else 2213 *read_only = !(file_info->protection & APR_WWRITE); 2214 2215#else /* WIN32 || __OS2__ || !APR_HAS_USER */ 2216 *read_only = (file_info->protection & APR_FREADONLY); 2217#endif 2218 2219 return SVN_NO_ERROR; 2220} 2221 2222svn_error_t * 2223svn_io__is_finfo_executable(svn_boolean_t *executable, 2224 apr_finfo_t *file_info, 2225 apr_pool_t *pool) 2226{ 2227#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__) 2228 apr_status_t apr_err; 2229 apr_uid_t uid; 2230 apr_gid_t gid; 2231 2232 *executable = FALSE; 2233 2234 apr_err = apr_uid_current(&uid, &gid, pool); 2235 2236 if (apr_err) 2237 return svn_error_wrap_apr(apr_err, _("Error getting UID of process")); 2238 2239 /* Check executable bit for current user. */ 2240 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS) 2241 *executable = (file_info->protection & APR_UEXECUTE); 2242 2243 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS) 2244 *executable = (file_info->protection & APR_GEXECUTE); 2245 2246 else 2247 *executable = (file_info->protection & APR_WEXECUTE); 2248 2249#else /* WIN32 || __OS2__ || !APR_HAS_USER */ 2250 *executable = FALSE; 2251#endif 2252 2253 return SVN_NO_ERROR; 2254} 2255 2256svn_error_t * 2257svn_io_is_file_executable(svn_boolean_t *executable, 2258 const char *path, 2259 apr_pool_t *pool) 2260{ 2261#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__) 2262 apr_finfo_t file_info; 2263 2264 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER, 2265 pool)); 2266 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool)); 2267 2268#else /* WIN32 || __OS2__ || !APR_HAS_USER */ 2269 *executable = FALSE; 2270#endif 2271 2272 return SVN_NO_ERROR; 2273} 2274 2275 2276/*** File locking. ***/ 2277#if !defined(WIN32) && !defined(__OS2__) 2278/* Clear all outstanding locks on ARG, an open apr_file_t *. */ 2279static apr_status_t 2280file_clear_locks(void *arg) 2281{ 2282 apr_status_t apr_err; 2283 apr_file_t *f = arg; 2284 2285 /* Remove locks. */ 2286 apr_err = apr_file_unlock(f); 2287 if (apr_err) 2288 return apr_err; 2289 2290 return 0; 2291} 2292#endif 2293 2294svn_error_t * 2295svn_io_lock_open_file(apr_file_t *lockfile_handle, 2296 svn_boolean_t exclusive, 2297 svn_boolean_t nonblocking, 2298 apr_pool_t *pool) 2299{ 2300 int locktype = APR_FLOCK_SHARED; 2301 apr_status_t apr_err; 2302 const char *fname; 2303 2304 if (exclusive) 2305 locktype = APR_FLOCK_EXCLUSIVE; 2306 if (nonblocking) 2307 locktype |= APR_FLOCK_NONBLOCK; 2308 2309 /* We need this only in case of an error but this is cheap to get - 2310 * so we do it here for clarity. */ 2311 apr_err = apr_file_name_get(&fname, lockfile_handle); 2312 if (apr_err) 2313 return svn_error_wrap_apr(apr_err, _("Can't get file name")); 2314 2315 /* Get lock on the filehandle. */ 2316 apr_err = apr_file_lock(lockfile_handle, locktype); 2317 2318 /* In deployments with two or more multithreaded servers running on 2319 the same system serving two or more fsfs repositories it is 2320 possible for a deadlock to occur when getting a write lock on 2321 db/txn-current-lock: 2322 2323 Process 1 Process 2 2324 --------- --------- 2325 thread 1: get lock in repos A 2326 thread 1: get lock in repos B 2327 thread 2: block getting lock in repos A 2328 thread 2: try to get lock in B *** deadlock *** 2329 2330 Retry for a while for the deadlock to clear. */ 2331 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype)); 2332 2333 if (apr_err) 2334 { 2335 switch (locktype & APR_FLOCK_TYPEMASK) 2336 { 2337 case APR_FLOCK_SHARED: 2338 return svn_error_wrap_apr(apr_err, 2339 _("Can't get shared lock on file '%s'"), 2340 try_utf8_from_internal_style(fname, pool)); 2341 case APR_FLOCK_EXCLUSIVE: 2342 return svn_error_wrap_apr(apr_err, 2343 _("Can't get exclusive lock on file '%s'"), 2344 try_utf8_from_internal_style(fname, pool)); 2345 default: 2346 SVN_ERR_MALFUNCTION(); 2347 } 2348 } 2349 2350/* On Windows and OS/2 file locks are automatically released when 2351 the file handle closes */ 2352#if !defined(WIN32) && !defined(__OS2__) 2353 apr_pool_cleanup_register(pool, lockfile_handle, 2354 file_clear_locks, 2355 apr_pool_cleanup_null); 2356#endif 2357 2358 return SVN_NO_ERROR; 2359} 2360 2361svn_error_t * 2362svn_io_unlock_open_file(apr_file_t *lockfile_handle, 2363 apr_pool_t *pool) 2364{ 2365 const char *fname; 2366 apr_status_t apr_err; 2367 2368 /* We need this only in case of an error but this is cheap to get - 2369 * so we do it here for clarity. */ 2370 apr_err = apr_file_name_get(&fname, lockfile_handle); 2371 if (apr_err) 2372 return svn_error_wrap_apr(apr_err, _("Can't get file name")); 2373 2374 /* The actual unlock attempt. */ 2375 apr_err = apr_file_unlock(lockfile_handle); 2376 if (apr_err) 2377 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"), 2378 try_utf8_from_internal_style(fname, pool)); 2379 2380/* On Windows and OS/2 file locks are automatically released when 2381 the file handle closes */ 2382#if !defined(WIN32) && !defined(__OS2__) 2383 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks); 2384#endif 2385 2386 return SVN_NO_ERROR; 2387} 2388 2389svn_error_t * 2390svn_io_file_lock2(const char *lock_file, 2391 svn_boolean_t exclusive, 2392 svn_boolean_t nonblocking, 2393 apr_pool_t *pool) 2394{ 2395 int locktype = APR_FLOCK_SHARED; 2396 apr_file_t *lockfile_handle; 2397 apr_int32_t flags; 2398 2399 if (exclusive) 2400 locktype = APR_FLOCK_EXCLUSIVE; 2401 2402 flags = APR_READ; 2403 if (locktype == APR_FLOCK_EXCLUSIVE) 2404 flags |= APR_WRITE; 2405 2406 /* locktype is never read after this block, so we don't need to bother 2407 setting it. If that were to ever change, uncomment the following 2408 block. 2409 if (nonblocking) 2410 locktype |= APR_FLOCK_NONBLOCK; 2411 */ 2412 2413 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags, 2414 APR_OS_DEFAULT, 2415 pool)); 2416 2417 /* Get lock on the filehandle. */ 2418 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool); 2419} 2420 2421svn_error_t * 2422svn_io__file_lock_autocreate(const char *lock_file, 2423 apr_pool_t *pool) 2424{ 2425 svn_error_t *err 2426 = svn_io_file_lock2(lock_file, TRUE, FALSE, pool); 2427 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 2428 { 2429 /* No lock file? No big deal; these are just empty files anyway. 2430 Create it and try again. */ 2431 svn_error_clear(err); 2432 2433 /* This file creation is racy. 2434 We don't care as long as file gets created at all. */ 2435 err = svn_io_file_create_empty(lock_file, pool); 2436 if (err && APR_STATUS_IS_EEXIST(err->apr_err)) 2437 { 2438 svn_error_clear(err); 2439 err = NULL; 2440 } 2441 2442 /* Finally, lock the file - if it exists */ 2443 if (!err) 2444 err = svn_io_file_lock2(lock_file, TRUE, FALSE, pool); 2445 } 2446 2447 return svn_error_trace(err); 2448} 2449 2450 2451 2452/* Data consistency/coherency operations. */ 2453 2454svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file, 2455 apr_pool_t *pool) 2456{ 2457 apr_os_file_t filehand; 2458 2459 /* ### In apr 1.4+ we could delegate most of this function to 2460 apr_file_sync(). The only major difference is that this doesn't 2461 contain the retry loop for EINTR on linux. */ 2462 2463 /* First make sure that any user-space buffered data is flushed. */ 2464 SVN_ERR(svn_io_file_flush(file, pool)); 2465 2466 apr_os_file_get(&filehand, file); 2467 2468 /* Call the operating system specific function to actually force the 2469 data to disk. */ 2470 { 2471#ifdef WIN32 2472 2473 if (! FlushFileBuffers(filehand)) 2474 return svn_error_wrap_apr(apr_get_os_error(), 2475 _("Can't flush file to disk")); 2476 2477#else 2478 int rv; 2479 2480 do { 2481#ifdef F_FULLFSYNC 2482 rv = fcntl(filehand, F_FULLFSYNC, 0); 2483#else 2484 rv = fsync(filehand); 2485#endif 2486 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error())); 2487 2488 /* If the file is in a memory filesystem, fsync() may return 2489 EINVAL. Presumably the user knows the risks, and we can just 2490 ignore the error. */ 2491 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error())) 2492 return SVN_NO_ERROR; 2493 2494 if (rv == -1) 2495 return svn_error_wrap_apr(apr_get_os_error(), 2496 _("Can't flush file to disk")); 2497 2498#endif 2499 } 2500 return SVN_NO_ERROR; 2501} 2502 2503 2504 2505/* TODO write test for these two functions, then refactor. */ 2506 2507/* Set RESULT to an svn_stringbuf_t containing the contents of FILE. 2508 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name 2509 isn't known. If CHECK_SIZE is TRUE, the function will attempt to 2510 first stat() the file to determine it's size before sucking its 2511 contents into the stringbuf. (Doing so can prevent unnecessary 2512 memory usage, an unwanted side effect of the stringbuf growth and 2513 reallocation mechanism.) */ 2514static svn_error_t * 2515stringbuf_from_aprfile(svn_stringbuf_t **result, 2516 const char *filename, 2517 apr_file_t *file, 2518 svn_boolean_t check_size, 2519 apr_pool_t *pool) 2520{ 2521 apr_size_t len; 2522 svn_error_t *err; 2523 svn_stringbuf_t *res = NULL; 2524 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE; 2525 char *buf; 2526 2527 /* If our caller wants us to check the size of the file for 2528 efficient memory handling, we'll try to do so. */ 2529 if (check_size) 2530 { 2531 apr_finfo_t finfo = { 0 }; 2532 2533 /* In some cases we get size 0 and no error for non files, 2534 so we also check for the name. (= cached in apr_file_t) */ 2535 if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname) 2536 { 2537 /* we've got the file length. Now, read it in one go. */ 2538 svn_boolean_t eof; 2539 res_initial_len = (apr_size_t)finfo.size; 2540 res = svn_stringbuf_create_ensure(res_initial_len, pool); 2541 SVN_ERR(svn_io_file_read_full2(file, res->data, 2542 res_initial_len, &res->len, 2543 &eof, pool)); 2544 res->data[res->len] = 0; 2545 2546 *result = res; 2547 return SVN_NO_ERROR; 2548 } 2549 } 2550 2551 /* XXX: We should check the incoming data for being of type binary. */ 2552 buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 2553 res = svn_stringbuf_create_ensure(res_initial_len, pool); 2554 2555 /* apr_file_read will not return data and eof in the same call. So this loop 2556 * is safe from missing read data. */ 2557 len = SVN__STREAM_CHUNK_SIZE; 2558 err = svn_io_file_read(file, buf, &len, pool); 2559 while (! err) 2560 { 2561 svn_stringbuf_appendbytes(res, buf, len); 2562 len = SVN__STREAM_CHUNK_SIZE; 2563 err = svn_io_file_read(file, buf, &len, pool); 2564 } 2565 2566 /* Having read all the data we *expect* EOF */ 2567 if (err && !APR_STATUS_IS_EOF(err->apr_err)) 2568 return svn_error_trace(err); 2569 svn_error_clear(err); 2570 2571 *result = res; 2572 return SVN_NO_ERROR; 2573} 2574 2575svn_error_t * 2576svn_stringbuf_from_file2(svn_stringbuf_t **result, 2577 const char *filename, 2578 apr_pool_t *pool) 2579{ 2580 apr_file_t *f; 2581 2582 if (filename[0] == '-' && filename[1] == '\0') 2583 { 2584 apr_status_t apr_err; 2585 if ((apr_err = apr_file_open_stdin(&f, pool))) 2586 return svn_error_wrap_apr(apr_err, _("Can't open stdin")); 2587 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool)); 2588 } 2589 else 2590 { 2591 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool)); 2592 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool)); 2593 } 2594 return svn_io_file_close(f, pool); 2595} 2596 2597 2598svn_error_t * 2599svn_stringbuf_from_file(svn_stringbuf_t **result, 2600 const char *filename, 2601 apr_pool_t *pool) 2602{ 2603 if (filename[0] == '-' && filename[1] == '\0') 2604 return svn_error_create 2605 (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2606 _("Reading from stdin is disallowed")); 2607 return svn_stringbuf_from_file2(result, filename, pool); 2608} 2609 2610svn_error_t * 2611svn_stringbuf_from_aprfile(svn_stringbuf_t **result, 2612 apr_file_t *file, 2613 apr_pool_t *pool) 2614{ 2615 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool); 2616} 2617 2618 2619 2620/* Deletion. */ 2621 2622svn_error_t * 2623svn_io_remove_file2(const char *path, 2624 svn_boolean_t ignore_enoent, 2625 apr_pool_t *scratch_pool) 2626{ 2627 apr_status_t apr_err; 2628 const char *path_apr; 2629 2630 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool)); 2631 2632 apr_err = apr_file_remove(path_apr, scratch_pool); 2633 2634#ifdef WIN32 2635 /* If the target is read only NTFS reports EACCESS and FAT/FAT32 2636 reports EEXIST */ 2637 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err)) 2638 { 2639 /* Set the destination file writable because Windows will not 2640 allow us to delete when path is read-only */ 2641 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool)); 2642 apr_err = apr_file_remove(path_apr, scratch_pool); 2643 2644 if (!apr_err) 2645 return SVN_NO_ERROR; 2646 } 2647 2648 /* Check to make sure we aren't trying to delete a directory */ 2649 if (apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED) 2650 || apr_err == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) 2651 { 2652 apr_finfo_t finfo; 2653 2654 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool) 2655 && finfo.filetype == APR_REG) 2656 { 2657 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr, scratch_pool)); 2658 } 2659 } 2660 2661 /* Just return the delete error */ 2662#endif 2663 2664 if (!apr_err) 2665 { 2666 return SVN_NO_ERROR; 2667 } 2668 else if (ignore_enoent && (APR_STATUS_IS_ENOENT(apr_err) 2669 || SVN__APR_STATUS_IS_ENOTDIR(apr_err))) 2670 { 2671 return SVN_NO_ERROR; 2672 } 2673 else 2674 { 2675 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"), 2676 svn_dirent_local_style(path, scratch_pool)); 2677 } 2678} 2679 2680 2681svn_error_t * 2682svn_io_remove_dir(const char *path, apr_pool_t *pool) 2683{ 2684 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 2685} 2686 2687/* 2688 Mac OS X has a bug where if you're reading the contents of a 2689 directory via readdir in a loop, and you remove one of the entries in 2690 the directory and the directory has 338 or more files in it you will 2691 skip over some of the entries in the directory. Needless to say, 2692 this causes problems if you are using this kind of loop inside a 2693 function that is recursively deleting a directory, because when you 2694 get around to removing the directory it will still have something in 2695 it. A similar problem has been observed in other BSDs. This bug has 2696 since been fixed. See http://www.vnode.ch/fixing_seekdir for details. 2697 2698 The workaround is to delete the files only _after_ the initial 2699 directory scan. A previous workaround involving rewinddir is 2700 problematic on Win32 and some NFS clients, notably NetBSD. 2701 2702 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and 2703 http://subversion.tigris.org/issues/show_bug.cgi?id=3501. 2704*/ 2705 2706/* Neither windows nor unix allows us to delete a non-empty 2707 directory. 2708 2709 This is a function to perform the equivalent of 'rm -rf'. */ 2710svn_error_t * 2711svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent, 2712 svn_cancel_func_t cancel_func, void *cancel_baton, 2713 apr_pool_t *pool) 2714{ 2715 svn_error_t *err; 2716 apr_pool_t *subpool; 2717 apr_hash_t *dirents; 2718 apr_hash_index_t *hi; 2719 2720 /* Check for pending cancellation request. 2721 If we need to bail out, do so early. */ 2722 2723 if (cancel_func) 2724 SVN_ERR((*cancel_func)(cancel_baton)); 2725 2726 subpool = svn_pool_create(pool); 2727 2728 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool); 2729 if (err) 2730 { 2731 /* if the directory doesn't exist, our mission is accomplished */ 2732 if (ignore_enoent && (APR_STATUS_IS_ENOENT(err->apr_err) 2733 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 2734 { 2735 svn_error_clear(err); 2736 return SVN_NO_ERROR; 2737 } 2738 return svn_error_trace(err); 2739 } 2740 2741 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi)) 2742 { 2743 const char *name = apr_hash_this_key(hi); 2744 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi); 2745 const char *fullpath; 2746 2747 fullpath = svn_dirent_join(path, name, subpool); 2748 if (dirent->kind == svn_node_dir) 2749 { 2750 /* Don't check for cancellation, the callee will immediately do so */ 2751 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func, 2752 cancel_baton, subpool)); 2753 } 2754 else 2755 { 2756 if (cancel_func) 2757 SVN_ERR((*cancel_func)(cancel_baton)); 2758 2759 err = svn_io_remove_file2(fullpath, FALSE, subpool); 2760 if (err) 2761 return svn_error_createf 2762 (err->apr_err, err, _("Can't remove '%s'"), 2763 svn_dirent_local_style(fullpath, subpool)); 2764 } 2765 } 2766 2767 svn_pool_destroy(subpool); 2768 2769 return svn_io_dir_remove_nonrecursive(path, pool); 2770} 2771 2772svn_error_t * 2773svn_io_get_dir_filenames(apr_hash_t **dirents, 2774 const char *path, 2775 apr_pool_t *pool) 2776{ 2777 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE, 2778 pool, pool)); 2779} 2780 2781svn_io_dirent2_t * 2782svn_io_dirent2_create(apr_pool_t *result_pool) 2783{ 2784 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent)); 2785 2786 /*dirent->kind = svn_node_none; 2787 dirent->special = FALSE;*/ 2788 dirent->filesize = SVN_INVALID_FILESIZE; 2789 /*dirent->mtime = 0;*/ 2790 2791 return dirent; 2792} 2793 2794svn_io_dirent2_t * 2795svn_io_dirent2_dup(const svn_io_dirent2_t *item, 2796 apr_pool_t *result_pool) 2797{ 2798 return apr_pmemdup(result_pool, 2799 item, 2800 sizeof(*item)); 2801} 2802 2803svn_error_t * 2804svn_io_get_dirents3(apr_hash_t **dirents, 2805 const char *path, 2806 svn_boolean_t only_check_type, 2807 apr_pool_t *result_pool, 2808 apr_pool_t *scratch_pool) 2809{ 2810 apr_status_t status; 2811 apr_dir_t *this_dir; 2812 apr_finfo_t this_entry; 2813 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME; 2814 2815 if (!only_check_type) 2816 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME; 2817 2818 *dirents = apr_hash_make(result_pool); 2819 2820 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool)); 2821 2822 for (status = apr_dir_read(&this_entry, flags, this_dir); 2823 status == APR_SUCCESS; 2824 status = apr_dir_read(&this_entry, flags, this_dir)) 2825 { 2826 if ((this_entry.name[0] == '.') 2827 && ((this_entry.name[1] == '\0') 2828 || ((this_entry.name[1] == '.') 2829 && (this_entry.name[2] == '\0')))) 2830 { 2831 continue; 2832 } 2833 else 2834 { 2835 const char *name; 2836 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool); 2837 2838 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool)); 2839 2840 map_apr_finfo_to_node_kind(&(dirent->kind), 2841 &(dirent->special), 2842 &this_entry); 2843 2844 if (!only_check_type) 2845 { 2846 dirent->filesize = this_entry.size; 2847 dirent->mtime = this_entry.mtime; 2848 } 2849 2850 svn_hash_sets(*dirents, name, dirent); 2851 } 2852 } 2853 2854 if (! (APR_STATUS_IS_ENOENT(status))) 2855 return svn_error_wrap_apr(status, _("Can't read directory '%s'"), 2856 svn_dirent_local_style(path, scratch_pool)); 2857 2858 status = apr_dir_close(this_dir); 2859 if (status) 2860 return svn_error_wrap_apr(status, _("Error closing directory '%s'"), 2861 svn_dirent_local_style(path, scratch_pool)); 2862 2863 return SVN_NO_ERROR; 2864} 2865 2866svn_error_t * 2867svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p, 2868 const char *path, 2869 svn_boolean_t verify_truename, 2870 svn_boolean_t ignore_enoent, 2871 apr_pool_t *result_pool, 2872 apr_pool_t *scratch_pool) 2873{ 2874 apr_finfo_t finfo; 2875 svn_io_dirent2_t *dirent; 2876 svn_error_t *err; 2877 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK 2878 | APR_FINFO_SIZE | APR_FINFO_MTIME; 2879 2880#if defined(WIN32) || defined(__OS2__) 2881 if (verify_truename) 2882 wanted |= APR_FINFO_NAME; 2883#endif 2884 2885 err = svn_io_stat(&finfo, path, wanted, scratch_pool); 2886 2887 if (err && ignore_enoent && 2888 (APR_STATUS_IS_ENOENT(err->apr_err) 2889 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 2890 { 2891 svn_error_clear(err); 2892 dirent = svn_io_dirent2_create(result_pool); 2893 SVN_ERR_ASSERT(dirent->kind == svn_node_none); 2894 2895 *dirent_p = dirent; 2896 return SVN_NO_ERROR; 2897 } 2898 SVN_ERR(err); 2899 2900#if defined(WIN32) || defined(__OS2__) || defined(DARWIN) 2901 if (verify_truename) 2902 { 2903 const char *requested_name = svn_dirent_basename(path, NULL); 2904 2905 if (requested_name[0] == '\0') 2906 { 2907 /* No parent directory. No need to stat/verify */ 2908 } 2909#if defined(WIN32) || defined(__OS2__) 2910 else if (finfo.name) 2911 { 2912 const char *name_on_disk; 2913 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path, 2914 scratch_pool)); 2915 2916 if (strcmp(name_on_disk, requested_name) /* != 0 */) 2917 { 2918 if (ignore_enoent) 2919 { 2920 *dirent_p = svn_io_dirent2_create(result_pool); 2921 return SVN_NO_ERROR; 2922 } 2923 else 2924 return svn_error_createf(APR_ENOENT, NULL, 2925 _("Path '%s' not found, case obstructed by '%s'"), 2926 svn_dirent_local_style(path, scratch_pool), 2927 name_on_disk); 2928 } 2929 } 2930#elif defined(DARWIN) 2931 /* Currently apr doesn't set finfo.name on DARWIN, returning 2932 APR_INCOMPLETE. 2933 ### Can we optimize this in another way? */ 2934 else 2935 { 2936 apr_hash_t *dirents; 2937 2938 err = svn_io_get_dirents3(&dirents, 2939 svn_dirent_dirname(path, scratch_pool), 2940 TRUE /* only_check_type */, 2941 scratch_pool, scratch_pool); 2942 2943 if (err && ignore_enoent 2944 && (APR_STATUS_IS_ENOENT(err->apr_err) 2945 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 2946 { 2947 svn_error_clear(err); 2948 2949 *dirent_p = svn_io_dirent2_create(result_pool); 2950 return SVN_NO_ERROR; 2951 } 2952 else 2953 SVN_ERR(err); 2954 2955 if (! svn_hash_gets(dirents, requested_name)) 2956 { 2957 if (ignore_enoent) 2958 { 2959 *dirent_p = svn_io_dirent2_create(result_pool); 2960 return SVN_NO_ERROR; 2961 } 2962 else 2963 return svn_error_createf(APR_ENOENT, NULL, 2964 _("Path '%s' not found"), 2965 svn_dirent_local_style(path, scratch_pool)); 2966 } 2967 } 2968#endif 2969 } 2970#endif 2971 2972 dirent = svn_io_dirent2_create(result_pool); 2973 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo); 2974 2975 dirent->filesize = finfo.size; 2976 dirent->mtime = finfo.mtime; 2977 2978 *dirent_p = dirent; 2979 2980 return SVN_NO_ERROR; 2981} 2982 2983/* Pool userdata key for the error file passed to svn_io_start_cmd(). */ 2984#define ERRFILE_KEY "svn-io-start-cmd-errfile" 2985 2986/* Handle an error from the child process (before command execution) by 2987 printing DESC and the error string corresponding to STATUS to stderr. */ 2988static void 2989handle_child_process_error(apr_pool_t *pool, apr_status_t status, 2990 const char *desc) 2991{ 2992 char errbuf[256]; 2993 apr_file_t *errfile; 2994 void *p; 2995 2996 /* We can't do anything if we get an error here, so just return. */ 2997 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool)) 2998 return; 2999 errfile = p; 3000 3001 if (errfile) 3002 /* What we get from APR is in native encoding. */ 3003 apr_file_printf(errfile, "%s: %s", 3004 desc, apr_strerror(status, errbuf, 3005 sizeof(errbuf))); 3006} 3007 3008 3009svn_error_t * 3010svn_io_start_cmd3(apr_proc_t *cmd_proc, 3011 const char *path, 3012 const char *cmd, 3013 const char *const *args, 3014 const char *const *env, 3015 svn_boolean_t inherit, 3016 svn_boolean_t infile_pipe, 3017 apr_file_t *infile, 3018 svn_boolean_t outfile_pipe, 3019 apr_file_t *outfile, 3020 svn_boolean_t errfile_pipe, 3021 apr_file_t *errfile, 3022 apr_pool_t *pool) 3023{ 3024 apr_status_t apr_err; 3025 apr_procattr_t *cmdproc_attr; 3026 int num_args; 3027 const char **args_native; 3028 const char *cmd_apr; 3029 3030 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe)); 3031 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe)); 3032 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe)); 3033 3034 /* Create the process attributes. */ 3035 apr_err = apr_procattr_create(&cmdproc_attr, pool); 3036 if (apr_err) 3037 return svn_error_wrap_apr(apr_err, 3038 _("Can't create process '%s' attributes"), 3039 cmd); 3040 3041 /* Make sure we invoke cmd directly, not through a shell. */ 3042 apr_err = apr_procattr_cmdtype_set(cmdproc_attr, 3043 inherit ? APR_PROGRAM_PATH : APR_PROGRAM); 3044 if (apr_err) 3045 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"), 3046 cmd); 3047 3048 /* Set the process's working directory. */ 3049 if (path) 3050 { 3051 const char *path_apr; 3052 3053 /* APR doesn't like our canonical path format for current directory */ 3054 if (path[0] == '\0') 3055 path = "."; 3056 3057 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 3058 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr); 3059 if (apr_err) 3060 return svn_error_wrap_apr(apr_err, 3061 _("Can't set process '%s' directory"), 3062 cmd); 3063 } 3064 3065 /* Use requested inputs and outputs. 3066 3067 ### Unfortunately each of these apr functions creates a pipe and then 3068 overwrites the pipe file descriptor with the descriptor we pass 3069 in. The pipes can then never be closed. This is an APR bug. */ 3070 if (infile) 3071 { 3072 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL); 3073 if (apr_err) 3074 return svn_error_wrap_apr(apr_err, 3075 _("Can't set process '%s' child input"), 3076 cmd); 3077 } 3078 if (outfile) 3079 { 3080 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL); 3081 if (apr_err) 3082 return svn_error_wrap_apr(apr_err, 3083 _("Can't set process '%s' child outfile"), 3084 cmd); 3085 } 3086 if (errfile) 3087 { 3088 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL); 3089 if (apr_err) 3090 return svn_error_wrap_apr(apr_err, 3091 _("Can't set process '%s' child errfile"), 3092 cmd); 3093 } 3094 3095 /* Forward request for pipes to APR. */ 3096 if (infile_pipe || outfile_pipe || errfile_pipe) 3097 { 3098 apr_err = apr_procattr_io_set(cmdproc_attr, 3099 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE, 3100 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE, 3101 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE); 3102 3103 if (apr_err) 3104 return svn_error_wrap_apr(apr_err, 3105 _("Can't set process '%s' stdio pipes"), 3106 cmd); 3107 } 3108 3109 /* Have the child print any problems executing its program to errfile. */ 3110 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool); 3111 if (apr_err) 3112 return svn_error_wrap_apr(apr_err, 3113 _("Can't set process '%s' child errfile for " 3114 "error handler"), 3115 cmd); 3116 apr_err = apr_procattr_child_errfn_set(cmdproc_attr, 3117 handle_child_process_error); 3118 if (apr_err) 3119 return svn_error_wrap_apr(apr_err, 3120 _("Can't set process '%s' error handler"), 3121 cmd); 3122 3123 /* Convert cmd and args from UTF-8 */ 3124 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool)); 3125 for (num_args = 0; args[num_args]; num_args++) 3126 ; 3127 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *)); 3128 args_native[num_args] = NULL; 3129 while (num_args--) 3130 { 3131 /* ### Well, it turns out that on APR on Windows expects all 3132 program args to be in UTF-8. Callers of svn_io_run_cmd 3133 should be aware of that. */ 3134 SVN_ERR(cstring_from_utf8(&args_native[num_args], 3135 args[num_args], pool)); 3136 } 3137 3138 3139 /* Start the cmd command. */ 3140 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native, 3141 inherit ? NULL : env, cmdproc_attr, pool); 3142 if (apr_err) 3143 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd); 3144 3145 return SVN_NO_ERROR; 3146} 3147 3148#undef ERRFILE_KEY 3149 3150svn_error_t * 3151svn_io_wait_for_cmd(apr_proc_t *cmd_proc, 3152 const char *cmd, 3153 int *exitcode, 3154 apr_exit_why_e *exitwhy, 3155 apr_pool_t *pool) 3156{ 3157 apr_status_t apr_err; 3158 apr_exit_why_e exitwhy_val; 3159 int exitcode_val; 3160 3161 /* The Win32 apr_proc_wait doesn't set this... */ 3162 exitwhy_val = APR_PROC_EXIT; 3163 3164 /* Wait for the cmd command to finish. */ 3165 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT); 3166 if (!APR_STATUS_IS_CHILD_DONE(apr_err)) 3167 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"), 3168 cmd); 3169 3170 if (exitwhy) 3171 *exitwhy = exitwhy_val; 3172 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val) 3173 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val)) 3174 return svn_error_createf 3175 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 3176 _("Process '%s' failed (signal %d, core dumped)"), 3177 cmd, exitcode_val); 3178 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)) 3179 return svn_error_createf 3180 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 3181 _("Process '%s' failed (signal %d)"), 3182 cmd, exitcode_val); 3183 else if (! APR_PROC_CHECK_EXIT(exitwhy_val)) 3184 /* Don't really know what happened here. */ 3185 return svn_error_createf 3186 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 3187 _("Process '%s' failed (exitwhy %d, exitcode %d)"), 3188 cmd, exitwhy_val, exitcode_val); 3189 3190 if (exitcode) 3191 *exitcode = exitcode_val; 3192 else if (exitcode_val != 0) 3193 return svn_error_createf 3194 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 3195 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val); 3196 3197 return SVN_NO_ERROR; 3198} 3199 3200 3201svn_error_t * 3202svn_io_run_cmd(const char *path, 3203 const char *cmd, 3204 const char *const *args, 3205 int *exitcode, 3206 apr_exit_why_e *exitwhy, 3207 svn_boolean_t inherit, 3208 apr_file_t *infile, 3209 apr_file_t *outfile, 3210 apr_file_t *errfile, 3211 apr_pool_t *pool) 3212{ 3213 apr_proc_t cmd_proc; 3214 3215 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit, 3216 FALSE, infile, FALSE, outfile, FALSE, errfile, 3217 pool)); 3218 3219 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool); 3220} 3221 3222 3223svn_error_t * 3224svn_io_run_diff2(const char *dir, 3225 const char *const *user_args, 3226 int num_user_args, 3227 const char *label1, 3228 const char *label2, 3229 const char *from, 3230 const char *to, 3231 int *pexitcode, 3232 apr_file_t *outfile, 3233 apr_file_t *errfile, 3234 const char *diff_cmd, 3235 apr_pool_t *pool) 3236{ 3237 const char **args; 3238 int i; 3239 int exitcode; 3240 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */ 3241 apr_pool_t *subpool = svn_pool_create(pool); 3242 3243 if (pexitcode == NULL) 3244 pexitcode = &exitcode; 3245 3246 if (user_args != NULL) 3247 nargs += num_user_args; 3248 else 3249 nargs += 1; /* -u */ 3250 3251 if (label1 != NULL) 3252 nargs += 2; /* the -L and the label itself */ 3253 if (label2 != NULL) 3254 nargs += 2; /* the -L and the label itself */ 3255 3256 args = apr_palloc(subpool, nargs * sizeof(char *)); 3257 3258 i = 0; 3259 args[i++] = diff_cmd; 3260 3261 if (user_args != NULL) 3262 { 3263 int j; 3264 for (j = 0; j < num_user_args; ++j) 3265 args[i++] = user_args[j]; 3266 } 3267 else 3268 args[i++] = "-u"; /* assume -u if the user didn't give us any args */ 3269 3270 if (label1 != NULL) 3271 { 3272 args[i++] = "-L"; 3273 args[i++] = label1; 3274 } 3275 if (label2 != NULL) 3276 { 3277 args[i++] = "-L"; 3278 args[i++] = label2; 3279 } 3280 3281 args[i++] = svn_dirent_local_style(from, subpool); 3282 args[i++] = svn_dirent_local_style(to, subpool); 3283 args[i++] = NULL; 3284 3285 SVN_ERR_ASSERT(i == nargs); 3286 3287 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE, 3288 NULL, outfile, errfile, subpool)); 3289 3290 /* The man page for (GNU) diff describes the return value as: 3291 3292 "An exit status of 0 means no differences were found, 1 means 3293 some differences were found, and 2 means trouble." 3294 3295 A return value of 2 typically occurs when diff cannot read its input 3296 or write to its output, but in any case we probably ought to return an 3297 error for anything other than 0 or 1 as the output is likely to be 3298 corrupt. 3299 */ 3300 if (*pexitcode != 0 && *pexitcode != 1) 3301 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, 3302 _("'%s' returned %d"), 3303 svn_dirent_local_style(diff_cmd, pool), 3304 *pexitcode); 3305 3306 svn_pool_destroy(subpool); 3307 3308 return SVN_NO_ERROR; 3309} 3310 3311 3312svn_error_t * 3313svn_io_run_diff3_3(int *exitcode, 3314 const char *dir, 3315 const char *mine, 3316 const char *older, 3317 const char *yours, 3318 const char *mine_label, 3319 const char *older_label, 3320 const char *yours_label, 3321 apr_file_t *merged, 3322 const char *diff3_cmd, 3323 const apr_array_header_t *user_args, 3324 apr_pool_t *pool) 3325{ 3326 const char **args = apr_palloc(pool, 3327 sizeof(char*) * (13 3328 + (user_args 3329 ? user_args->nelts 3330 : 1))); 3331#ifndef NDEBUG 3332 int nargs = 12; 3333#endif 3334 int i = 0; 3335 3336 /* Labels fall back to sensible defaults if not specified. */ 3337 if (mine_label == NULL) 3338 mine_label = ".working"; 3339 if (older_label == NULL) 3340 older_label = ".old"; 3341 if (yours_label == NULL) 3342 yours_label = ".new"; 3343 3344 /* Set up diff3 command line. */ 3345 args[i++] = diff3_cmd; 3346 if (user_args) 3347 { 3348 int j; 3349 for (j = 0; j < user_args->nelts; ++j) 3350 args[i++] = APR_ARRAY_IDX(user_args, j, const char *); 3351#ifndef NDEBUG 3352 nargs += user_args->nelts; 3353#endif 3354 } 3355 else 3356 { 3357 args[i++] = "-E"; /* We tried "-A" here, but that caused 3358 overlapping identical changes to 3359 conflict. See issue #682. */ 3360#ifndef NDEBUG 3361 ++nargs; 3362#endif 3363 } 3364 args[i++] = "-m"; 3365 args[i++] = "-L"; 3366 args[i++] = mine_label; 3367 args[i++] = "-L"; 3368 args[i++] = older_label; /* note: this label is ignored if 3369 using 2-part markers, which is the 3370 case with "-E". */ 3371 args[i++] = "-L"; 3372 args[i++] = yours_label; 3373#ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG 3374 { 3375 svn_boolean_t has_arg; 3376 3377 /* ### FIXME: we really shouldn't be reading the config here; 3378 instead, the necessary bits should be passed in by the caller. 3379 But should we add another parameter to this function, when the 3380 whole external diff3 thing might eventually go away? */ 3381 apr_hash_t *config; 3382 svn_config_t *cfg; 3383 3384 SVN_ERR(svn_config_get_config(&config, pool)); 3385 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; 3386 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS, 3387 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG, 3388 TRUE)); 3389 if (has_arg) 3390 { 3391 const char *diff_cmd, *diff_utf8; 3392 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS, 3393 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF); 3394 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool)); 3395 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, 3396 SVN_VA_NULL); 3397#ifndef NDEBUG 3398 ++nargs; 3399#endif 3400 } 3401 } 3402#endif 3403 args[i++] = svn_dirent_local_style(mine, pool); 3404 args[i++] = svn_dirent_local_style(older, pool); 3405 args[i++] = svn_dirent_local_style(yours, pool); 3406 args[i++] = NULL; 3407#ifndef NDEBUG 3408 SVN_ERR_ASSERT(i == nargs); 3409#endif 3410 3411 /* Run diff3, output the merged text into the scratch file. */ 3412 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args, 3413 exitcode, NULL, 3414 TRUE, /* keep environment */ 3415 NULL, merged, NULL, 3416 pool)); 3417 3418 /* According to the diff3 docs, a '0' means the merge was clean, and 3419 '1' means conflict markers were found. Anything else is real 3420 error. */ 3421 if ((*exitcode != 0) && (*exitcode != 1)) 3422 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, 3423 _("Error running '%s': exitcode was %d, " 3424 "args were:" 3425 "\nin directory '%s', basenames:\n%s\n%s\n%s"), 3426 svn_dirent_local_style(diff3_cmd, pool), 3427 *exitcode, 3428 svn_dirent_local_style(dir, pool), 3429 /* Don't call svn_path_local_style() on 3430 the basenames. We don't want them to 3431 be absolute, and we don't need the 3432 separator conversion. */ 3433 mine, older, yours); 3434 3435 return SVN_NO_ERROR; 3436} 3437 3438 3439/* Canonicalize a string for hashing. Modifies KEY in place. */ 3440static APR_INLINE char * 3441fileext_tolower(char *key) 3442{ 3443 register char *p; 3444 for (p = key; *p != 0; ++p) 3445 *p = (char)apr_tolower(*p); 3446 return key; 3447} 3448 3449 3450svn_error_t * 3451svn_io_parse_mimetypes_file(apr_hash_t **type_map, 3452 const char *mimetypes_file, 3453 apr_pool_t *pool) 3454{ 3455 svn_error_t *err = SVN_NO_ERROR; 3456 apr_hash_t *types = apr_hash_make(pool); 3457 svn_boolean_t eof = FALSE; 3458 svn_stringbuf_t *buf; 3459 apr_pool_t *subpool = svn_pool_create(pool); 3460 apr_file_t *types_file; 3461 svn_stream_t *mimetypes_stream; 3462 3463 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file, 3464 APR_READ, APR_OS_DEFAULT, pool)); 3465 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool); 3466 3467 while (1) 3468 { 3469 apr_array_header_t *tokens; 3470 const char *type; 3471 3472 svn_pool_clear(subpool); 3473 3474 /* Read a line. */ 3475 if ((err = svn_stream_readline(mimetypes_stream, &buf, 3476 APR_EOL_STR, &eof, subpool))) 3477 break; 3478 3479 /* Only pay attention to non-empty, non-comment lines. */ 3480 if (buf->len) 3481 { 3482 int i; 3483 3484 if (buf->data[0] == '#') 3485 continue; 3486 3487 /* Tokenize (into our return pool). */ 3488 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool); 3489 if (tokens->nelts < 2) 3490 continue; 3491 3492 /* The first token in a multi-token line is the media type. 3493 Subsequent tokens are filename extensions associated with 3494 that media type. */ 3495 type = APR_ARRAY_IDX(tokens, 0, const char *); 3496 for (i = 1; i < tokens->nelts; i++) 3497 { 3498 /* We can safely address 'ext' as a non-const string because 3499 * we know svn_cstring_split() allocated it in 'pool' for us. */ 3500 char *ext = APR_ARRAY_IDX(tokens, i, char *); 3501 fileext_tolower(ext); 3502 svn_hash_sets(types, ext, type); 3503 } 3504 } 3505 if (eof) 3506 break; 3507 } 3508 svn_pool_destroy(subpool); 3509 3510 /* If there was an error above, close the file (ignoring any error 3511 from *that*) and return the originally error. */ 3512 if (err) 3513 { 3514 svn_error_clear(svn_stream_close(mimetypes_stream)); 3515 return err; 3516 } 3517 3518 /* Close the stream (which closes the underlying file, too). */ 3519 SVN_ERR(svn_stream_close(mimetypes_stream)); 3520 3521 *type_map = types; 3522 return SVN_NO_ERROR; 3523} 3524 3525 3526svn_error_t * 3527svn_io_detect_mimetype2(const char **mimetype, 3528 const char *file, 3529 apr_hash_t *mimetype_map, 3530 apr_pool_t *pool) 3531{ 3532 static const char * const generic_binary = "application/octet-stream"; 3533 3534 svn_node_kind_t kind; 3535 apr_file_t *fh; 3536 svn_error_t *err; 3537 unsigned char block[1024]; 3538 apr_size_t amt_read = sizeof(block); 3539 3540 /* Default return value is NULL. */ 3541 *mimetype = NULL; 3542 3543 /* If there is a mimetype_map provided, we'll first try to look up 3544 our file's extension in the map. Failing that, we'll run the 3545 heuristic. */ 3546 if (mimetype_map) 3547 { 3548 const char *type_from_map; 3549 char *path_ext; /* Can point to physical const memory but only when 3550 svn_path_splitext sets it to "". */ 3551 svn_path_splitext(NULL, (const char **)&path_ext, file, pool); 3552 fileext_tolower(path_ext); 3553 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext))) 3554 { 3555 *mimetype = type_from_map; 3556 return SVN_NO_ERROR; 3557 } 3558 } 3559 3560 /* See if this file even exists, and make sure it really is a file. */ 3561 SVN_ERR(svn_io_check_path(file, &kind, pool)); 3562 if (kind != svn_node_file) 3563 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 3564 _("Can't detect MIME type of non-file '%s'"), 3565 svn_dirent_local_style(file, pool)); 3566 3567 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool)); 3568 3569 /* Read a block of data from FILE. */ 3570 err = svn_io_file_read(fh, block, &amt_read, pool); 3571 if (err && ! APR_STATUS_IS_EOF(err->apr_err)) 3572 return err; 3573 svn_error_clear(err); 3574 3575 /* Now close the file. No use keeping it open any more. */ 3576 SVN_ERR(svn_io_file_close(fh, pool)); 3577 3578 if (svn_io_is_binary_data(block, amt_read)) 3579 *mimetype = generic_binary; 3580 3581 return SVN_NO_ERROR; 3582} 3583 3584 3585svn_boolean_t 3586svn_io_is_binary_data(const void *data, apr_size_t len) 3587{ 3588 const unsigned char *buf = data; 3589 3590 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) 3591 { 3592 /* This is an empty UTF-8 file which only contains the UTF-8 BOM. 3593 * Treat it as plain text. */ 3594 return FALSE; 3595 } 3596 3597 /* Right now, this function is going to be really stupid. It's 3598 going to examine the block of data, and make sure that 15% 3599 of the bytes are such that their value is in the ranges 0x07-0x0D 3600 or 0x20-0x7F, and that none of those bytes is 0x00. If those 3601 criteria are not met, we're calling it binary. 3602 3603 NOTE: Originally, I intended to target 85% of the bytes being in 3604 the specified ranges, but I flubbed the condition. At any rate, 3605 folks aren't complaining, so I'm not sure that it's worth 3606 adjusting this retroactively now. --cmpilato */ 3607 if (len > 0) 3608 { 3609 apr_size_t i; 3610 apr_size_t binary_count = 0; 3611 3612 /* Run through the data we've read, counting the 'binary-ish' 3613 bytes. HINT: If we see a 0x00 byte, we'll set our count to its 3614 max and stop reading the file. */ 3615 for (i = 0; i < len; i++) 3616 { 3617 if (buf[i] == 0) 3618 { 3619 binary_count = len; 3620 break; 3621 } 3622 if ((buf[i] < 0x07) 3623 || ((buf[i] > 0x0D) && (buf[i] < 0x20)) 3624 || (buf[i] > 0x7F)) 3625 { 3626 binary_count++; 3627 } 3628 } 3629 3630 return (((binary_count * 1000) / len) > 850); 3631 } 3632 3633 return FALSE; 3634} 3635 3636 3637svn_error_t * 3638svn_io_detect_mimetype(const char **mimetype, 3639 const char *file, 3640 apr_pool_t *pool) 3641{ 3642 return svn_io_detect_mimetype2(mimetype, file, NULL, pool); 3643} 3644 3645 3646svn_error_t * 3647svn_io_file_open(apr_file_t **new_file, const char *fname, 3648 apr_int32_t flag, apr_fileperms_t perm, 3649 apr_pool_t *pool) 3650{ 3651 const char *fname_apr; 3652 apr_status_t status; 3653 3654 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool)); 3655 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE, 3656 pool); 3657 3658 if (status) 3659 return svn_error_wrap_apr(status, _("Can't open file '%s'"), 3660 svn_dirent_local_style(fname, pool)); 3661 else 3662 return SVN_NO_ERROR; 3663} 3664 3665 3666static APR_INLINE svn_error_t * 3667do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status, 3668 const char *msg, const char *msg_no_name, 3669 apr_pool_t *pool) 3670{ 3671 const char *name; 3672 svn_error_t *err; 3673 3674 if (! status) 3675 return SVN_NO_ERROR; 3676 3677 err = svn_io_file_name_get(&name, file, pool); 3678 if (err) 3679 name = NULL; 3680 svn_error_clear(err); 3681 3682 /* ### Issue #3014: Return a specific error for broken pipes, 3683 * ### with a single element in the error chain. */ 3684 if (SVN__APR_STATUS_IS_EPIPE(status)) 3685 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL); 3686 3687 if (name) 3688 return svn_error_wrap_apr(status, _(msg), 3689 try_utf8_from_internal_style(name, pool)); 3690 else 3691 return svn_error_wrap_apr(status, "%s", _(msg_no_name)); 3692} 3693 3694 3695svn_error_t * 3696svn_io_file_close(apr_file_t *file, apr_pool_t *pool) 3697{ 3698 return do_io_file_wrapper_cleanup(file, apr_file_close(file), 3699 N_("Can't close file '%s'"), 3700 N_("Can't close stream"), 3701 pool); 3702} 3703 3704 3705svn_error_t * 3706svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool) 3707{ 3708 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file), 3709 N_("Can't read file '%s'"), 3710 N_("Can't read stream"), 3711 pool); 3712} 3713 3714 3715svn_error_t * 3716svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool) 3717{ 3718 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file), 3719 N_("Can't write file '%s'"), 3720 N_("Can't write stream"), 3721 pool); 3722} 3723 3724 3725svn_error_t * 3726svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted, 3727 apr_file_t *file, apr_pool_t *pool) 3728{ 3729 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */ 3730 wanted &= ~SVN__APR_FINFO_MASK_OUT; 3731 3732 return do_io_file_wrapper_cleanup( 3733 file, apr_file_info_get(finfo, wanted, file), 3734 N_("Can't get attribute information from file '%s'"), 3735 N_("Can't get attribute information from stream"), 3736 pool); 3737} 3738 3739 3740svn_error_t * 3741svn_io_file_read(apr_file_t *file, void *buf, 3742 apr_size_t *nbytes, apr_pool_t *pool) 3743{ 3744 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes), 3745 N_("Can't read file '%s'"), 3746 N_("Can't read stream"), 3747 pool); 3748} 3749 3750 3751svn_error_t * 3752svn_io_file_read_full2(apr_file_t *file, void *buf, 3753 apr_size_t nbytes, apr_size_t *bytes_read, 3754 svn_boolean_t *hit_eof, 3755 apr_pool_t *pool) 3756{ 3757 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read); 3758 if (hit_eof) 3759 { 3760 if (APR_STATUS_IS_EOF(status)) 3761 { 3762 *hit_eof = TRUE; 3763 return SVN_NO_ERROR; 3764 } 3765 else 3766 *hit_eof = FALSE; 3767 } 3768 3769 return do_io_file_wrapper_cleanup(file, status, 3770 N_("Can't read file '%s'"), 3771 N_("Can't read stream"), 3772 pool); 3773} 3774 3775 3776svn_error_t * 3777svn_io_file_seek(apr_file_t *file, apr_seek_where_t where, 3778 apr_off_t *offset, apr_pool_t *pool) 3779{ 3780 return do_io_file_wrapper_cleanup( 3781 file, apr_file_seek(file, where, offset), 3782 N_("Can't set position pointer in file '%s'"), 3783 N_("Can't set position pointer in stream"), 3784 pool); 3785} 3786 3787svn_error_t * 3788svn_io_file_aligned_seek(apr_file_t *file, 3789 apr_off_t block_size, 3790 apr_off_t *buffer_start, 3791 apr_off_t offset, 3792 apr_pool_t *scratch_pool) 3793{ 3794 const apr_size_t apr_default_buffer_size = 4096; 3795 apr_size_t file_buffer_size = apr_default_buffer_size; 3796 apr_off_t desired_offset = 0; 3797 apr_off_t current = 0; 3798 apr_off_t aligned_offset = 0; 3799 svn_boolean_t fill_buffer = FALSE; 3800 3801 /* paranoia check: huge blocks on 32 bit machines may cause overflows */ 3802 SVN_ERR_ASSERT(block_size == (apr_size_t)block_size); 3803 3804 /* default for invalid block sizes */ 3805 if (block_size == 0) 3806 block_size = apr_default_buffer_size; 3807 3808 file_buffer_size = apr_file_buffer_size_get(file); 3809 3810 /* don't try to set a buffer size for non-buffered files! */ 3811 if (file_buffer_size == 0) 3812 { 3813 aligned_offset = offset; 3814 } 3815 else if (file_buffer_size != (apr_size_t)block_size) 3816 { 3817 /* FILE has the wrong buffer size. correct it */ 3818 char *buffer; 3819 file_buffer_size = (apr_size_t)block_size; 3820 buffer = apr_palloc(apr_file_pool_get(file), file_buffer_size); 3821 apr_file_buffer_set(file, buffer, file_buffer_size); 3822 3823 /* seek to the start of the block and cause APR to read 1 block */ 3824 aligned_offset = offset - (offset % block_size); 3825 fill_buffer = TRUE; 3826 } 3827 else 3828 { 3829 aligned_offset = offset - (offset % file_buffer_size); 3830 3831 /* We have no way to determine the block start of an APR file. 3832 Furthermore, we don't want to throw away the current buffer 3833 contents. Thus, we re-align the buffer only if the CURRENT 3834 offset definitely lies outside the desired, aligned buffer. 3835 This covers the typical case of linear reads getting very 3836 close to OFFSET but reading the previous / following block. 3837 3838 Note that ALIGNED_OFFSET may still be within the current 3839 buffer and no I/O will actually happen in the FILL_BUFFER 3840 section below. 3841 */ 3842 SVN_ERR(svn_io_file_seek(file, APR_CUR, ¤t, scratch_pool)); 3843 fill_buffer = aligned_offset + file_buffer_size <= current 3844 || current <= aligned_offset; 3845 } 3846 3847 if (fill_buffer) 3848 { 3849 char dummy; 3850 apr_status_t status; 3851 3852 /* seek to the start of the block and cause APR to read 1 block */ 3853 SVN_ERR(svn_io_file_seek(file, APR_SET, &aligned_offset, 3854 scratch_pool)); 3855 status = apr_file_getc(&dummy, file); 3856 3857 /* read may fail if we seek to or behind EOF. That's ok then. */ 3858 if (status != APR_SUCCESS && !APR_STATUS_IS_EOF(status)) 3859 return do_io_file_wrapper_cleanup(file, status, 3860 N_("Can't read file '%s'"), 3861 N_("Can't read stream"), 3862 scratch_pool); 3863 } 3864 3865 /* finally, seek to the OFFSET the caller wants */ 3866 desired_offset = offset; 3867 SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, scratch_pool)); 3868 if (desired_offset != offset) 3869 return do_io_file_wrapper_cleanup(file, APR_EOF, 3870 N_("Can't seek in file '%s'"), 3871 N_("Can't seek in stream"), 3872 scratch_pool); 3873 3874 /* return the buffer start that we (probably) enforced */ 3875 if (buffer_start) 3876 *buffer_start = aligned_offset; 3877 3878 return SVN_NO_ERROR; 3879} 3880 3881 3882svn_error_t * 3883svn_io_file_write(apr_file_t *file, const void *buf, 3884 apr_size_t *nbytes, apr_pool_t *pool) 3885{ 3886 return svn_error_trace(do_io_file_wrapper_cleanup( 3887 file, apr_file_write(file, buf, nbytes), 3888 N_("Can't write to file '%s'"), 3889 N_("Can't write to stream"), 3890 pool)); 3891} 3892 3893svn_error_t * 3894svn_io_file_flush(apr_file_t *file, 3895 apr_pool_t *scratch_pool) 3896{ 3897 return svn_error_trace(do_io_file_wrapper_cleanup( 3898 file, apr_file_flush(file), 3899 N_("Can't flush file '%s'"), 3900 N_("Can't flush stream"), 3901 scratch_pool)); 3902} 3903 3904svn_error_t * 3905svn_io_file_write_full(apr_file_t *file, const void *buf, 3906 apr_size_t nbytes, apr_size_t *bytes_written, 3907 apr_pool_t *pool) 3908{ 3909 /* We cannot simply call apr_file_write_full on Win32 as it may fail 3910 for larger values of NBYTES. In that case, we have to emulate the 3911 "_full" part here. Thus, always call apr_file_write directly on 3912 Win32 as this minimizes overhead for small data buffers. */ 3913#ifdef WIN32 3914#define MAXBUFSIZE 30*1024 3915 apr_size_t bw = nbytes; 3916 apr_size_t to_write = nbytes; 3917 3918 /* try a simple "write everything at once" first */ 3919 apr_status_t rv = apr_file_write(file, buf, &bw); 3920 buf = (char *)buf + bw; 3921 to_write -= bw; 3922 3923 /* if the OS cannot handle that, use smaller chunks */ 3924 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY) 3925 && nbytes > MAXBUFSIZE) 3926 { 3927 do { 3928 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write; 3929 rv = apr_file_write(file, buf, &bw); 3930 buf = (char *)buf + bw; 3931 to_write -= bw; 3932 } while (rv == APR_SUCCESS && to_write > 0); 3933 } 3934 3935 /* bytes_written may actually be NULL */ 3936 if (bytes_written) 3937 *bytes_written = nbytes - to_write; 3938#undef MAXBUFSIZE 3939#else 3940 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written); 3941#endif 3942 3943 return svn_error_trace(do_io_file_wrapper_cleanup( 3944 file, rv, 3945 N_("Can't write to file '%s'"), 3946 N_("Can't write to stream"), 3947 pool)); 3948} 3949 3950 3951svn_error_t * 3952svn_io_write_unique(const char **tmp_path, 3953 const char *dirpath, 3954 const void *buf, 3955 apr_size_t nbytes, 3956 svn_io_file_del_t delete_when, 3957 apr_pool_t *pool) 3958{ 3959 apr_file_t *new_file; 3960 svn_error_t *err; 3961 3962 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath, 3963 delete_when, pool, pool)); 3964 3965 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool); 3966 3967 if (!err) 3968 { 3969 /* svn_io_file_flush_to_disk() can be very expensive, so use the 3970 cheaper standard flush if the file is created as temporary file 3971 anyway */ 3972 if (delete_when == svn_io_file_del_none) 3973 err = svn_io_file_flush_to_disk(new_file, pool); 3974 else 3975 err = svn_io_file_flush(new_file, pool); 3976 } 3977 3978 return svn_error_trace( 3979 svn_error_compose_create(err, 3980 svn_io_file_close(new_file, pool))); 3981} 3982 3983svn_error_t * 3984svn_io_write_atomic(const char *final_path, 3985 const void *buf, 3986 apr_size_t nbytes, 3987 const char *copy_perms_path, 3988 apr_pool_t *scratch_pool) 3989{ 3990 apr_file_t *tmp_file; 3991 const char *tmp_path; 3992 svn_error_t *err; 3993 const char *dirname = svn_dirent_dirname(final_path, scratch_pool); 3994 3995 SVN_ERR(svn_io_open_unique_file3(&tmp_file, &tmp_path, dirname, 3996 svn_io_file_del_none, 3997 scratch_pool, scratch_pool)); 3998 3999 err = svn_io_file_write_full(tmp_file, buf, nbytes, NULL, scratch_pool); 4000 4001 if (!err) 4002 err = svn_io_file_flush_to_disk(tmp_file, scratch_pool); 4003 4004 err = svn_error_compose_create(err, 4005 svn_io_file_close(tmp_file, scratch_pool)); 4006 4007 if (!err && copy_perms_path) 4008 err = svn_io_copy_perms(copy_perms_path, tmp_path, scratch_pool); 4009 4010 if (!err) 4011 err = svn_io_file_rename(tmp_path, final_path, scratch_pool); 4012 4013 if (err) 4014 { 4015 err = svn_error_compose_create(err, 4016 svn_io_remove_file2(tmp_path, TRUE, 4017 scratch_pool)); 4018 4019 return svn_error_createf(err->apr_err, err, 4020 _("Can't write '%s' atomically"), 4021 svn_dirent_local_style(final_path, 4022 scratch_pool)); 4023 } 4024 4025#ifdef __linux__ 4026 { 4027 /* Linux has the unusual feature that fsync() on a file is not 4028 enough to ensure that a file's directory entries have been 4029 flushed to disk; you have to fsync the directory as well. 4030 On other operating systems, we'd only be asking for trouble 4031 by trying to open and fsync a directory. */ 4032 apr_file_t *file; 4033 4034 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, 4035 scratch_pool)); 4036 SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); 4037 SVN_ERR(svn_io_file_close(file, scratch_pool)); 4038 } 4039#endif 4040 4041 return SVN_NO_ERROR; 4042} 4043 4044svn_error_t * 4045svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool) 4046{ 4047 /* This is a work-around. APR would flush the write buffer 4048 _after_ truncating the file causing now invalid buffered 4049 data to be written behind OFFSET. */ 4050 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file), 4051 N_("Can't flush file '%s'"), 4052 N_("Can't flush stream"), 4053 pool)); 4054 4055 return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset), 4056 N_("Can't truncate file '%s'"), 4057 N_("Can't truncate stream"), 4058 pool); 4059} 4060 4061 4062svn_error_t * 4063svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit, 4064 apr_pool_t *pool) 4065{ 4066 /* variables */ 4067 apr_size_t total_read = 0; 4068 svn_boolean_t eof = FALSE; 4069 const char *name; 4070 svn_error_t *err; 4071 apr_size_t buf_size = *limit; 4072 4073 while (buf_size > 0) 4074 { 4075 /* read a fair chunk of data at once. But don't get too ambitious 4076 * as that would result in too much waste. Also make sure we can 4077 * put a NUL after the last byte read. 4078 */ 4079 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128; 4080 apr_size_t bytes_read = 0; 4081 char *eol; 4082 4083 if (to_read == 0) 4084 break; 4085 4086 /* read data block (or just a part of it) */ 4087 SVN_ERR(svn_io_file_read_full2(file, buf, to_read, 4088 &bytes_read, &eof, pool)); 4089 4090 /* look or a newline char */ 4091 buf[bytes_read] = 0; 4092 eol = strchr(buf, '\n'); 4093 if (eol) 4094 { 4095 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read; 4096 4097 *eol = 0; 4098 *limit = total_read + (eol - buf); 4099 4100 /* correct the file pointer: 4101 * appear as though we just had read the newline char 4102 */ 4103 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool)); 4104 4105 return SVN_NO_ERROR; 4106 } 4107 else if (eof) 4108 { 4109 /* no EOL found but we hit the end of the file. 4110 * Generate a nice EOF error object and return it. 4111 */ 4112 char dummy; 4113 SVN_ERR(svn_io_file_getc(&dummy, file, pool)); 4114 } 4115 4116 /* next data chunk */ 4117 buf_size -= bytes_read; 4118 buf += bytes_read; 4119 total_read += bytes_read; 4120 } 4121 4122 /* buffer limit has been exceeded without finding the EOL */ 4123 err = svn_io_file_name_get(&name, file, pool); 4124 if (err) 4125 name = NULL; 4126 svn_error_clear(err); 4127 4128 if (name) 4129 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 4130 _("Can't read length line in file '%s'"), 4131 svn_dirent_local_style(name, pool)); 4132 else 4133 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 4134 _("Can't read length line in stream")); 4135} 4136 4137 4138svn_error_t * 4139svn_io_stat(apr_finfo_t *finfo, const char *fname, 4140 apr_int32_t wanted, apr_pool_t *pool) 4141{ 4142 apr_status_t status; 4143 const char *fname_apr; 4144 4145 /* APR doesn't like "" directories */ 4146 if (fname[0] == '\0') 4147 fname = "."; 4148 4149 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool)); 4150 4151 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */ 4152 wanted &= ~SVN__APR_FINFO_MASK_OUT; 4153 4154 status = apr_stat(finfo, fname_apr, wanted, pool); 4155 if (status) 4156 return svn_error_wrap_apr(status, _("Can't stat '%s'"), 4157 svn_dirent_local_style(fname, pool)); 4158 4159 return SVN_NO_ERROR; 4160} 4161 4162 4163svn_error_t * 4164svn_io_file_rename(const char *from_path, const char *to_path, 4165 apr_pool_t *pool) 4166{ 4167 apr_status_t status = APR_SUCCESS; 4168 const char *from_path_apr, *to_path_apr; 4169 4170 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool)); 4171 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool)); 4172 4173 status = apr_file_rename(from_path_apr, to_path_apr, pool); 4174 4175#if defined(WIN32) || defined(__OS2__) 4176 /* If the target file is read only NTFS reports EACCESS and 4177 FAT/FAT32 reports EEXIST */ 4178 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) 4179 { 4180 /* Set the destination file writable because Windows will not 4181 allow us to rename when to_path is read-only, but will 4182 allow renaming when from_path is read only. */ 4183 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); 4184 4185 status = apr_file_rename(from_path_apr, to_path_apr, pool); 4186 } 4187 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool)); 4188#endif /* WIN32 || __OS2__ */ 4189 4190 if (status) 4191 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"), 4192 svn_dirent_local_style(from_path, pool), 4193 svn_dirent_local_style(to_path, pool)); 4194 4195 return SVN_NO_ERROR; 4196} 4197 4198 4199svn_error_t * 4200svn_io_file_move(const char *from_path, const char *to_path, 4201 apr_pool_t *pool) 4202{ 4203 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool); 4204 4205 if (err && APR_STATUS_IS_EXDEV(err->apr_err)) 4206 { 4207 const char *tmp_to_path; 4208 4209 svn_error_clear(err); 4210 4211 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path, 4212 svn_dirent_dirname(to_path, pool), 4213 svn_io_file_del_none, 4214 pool, pool)); 4215 4216 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool); 4217 if (err) 4218 goto failed_tmp; 4219 4220 err = svn_io_file_rename(tmp_to_path, to_path, pool); 4221 if (err) 4222 goto failed_tmp; 4223 4224 err = svn_io_remove_file2(from_path, FALSE, pool); 4225 if (! err) 4226 return SVN_NO_ERROR; 4227 4228 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool)); 4229 4230 return err; 4231 4232 failed_tmp: 4233 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool)); 4234 } 4235 4236 return err; 4237} 4238 4239/* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden. 4240 HIDDEN determines if the hidden attribute 4241 should be set on the newly created directory. */ 4242static svn_error_t * 4243dir_make(const char *path, apr_fileperms_t perm, 4244 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool) 4245{ 4246 apr_status_t status; 4247 const char *path_apr; 4248 4249 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 4250 4251 /* APR doesn't like "" directories */ 4252 if (path_apr[0] == '\0') 4253 path_apr = "."; 4254 4255#if (APR_OS_DEFAULT & APR_WSTICKY) 4256 /* The APR shipped with httpd 2.0.50 contains a bug where 4257 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits. 4258 There is a special case for file creation, but not directory 4259 creation, so directories wind up getting created with the sticky 4260 bit set. (There is no such thing as a setuid directory, and the 4261 setgid bit is apparently ignored at mkdir() time.) If we detect 4262 this problem, work around it by unsetting those bits if we are 4263 passed APR_OS_DEFAULT. */ 4264 if (perm == APR_OS_DEFAULT) 4265 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY); 4266#endif 4267 4268 status = apr_dir_make(path_apr, perm, pool); 4269 4270#ifdef WIN32 4271 /* Don't retry on ERROR_ACCESS_DENIED, as that typically signals a 4272 permanent error */ 4273 if (status == APR_FROM_OS_ERROR(ERROR_SHARING_VIOLATION)) 4274 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool)); 4275#endif 4276 4277 if (status) 4278 return svn_error_wrap_apr(status, _("Can't create directory '%s'"), 4279 svn_dirent_local_style(path, pool)); 4280 4281#ifdef APR_FILE_ATTR_HIDDEN 4282 if (hidden) 4283 { 4284#ifndef WIN32 4285 status = apr_file_attrs_set(path_apr, 4286 APR_FILE_ATTR_HIDDEN, 4287 APR_FILE_ATTR_HIDDEN, 4288 pool); 4289 if (status) 4290 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"), 4291 svn_dirent_local_style(path, pool)); 4292#else 4293 /* on Windows, use our wrapper so we can also set the 4294 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */ 4295 svn_error_t *err = 4296 io_win_file_attrs_set(path_apr, 4297 FILE_ATTRIBUTE_HIDDEN | 4298 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, 4299 FILE_ATTRIBUTE_HIDDEN | 4300 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, 4301 pool); 4302 if (err) 4303 return svn_error_createf(err->apr_err, err, 4304 _("Can't hide directory '%s'"), 4305 svn_dirent_local_style(path, pool)); 4306#endif /* WIN32 */ 4307 } 4308#endif /* APR_FILE_ATTR_HIDDEN */ 4309 4310/* Windows does not implement sgid. Skip here because retrieving 4311 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented 4312 to be 'incredibly expensive'. */ 4313#ifndef WIN32 4314 if (sgid) 4315 { 4316 apr_finfo_t finfo; 4317 4318 /* Per our contract, don't do error-checking. Some filesystems 4319 * don't support the sgid bit, and that's okay. */ 4320 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool); 4321 4322 if (!status) 4323 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID); 4324 } 4325#endif 4326 4327 return SVN_NO_ERROR; 4328} 4329 4330svn_error_t * 4331svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool) 4332{ 4333 return dir_make(path, perm, FALSE, FALSE, pool); 4334} 4335 4336svn_error_t * 4337svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm, 4338 apr_pool_t *pool) 4339{ 4340 return dir_make(path, perm, TRUE, FALSE, pool); 4341} 4342 4343svn_error_t * 4344svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm, 4345 apr_pool_t *pool) 4346{ 4347 return dir_make(path, perm, FALSE, TRUE, pool); 4348} 4349 4350 4351svn_error_t * 4352svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool) 4353{ 4354 apr_status_t status; 4355 const char *dirname_apr; 4356 4357 /* APR doesn't like "" directories */ 4358 if (dirname[0] == '\0') 4359 dirname = "."; 4360 4361 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool)); 4362 4363 status = apr_dir_open(new_dir, dirname_apr, pool); 4364 if (status) 4365 return svn_error_wrap_apr(status, _("Can't open directory '%s'"), 4366 svn_dirent_local_style(dirname, pool)); 4367 4368 return SVN_NO_ERROR; 4369} 4370 4371svn_error_t * 4372svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool) 4373{ 4374 apr_status_t status; 4375 const char *dirname_apr; 4376 4377 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool)); 4378 4379 status = apr_dir_remove(dirname_apr, pool); 4380 4381#ifdef WIN32 4382 { 4383 svn_boolean_t retry = TRUE; 4384 4385 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY) 4386 { 4387 apr_status_t empty_status = dir_is_empty(dirname_apr, pool); 4388 4389 if (APR_STATUS_IS_ENOTEMPTY(empty_status)) 4390 retry = FALSE; 4391 } 4392 4393 if (retry) 4394 { 4395 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool)); 4396 } 4397 } 4398#endif 4399 if (status) 4400 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"), 4401 svn_dirent_local_style(dirname, pool)); 4402 4403 return SVN_NO_ERROR; 4404} 4405 4406 4407svn_error_t * 4408svn_io_dir_read(apr_finfo_t *finfo, 4409 apr_int32_t wanted, 4410 apr_dir_t *thedir, 4411 apr_pool_t *pool) 4412{ 4413 apr_status_t status; 4414 4415 status = apr_dir_read(finfo, wanted, thedir); 4416 4417 if (status) 4418 return svn_error_wrap_apr(status, _("Can't read directory")); 4419 4420 /* It would be nice to use entry_name_to_utf8() below, but can we 4421 get the dir's path out of an apr_dir_t? I don't see a reliable 4422 way to do it. */ 4423 4424 if (finfo->fname) 4425 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool)); 4426 4427 if (finfo->name) 4428 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool)); 4429 4430 return SVN_NO_ERROR; 4431} 4432 4433svn_error_t * 4434svn_io_dir_close(apr_dir_t *thedir) 4435{ 4436 apr_status_t apr_err = apr_dir_close(thedir); 4437 if (apr_err) 4438 return svn_error_wrap_apr(apr_err, _("Error closing directory")); 4439 4440 return SVN_NO_ERROR; 4441} 4442 4443svn_error_t * 4444svn_io_dir_walk2(const char *dirname, 4445 apr_int32_t wanted, 4446 svn_io_walk_func_t walk_func, 4447 void *walk_baton, 4448 apr_pool_t *pool) 4449{ 4450 apr_status_t apr_err; 4451 apr_dir_t *handle; 4452 apr_pool_t *subpool; 4453 const char *dirname_apr; 4454 apr_finfo_t finfo; 4455 4456 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME; 4457 4458 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */ 4459 wanted &= ~SVN__APR_FINFO_MASK_OUT; 4460 4461 /* The documentation for apr_dir_read used to state that "." and ".." 4462 will be returned as the first two files, but it doesn't 4463 work that way in practice, in particular ext3 on Linux-2.6 doesn't 4464 follow the rules. For details see 4465 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666 4466 4467 If APR ever does implement "dot-first" then it would be possible to 4468 remove the svn_io_stat and walk_func calls and use the walk_func 4469 inside the loop. 4470 4471 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is 4472 documented to provide it, so we have to do a bit extra. */ 4473 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool)); 4474 SVN_ERR(cstring_from_utf8(&finfo.name, 4475 svn_dirent_basename(dirname, pool), 4476 pool)); 4477 finfo.valid |= APR_FINFO_NAME; 4478 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool)); 4479 4480 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool)); 4481 4482 /* APR doesn't like "" directories */ 4483 if (dirname_apr[0] == '\0') 4484 dirname_apr = "."; 4485 4486 apr_err = apr_dir_open(&handle, dirname_apr, pool); 4487 if (apr_err) 4488 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"), 4489 svn_dirent_local_style(dirname, pool)); 4490 4491 /* iteration subpool */ 4492 subpool = svn_pool_create(pool); 4493 4494 while (1) 4495 { 4496 const char *name_utf8; 4497 const char *full_path; 4498 4499 svn_pool_clear(subpool); 4500 4501 apr_err = apr_dir_read(&finfo, wanted, handle); 4502 if (APR_STATUS_IS_ENOENT(apr_err)) 4503 break; 4504 else if (apr_err) 4505 { 4506 return svn_error_wrap_apr(apr_err, 4507 _("Can't read directory entry in '%s'"), 4508 svn_dirent_local_style(dirname, pool)); 4509 } 4510 4511 if (finfo.filetype == APR_DIR) 4512 { 4513 if (finfo.name[0] == '.' 4514 && (finfo.name[1] == '\0' 4515 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))) 4516 /* skip "." and ".." */ 4517 continue; 4518 4519 /* some other directory. recurse. it will be passed to the 4520 callback inside the recursion. */ 4521 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname, 4522 subpool)); 4523 full_path = svn_dirent_join(dirname, name_utf8, subpool); 4524 SVN_ERR(svn_io_dir_walk2(full_path, 4525 wanted, 4526 walk_func, 4527 walk_baton, 4528 subpool)); 4529 } 4530 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK) 4531 { 4532 /* some other directory. pass it to the callback. */ 4533 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname, 4534 subpool)); 4535 full_path = svn_dirent_join(dirname, name_utf8, subpool); 4536 SVN_ERR((*walk_func)(walk_baton, 4537 full_path, 4538 &finfo, 4539 subpool)); 4540 } 4541 /* else: 4542 Some other type of file; skip it for now. We've reserved the 4543 right to expand our coverage here in the future, though, 4544 without revving this API. 4545 */ 4546 } 4547 4548 svn_pool_destroy(subpool); 4549 4550 apr_err = apr_dir_close(handle); 4551 if (apr_err) 4552 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"), 4553 svn_dirent_local_style(dirname, pool)); 4554 4555 return SVN_NO_ERROR; 4556} 4557 4558 4559 4560/** 4561 * Determine if a directory is empty or not. 4562 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not. 4563 * @param path The directory. 4564 * @param pool Used for temporary allocation. 4565 * @remark If path is not a directory, or some other error occurs, 4566 * then return the appropriate apr status code. 4567 * 4568 * (This function is written in APR style, in anticipation of 4569 * perhaps someday being moved to APR as 'apr_dir_is_empty'.) 4570 */ 4571static apr_status_t 4572dir_is_empty(const char *dir, apr_pool_t *pool) 4573{ 4574 apr_status_t apr_err; 4575 apr_dir_t *dir_handle; 4576 apr_finfo_t finfo; 4577 apr_status_t retval = APR_SUCCESS; 4578 4579 /* APR doesn't like "" directories */ 4580 if (dir[0] == '\0') 4581 dir = "."; 4582 4583 apr_err = apr_dir_open(&dir_handle, dir, pool); 4584 if (apr_err != APR_SUCCESS) 4585 return apr_err; 4586 4587 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle); 4588 apr_err == APR_SUCCESS; 4589 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle)) 4590 { 4591 /* Ignore entries for this dir and its parent, robustly. 4592 (APR promises that they'll come first, so technically 4593 this guard could be moved outside the loop. But Ryan Bloom 4594 says he doesn't believe it, and I believe him. */ 4595 if (! (finfo.name[0] == '.' 4596 && (finfo.name[1] == '\0' 4597 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))) 4598 { 4599 retval = APR_ENOTEMPTY; 4600 break; 4601 } 4602 } 4603 4604 /* Make sure we broke out of the loop for the right reason. */ 4605 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err)) 4606 return apr_err; 4607 4608 apr_err = apr_dir_close(dir_handle); 4609 if (apr_err != APR_SUCCESS) 4610 return apr_err; 4611 4612 return retval; 4613} 4614 4615 4616svn_error_t * 4617svn_io_dir_empty(svn_boolean_t *is_empty_p, 4618 const char *path, 4619 apr_pool_t *pool) 4620{ 4621 apr_status_t status; 4622 const char *path_apr; 4623 4624 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 4625 4626 status = dir_is_empty(path_apr, pool); 4627 4628 if (!status) 4629 *is_empty_p = TRUE; 4630 else if (APR_STATUS_IS_ENOTEMPTY(status)) 4631 *is_empty_p = FALSE; 4632 else 4633 return svn_error_wrap_apr(status, _("Can't check directory '%s'"), 4634 svn_dirent_local_style(path, pool)); 4635 4636 return SVN_NO_ERROR; 4637} 4638 4639 4640 4641/*** Version/format files ***/ 4642 4643svn_error_t * 4644svn_io_write_version_file(const char *path, 4645 int version, 4646 apr_pool_t *pool) 4647{ 4648 const char *path_tmp; 4649 const char *format_contents = apr_psprintf(pool, "%d\n", version); 4650 4651 SVN_ERR_ASSERT(version >= 0); 4652 4653 SVN_ERR(svn_io_write_unique(&path_tmp, 4654 svn_dirent_dirname(path, pool), 4655 format_contents, strlen(format_contents), 4656 svn_io_file_del_none, pool)); 4657 4658#if defined(WIN32) || defined(__OS2__) 4659 /* make the destination writable, but only on Windows, because 4660 Windows does not let us replace read-only files. */ 4661 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool)); 4662#endif /* WIN32 || __OS2__ */ 4663 4664 /* rename the temp file as the real destination */ 4665 SVN_ERR(svn_io_file_rename(path_tmp, path, pool)); 4666 4667 /* And finally remove the perms to make it read only */ 4668 return svn_io_set_file_read_only(path, FALSE, pool); 4669} 4670 4671 4672svn_error_t * 4673svn_io_read_version_file(int *version, 4674 const char *path, 4675 apr_pool_t *pool) 4676{ 4677 apr_file_t *format_file; 4678 char buf[80]; 4679 apr_size_t len; 4680 svn_error_t *err; 4681 4682 /* Read a chunk of data from PATH */ 4683 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ, 4684 APR_OS_DEFAULT, pool)); 4685 len = sizeof(buf); 4686 err = svn_io_file_read(format_file, buf, &len, pool); 4687 4688 /* Close the file. */ 4689 SVN_ERR(svn_error_compose_create(err, 4690 svn_io_file_close(format_file, pool))); 4691 4692 /* If there was no data in PATH, return an error. */ 4693 if (len == 0) 4694 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, 4695 _("Reading '%s'"), 4696 svn_dirent_local_style(path, pool)); 4697 4698 /* Check that the first line contains only digits. */ 4699 { 4700 apr_size_t i; 4701 4702 for (i = 0; i < len; ++i) 4703 { 4704 char c = buf[i]; 4705 4706 if (i > 0 && (c == '\r' || c == '\n')) 4707 { 4708 buf[i] = '\0'; 4709 break; 4710 } 4711 if (! svn_ctype_isdigit(c)) 4712 return svn_error_createf 4713 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 4714 _("First line of '%s' contains non-digit"), 4715 svn_dirent_local_style(path, pool)); 4716 } 4717 } 4718 4719 /* Convert to integer. */ 4720 SVN_ERR(svn_cstring_atoi(version, buf)); 4721 4722 return SVN_NO_ERROR; 4723} 4724 4725 4726/* Do a byte-for-byte comparison of FILE1 and FILE2. */ 4727static svn_error_t * 4728contents_identical_p(svn_boolean_t *identical_p, 4729 const char *file1, 4730 const char *file2, 4731 apr_pool_t *pool) 4732{ 4733 svn_error_t *err; 4734 apr_size_t bytes_read1, bytes_read2; 4735 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 4736 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 4737 apr_file_t *file1_h; 4738 apr_file_t *file2_h; 4739 svn_boolean_t eof1 = FALSE; 4740 svn_boolean_t eof2 = FALSE; 4741 4742 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT, 4743 pool)); 4744 4745 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT, 4746 pool); 4747 4748 if (err) 4749 return svn_error_trace( 4750 svn_error_compose_create(err, 4751 svn_io_file_close(file1_h, pool))); 4752 4753 *identical_p = TRUE; /* assume TRUE, until disproved below */ 4754 while (!err && !eof1 && !eof2) 4755 { 4756 err = svn_io_file_read_full2(file1_h, buf1, 4757 SVN__STREAM_CHUNK_SIZE, &bytes_read1, 4758 &eof1, pool); 4759 if (err) 4760 break; 4761 4762 err = svn_io_file_read_full2(file2_h, buf2, 4763 SVN__STREAM_CHUNK_SIZE, &bytes_read2, 4764 &eof2, pool); 4765 if (err) 4766 break; 4767 4768 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1)) 4769 { 4770 *identical_p = FALSE; 4771 break; 4772 } 4773 } 4774 4775 /* Special case: one file being a prefix of the other and the shorter 4776 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */ 4777 if (!err && (eof1 != eof2)) 4778 *identical_p = FALSE; 4779 4780 return svn_error_trace( 4781 svn_error_compose_create( 4782 err, 4783 svn_error_compose_create(svn_io_file_close(file1_h, pool), 4784 svn_io_file_close(file2_h, pool)))); 4785} 4786 4787 4788 4789/* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */ 4790static svn_error_t * 4791contents_three_identical_p(svn_boolean_t *identical_p12, 4792 svn_boolean_t *identical_p23, 4793 svn_boolean_t *identical_p13, 4794 const char *file1, 4795 const char *file2, 4796 const char *file3, 4797 apr_pool_t *scratch_pool) 4798{ 4799 svn_error_t *err; 4800 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 4801 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 4802 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 4803 apr_file_t *file1_h; 4804 apr_file_t *file2_h; 4805 apr_file_t *file3_h; 4806 svn_boolean_t eof1 = FALSE; 4807 svn_boolean_t eof2 = FALSE; 4808 svn_boolean_t eof3 = FALSE; 4809 4810 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT, 4811 scratch_pool)); 4812 4813 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT, 4814 scratch_pool); 4815 4816 if (err) 4817 return svn_error_trace( 4818 svn_error_compose_create(err, 4819 svn_io_file_close(file1_h, scratch_pool))); 4820 4821 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT, 4822 scratch_pool); 4823 4824 if (err) 4825 return svn_error_trace( 4826 svn_error_compose_create( 4827 err, 4828 svn_error_compose_create(svn_io_file_close(file1_h, 4829 scratch_pool), 4830 svn_io_file_close(file2_h, 4831 scratch_pool)))); 4832 4833 /* assume TRUE, until disproved below */ 4834 *identical_p12 = *identical_p23 = *identical_p13 = TRUE; 4835 /* We need to read as long as no error occurs, and as long as one of the 4836 * flags could still change due to a read operation */ 4837 while (!err 4838 && ((*identical_p12 && !eof1 && !eof2) 4839 || (*identical_p23 && !eof2 && !eof3) 4840 || (*identical_p13 && !eof1 && !eof3))) 4841 { 4842 apr_size_t bytes_read1, bytes_read2, bytes_read3; 4843 svn_boolean_t read_1, read_2, read_3; 4844 4845 read_1 = read_2 = read_3 = FALSE; 4846 4847 /* As long as a file is not at the end yet, and it is still 4848 * potentially identical to another file, we read the next chunk.*/ 4849 if (!eof1 && (*identical_p12 || *identical_p13)) 4850 { 4851 err = svn_io_file_read_full2(file1_h, buf1, 4852 SVN__STREAM_CHUNK_SIZE, &bytes_read1, 4853 &eof1, scratch_pool); 4854 if (err) 4855 break; 4856 read_1 = TRUE; 4857 } 4858 4859 if (!eof2 && (*identical_p12 || *identical_p23)) 4860 { 4861 err = svn_io_file_read_full2(file2_h, buf2, 4862 SVN__STREAM_CHUNK_SIZE, &bytes_read2, 4863 &eof2, scratch_pool); 4864 if (err) 4865 break; 4866 read_2 = TRUE; 4867 } 4868 4869 if (!eof3 && (*identical_p13 || *identical_p23)) 4870 { 4871 err = svn_io_file_read_full2(file3_h, buf3, 4872 SVN__STREAM_CHUNK_SIZE, &bytes_read3, 4873 &eof3, scratch_pool); 4874 if (err) 4875 break; 4876 read_3 = TRUE; 4877 } 4878 4879 /* If the files are still marked identical, and at least one of them 4880 * is not at the end of file, we check whether they differ, and set 4881 * their flag to false then. */ 4882 if (*identical_p12 4883 && (read_1 || read_2) 4884 && ((eof1 != eof2) 4885 || (bytes_read1 != bytes_read2) 4886 || memcmp(buf1, buf2, bytes_read1))) 4887 { 4888 *identical_p12 = FALSE; 4889 } 4890 4891 if (*identical_p23 4892 && (read_2 || read_3) 4893 && ((eof2 != eof3) 4894 || (bytes_read2 != bytes_read3) 4895 || memcmp(buf2, buf3, bytes_read2))) 4896 { 4897 *identical_p23 = FALSE; 4898 } 4899 4900 if (*identical_p13 4901 && (read_1 || read_3) 4902 && ((eof1 != eof3) 4903 || (bytes_read1 != bytes_read3) 4904 || memcmp(buf1, buf3, bytes_read3))) 4905 { 4906 *identical_p13 = FALSE; 4907 } 4908 } 4909 4910 return svn_error_trace( 4911 svn_error_compose_create( 4912 err, 4913 svn_error_compose_create( 4914 svn_io_file_close(file1_h, scratch_pool), 4915 svn_error_compose_create( 4916 svn_io_file_close(file2_h, scratch_pool), 4917 svn_io_file_close(file3_h, scratch_pool))))); 4918} 4919 4920 4921 4922svn_error_t * 4923svn_io_files_contents_same_p(svn_boolean_t *same, 4924 const char *file1, 4925 const char *file2, 4926 apr_pool_t *pool) 4927{ 4928 svn_boolean_t q; 4929 4930 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool)); 4931 4932 if (q) 4933 { 4934 *same = FALSE; 4935 return SVN_NO_ERROR; 4936 } 4937 4938 SVN_ERR(contents_identical_p(&q, file1, file2, pool)); 4939 4940 if (q) 4941 *same = TRUE; 4942 else 4943 *same = FALSE; 4944 4945 return SVN_NO_ERROR; 4946} 4947 4948svn_error_t * 4949svn_io_files_contents_three_same_p(svn_boolean_t *same12, 4950 svn_boolean_t *same23, 4951 svn_boolean_t *same13, 4952 const char *file1, 4953 const char *file2, 4954 const char *file3, 4955 apr_pool_t *scratch_pool) 4956{ 4957 svn_boolean_t diff_size12, diff_size23, diff_size13; 4958 4959 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12, 4960 &diff_size23, 4961 &diff_size13, 4962 file1, 4963 file2, 4964 file3, 4965 scratch_pool)); 4966 4967 if (diff_size12 && diff_size23 && diff_size13) 4968 { 4969 *same12 = *same23 = *same13 = FALSE; 4970 } 4971 else if (diff_size12 && diff_size23) 4972 { 4973 *same12 = *same23 = FALSE; 4974 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool)); 4975 } 4976 else if (diff_size23 && diff_size13) 4977 { 4978 *same23 = *same13 = FALSE; 4979 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool)); 4980 } 4981 else if (diff_size12 && diff_size13) 4982 { 4983 *same12 = *same13 = FALSE; 4984 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool)); 4985 } 4986 else 4987 { 4988 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13); 4989 SVN_ERR(contents_three_identical_p(same12, same23, same13, 4990 file1, file2, file3, 4991 scratch_pool)); 4992 } 4993 4994 return SVN_NO_ERROR; 4995} 4996 4997#ifdef WIN32 4998/* Counter value of file_mktemp request (used in a threadsafe way), to make 4999 sure that a single process normally never generates the same tempname 5000 twice */ 5001static volatile apr_uint32_t tempname_counter = 0; 5002#endif 5003 5004/* Creates a new temporary file in DIRECTORY with apr flags FLAGS. 5005 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name. 5006 Perform temporary allocations in SCRATCH_POOL and the result in 5007 RESULT_POOL. */ 5008static svn_error_t * 5009temp_file_create(apr_file_t **new_file, 5010 const char **new_file_name, 5011 const char *directory, 5012 apr_int32_t flags, 5013 apr_pool_t *result_pool, 5014 apr_pool_t *scratch_pool) 5015{ 5016#ifndef WIN32 5017 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool); 5018 const char *templ_apr; 5019 apr_status_t status; 5020 5021 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool)); 5022 5023 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the 5024 data available in POOL and we need a non-const pointer here, 5025 as apr changes the template to return the new filename. */ 5026 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool); 5027 5028 if (status) 5029 return svn_error_wrap_apr(status, _("Can't create temporary file from " 5030 "template '%s'"), templ); 5031 5032 /* Translate the returned path back to utf-8 before returning it */ 5033 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name, 5034 templ_apr, 5035 result_pool)); 5036#else 5037 /* The Windows implementation of apr_file_mktemp doesn't handle access 5038 denied errors correctly. Therefore we implement our own temp file 5039 creation function here. */ 5040 5041 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(), 5042 ### the function we used before. But we try to guess a more unique 5043 ### name before trying if it exists. */ 5044 5045 /* Offset by some time value and a unique request nr to make the number 5046 +- unique for both this process and on the computer */ 5047 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter) 5048 + GetCurrentProcessId(); 5049 int i; 5050 5051 /* ### Maybe use an iterpool? */ 5052 for (i = 0; i <= 99999; i++) 5053 { 5054 apr_uint32_t unique_nr; 5055 const char *unique_name; 5056 const char *unique_name_apr; 5057 apr_file_t *try_file; 5058 apr_status_t apr_err; 5059 5060 /* Generate a number that should be unique for this application and 5061 usually for the entire computer to reduce the number of cycles 5062 through this loop. (A bit of calculation is much cheaper then 5063 disk io) */ 5064 unique_nr = baseNr + 3 * i; 5065 5066 unique_name = svn_dirent_join(directory, 5067 apr_psprintf(scratch_pool, "svn-%X", 5068 unique_nr), 5069 scratch_pool); 5070 5071 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool)); 5072 5073 apr_err = file_open(&try_file, unique_name_apr, flags, 5074 APR_OS_DEFAULT, FALSE, scratch_pool); 5075 5076 if (APR_STATUS_IS_EEXIST(apr_err)) 5077 continue; 5078 else if (apr_err) 5079 { 5080 /* On Win32, CreateFile fails with an "Access Denied" error 5081 code, rather than "File Already Exists", if the colliding 5082 name belongs to a directory. */ 5083 5084 if (APR_STATUS_IS_EACCES(apr_err)) 5085 { 5086 apr_finfo_t finfo; 5087 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr, 5088 APR_FINFO_TYPE, scratch_pool); 5089 5090 if (!apr_err_2 && finfo.filetype == APR_DIR) 5091 continue; 5092 5093 apr_err_2 = APR_TO_OS_ERROR(apr_err); 5094 5095 if (apr_err_2 == ERROR_ACCESS_DENIED || 5096 apr_err_2 == ERROR_SHARING_VIOLATION) 5097 { 5098 /* The file is in use by another process or is hidden; 5099 create a new name, but don't do this 99999 times in 5100 case the folder is not writable */ 5101 i += 797; 5102 continue; 5103 } 5104 5105 /* Else fall through and return the original error. */ 5106 } 5107 5108 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"), 5109 svn_dirent_local_style(unique_name, 5110 scratch_pool)); 5111 } 5112 else 5113 { 5114 /* Move file to the right pool */ 5115 apr_err = apr_file_setaside(new_file, try_file, result_pool); 5116 5117 if (apr_err) 5118 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"), 5119 svn_dirent_local_style(unique_name, 5120 scratch_pool)); 5121 5122 *new_file_name = apr_pstrdup(result_pool, unique_name); 5123 5124 return SVN_NO_ERROR; 5125 } 5126 } 5127 5128 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED, 5129 NULL, 5130 _("Unable to make name in '%s'"), 5131 svn_dirent_local_style(directory, scratch_pool)); 5132#endif 5133} 5134 5135/* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */ 5136svn_error_t * 5137svn_io_file_name_get(const char **filename, 5138 apr_file_t *file, 5139 apr_pool_t *pool) 5140{ 5141 const char *fname_apr; 5142 apr_status_t status; 5143 5144 status = apr_file_name_get(&fname_apr, file); 5145 if (status) 5146 return svn_error_wrap_apr(status, _("Can't get file name")); 5147 5148 if (fname_apr) 5149 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool)); 5150 else 5151 *filename = NULL; 5152 5153 return SVN_NO_ERROR; 5154} 5155 5156 5157svn_error_t * 5158svn_io_open_unique_file3(apr_file_t **file, 5159 const char **unique_path, 5160 const char *dirpath, 5161 svn_io_file_del_t delete_when, 5162 apr_pool_t *result_pool, 5163 apr_pool_t *scratch_pool) 5164{ 5165 apr_file_t *tempfile; 5166 const char *tempname; 5167 struct temp_file_cleanup_s *baton = NULL; 5168 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL | 5169 APR_BUFFERED | APR_BINARY); 5170#if !defined(WIN32) && !defined(__OS2__) 5171 apr_fileperms_t perms; 5172 svn_boolean_t using_system_temp_dir = FALSE; 5173#endif 5174 5175 SVN_ERR_ASSERT(file || unique_path); 5176 if (file) 5177 *file = NULL; 5178 if (unique_path) 5179 *unique_path = NULL; 5180 5181 if (dirpath == NULL) 5182 { 5183#if !defined(WIN32) && !defined(__OS2__) 5184 using_system_temp_dir = TRUE; 5185#endif 5186 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool)); 5187 } 5188 5189 switch (delete_when) 5190 { 5191 case svn_io_file_del_on_pool_cleanup: 5192 baton = apr_palloc(result_pool, sizeof(*baton)); 5193 baton->pool = result_pool; 5194 baton->fname_apr = NULL; 5195 5196 /* Because cleanups are run LIFO, we need to make sure to register 5197 our cleanup before the apr_file_close cleanup: 5198 5199 On Windows, you can't remove an open file. 5200 */ 5201 apr_pool_cleanup_register(result_pool, baton, 5202 temp_file_plain_cleanup_handler, 5203 temp_file_child_cleanup_handler); 5204 5205 break; 5206 case svn_io_file_del_on_close: 5207 flags |= APR_DELONCLOSE; 5208 break; 5209 default: 5210 break; 5211 } 5212 5213 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags, 5214 result_pool, scratch_pool)); 5215 5216#if !defined(WIN32) && !defined(__OS2__) 5217 /* apr_file_mktemp() creates files with mode 0600. 5218 * This is appropriate if we're using a system temp dir since we don't 5219 * want to leak sensitive data into temp files other users can read. 5220 * If we're not using a system temp dir we're probably using the 5221 * .svn/tmp area and it's likely that the tempfile will end up being 5222 * copied or renamed into the working copy. 5223 * This would cause working files having mode 0600 while users might 5224 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this 5225 * case, but only if the umask allows it. */ 5226 if (!using_system_temp_dir) 5227 { 5228 svn_error_t *err; 5229 5230 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool)); 5231 err = file_perms_set2(tempfile, perms, scratch_pool); 5232 if (err) 5233 { 5234 if (APR_STATUS_IS_INCOMPLETE(err->apr_err) || 5235 APR_STATUS_IS_ENOTIMPL(err->apr_err)) 5236 svn_error_clear(err); 5237 else 5238 { 5239 return svn_error_quick_wrapf( 5240 err, _("Can't set permissions on '%s'"), 5241 svn_dirent_local_style(tempname, scratch_pool)); 5242 } 5243 } 5244 } 5245#endif 5246 5247 if (file) 5248 *file = tempfile; 5249 else 5250 SVN_ERR(svn_io_file_close(tempfile, scratch_pool)); 5251 5252 if (unique_path) 5253 *unique_path = tempname; /* Was allocated in result_pool */ 5254 5255 if (baton) 5256 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool)); 5257 5258 return SVN_NO_ERROR; 5259} 5260 5261svn_error_t * 5262svn_io_file_readline(apr_file_t *file, 5263 svn_stringbuf_t **stringbuf, 5264 const char **eol, 5265 svn_boolean_t *eof, 5266 apr_size_t max_len, 5267 apr_pool_t *result_pool, 5268 apr_pool_t *scratch_pool) 5269{ 5270 svn_stringbuf_t *str; 5271 const char *eol_str; 5272 apr_size_t numbytes; 5273 char c; 5274 apr_size_t len; 5275 svn_boolean_t found_eof; 5276 5277 str = svn_stringbuf_create_ensure(80, result_pool); 5278 5279 /* Read bytes into STR up to and including, but not storing, 5280 * the next EOL sequence. */ 5281 eol_str = NULL; 5282 numbytes = 1; 5283 len = 0; 5284 found_eof = FALSE; 5285 while (!found_eof) 5286 { 5287 if (len < max_len) 5288 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes, 5289 &found_eof, scratch_pool)); 5290 len++; 5291 if (numbytes != 1 || len > max_len) 5292 { 5293 found_eof = TRUE; 5294 break; 5295 } 5296 5297 if (c == '\n') 5298 { 5299 eol_str = "\n"; 5300 } 5301 else if (c == '\r') 5302 { 5303 eol_str = "\r"; 5304 5305 if (!found_eof && len < max_len) 5306 { 5307 apr_off_t pos; 5308 5309 /* Check for "\r\n" by peeking at the next byte. */ 5310 pos = 0; 5311 SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool)); 5312 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes, 5313 &found_eof, scratch_pool)); 5314 if (numbytes == 1 && c == '\n') 5315 { 5316 eol_str = "\r\n"; 5317 len++; 5318 } 5319 else 5320 { 5321 /* Pretend we never peeked. */ 5322 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool)); 5323 found_eof = FALSE; 5324 numbytes = 1; 5325 } 5326 } 5327 } 5328 else 5329 svn_stringbuf_appendbyte(str, c); 5330 5331 if (eol_str) 5332 break; 5333 } 5334 5335 if (eol) 5336 *eol = eol_str; 5337 if (eof) 5338 *eof = found_eof; 5339 *stringbuf = str; 5340 5341 return SVN_NO_ERROR; 5342} 5343