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