io.c revision 269847
11638Srgrimes/* 21638Srgrimes * io.c: shared file reading, writing, and probing code. 31638Srgrimes * 41638Srgrimes * ==================================================================== 51638Srgrimes * Licensed to the Apache Software Foundation (ASF) under one 61638Srgrimes * or more contributor license agreements. See the NOTICE file 71638Srgrimes * distributed with this work for additional information 81638Srgrimes * regarding copyright ownership. The ASF licenses this file 91638Srgrimes * to you under the Apache License, Version 2.0 (the 101638Srgrimes * "License"); you may not use this file except in compliance 111638Srgrimes * with the License. You may obtain a copy of the License at 121638Srgrimes * 131638Srgrimes * http://www.apache.org/licenses/LICENSE-2.0 141638Srgrimes * 151638Srgrimes * Unless required by applicable law or agreed to in writing, 161638Srgrimes * software distributed under the License is distributed on an 171638Srgrimes * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 181638Srgrimes * KIND, either express or implied. See the License for the 191638Srgrimes * specific language governing permissions and limitations 201638Srgrimes * under the License. 211638Srgrimes * ==================================================================== 221638Srgrimes */ 231638Srgrimes 241638Srgrimes 251638Srgrimes 261638Srgrimes#include <stdio.h> 271638Srgrimes 281638Srgrimes#ifndef WIN32 291638Srgrimes#include <unistd.h> 301638Srgrimes#endif 311638Srgrimes 321638Srgrimes#ifndef APR_STATUS_IS_EPERM 331638Srgrimes#include <errno.h> 341638Srgrimes#ifdef EPERM 351638Srgrimes#define APR_STATUS_IS_EPERM(s) ((s) == EPERM) 361638Srgrimes#else 371638Srgrimes#define APR_STATUS_IS_EPERM(s) (0) 381638Srgrimes#endif 391638Srgrimes#endif 401638Srgrimes 411638Srgrimes#include <apr_lib.h> 421638Srgrimes#include <apr_pools.h> 431638Srgrimes#include <apr_file_io.h> 441638Srgrimes#include <apr_file_info.h> 451638Srgrimes#include <apr_general.h> 461638Srgrimes#include <apr_strings.h> 471638Srgrimes#include <apr_portable.h> 481638Srgrimes#include <apr_md5.h> 491638Srgrimes 501638Srgrimes#ifdef WIN32 511638Srgrimes#include <arch/win32/apr_arch_file_io.h> 521638Srgrimes#endif 531638Srgrimes 541638Srgrimes#include "svn_hash.h" 551638Srgrimes#include "svn_types.h" 561638Srgrimes#include "svn_dirent_uri.h" 571638Srgrimes#include "svn_path.h" 581638Srgrimes#include "svn_string.h" 591638Srgrimes#include "svn_error.h" 601638Srgrimes#include "svn_io.h" 611638Srgrimes#include "svn_pools.h" 621638Srgrimes#include "svn_utf.h" 631638Srgrimes#include "svn_config.h" 641638Srgrimes#include "svn_private_config.h" 651638Srgrimes#include "svn_ctype.h" 661638Srgrimes 671638Srgrimes#include "private/svn_atomic.h" 681638Srgrimes#include "private/svn_io_private.h" 691638Srgrimes 701638Srgrimes#define SVN_SLEEP_ENV_VAR "SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS" 711638Srgrimes 721638Srgrimes/* 731638Srgrimes Windows is 'aided' by a number of types of applications that 741638Srgrimes follow other applications around and open up files they have 751638Srgrimes changed for various reasons (the most intrusive are virus 761638Srgrimes scanners). So, if one of these other apps has glommed onto 771638Srgrimes our file we may get an 'access denied' error. 781638Srgrimes 791638Srgrimes This retry loop does not completely solve the problem (who 801638Srgrimes knows how long the other app is going to hold onto it for), but 811638Srgrimes goes a long way towards minimizing it. It is not an infinite 821638Srgrimes loop because there might really be an error. 831638Srgrimes 841638Srgrimes Another reason for retrying delete operations on Windows 851638Srgrimes is that they are asynchronous -- the file or directory is not 861638Srgrimes actually deleted until the last handle to it is closed. The 871638Srgrimes retry loop cannot completely solve this problem either, but can 881638Srgrimes help mitigate it. 891638Srgrimes*/ 901638Srgrimes#define RETRY_MAX_ATTEMPTS 100 911638Srgrimes#define RETRY_INITIAL_SLEEP 1000 921638Srgrimes#define RETRY_MAX_SLEEP 128000 931638Srgrimes 941638Srgrimes#define RETRY_LOOP(err, expr, retry_test, sleep_test) \ 951638Srgrimes do \ 961638Srgrimes { \ 971638Srgrimes apr_status_t os_err = APR_TO_OS_ERROR(err); \ 981638Srgrimes int sleep_count = RETRY_INITIAL_SLEEP; \ 991638Srgrimes int retries; \ 1001638Srgrimes for (retries = 0; \ 1011638Srgrimes retries < RETRY_MAX_ATTEMPTS && (retry_test); \ 1021638Srgrimes os_err = APR_TO_OS_ERROR(err)) \ 1031638Srgrimes { \ 1041638Srgrimes if (sleep_test) \ 1051638Srgrimes { \ 1061638Srgrimes ++retries; \ 1071638Srgrimes apr_sleep(sleep_count); \ 1081638Srgrimes if (sleep_count < RETRY_MAX_SLEEP) \ 1091638Srgrimes sleep_count *= 2; \ 1101638Srgrimes } \ 1111638Srgrimes (err) = (expr); \ 1121638Srgrimes } \ 1131638Srgrimes } \ 1141638Srgrimes while (0) 1151638Srgrimes 1161638Srgrimes#if defined(EDEADLK) && APR_HAS_THREADS 1171638Srgrimes#define FILE_LOCK_RETRY_LOOP(err, expr) \ 1181638Srgrimes RETRY_LOOP(err, \ 1191638Srgrimes expr, \ 1201638Srgrimes (APR_STATUS_IS_EINTR(err) || os_err == EDEADLK), \ 1211638Srgrimes (!APR_STATUS_IS_EINTR(err))) 1221638Srgrimes#else 1231638Srgrimes#define FILE_LOCK_RETRY_LOOP(err, expr) \ 1241638Srgrimes RETRY_LOOP(err, \ 1251638Srgrimes expr, \ 1261638Srgrimes (APR_STATUS_IS_EINTR(err)), \ 1271638Srgrimes 0) 1281638Srgrimes#endif 1291638Srgrimes 1301638Srgrimes#ifndef WIN32_RETRY_LOOP 1311638Srgrimes#if defined(WIN32) && !defined(SVN_NO_WIN32_RETRY_LOOP) 1321638Srgrimes#define WIN32_RETRY_LOOP(err, expr) \ 1331638Srgrimes RETRY_LOOP(err, expr, (os_err == ERROR_ACCESS_DENIED \ 1341638Srgrimes || os_err == ERROR_SHARING_VIOLATION \ 1351638Srgrimes || os_err == ERROR_DIR_NOT_EMPTY), \ 1361638Srgrimes 1) 1371638Srgrimes#else 1381638Srgrimes#define WIN32_RETRY_LOOP(err, expr) ((void)0) 1391638Srgrimes#endif 1401638Srgrimes#endif 1411638Srgrimes 1421638Srgrimes/* 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 some sub-second resolution. On Windows 1247 it is likely to be sub-millisecond; on Linux systems it depends 1248 on the filesystem, ext4 is typically 1ms, 4ms or 10ms resolution. 1249 1250 ## Perhaps find a better algorithm here. This will fail once 1251 in every 1000 cases on a millisecond precision filesystem 1252 if the mtime happens to be an exact second. 1253 1254 But better to fail once in every thousand cases than every 1255 time, like we did before. 1256 1257 Note for further research on algorithm: 1258 FAT32 has < 1 sec precision on ctime, but 2 sec on mtime. 1259 1260 Linux/ext4 with CONFIG_HZ=250 has high resolution 1261 apr_time_now and although the filesystem timestamps 1262 have similar high precision they are only updated with 1263 a coarser 4ms resolution. */ 1264 1265 /* 10 milliseconds after now. */ 1266#ifndef SVN_HI_RES_SLEEP_MS 1267#define SVN_HI_RES_SLEEP_MS 10 1268#endif 1269 then = now + apr_time_from_msec(SVN_HI_RES_SLEEP_MS); 1270 } 1271 1272 /* Remove time taken to do stat() from sleep. */ 1273 now = apr_time_now(); 1274 } 1275 1276 if (now >= then) 1277 return; /* Passing negative values may suspend indefinitely (Windows) */ 1278 1279 /* (t < 1000 will be round to 0 in apr) */ 1280 if (then - now < 1000) 1281 apr_sleep(1000); 1282 else 1283 apr_sleep(then - now); 1284} 1285 1286 1287svn_error_t * 1288svn_io_filesizes_different_p(svn_boolean_t *different_p, 1289 const char *file1, 1290 const char *file2, 1291 apr_pool_t *pool) 1292{ 1293 apr_finfo_t finfo1; 1294 apr_finfo_t finfo2; 1295 apr_status_t status; 1296 const char *file1_apr, *file2_apr; 1297 1298 /* Not using svn_io_stat() because don't want to generate 1299 svn_error_t objects for non-error conditions. */ 1300 1301 SVN_ERR(cstring_from_utf8(&file1_apr, file1, pool)); 1302 SVN_ERR(cstring_from_utf8(&file2_apr, file2, pool)); 1303 1304 /* Stat both files */ 1305 status = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, pool); 1306 if (status) 1307 { 1308 /* If we got an error stat'ing a file, it could be because the 1309 file was removed... or who knows. Whatever the case, we 1310 don't know if the filesizes are definitely different, so 1311 assume that they're not. */ 1312 *different_p = FALSE; 1313 return SVN_NO_ERROR; 1314 } 1315 1316 status = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, pool); 1317 if (status) 1318 { 1319 /* See previous comment. */ 1320 *different_p = FALSE; 1321 return SVN_NO_ERROR; 1322 } 1323 1324 /* Examine file sizes */ 1325 if (finfo1.size == finfo2.size) 1326 *different_p = FALSE; 1327 else 1328 *different_p = TRUE; 1329 1330 return SVN_NO_ERROR; 1331} 1332 1333 1334svn_error_t * 1335svn_io_filesizes_three_different_p(svn_boolean_t *different_p12, 1336 svn_boolean_t *different_p23, 1337 svn_boolean_t *different_p13, 1338 const char *file1, 1339 const char *file2, 1340 const char *file3, 1341 apr_pool_t *scratch_pool) 1342{ 1343 apr_finfo_t finfo1, finfo2, finfo3; 1344 apr_status_t status1, status2, status3; 1345 const char *file1_apr, *file2_apr, *file3_apr; 1346 1347 /* Not using svn_io_stat() because don't want to generate 1348 svn_error_t objects for non-error conditions. */ 1349 1350 SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool)); 1351 SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool)); 1352 SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool)); 1353 1354 /* Stat all three files */ 1355 status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool); 1356 status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool); 1357 status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool); 1358 1359 /* If we got an error stat'ing a file, it could be because the 1360 file was removed... or who knows. Whatever the case, we 1361 don't know if the filesizes are definitely different, so 1362 assume that they're not. */ 1363 *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size; 1364 *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size; 1365 *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size; 1366 1367 return SVN_NO_ERROR; 1368} 1369 1370 1371svn_error_t * 1372svn_io_file_checksum2(svn_checksum_t **checksum, 1373 const char *file, 1374 svn_checksum_kind_t kind, 1375 apr_pool_t *pool) 1376{ 1377 svn_stream_t *file_stream; 1378 svn_stream_t *checksum_stream; 1379 apr_file_t* f; 1380 1381 SVN_ERR(svn_io_file_open(&f, file, APR_READ, APR_OS_DEFAULT, pool)); 1382 file_stream = svn_stream_from_aprfile2(f, FALSE, pool); 1383 checksum_stream = svn_stream_checksummed2(file_stream, checksum, NULL, kind, 1384 TRUE, pool); 1385 1386 /* Because the checksummed stream will force the reading (and 1387 checksumming) of all the file's bytes, we can just close the stream 1388 and let its magic work. */ 1389 return svn_stream_close(checksum_stream); 1390} 1391 1392 1393svn_error_t * 1394svn_io_file_checksum(unsigned char digest[], 1395 const char *file, 1396 apr_pool_t *pool) 1397{ 1398 svn_checksum_t *checksum; 1399 1400 SVN_ERR(svn_io_file_checksum2(&checksum, file, svn_checksum_md5, pool)); 1401 memcpy(digest, checksum->digest, APR_MD5_DIGESTSIZE); 1402 1403 return SVN_NO_ERROR; 1404} 1405 1406 1407 1408/*** Permissions and modes. ***/ 1409 1410#if !defined(WIN32) && !defined(__OS2__) 1411/* Given the file specified by PATH, attempt to create an 1412 identical version of it owned by the current user. This is done by 1413 moving it to a temporary location, copying the file back to its old 1414 path, then deleting the temporarily moved version. All temporary 1415 allocations are done in POOL. */ 1416static svn_error_t * 1417reown_file(const char *path, 1418 apr_pool_t *pool) 1419{ 1420 const char *unique_name; 1421 1422 SVN_ERR(svn_io_open_unique_file3(NULL, &unique_name, 1423 svn_dirent_dirname(path, pool), 1424 svn_io_file_del_none, pool, pool)); 1425 SVN_ERR(svn_io_file_rename(path, unique_name, pool)); 1426 SVN_ERR(svn_io_copy_file(unique_name, path, TRUE, pool)); 1427 return svn_error_trace(svn_io_remove_file2(unique_name, FALSE, pool)); 1428} 1429 1430/* Determine what the PERMS for a new file should be by looking at the 1431 permissions of a temporary file that we create. 1432 Unfortunately, umask() as defined in POSIX provides no thread-safe way 1433 to get at the current value of the umask, so what we're doing here is 1434 the only way we have to determine which combination of write bits 1435 (User/Group/World) should be set by default. 1436 Make temporary allocations in SCRATCH_POOL. */ 1437static svn_error_t * 1438get_default_file_perms(apr_fileperms_t *perms, apr_pool_t *scratch_pool) 1439{ 1440 /* the default permissions as read from the temp folder */ 1441 static apr_fileperms_t default_perms = 0; 1442 1443 /* Technically, this "racy": Multiple threads may use enter here and 1444 try to figure out the default permission concurrently. That's fine 1445 since they will end up with the same results. Even more technical, 1446 apr_fileperms_t is an atomic type on 32+ bit machines. 1447 */ 1448 if (default_perms == 0) 1449 { 1450 apr_finfo_t finfo; 1451 apr_file_t *fd; 1452 const char *fname_base, *fname; 1453 apr_uint32_t randomish; 1454 svn_error_t *err; 1455 1456 /* Get the perms for a newly created file to find out what bits 1457 should be set. 1458 1459 Explictly delete the file because we want this file to be as 1460 short-lived as possible since its presence means other 1461 processes may have to try multiple names. 1462 1463 Using svn_io_open_uniquely_named() here because other tempfile 1464 creation functions tweak the permission bits of files they create. 1465 */ 1466 randomish = ((apr_uint32_t)(apr_uintptr_t)scratch_pool 1467 + (apr_uint32_t)apr_time_now()); 1468 fname_base = apr_psprintf(scratch_pool, "svn-%08x", randomish); 1469 1470 SVN_ERR(svn_io_open_uniquely_named(&fd, &fname, NULL, fname_base, 1471 NULL, svn_io_file_del_none, 1472 scratch_pool, scratch_pool)); 1473 err = svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool); 1474 err = svn_error_compose_create(err, svn_io_file_close(fd, scratch_pool)); 1475 err = svn_error_compose_create(err, svn_io_remove_file2(fname, TRUE, 1476 scratch_pool)); 1477 SVN_ERR(err); 1478 *perms = finfo.protection; 1479 default_perms = finfo.protection; 1480 } 1481 else 1482 *perms = default_perms; 1483 1484 return SVN_NO_ERROR; 1485} 1486 1487/* OR together permission bits of the file FD and the default permissions 1488 of a file as determined by get_default_file_perms(). Do temporary 1489 allocations in SCRATCH_POOL. */ 1490static svn_error_t * 1491merge_default_file_perms(apr_file_t *fd, apr_fileperms_t *perms, 1492 apr_pool_t *scratch_pool) 1493{ 1494 apr_finfo_t finfo; 1495 apr_fileperms_t default_perms; 1496 1497 SVN_ERR(get_default_file_perms(&default_perms, scratch_pool)); 1498 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_PROT, fd, scratch_pool)); 1499 1500 /* Glom the perms together. */ 1501 *perms = default_perms | finfo.protection; 1502 return SVN_NO_ERROR; 1503} 1504 1505/* This is a helper function for the svn_io_set_file_read* functions 1506 that attempts to honor the users umask when dealing with 1507 permission changes. It is a no-op when invoked on a symlink. */ 1508static svn_error_t * 1509io_set_file_perms(const char *path, 1510 svn_boolean_t change_readwrite, 1511 svn_boolean_t enable_write, 1512 svn_boolean_t change_executable, 1513 svn_boolean_t executable, 1514 svn_boolean_t ignore_enoent, 1515 apr_pool_t *pool) 1516{ 1517 apr_status_t status; 1518 const char *path_apr; 1519 apr_finfo_t finfo; 1520 apr_fileperms_t perms_to_set; 1521 1522 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 1523 1524 /* Try to change only a minimal amount of the perms first 1525 by getting the current perms and adding bits 1526 only on where read perms are granted. If this fails 1527 fall through to just setting file attributes. */ 1528 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT | APR_FINFO_LINK, pool); 1529 if (status) 1530 { 1531 if (ignore_enoent && APR_STATUS_IS_ENOENT(status)) 1532 return SVN_NO_ERROR; 1533 else if (status != APR_ENOTIMPL) 1534 return svn_error_wrap_apr(status, 1535 _("Can't change perms of file '%s'"), 1536 svn_dirent_local_style(path, pool)); 1537 return SVN_NO_ERROR; 1538 } 1539 1540 if (finfo.filetype == APR_LNK) 1541 return SVN_NO_ERROR; 1542 1543 perms_to_set = finfo.protection; 1544 if (change_readwrite) 1545 { 1546 if (enable_write) /* Make read-write. */ 1547 { 1548 /* Tweak the owner bits only. The group/other bits aren't safe to 1549 * touch because we may end up setting them in undesired ways. */ 1550 perms_to_set |= (APR_UREAD|APR_UWRITE); 1551 } 1552 else 1553 { 1554 if (finfo.protection & APR_UREAD) 1555 perms_to_set &= ~APR_UWRITE; 1556 if (finfo.protection & APR_GREAD) 1557 perms_to_set &= ~APR_GWRITE; 1558 if (finfo.protection & APR_WREAD) 1559 perms_to_set &= ~APR_WWRITE; 1560 } 1561 } 1562 1563 if (change_executable) 1564 { 1565 if (executable) 1566 { 1567 if (finfo.protection & APR_UREAD) 1568 perms_to_set |= APR_UEXECUTE; 1569 if (finfo.protection & APR_GREAD) 1570 perms_to_set |= APR_GEXECUTE; 1571 if (finfo.protection & APR_WREAD) 1572 perms_to_set |= APR_WEXECUTE; 1573 } 1574 else 1575 { 1576 if (finfo.protection & APR_UREAD) 1577 perms_to_set &= ~APR_UEXECUTE; 1578 if (finfo.protection & APR_GREAD) 1579 perms_to_set &= ~APR_GEXECUTE; 1580 if (finfo.protection & APR_WREAD) 1581 perms_to_set &= ~APR_WEXECUTE; 1582 } 1583 } 1584 1585 /* If we aren't changing anything then just return, this saves 1586 some system calls and helps with shared working copies */ 1587 if (perms_to_set == finfo.protection) 1588 return SVN_NO_ERROR; 1589 1590 status = apr_file_perms_set(path_apr, perms_to_set); 1591 if (!status) 1592 return SVN_NO_ERROR; 1593 1594 if (APR_STATUS_IS_EPERM(status)) 1595 { 1596 /* We don't have permissions to change the 1597 permissions! Try a move, copy, and delete 1598 workaround to see if we can get the file owned by 1599 us. If these succeed, try the permissions set 1600 again. 1601 1602 Note that we only attempt this in the 1603 stat-available path. This assumes that the 1604 move-copy workaround will only be helpful on 1605 platforms that implement apr_stat. */ 1606 SVN_ERR(reown_file(path, pool)); 1607 status = apr_file_perms_set(path_apr, perms_to_set); 1608 } 1609 1610 if (!status) 1611 return SVN_NO_ERROR; 1612 1613 if (ignore_enoent && APR_STATUS_IS_ENOENT(status)) 1614 return SVN_NO_ERROR; 1615 else if (status == APR_ENOTIMPL) 1616 { 1617 /* At least try to set the attributes. */ 1618 apr_fileattrs_t attrs = 0; 1619 apr_fileattrs_t attrs_values = 0; 1620 1621 if (change_readwrite) 1622 { 1623 attrs = APR_FILE_ATTR_READONLY; 1624 if (!enable_write) 1625 attrs_values = APR_FILE_ATTR_READONLY; 1626 } 1627 if (change_executable) 1628 { 1629 attrs = APR_FILE_ATTR_EXECUTABLE; 1630 if (executable) 1631 attrs_values = APR_FILE_ATTR_EXECUTABLE; 1632 } 1633 status = apr_file_attrs_set(path_apr, attrs, attrs_values, pool); 1634 } 1635 1636 return svn_error_wrap_apr(status, 1637 _("Can't change perms of file '%s'"), 1638 svn_dirent_local_style(path, pool)); 1639} 1640#endif /* !WIN32 && !__OS2__ */ 1641 1642#ifdef WIN32 1643#if APR_HAS_UNICODE_FS 1644/* copy of the apr function utf8_to_unicode_path since apr doesn't export this one */ 1645static apr_status_t io_utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen, 1646 const char* srcstr) 1647{ 1648 /* TODO: The computations could preconvert the string to determine 1649 * the true size of the retstr, but that's a memory over speed 1650 * tradeoff that isn't appropriate this early in development. 1651 * 1652 * Allocate the maximum string length based on leading 4 1653 * characters of \\?\ (allowing nearly unlimited path lengths) 1654 * plus the trailing null, then transform /'s into \\'s since 1655 * the \\?\ form doesn't allow '/' path separators. 1656 * 1657 * Note that the \\?\ form only works for local drive paths, and 1658 * \\?\UNC\ is needed UNC paths. 1659 */ 1660 apr_size_t srcremains = strlen(srcstr) + 1; 1661 apr_wchar_t *t = retstr; 1662 apr_status_t rv; 1663 1664 /* This is correct, we don't twist the filename if it will 1665 * definitely be shorter than 248 characters. It merits some 1666 * performance testing to see if this has any effect, but there 1667 * seem to be applications that get confused by the resulting 1668 * Unicode \\?\ style file names, especially if they use argv[0] 1669 * or call the Win32 API functions such as GetModuleName, etc. 1670 * Not every application is prepared to handle such names. 1671 * 1672 * Note also this is shorter than MAX_PATH, as directory paths 1673 * are actually limited to 248 characters. 1674 * 1675 * Note that a utf-8 name can never result in more wide chars 1676 * than the original number of utf-8 narrow chars. 1677 */ 1678 if (srcremains > 248) { 1679 if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) { 1680 wcscpy (retstr, L"\\\\?\\"); 1681 retlen -= 4; 1682 t += 4; 1683 } 1684 else if ((srcstr[0] == '/' || srcstr[0] == '\\') 1685 && (srcstr[1] == '/' || srcstr[1] == '\\') 1686 && (srcstr[2] != '?')) { 1687 /* Skip the slashes */ 1688 srcstr += 2; 1689 srcremains -= 2; 1690 wcscpy (retstr, L"\\\\?\\UNC\\"); 1691 retlen -= 8; 1692 t += 8; 1693 } 1694 } 1695 1696 if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) { 1697 return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv; 1698 } 1699 if (srcremains) { 1700 return APR_ENAMETOOLONG; 1701 } 1702 for (; *t; ++t) 1703 if (*t == L'/') 1704 *t = L'\\'; 1705 return APR_SUCCESS; 1706} 1707#endif 1708 1709static apr_status_t io_win_file_attrs_set(const char *fname, 1710 DWORD attributes, 1711 DWORD attr_mask, 1712 apr_pool_t *pool) 1713{ 1714 /* this is an implementation of apr_file_attrs_set() but one 1715 that uses the proper Windows attributes instead of the apr 1716 attributes. This way, we can apply any Windows file and 1717 folder attributes even if apr doesn't implement them */ 1718 DWORD flags; 1719 apr_status_t rv; 1720#if APR_HAS_UNICODE_FS 1721 apr_wchar_t wfname[APR_PATH_MAX]; 1722#endif 1723 1724#if APR_HAS_UNICODE_FS 1725 IF_WIN_OS_IS_UNICODE 1726 { 1727 if (rv = io_utf8_to_unicode_path(wfname, 1728 sizeof(wfname) / sizeof(wfname[0]), 1729 fname)) 1730 return rv; 1731 flags = GetFileAttributesW(wfname); 1732 } 1733#endif 1734#if APR_HAS_ANSI_FS 1735 ELSE_WIN_OS_IS_ANSI 1736 { 1737 flags = GetFileAttributesA(fname); 1738 } 1739#endif 1740 1741 if (flags == 0xFFFFFFFF) 1742 return apr_get_os_error(); 1743 1744 flags &= ~attr_mask; 1745 flags |= (attributes & attr_mask); 1746 1747#if APR_HAS_UNICODE_FS 1748 IF_WIN_OS_IS_UNICODE 1749 { 1750 rv = SetFileAttributesW(wfname, flags); 1751 } 1752#endif 1753#if APR_HAS_ANSI_FS 1754 ELSE_WIN_OS_IS_ANSI 1755 { 1756 rv = SetFileAttributesA(fname, flags); 1757 } 1758#endif 1759 1760 if (rv == 0) 1761 return apr_get_os_error(); 1762 1763 return APR_SUCCESS; 1764} 1765 1766#endif 1767 1768svn_error_t * 1769svn_io_set_file_read_write_carefully(const char *path, 1770 svn_boolean_t enable_write, 1771 svn_boolean_t ignore_enoent, 1772 apr_pool_t *pool) 1773{ 1774 if (enable_write) 1775 return svn_io_set_file_read_write(path, ignore_enoent, pool); 1776 return svn_io_set_file_read_only(path, ignore_enoent, pool); 1777} 1778 1779svn_error_t * 1780svn_io_set_file_read_only(const char *path, 1781 svn_boolean_t ignore_enoent, 1782 apr_pool_t *pool) 1783{ 1784 /* On Windows and OS/2, just set the file attributes -- on unix call 1785 our internal function which attempts to honor the umask. */ 1786#if !defined(WIN32) && !defined(__OS2__) 1787 return io_set_file_perms(path, TRUE, FALSE, FALSE, FALSE, 1788 ignore_enoent, pool); 1789#else 1790 apr_status_t status; 1791 const char *path_apr; 1792 1793 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 1794 1795 status = apr_file_attrs_set(path_apr, 1796 APR_FILE_ATTR_READONLY, 1797 APR_FILE_ATTR_READONLY, 1798 pool); 1799 1800 if (status && status != APR_ENOTIMPL) 1801 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status)) 1802 return svn_error_wrap_apr(status, 1803 _("Can't set file '%s' read-only"), 1804 svn_dirent_local_style(path, pool)); 1805 1806 return SVN_NO_ERROR; 1807#endif 1808} 1809 1810 1811svn_error_t * 1812svn_io_set_file_read_write(const char *path, 1813 svn_boolean_t ignore_enoent, 1814 apr_pool_t *pool) 1815{ 1816 /* On Windows and OS/2, just set the file attributes -- on unix call 1817 our internal function which attempts to honor the umask. */ 1818#if !defined(WIN32) && !defined(__OS2__) 1819 return io_set_file_perms(path, TRUE, TRUE, FALSE, FALSE, 1820 ignore_enoent, pool); 1821#else 1822 apr_status_t status; 1823 const char *path_apr; 1824 1825 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 1826 1827 status = apr_file_attrs_set(path_apr, 1828 0, 1829 APR_FILE_ATTR_READONLY, 1830 pool); 1831 1832 if (status && status != APR_ENOTIMPL) 1833 if (!ignore_enoent || !APR_STATUS_IS_ENOENT(status)) 1834 return svn_error_wrap_apr(status, 1835 _("Can't set file '%s' read-write"), 1836 svn_dirent_local_style(path, pool)); 1837 1838 return SVN_NO_ERROR; 1839#endif 1840} 1841 1842svn_error_t * 1843svn_io_set_file_executable(const char *path, 1844 svn_boolean_t executable, 1845 svn_boolean_t ignore_enoent, 1846 apr_pool_t *pool) 1847{ 1848 /* On Windows and OS/2, just exit -- on unix call our internal function 1849 which attempts to honor the umask. */ 1850#if (!defined(WIN32) && !defined(__OS2__)) 1851 return io_set_file_perms(path, FALSE, FALSE, TRUE, executable, 1852 ignore_enoent, pool); 1853#else 1854 return SVN_NO_ERROR; 1855#endif 1856} 1857 1858 1859svn_error_t * 1860svn_io__is_finfo_read_only(svn_boolean_t *read_only, 1861 apr_finfo_t *file_info, 1862 apr_pool_t *pool) 1863{ 1864#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__) 1865 apr_status_t apr_err; 1866 apr_uid_t uid; 1867 apr_gid_t gid; 1868 1869 *read_only = FALSE; 1870 1871 apr_err = apr_uid_current(&uid, &gid, pool); 1872 1873 if (apr_err) 1874 return svn_error_wrap_apr(apr_err, _("Error getting UID of process")); 1875 1876 /* Check write bit for current user. */ 1877 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS) 1878 *read_only = !(file_info->protection & APR_UWRITE); 1879 1880 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS) 1881 *read_only = !(file_info->protection & APR_GWRITE); 1882 1883 else 1884 *read_only = !(file_info->protection & APR_WWRITE); 1885 1886#else /* WIN32 || __OS2__ || !APR_HAS_USER */ 1887 *read_only = (file_info->protection & APR_FREADONLY); 1888#endif 1889 1890 return SVN_NO_ERROR; 1891} 1892 1893svn_error_t * 1894svn_io__is_finfo_executable(svn_boolean_t *executable, 1895 apr_finfo_t *file_info, 1896 apr_pool_t *pool) 1897{ 1898#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__) 1899 apr_status_t apr_err; 1900 apr_uid_t uid; 1901 apr_gid_t gid; 1902 1903 *executable = FALSE; 1904 1905 apr_err = apr_uid_current(&uid, &gid, pool); 1906 1907 if (apr_err) 1908 return svn_error_wrap_apr(apr_err, _("Error getting UID of process")); 1909 1910 /* Check executable bit for current user. */ 1911 if (apr_uid_compare(uid, file_info->user) == APR_SUCCESS) 1912 *executable = (file_info->protection & APR_UEXECUTE); 1913 1914 else if (apr_gid_compare(gid, file_info->group) == APR_SUCCESS) 1915 *executable = (file_info->protection & APR_GEXECUTE); 1916 1917 else 1918 *executable = (file_info->protection & APR_WEXECUTE); 1919 1920#else /* WIN32 || __OS2__ || !APR_HAS_USER */ 1921 *executable = FALSE; 1922#endif 1923 1924 return SVN_NO_ERROR; 1925} 1926 1927svn_error_t * 1928svn_io_is_file_executable(svn_boolean_t *executable, 1929 const char *path, 1930 apr_pool_t *pool) 1931{ 1932#if defined(APR_HAS_USER) && !defined(WIN32) &&!defined(__OS2__) 1933 apr_finfo_t file_info; 1934 1935 SVN_ERR(svn_io_stat(&file_info, path, APR_FINFO_PROT | APR_FINFO_OWNER, 1936 pool)); 1937 SVN_ERR(svn_io__is_finfo_executable(executable, &file_info, pool)); 1938 1939#else /* WIN32 || __OS2__ || !APR_HAS_USER */ 1940 *executable = FALSE; 1941#endif 1942 1943 return SVN_NO_ERROR; 1944} 1945 1946 1947/*** File locking. ***/ 1948#if !defined(WIN32) && !defined(__OS2__) 1949/* Clear all outstanding locks on ARG, an open apr_file_t *. */ 1950static apr_status_t 1951file_clear_locks(void *arg) 1952{ 1953 apr_status_t apr_err; 1954 apr_file_t *f = arg; 1955 1956 /* Remove locks. */ 1957 apr_err = apr_file_unlock(f); 1958 if (apr_err) 1959 return apr_err; 1960 1961 return 0; 1962} 1963#endif 1964 1965svn_error_t * 1966svn_io_lock_open_file(apr_file_t *lockfile_handle, 1967 svn_boolean_t exclusive, 1968 svn_boolean_t nonblocking, 1969 apr_pool_t *pool) 1970{ 1971 int locktype = APR_FLOCK_SHARED; 1972 apr_status_t apr_err; 1973 const char *fname; 1974 1975 if (exclusive) 1976 locktype = APR_FLOCK_EXCLUSIVE; 1977 if (nonblocking) 1978 locktype |= APR_FLOCK_NONBLOCK; 1979 1980 /* We need this only in case of an error but this is cheap to get - 1981 * so we do it here for clarity. */ 1982 apr_err = apr_file_name_get(&fname, lockfile_handle); 1983 if (apr_err) 1984 return svn_error_wrap_apr(apr_err, _("Can't get file name")); 1985 1986 /* Get lock on the filehandle. */ 1987 apr_err = apr_file_lock(lockfile_handle, locktype); 1988 1989 /* In deployments with two or more multithreaded servers running on 1990 the same system serving two or more fsfs repositories it is 1991 possible for a deadlock to occur when getting a write lock on 1992 db/txn-current-lock: 1993 1994 Process 1 Process 2 1995 --------- --------- 1996 thread 1: get lock in repos A 1997 thread 1: get lock in repos B 1998 thread 2: block getting lock in repos A 1999 thread 2: try to get lock in B *** deadlock *** 2000 2001 Retry for a while for the deadlock to clear. */ 2002 FILE_LOCK_RETRY_LOOP(apr_err, apr_file_lock(lockfile_handle, locktype)); 2003 2004 if (apr_err) 2005 { 2006 switch (locktype & APR_FLOCK_TYPEMASK) 2007 { 2008 case APR_FLOCK_SHARED: 2009 return svn_error_wrap_apr(apr_err, 2010 _("Can't get shared lock on file '%s'"), 2011 try_utf8_from_internal_style(fname, pool)); 2012 case APR_FLOCK_EXCLUSIVE: 2013 return svn_error_wrap_apr(apr_err, 2014 _("Can't get exclusive lock on file '%s'"), 2015 try_utf8_from_internal_style(fname, pool)); 2016 default: 2017 SVN_ERR_MALFUNCTION(); 2018 } 2019 } 2020 2021/* On Windows and OS/2 file locks are automatically released when 2022 the file handle closes */ 2023#if !defined(WIN32) && !defined(__OS2__) 2024 apr_pool_cleanup_register(pool, lockfile_handle, 2025 file_clear_locks, 2026 apr_pool_cleanup_null); 2027#endif 2028 2029 return SVN_NO_ERROR; 2030} 2031 2032svn_error_t * 2033svn_io_unlock_open_file(apr_file_t *lockfile_handle, 2034 apr_pool_t *pool) 2035{ 2036 const char *fname; 2037 apr_status_t apr_err; 2038 2039 /* We need this only in case of an error but this is cheap to get - 2040 * so we do it here for clarity. */ 2041 apr_err = apr_file_name_get(&fname, lockfile_handle); 2042 if (apr_err) 2043 return svn_error_wrap_apr(apr_err, _("Can't get file name")); 2044 2045 /* The actual unlock attempt. */ 2046 apr_err = apr_file_unlock(lockfile_handle); 2047 if (apr_err) 2048 return svn_error_wrap_apr(apr_err, _("Can't unlock file '%s'"), 2049 try_utf8_from_internal_style(fname, pool)); 2050 2051/* On Windows and OS/2 file locks are automatically released when 2052 the file handle closes */ 2053#if !defined(WIN32) && !defined(__OS2__) 2054 apr_pool_cleanup_kill(pool, lockfile_handle, file_clear_locks); 2055#endif 2056 2057 return SVN_NO_ERROR; 2058} 2059 2060svn_error_t * 2061svn_io_file_lock2(const char *lock_file, 2062 svn_boolean_t exclusive, 2063 svn_boolean_t nonblocking, 2064 apr_pool_t *pool) 2065{ 2066 int locktype = APR_FLOCK_SHARED; 2067 apr_file_t *lockfile_handle; 2068 apr_int32_t flags; 2069 2070 if (exclusive) 2071 locktype = APR_FLOCK_EXCLUSIVE; 2072 2073 flags = APR_READ; 2074 if (locktype == APR_FLOCK_EXCLUSIVE) 2075 flags |= APR_WRITE; 2076 2077 /* locktype is never read after this block, so we don't need to bother 2078 setting it. If that were to ever change, uncomment the following 2079 block. 2080 if (nonblocking) 2081 locktype |= APR_FLOCK_NONBLOCK; 2082 */ 2083 2084 SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags, 2085 APR_OS_DEFAULT, 2086 pool)); 2087 2088 /* Get lock on the filehandle. */ 2089 return svn_io_lock_open_file(lockfile_handle, exclusive, nonblocking, pool); 2090} 2091 2092 2093 2094/* Data consistency/coherency operations. */ 2095 2096svn_error_t *svn_io_file_flush_to_disk(apr_file_t *file, 2097 apr_pool_t *pool) 2098{ 2099 apr_os_file_t filehand; 2100 2101 /* First make sure that any user-space buffered data is flushed. */ 2102 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file), 2103 N_("Can't flush file '%s'"), 2104 N_("Can't flush stream"), 2105 pool)); 2106 2107 apr_os_file_get(&filehand, file); 2108 2109 /* Call the operating system specific function to actually force the 2110 data to disk. */ 2111 { 2112#ifdef WIN32 2113 2114 if (! FlushFileBuffers(filehand)) 2115 return svn_error_wrap_apr(apr_get_os_error(), 2116 _("Can't flush file to disk")); 2117 2118#else 2119 int rv; 2120 2121 do { 2122 rv = fsync(filehand); 2123 } while (rv == -1 && APR_STATUS_IS_EINTR(apr_get_os_error())); 2124 2125 /* If the file is in a memory filesystem, fsync() may return 2126 EINVAL. Presumably the user knows the risks, and we can just 2127 ignore the error. */ 2128 if (rv == -1 && APR_STATUS_IS_EINVAL(apr_get_os_error())) 2129 return SVN_NO_ERROR; 2130 2131 if (rv == -1) 2132 return svn_error_wrap_apr(apr_get_os_error(), 2133 _("Can't flush file to disk")); 2134 2135#endif 2136 } 2137 return SVN_NO_ERROR; 2138} 2139 2140 2141 2142/* TODO write test for these two functions, then refactor. */ 2143 2144/* Set RESULT to an svn_stringbuf_t containing the contents of FILE. 2145 FILENAME is the FILE's on-disk APR-safe name, or NULL if that name 2146 isn't known. If CHECK_SIZE is TRUE, the function will attempt to 2147 first stat() the file to determine it's size before sucking its 2148 contents into the stringbuf. (Doing so can prevent unnecessary 2149 memory usage, an unwanted side effect of the stringbuf growth and 2150 reallocation mechanism.) */ 2151static svn_error_t * 2152stringbuf_from_aprfile(svn_stringbuf_t **result, 2153 const char *filename, 2154 apr_file_t *file, 2155 svn_boolean_t check_size, 2156 apr_pool_t *pool) 2157{ 2158 apr_size_t len; 2159 svn_error_t *err; 2160 svn_stringbuf_t *res = NULL; 2161 apr_size_t res_initial_len = SVN__STREAM_CHUNK_SIZE; 2162 char *buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 2163 2164 /* If our caller wants us to check the size of the file for 2165 efficient memory handling, we'll try to do so. */ 2166 if (check_size) 2167 { 2168 apr_status_t status; 2169 2170 /* If our caller didn't tell us the file's name, we'll ask APR 2171 if it knows the name. No problem if we can't figure it out. */ 2172 if (! filename) 2173 { 2174 const char *filename_apr; 2175 if (! (status = apr_file_name_get(&filename_apr, file))) 2176 filename = filename_apr; 2177 } 2178 2179 /* If we now know the filename, try to stat(). If we succeed, 2180 we know how to allocate our stringbuf. */ 2181 if (filename) 2182 { 2183 apr_finfo_t finfo; 2184 if (! (status = apr_stat(&finfo, filename, APR_FINFO_MIN, pool))) 2185 res_initial_len = (apr_size_t)finfo.size; 2186 } 2187 } 2188 2189 2190 /* XXX: We should check the incoming data for being of type binary. */ 2191 2192 res = svn_stringbuf_create_ensure(res_initial_len, pool); 2193 2194 /* apr_file_read will not return data and eof in the same call. So this loop 2195 * is safe from missing read data. */ 2196 len = SVN__STREAM_CHUNK_SIZE; 2197 err = svn_io_file_read(file, buf, &len, pool); 2198 while (! err) 2199 { 2200 svn_stringbuf_appendbytes(res, buf, len); 2201 len = SVN__STREAM_CHUNK_SIZE; 2202 err = svn_io_file_read(file, buf, &len, pool); 2203 } 2204 2205 /* Having read all the data we *expect* EOF */ 2206 if (err && !APR_STATUS_IS_EOF(err->apr_err)) 2207 return err; 2208 svn_error_clear(err); 2209 2210 *result = res; 2211 return SVN_NO_ERROR; 2212} 2213 2214svn_error_t * 2215svn_stringbuf_from_file2(svn_stringbuf_t **result, 2216 const char *filename, 2217 apr_pool_t *pool) 2218{ 2219 apr_file_t *f; 2220 2221 if (filename[0] == '-' && filename[1] == '\0') 2222 { 2223 apr_status_t apr_err; 2224 if ((apr_err = apr_file_open_stdin(&f, pool))) 2225 return svn_error_wrap_apr(apr_err, _("Can't open stdin")); 2226 SVN_ERR(stringbuf_from_aprfile(result, NULL, f, FALSE, pool)); 2227 } 2228 else 2229 { 2230 SVN_ERR(svn_io_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, pool)); 2231 SVN_ERR(stringbuf_from_aprfile(result, filename, f, TRUE, pool)); 2232 } 2233 return svn_io_file_close(f, pool); 2234} 2235 2236 2237svn_error_t * 2238svn_stringbuf_from_file(svn_stringbuf_t **result, 2239 const char *filename, 2240 apr_pool_t *pool) 2241{ 2242 if (filename[0] == '-' && filename[1] == '\0') 2243 return svn_error_create 2244 (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2245 _("Reading from stdin is disallowed")); 2246 return svn_stringbuf_from_file2(result, filename, pool); 2247} 2248 2249svn_error_t * 2250svn_stringbuf_from_aprfile(svn_stringbuf_t **result, 2251 apr_file_t *file, 2252 apr_pool_t *pool) 2253{ 2254 return stringbuf_from_aprfile(result, NULL, file, TRUE, pool); 2255} 2256 2257 2258 2259/* Deletion. */ 2260 2261svn_error_t * 2262svn_io_remove_file2(const char *path, 2263 svn_boolean_t ignore_enoent, 2264 apr_pool_t *scratch_pool) 2265{ 2266 apr_status_t apr_err; 2267 const char *path_apr; 2268 2269 SVN_ERR(cstring_from_utf8(&path_apr, path, scratch_pool)); 2270 2271 apr_err = apr_file_remove(path_apr, scratch_pool); 2272 if (!apr_err 2273 || (ignore_enoent 2274 && (APR_STATUS_IS_ENOENT(apr_err) 2275 || SVN__APR_STATUS_IS_ENOTDIR(apr_err)))) 2276 return SVN_NO_ERROR; 2277 2278#ifdef WIN32 2279 /* If the target is read only NTFS reports EACCESS and FAT/FAT32 2280 reports EEXIST */ 2281 if (APR_STATUS_IS_EACCES(apr_err) || APR_STATUS_IS_EEXIST(apr_err)) 2282 { 2283 /* Set the destination file writable because Windows will not 2284 allow us to delete when path is read-only */ 2285 SVN_ERR(svn_io_set_file_read_write(path, ignore_enoent, scratch_pool)); 2286 apr_err = apr_file_remove(path_apr, scratch_pool); 2287 2288 if (!apr_err) 2289 return SVN_NO_ERROR; 2290 } 2291 2292 { 2293 apr_status_t os_err = APR_TO_OS_ERROR(apr_err); 2294 /* Check to make sure we aren't trying to delete a directory */ 2295 if (os_err == ERROR_ACCESS_DENIED || os_err == ERROR_SHARING_VIOLATION) 2296 { 2297 apr_finfo_t finfo; 2298 2299 if (!apr_stat(&finfo, path_apr, APR_FINFO_TYPE, scratch_pool) 2300 && finfo.filetype == APR_REG) 2301 { 2302 WIN32_RETRY_LOOP(apr_err, apr_file_remove(path_apr, 2303 scratch_pool)); 2304 } 2305 } 2306 2307 /* Just return the delete error */ 2308 } 2309#endif 2310 2311 if (apr_err) 2312 return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"), 2313 svn_dirent_local_style(path, scratch_pool)); 2314 2315 return SVN_NO_ERROR; 2316} 2317 2318 2319svn_error_t * 2320svn_io_remove_dir(const char *path, apr_pool_t *pool) 2321{ 2322 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 2323} 2324 2325/* 2326 Mac OS X has a bug where if you're reading the contents of a 2327 directory via readdir in a loop, and you remove one of the entries in 2328 the directory and the directory has 338 or more files in it you will 2329 skip over some of the entries in the directory. Needless to say, 2330 this causes problems if you are using this kind of loop inside a 2331 function that is recursively deleting a directory, because when you 2332 get around to removing the directory it will still have something in 2333 it. A similar problem has been observed in other BSDs. This bug has 2334 since been fixed. See http://www.vnode.ch/fixing_seekdir for details. 2335 2336 The workaround is to delete the files only _after_ the initial 2337 directory scan. A previous workaround involving rewinddir is 2338 problematic on Win32 and some NFS clients, notably NetBSD. 2339 2340 See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and 2341 http://subversion.tigris.org/issues/show_bug.cgi?id=3501. 2342*/ 2343 2344/* Neither windows nor unix allows us to delete a non-empty 2345 directory. 2346 2347 This is a function to perform the equivalent of 'rm -rf'. */ 2348svn_error_t * 2349svn_io_remove_dir2(const char *path, svn_boolean_t ignore_enoent, 2350 svn_cancel_func_t cancel_func, void *cancel_baton, 2351 apr_pool_t *pool) 2352{ 2353 svn_error_t *err; 2354 apr_pool_t *subpool; 2355 apr_hash_t *dirents; 2356 apr_hash_index_t *hi; 2357 2358 /* Check for pending cancellation request. 2359 If we need to bail out, do so early. */ 2360 2361 if (cancel_func) 2362 SVN_ERR((*cancel_func)(cancel_baton)); 2363 2364 subpool = svn_pool_create(pool); 2365 2366 err = svn_io_get_dirents3(&dirents, path, TRUE, subpool, subpool); 2367 if (err) 2368 { 2369 /* if the directory doesn't exist, our mission is accomplished */ 2370 if (ignore_enoent && APR_STATUS_IS_ENOENT(err->apr_err)) 2371 { 2372 svn_error_clear(err); 2373 return SVN_NO_ERROR; 2374 } 2375 return svn_error_trace(err); 2376 } 2377 2378 for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi)) 2379 { 2380 const char *name = svn__apr_hash_index_key(hi); 2381 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); 2382 const char *fullpath; 2383 2384 fullpath = svn_dirent_join(path, name, subpool); 2385 if (dirent->kind == svn_node_dir) 2386 { 2387 /* Don't check for cancellation, the callee will immediately do so */ 2388 SVN_ERR(svn_io_remove_dir2(fullpath, FALSE, cancel_func, 2389 cancel_baton, subpool)); 2390 } 2391 else 2392 { 2393 if (cancel_func) 2394 SVN_ERR((*cancel_func)(cancel_baton)); 2395 2396 err = svn_io_remove_file2(fullpath, FALSE, subpool); 2397 if (err) 2398 return svn_error_createf 2399 (err->apr_err, err, _("Can't remove '%s'"), 2400 svn_dirent_local_style(fullpath, subpool)); 2401 } 2402 } 2403 2404 svn_pool_destroy(subpool); 2405 2406 return svn_io_dir_remove_nonrecursive(path, pool); 2407} 2408 2409svn_error_t * 2410svn_io_get_dir_filenames(apr_hash_t **dirents, 2411 const char *path, 2412 apr_pool_t *pool) 2413{ 2414 return svn_error_trace(svn_io_get_dirents3(dirents, path, TRUE, 2415 pool, pool)); 2416} 2417 2418svn_io_dirent2_t * 2419svn_io_dirent2_create(apr_pool_t *result_pool) 2420{ 2421 svn_io_dirent2_t *dirent = apr_pcalloc(result_pool, sizeof(*dirent)); 2422 2423 /*dirent->kind = svn_node_none; 2424 dirent->special = FALSE;*/ 2425 dirent->filesize = SVN_INVALID_FILESIZE; 2426 /*dirent->mtime = 0;*/ 2427 2428 return dirent; 2429} 2430 2431svn_io_dirent2_t * 2432svn_io_dirent2_dup(const svn_io_dirent2_t *item, 2433 apr_pool_t *result_pool) 2434{ 2435 return apr_pmemdup(result_pool, 2436 item, 2437 sizeof(*item)); 2438} 2439 2440svn_error_t * 2441svn_io_get_dirents3(apr_hash_t **dirents, 2442 const char *path, 2443 svn_boolean_t only_check_type, 2444 apr_pool_t *result_pool, 2445 apr_pool_t *scratch_pool) 2446{ 2447 apr_status_t status; 2448 apr_dir_t *this_dir; 2449 apr_finfo_t this_entry; 2450 apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME; 2451 2452 if (!only_check_type) 2453 flags |= APR_FINFO_SIZE | APR_FINFO_MTIME; 2454 2455 *dirents = apr_hash_make(result_pool); 2456 2457 SVN_ERR(svn_io_dir_open(&this_dir, path, scratch_pool)); 2458 2459 for (status = apr_dir_read(&this_entry, flags, this_dir); 2460 status == APR_SUCCESS; 2461 status = apr_dir_read(&this_entry, flags, this_dir)) 2462 { 2463 if ((this_entry.name[0] == '.') 2464 && ((this_entry.name[1] == '\0') 2465 || ((this_entry.name[1] == '.') 2466 && (this_entry.name[2] == '\0')))) 2467 { 2468 continue; 2469 } 2470 else 2471 { 2472 const char *name; 2473 svn_io_dirent2_t *dirent = svn_io_dirent2_create(result_pool); 2474 2475 SVN_ERR(entry_name_to_utf8(&name, this_entry.name, path, result_pool)); 2476 2477 map_apr_finfo_to_node_kind(&(dirent->kind), 2478 &(dirent->special), 2479 &this_entry); 2480 2481 if (!only_check_type) 2482 { 2483 dirent->filesize = this_entry.size; 2484 dirent->mtime = this_entry.mtime; 2485 } 2486 2487 svn_hash_sets(*dirents, name, dirent); 2488 } 2489 } 2490 2491 if (! (APR_STATUS_IS_ENOENT(status))) 2492 return svn_error_wrap_apr(status, _("Can't read directory '%s'"), 2493 svn_dirent_local_style(path, scratch_pool)); 2494 2495 status = apr_dir_close(this_dir); 2496 if (status) 2497 return svn_error_wrap_apr(status, _("Error closing directory '%s'"), 2498 svn_dirent_local_style(path, scratch_pool)); 2499 2500 return SVN_NO_ERROR; 2501} 2502 2503svn_error_t * 2504svn_io_stat_dirent2(const svn_io_dirent2_t **dirent_p, 2505 const char *path, 2506 svn_boolean_t verify_truename, 2507 svn_boolean_t ignore_enoent, 2508 apr_pool_t *result_pool, 2509 apr_pool_t *scratch_pool) 2510{ 2511 apr_finfo_t finfo; 2512 svn_io_dirent2_t *dirent; 2513 svn_error_t *err; 2514 apr_int32_t wanted = APR_FINFO_TYPE | APR_FINFO_LINK 2515 | APR_FINFO_SIZE | APR_FINFO_MTIME; 2516 2517#if defined(WIN32) || defined(__OS2__) 2518 if (verify_truename) 2519 wanted |= APR_FINFO_NAME; 2520#endif 2521 2522 err = svn_io_stat(&finfo, path, wanted, scratch_pool); 2523 2524 if (err && ignore_enoent && 2525 (APR_STATUS_IS_ENOENT(err->apr_err) 2526 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 2527 { 2528 svn_error_clear(err); 2529 dirent = svn_io_dirent2_create(result_pool); 2530 SVN_ERR_ASSERT(dirent->kind == svn_node_none); 2531 2532 *dirent_p = dirent; 2533 return SVN_NO_ERROR; 2534 } 2535 SVN_ERR(err); 2536 2537#if defined(WIN32) || defined(__OS2__) || defined(DARWIN) 2538 if (verify_truename) 2539 { 2540 const char *requested_name = svn_dirent_basename(path, NULL); 2541 2542 if (requested_name[0] == '\0') 2543 { 2544 /* No parent directory. No need to stat/verify */ 2545 } 2546#if defined(WIN32) || defined(__OS2__) 2547 else if (finfo.name) 2548 { 2549 const char *name_on_disk; 2550 SVN_ERR(entry_name_to_utf8(&name_on_disk, finfo.name, path, 2551 scratch_pool)); 2552 2553 if (strcmp(name_on_disk, requested_name) /* != 0 */) 2554 { 2555 if (ignore_enoent) 2556 { 2557 *dirent_p = svn_io_dirent2_create(result_pool); 2558 return SVN_NO_ERROR; 2559 } 2560 else 2561 return svn_error_createf(APR_ENOENT, NULL, 2562 _("Path '%s' not found, case obstructed by '%s'"), 2563 svn_dirent_local_style(path, scratch_pool), 2564 name_on_disk); 2565 } 2566 } 2567#elif defined(DARWIN) 2568 /* Currently apr doesn't set finfo.name on DARWIN, returning 2569 APR_INCOMPLETE. 2570 ### Can we optimize this in another way? */ 2571 else 2572 { 2573 apr_hash_t *dirents; 2574 2575 err = svn_io_get_dirents3(&dirents, 2576 svn_dirent_dirname(path, scratch_pool), 2577 TRUE /* only_check_type */, 2578 scratch_pool, scratch_pool); 2579 2580 if (err && ignore_enoent 2581 && (APR_STATUS_IS_ENOENT(err->apr_err) 2582 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 2583 { 2584 svn_error_clear(err); 2585 2586 *dirent_p = svn_io_dirent2_create(result_pool); 2587 return SVN_NO_ERROR; 2588 } 2589 else 2590 SVN_ERR(err); 2591 2592 if (! svn_hash_gets(dirents, requested_name)) 2593 { 2594 if (ignore_enoent) 2595 { 2596 *dirent_p = svn_io_dirent2_create(result_pool); 2597 return SVN_NO_ERROR; 2598 } 2599 else 2600 return svn_error_createf(APR_ENOENT, NULL, 2601 _("Path '%s' not found"), 2602 svn_dirent_local_style(path, scratch_pool)); 2603 } 2604 } 2605#endif 2606 } 2607#endif 2608 2609 dirent = svn_io_dirent2_create(result_pool); 2610 map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo); 2611 2612 dirent->filesize = finfo.size; 2613 dirent->mtime = finfo.mtime; 2614 2615 *dirent_p = dirent; 2616 2617 return SVN_NO_ERROR; 2618} 2619 2620/* Pool userdata key for the error file passed to svn_io_start_cmd(). */ 2621#define ERRFILE_KEY "svn-io-start-cmd-errfile" 2622 2623/* Handle an error from the child process (before command execution) by 2624 printing DESC and the error string corresponding to STATUS to stderr. */ 2625static void 2626handle_child_process_error(apr_pool_t *pool, apr_status_t status, 2627 const char *desc) 2628{ 2629 char errbuf[256]; 2630 apr_file_t *errfile; 2631 void *p; 2632 2633 /* We can't do anything if we get an error here, so just return. */ 2634 if (apr_pool_userdata_get(&p, ERRFILE_KEY, pool)) 2635 return; 2636 errfile = p; 2637 2638 if (errfile) 2639 /* What we get from APR is in native encoding. */ 2640 apr_file_printf(errfile, "%s: %s", 2641 desc, apr_strerror(status, errbuf, 2642 sizeof(errbuf))); 2643} 2644 2645 2646svn_error_t * 2647svn_io_start_cmd3(apr_proc_t *cmd_proc, 2648 const char *path, 2649 const char *cmd, 2650 const char *const *args, 2651 const char *const *env, 2652 svn_boolean_t inherit, 2653 svn_boolean_t infile_pipe, 2654 apr_file_t *infile, 2655 svn_boolean_t outfile_pipe, 2656 apr_file_t *outfile, 2657 svn_boolean_t errfile_pipe, 2658 apr_file_t *errfile, 2659 apr_pool_t *pool) 2660{ 2661 apr_status_t apr_err; 2662 apr_procattr_t *cmdproc_attr; 2663 int num_args; 2664 const char **args_native; 2665 const char *cmd_apr; 2666 2667 SVN_ERR_ASSERT(!((infile != NULL) && infile_pipe)); 2668 SVN_ERR_ASSERT(!((outfile != NULL) && outfile_pipe)); 2669 SVN_ERR_ASSERT(!((errfile != NULL) && errfile_pipe)); 2670 2671 /* Create the process attributes. */ 2672 apr_err = apr_procattr_create(&cmdproc_attr, pool); 2673 if (apr_err) 2674 return svn_error_wrap_apr(apr_err, 2675 _("Can't create process '%s' attributes"), 2676 cmd); 2677 2678 /* Make sure we invoke cmd directly, not through a shell. */ 2679 apr_err = apr_procattr_cmdtype_set(cmdproc_attr, 2680 inherit ? APR_PROGRAM_PATH : APR_PROGRAM); 2681 if (apr_err) 2682 return svn_error_wrap_apr(apr_err, _("Can't set process '%s' cmdtype"), 2683 cmd); 2684 2685 /* Set the process's working directory. */ 2686 if (path) 2687 { 2688 const char *path_apr; 2689 2690 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 2691 apr_err = apr_procattr_dir_set(cmdproc_attr, path_apr); 2692 if (apr_err) 2693 return svn_error_wrap_apr(apr_err, 2694 _("Can't set process '%s' directory"), 2695 cmd); 2696 } 2697 2698 /* Use requested inputs and outputs. 2699 2700 ### Unfortunately each of these apr functions creates a pipe and then 2701 overwrites the pipe file descriptor with the descriptor we pass 2702 in. The pipes can then never be closed. This is an APR bug. */ 2703 if (infile) 2704 { 2705 apr_err = apr_procattr_child_in_set(cmdproc_attr, infile, NULL); 2706 if (apr_err) 2707 return svn_error_wrap_apr(apr_err, 2708 _("Can't set process '%s' child input"), 2709 cmd); 2710 } 2711 if (outfile) 2712 { 2713 apr_err = apr_procattr_child_out_set(cmdproc_attr, outfile, NULL); 2714 if (apr_err) 2715 return svn_error_wrap_apr(apr_err, 2716 _("Can't set process '%s' child outfile"), 2717 cmd); 2718 } 2719 if (errfile) 2720 { 2721 apr_err = apr_procattr_child_err_set(cmdproc_attr, errfile, NULL); 2722 if (apr_err) 2723 return svn_error_wrap_apr(apr_err, 2724 _("Can't set process '%s' child errfile"), 2725 cmd); 2726 } 2727 2728 /* Forward request for pipes to APR. */ 2729 if (infile_pipe || outfile_pipe || errfile_pipe) 2730 { 2731 apr_err = apr_procattr_io_set(cmdproc_attr, 2732 infile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE, 2733 outfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE, 2734 errfile_pipe ? APR_FULL_BLOCK : APR_NO_PIPE); 2735 2736 if (apr_err) 2737 return svn_error_wrap_apr(apr_err, 2738 _("Can't set process '%s' stdio pipes"), 2739 cmd); 2740 } 2741 2742 /* Have the child print any problems executing its program to errfile. */ 2743 apr_err = apr_pool_userdata_set(errfile, ERRFILE_KEY, NULL, pool); 2744 if (apr_err) 2745 return svn_error_wrap_apr(apr_err, 2746 _("Can't set process '%s' child errfile for " 2747 "error handler"), 2748 cmd); 2749 apr_err = apr_procattr_child_errfn_set(cmdproc_attr, 2750 handle_child_process_error); 2751 if (apr_err) 2752 return svn_error_wrap_apr(apr_err, 2753 _("Can't set process '%s' error handler"), 2754 cmd); 2755 2756 /* Convert cmd and args from UTF-8 */ 2757 SVN_ERR(cstring_from_utf8(&cmd_apr, cmd, pool)); 2758 for (num_args = 0; args[num_args]; num_args++) 2759 ; 2760 args_native = apr_palloc(pool, (num_args + 1) * sizeof(char *)); 2761 args_native[num_args] = NULL; 2762 while (num_args--) 2763 { 2764 /* ### Well, it turns out that on APR on Windows expects all 2765 program args to be in UTF-8. Callers of svn_io_run_cmd 2766 should be aware of that. */ 2767 SVN_ERR(cstring_from_utf8(&args_native[num_args], 2768 args[num_args], pool)); 2769 } 2770 2771 2772 /* Start the cmd command. */ 2773 apr_err = apr_proc_create(cmd_proc, cmd_apr, args_native, 2774 inherit ? NULL : env, cmdproc_attr, pool); 2775 if (apr_err) 2776 return svn_error_wrap_apr(apr_err, _("Can't start process '%s'"), cmd); 2777 2778 return SVN_NO_ERROR; 2779} 2780 2781#undef ERRFILE_KEY 2782 2783svn_error_t * 2784svn_io_wait_for_cmd(apr_proc_t *cmd_proc, 2785 const char *cmd, 2786 int *exitcode, 2787 apr_exit_why_e *exitwhy, 2788 apr_pool_t *pool) 2789{ 2790 apr_status_t apr_err; 2791 apr_exit_why_e exitwhy_val; 2792 int exitcode_val; 2793 2794 /* The Win32 apr_proc_wait doesn't set this... */ 2795 exitwhy_val = APR_PROC_EXIT; 2796 2797 /* Wait for the cmd command to finish. */ 2798 apr_err = apr_proc_wait(cmd_proc, &exitcode_val, &exitwhy_val, APR_WAIT); 2799 if (!APR_STATUS_IS_CHILD_DONE(apr_err)) 2800 return svn_error_wrap_apr(apr_err, _("Error waiting for process '%s'"), 2801 cmd); 2802 2803 if (exitwhy) 2804 *exitwhy = exitwhy_val; 2805 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val) 2806 && APR_PROC_CHECK_CORE_DUMP(exitwhy_val)) 2807 return svn_error_createf 2808 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 2809 _("Process '%s' failed (signal %d, core dumped)"), 2810 cmd, exitcode_val); 2811 else if (APR_PROC_CHECK_SIGNALED(exitwhy_val)) 2812 return svn_error_createf 2813 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 2814 _("Process '%s' failed (signal %d)"), 2815 cmd, exitcode_val); 2816 else if (! APR_PROC_CHECK_EXIT(exitwhy_val)) 2817 /* Don't really know what happened here. */ 2818 return svn_error_createf 2819 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 2820 _("Process '%s' failed (exitwhy %d, exitcode %d)"), 2821 cmd, exitwhy_val, exitcode_val); 2822 2823 if (exitcode) 2824 *exitcode = exitcode_val; 2825 else if (exitcode_val != 0) 2826 return svn_error_createf 2827 (SVN_ERR_EXTERNAL_PROGRAM, NULL, 2828 _("Process '%s' returned error exitcode %d"), cmd, exitcode_val); 2829 2830 return SVN_NO_ERROR; 2831} 2832 2833 2834svn_error_t * 2835svn_io_run_cmd(const char *path, 2836 const char *cmd, 2837 const char *const *args, 2838 int *exitcode, 2839 apr_exit_why_e *exitwhy, 2840 svn_boolean_t inherit, 2841 apr_file_t *infile, 2842 apr_file_t *outfile, 2843 apr_file_t *errfile, 2844 apr_pool_t *pool) 2845{ 2846 apr_proc_t cmd_proc; 2847 2848 SVN_ERR(svn_io_start_cmd3(&cmd_proc, path, cmd, args, NULL, inherit, 2849 FALSE, infile, FALSE, outfile, FALSE, errfile, 2850 pool)); 2851 2852 return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool); 2853} 2854 2855 2856svn_error_t * 2857svn_io_run_diff2(const char *dir, 2858 const char *const *user_args, 2859 int num_user_args, 2860 const char *label1, 2861 const char *label2, 2862 const char *from, 2863 const char *to, 2864 int *pexitcode, 2865 apr_file_t *outfile, 2866 apr_file_t *errfile, 2867 const char *diff_cmd, 2868 apr_pool_t *pool) 2869{ 2870 const char **args; 2871 int i; 2872 int exitcode; 2873 int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */ 2874 apr_pool_t *subpool = svn_pool_create(pool); 2875 2876 if (pexitcode == NULL) 2877 pexitcode = &exitcode; 2878 2879 if (user_args != NULL) 2880 nargs += num_user_args; 2881 else 2882 nargs += 1; /* -u */ 2883 2884 if (label1 != NULL) 2885 nargs += 2; /* the -L and the label itself */ 2886 if (label2 != NULL) 2887 nargs += 2; /* the -L and the label itself */ 2888 2889 args = apr_palloc(subpool, nargs * sizeof(char *)); 2890 2891 i = 0; 2892 args[i++] = diff_cmd; 2893 2894 if (user_args != NULL) 2895 { 2896 int j; 2897 for (j = 0; j < num_user_args; ++j) 2898 args[i++] = user_args[j]; 2899 } 2900 else 2901 args[i++] = "-u"; /* assume -u if the user didn't give us any args */ 2902 2903 if (label1 != NULL) 2904 { 2905 args[i++] = "-L"; 2906 args[i++] = label1; 2907 } 2908 if (label2 != NULL) 2909 { 2910 args[i++] = "-L"; 2911 args[i++] = label2; 2912 } 2913 2914 args[i++] = svn_dirent_local_style(from, subpool); 2915 args[i++] = svn_dirent_local_style(to, subpool); 2916 args[i++] = NULL; 2917 2918 SVN_ERR_ASSERT(i == nargs); 2919 2920 SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE, 2921 NULL, outfile, errfile, subpool)); 2922 2923 /* The man page for (GNU) diff describes the return value as: 2924 2925 "An exit status of 0 means no differences were found, 1 means 2926 some differences were found, and 2 means trouble." 2927 2928 A return value of 2 typically occurs when diff cannot read its input 2929 or write to its output, but in any case we probably ought to return an 2930 error for anything other than 0 or 1 as the output is likely to be 2931 corrupt. 2932 */ 2933 if (*pexitcode != 0 && *pexitcode != 1) 2934 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, 2935 _("'%s' returned %d"), 2936 svn_dirent_local_style(diff_cmd, pool), 2937 *pexitcode); 2938 2939 svn_pool_destroy(subpool); 2940 2941 return SVN_NO_ERROR; 2942} 2943 2944 2945svn_error_t * 2946svn_io_run_diff3_3(int *exitcode, 2947 const char *dir, 2948 const char *mine, 2949 const char *older, 2950 const char *yours, 2951 const char *mine_label, 2952 const char *older_label, 2953 const char *yours_label, 2954 apr_file_t *merged, 2955 const char *diff3_cmd, 2956 const apr_array_header_t *user_args, 2957 apr_pool_t *pool) 2958{ 2959 const char **args = apr_palloc(pool, 2960 sizeof(char*) * (13 2961 + (user_args 2962 ? user_args->nelts 2963 : 1))); 2964#ifndef NDEBUG 2965 int nargs = 12; 2966#endif 2967 int i = 0; 2968 2969 /* Labels fall back to sensible defaults if not specified. */ 2970 if (mine_label == NULL) 2971 mine_label = ".working"; 2972 if (older_label == NULL) 2973 older_label = ".old"; 2974 if (yours_label == NULL) 2975 yours_label = ".new"; 2976 2977 /* Set up diff3 command line. */ 2978 args[i++] = diff3_cmd; 2979 if (user_args) 2980 { 2981 int j; 2982 for (j = 0; j < user_args->nelts; ++j) 2983 args[i++] = APR_ARRAY_IDX(user_args, j, const char *); 2984#ifndef NDEBUG 2985 nargs += user_args->nelts; 2986#endif 2987 } 2988 else 2989 { 2990 args[i++] = "-E"; /* We tried "-A" here, but that caused 2991 overlapping identical changes to 2992 conflict. See issue #682. */ 2993#ifndef NDEBUG 2994 ++nargs; 2995#endif 2996 } 2997 args[i++] = "-m"; 2998 args[i++] = "-L"; 2999 args[i++] = mine_label; 3000 args[i++] = "-L"; 3001 args[i++] = older_label; /* note: this label is ignored if 3002 using 2-part markers, which is the 3003 case with "-E". */ 3004 args[i++] = "-L"; 3005 args[i++] = yours_label; 3006#ifdef SVN_DIFF3_HAS_DIFF_PROGRAM_ARG 3007 { 3008 svn_boolean_t has_arg; 3009 3010 /* ### FIXME: we really shouldn't be reading the config here; 3011 instead, the necessary bits should be passed in by the caller. 3012 But should we add another parameter to this function, when the 3013 whole external diff3 thing might eventually go away? */ 3014 apr_hash_t *config; 3015 svn_config_t *cfg; 3016 3017 SVN_ERR(svn_config_get_config(&config, pool)); 3018 cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; 3019 SVN_ERR(svn_config_get_bool(cfg, &has_arg, SVN_CONFIG_SECTION_HELPERS, 3020 SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG, 3021 TRUE)); 3022 if (has_arg) 3023 { 3024 const char *diff_cmd, *diff_utf8; 3025 svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS, 3026 SVN_CONFIG_OPTION_DIFF_CMD, SVN_CLIENT_DIFF); 3027 SVN_ERR(cstring_to_utf8(&diff_utf8, diff_cmd, pool)); 3028 args[i++] = apr_pstrcat(pool, "--diff-program=", diff_utf8, NULL); 3029#ifndef NDEBUG 3030 ++nargs; 3031#endif 3032 } 3033 } 3034#endif 3035 args[i++] = svn_dirent_local_style(mine, pool); 3036 args[i++] = svn_dirent_local_style(older, pool); 3037 args[i++] = svn_dirent_local_style(yours, pool); 3038 args[i++] = NULL; 3039#ifndef NDEBUG 3040 SVN_ERR_ASSERT(i == nargs); 3041#endif 3042 3043 /* Run diff3, output the merged text into the scratch file. */ 3044 SVN_ERR(svn_io_run_cmd(dir, diff3_cmd, args, 3045 exitcode, NULL, 3046 TRUE, /* keep environment */ 3047 NULL, merged, NULL, 3048 pool)); 3049 3050 /* According to the diff3 docs, a '0' means the merge was clean, and 3051 '1' means conflict markers were found. Anything else is real 3052 error. */ 3053 if ((*exitcode != 0) && (*exitcode != 1)) 3054 return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL, 3055 _("Error running '%s': exitcode was %d, " 3056 "args were:" 3057 "\nin directory '%s', basenames:\n%s\n%s\n%s"), 3058 svn_dirent_local_style(diff3_cmd, pool), 3059 *exitcode, 3060 svn_dirent_local_style(dir, pool), 3061 /* Don't call svn_path_local_style() on 3062 the basenames. We don't want them to 3063 be absolute, and we don't need the 3064 separator conversion. */ 3065 mine, older, yours); 3066 3067 return SVN_NO_ERROR; 3068} 3069 3070 3071/* Canonicalize a string for hashing. Modifies KEY in place. */ 3072static APR_INLINE char * 3073fileext_tolower(char *key) 3074{ 3075 register char *p; 3076 for (p = key; *p != 0; ++p) 3077 *p = (char)apr_tolower(*p); 3078 return key; 3079} 3080 3081 3082svn_error_t * 3083svn_io_parse_mimetypes_file(apr_hash_t **type_map, 3084 const char *mimetypes_file, 3085 apr_pool_t *pool) 3086{ 3087 svn_error_t *err = SVN_NO_ERROR; 3088 apr_hash_t *types = apr_hash_make(pool); 3089 svn_boolean_t eof = FALSE; 3090 svn_stringbuf_t *buf; 3091 apr_pool_t *subpool = svn_pool_create(pool); 3092 apr_file_t *types_file; 3093 svn_stream_t *mimetypes_stream; 3094 3095 SVN_ERR(svn_io_file_open(&types_file, mimetypes_file, 3096 APR_READ, APR_OS_DEFAULT, pool)); 3097 mimetypes_stream = svn_stream_from_aprfile2(types_file, FALSE, pool); 3098 3099 while (1) 3100 { 3101 apr_array_header_t *tokens; 3102 const char *type; 3103 3104 svn_pool_clear(subpool); 3105 3106 /* Read a line. */ 3107 if ((err = svn_stream_readline(mimetypes_stream, &buf, 3108 APR_EOL_STR, &eof, subpool))) 3109 break; 3110 3111 /* Only pay attention to non-empty, non-comment lines. */ 3112 if (buf->len) 3113 { 3114 int i; 3115 3116 if (buf->data[0] == '#') 3117 continue; 3118 3119 /* Tokenize (into our return pool). */ 3120 tokens = svn_cstring_split(buf->data, " \t", TRUE, pool); 3121 if (tokens->nelts < 2) 3122 continue; 3123 3124 /* The first token in a multi-token line is the media type. 3125 Subsequent tokens are filename extensions associated with 3126 that media type. */ 3127 type = APR_ARRAY_IDX(tokens, 0, const char *); 3128 for (i = 1; i < tokens->nelts; i++) 3129 { 3130 /* We can safely address 'ext' as a non-const string because 3131 * we know svn_cstring_split() allocated it in 'pool' for us. */ 3132 char *ext = APR_ARRAY_IDX(tokens, i, char *); 3133 fileext_tolower(ext); 3134 svn_hash_sets(types, ext, type); 3135 } 3136 } 3137 if (eof) 3138 break; 3139 } 3140 svn_pool_destroy(subpool); 3141 3142 /* If there was an error above, close the file (ignoring any error 3143 from *that*) and return the originally error. */ 3144 if (err) 3145 { 3146 svn_error_clear(svn_stream_close(mimetypes_stream)); 3147 return err; 3148 } 3149 3150 /* Close the stream (which closes the underlying file, too). */ 3151 SVN_ERR(svn_stream_close(mimetypes_stream)); 3152 3153 *type_map = types; 3154 return SVN_NO_ERROR; 3155} 3156 3157 3158svn_error_t * 3159svn_io_detect_mimetype2(const char **mimetype, 3160 const char *file, 3161 apr_hash_t *mimetype_map, 3162 apr_pool_t *pool) 3163{ 3164 static const char * const generic_binary = "application/octet-stream"; 3165 3166 svn_node_kind_t kind; 3167 apr_file_t *fh; 3168 svn_error_t *err; 3169 unsigned char block[1024]; 3170 apr_size_t amt_read = sizeof(block); 3171 3172 /* Default return value is NULL. */ 3173 *mimetype = NULL; 3174 3175 /* If there is a mimetype_map provided, we'll first try to look up 3176 our file's extension in the map. Failing that, we'll run the 3177 heuristic. */ 3178 if (mimetype_map) 3179 { 3180 const char *type_from_map; 3181 char *path_ext; /* Can point to physical const memory but only when 3182 svn_path_splitext sets it to "". */ 3183 svn_path_splitext(NULL, (const char **)&path_ext, file, pool); 3184 fileext_tolower(path_ext); 3185 if ((type_from_map = svn_hash_gets(mimetype_map, path_ext))) 3186 { 3187 *mimetype = type_from_map; 3188 return SVN_NO_ERROR; 3189 } 3190 } 3191 3192 /* See if this file even exists, and make sure it really is a file. */ 3193 SVN_ERR(svn_io_check_path(file, &kind, pool)); 3194 if (kind != svn_node_file) 3195 return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, 3196 _("Can't detect MIME type of non-file '%s'"), 3197 svn_dirent_local_style(file, pool)); 3198 3199 SVN_ERR(svn_io_file_open(&fh, file, APR_READ, 0, pool)); 3200 3201 /* Read a block of data from FILE. */ 3202 err = svn_io_file_read(fh, block, &amt_read, pool); 3203 if (err && ! APR_STATUS_IS_EOF(err->apr_err)) 3204 return err; 3205 svn_error_clear(err); 3206 3207 /* Now close the file. No use keeping it open any more. */ 3208 SVN_ERR(svn_io_file_close(fh, pool)); 3209 3210 if (svn_io_is_binary_data(block, amt_read)) 3211 *mimetype = generic_binary; 3212 3213 return SVN_NO_ERROR; 3214} 3215 3216 3217svn_boolean_t 3218svn_io_is_binary_data(const void *data, apr_size_t len) 3219{ 3220 const unsigned char *buf = data; 3221 3222 if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) 3223 { 3224 /* This is an empty UTF-8 file which only contains the UTF-8 BOM. 3225 * Treat it as plain text. */ 3226 return FALSE; 3227 } 3228 3229 /* Right now, this function is going to be really stupid. It's 3230 going to examine the block of data, and make sure that 15% 3231 of the bytes are such that their value is in the ranges 0x07-0x0D 3232 or 0x20-0x7F, and that none of those bytes is 0x00. If those 3233 criteria are not met, we're calling it binary. 3234 3235 NOTE: Originally, I intended to target 85% of the bytes being in 3236 the specified ranges, but I flubbed the condition. At any rate, 3237 folks aren't complaining, so I'm not sure that it's worth 3238 adjusting this retroactively now. --cmpilato */ 3239 if (len > 0) 3240 { 3241 apr_size_t i; 3242 apr_size_t binary_count = 0; 3243 3244 /* Run through the data we've read, counting the 'binary-ish' 3245 bytes. HINT: If we see a 0x00 byte, we'll set our count to its 3246 max and stop reading the file. */ 3247 for (i = 0; i < len; i++) 3248 { 3249 if (buf[i] == 0) 3250 { 3251 binary_count = len; 3252 break; 3253 } 3254 if ((buf[i] < 0x07) 3255 || ((buf[i] > 0x0D) && (buf[i] < 0x20)) 3256 || (buf[i] > 0x7F)) 3257 { 3258 binary_count++; 3259 } 3260 } 3261 3262 return (((binary_count * 1000) / len) > 850); 3263 } 3264 3265 return FALSE; 3266} 3267 3268 3269svn_error_t * 3270svn_io_detect_mimetype(const char **mimetype, 3271 const char *file, 3272 apr_pool_t *pool) 3273{ 3274 return svn_io_detect_mimetype2(mimetype, file, NULL, pool); 3275} 3276 3277 3278svn_error_t * 3279svn_io_file_open(apr_file_t **new_file, const char *fname, 3280 apr_int32_t flag, apr_fileperms_t perm, 3281 apr_pool_t *pool) 3282{ 3283 const char *fname_apr; 3284 apr_status_t status; 3285 3286 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool)); 3287 status = file_open(new_file, fname_apr, flag | APR_BINARY, perm, TRUE, 3288 pool); 3289 3290 if (status) 3291 return svn_error_wrap_apr(status, _("Can't open file '%s'"), 3292 svn_dirent_local_style(fname, pool)); 3293 else 3294 return SVN_NO_ERROR; 3295} 3296 3297 3298static APR_INLINE svn_error_t * 3299do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status, 3300 const char *msg, const char *msg_no_name, 3301 apr_pool_t *pool) 3302{ 3303 const char *name; 3304 svn_error_t *err; 3305 3306 if (! status) 3307 return SVN_NO_ERROR; 3308 3309 err = svn_io_file_name_get(&name, file, pool); 3310 if (err) 3311 name = NULL; 3312 svn_error_clear(err); 3313 3314 /* ### Issue #3014: Return a specific error for broken pipes, 3315 * ### with a single element in the error chain. */ 3316 if (SVN__APR_STATUS_IS_EPIPE(status)) 3317 return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL); 3318 3319 if (name) 3320 return svn_error_wrap_apr(status, _(msg), 3321 try_utf8_from_internal_style(name, pool)); 3322 else 3323 return svn_error_wrap_apr(status, "%s", _(msg_no_name)); 3324} 3325 3326 3327svn_error_t * 3328svn_io_file_close(apr_file_t *file, apr_pool_t *pool) 3329{ 3330 return do_io_file_wrapper_cleanup(file, apr_file_close(file), 3331 N_("Can't close file '%s'"), 3332 N_("Can't close stream"), 3333 pool); 3334} 3335 3336 3337svn_error_t * 3338svn_io_file_getc(char *ch, apr_file_t *file, apr_pool_t *pool) 3339{ 3340 return do_io_file_wrapper_cleanup(file, apr_file_getc(ch, file), 3341 N_("Can't read file '%s'"), 3342 N_("Can't read stream"), 3343 pool); 3344} 3345 3346 3347svn_error_t * 3348svn_io_file_putc(char ch, apr_file_t *file, apr_pool_t *pool) 3349{ 3350 return do_io_file_wrapper_cleanup(file, apr_file_putc(ch, file), 3351 N_("Can't write file '%s'"), 3352 N_("Can't write stream"), 3353 pool); 3354} 3355 3356 3357svn_error_t * 3358svn_io_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted, 3359 apr_file_t *file, apr_pool_t *pool) 3360{ 3361 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */ 3362 wanted &= ~SVN__APR_FINFO_MASK_OUT; 3363 3364 return do_io_file_wrapper_cleanup( 3365 file, apr_file_info_get(finfo, wanted, file), 3366 N_("Can't get attribute information from file '%s'"), 3367 N_("Can't get attribute information from stream"), 3368 pool); 3369} 3370 3371 3372svn_error_t * 3373svn_io_file_read(apr_file_t *file, void *buf, 3374 apr_size_t *nbytes, apr_pool_t *pool) 3375{ 3376 return do_io_file_wrapper_cleanup(file, apr_file_read(file, buf, nbytes), 3377 N_("Can't read file '%s'"), 3378 N_("Can't read stream"), 3379 pool); 3380} 3381 3382 3383svn_error_t * 3384svn_io_file_read_full2(apr_file_t *file, void *buf, 3385 apr_size_t nbytes, apr_size_t *bytes_read, 3386 svn_boolean_t *hit_eof, 3387 apr_pool_t *pool) 3388{ 3389 apr_status_t status = apr_file_read_full(file, buf, nbytes, bytes_read); 3390 if (hit_eof) 3391 { 3392 if (APR_STATUS_IS_EOF(status)) 3393 { 3394 *hit_eof = TRUE; 3395 return SVN_NO_ERROR; 3396 } 3397 else 3398 *hit_eof = FALSE; 3399 } 3400 3401 return do_io_file_wrapper_cleanup(file, status, 3402 N_("Can't read file '%s'"), 3403 N_("Can't read stream"), 3404 pool); 3405} 3406 3407 3408svn_error_t * 3409svn_io_file_seek(apr_file_t *file, apr_seek_where_t where, 3410 apr_off_t *offset, apr_pool_t *pool) 3411{ 3412 return do_io_file_wrapper_cleanup( 3413 file, apr_file_seek(file, where, offset), 3414 N_("Can't set position pointer in file '%s'"), 3415 N_("Can't set position pointer in stream"), 3416 pool); 3417} 3418 3419 3420svn_error_t * 3421svn_io_file_write(apr_file_t *file, const void *buf, 3422 apr_size_t *nbytes, apr_pool_t *pool) 3423{ 3424 return svn_error_trace(do_io_file_wrapper_cleanup( 3425 file, apr_file_write(file, buf, nbytes), 3426 N_("Can't write to file '%s'"), 3427 N_("Can't write to stream"), 3428 pool)); 3429} 3430 3431 3432svn_error_t * 3433svn_io_file_write_full(apr_file_t *file, const void *buf, 3434 apr_size_t nbytes, apr_size_t *bytes_written, 3435 apr_pool_t *pool) 3436{ 3437 /* We cannot simply call apr_file_write_full on Win32 as it may fail 3438 for larger values of NBYTES. In that case, we have to emulate the 3439 "_full" part here. Thus, always call apr_file_write directly on 3440 Win32 as this minimizes overhead for small data buffers. */ 3441#ifdef WIN32 3442#define MAXBUFSIZE 30*1024 3443 apr_size_t bw = nbytes; 3444 apr_size_t to_write = nbytes; 3445 3446 /* try a simple "write everything at once" first */ 3447 apr_status_t rv = apr_file_write(file, buf, &bw); 3448 buf = (char *)buf + bw; 3449 to_write -= bw; 3450 3451 /* if the OS cannot handle that, use smaller chunks */ 3452 if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY) 3453 && nbytes > MAXBUFSIZE) 3454 { 3455 do { 3456 bw = to_write > MAXBUFSIZE ? MAXBUFSIZE : to_write; 3457 rv = apr_file_write(file, buf, &bw); 3458 buf = (char *)buf + bw; 3459 to_write -= bw; 3460 } while (rv == APR_SUCCESS && to_write > 0); 3461 } 3462 3463 /* bytes_written may actually be NULL */ 3464 if (bytes_written) 3465 *bytes_written = nbytes - to_write; 3466#undef MAXBUFSIZE 3467#else 3468 apr_status_t rv = apr_file_write_full(file, buf, nbytes, bytes_written); 3469#endif 3470 3471 return svn_error_trace(do_io_file_wrapper_cleanup( 3472 file, rv, 3473 N_("Can't write to file '%s'"), 3474 N_("Can't write to stream"), 3475 pool)); 3476} 3477 3478 3479svn_error_t * 3480svn_io_write_unique(const char **tmp_path, 3481 const char *dirpath, 3482 const void *buf, 3483 apr_size_t nbytes, 3484 svn_io_file_del_t delete_when, 3485 apr_pool_t *pool) 3486{ 3487 apr_file_t *new_file; 3488 svn_error_t *err; 3489 3490 SVN_ERR(svn_io_open_unique_file3(&new_file, tmp_path, dirpath, 3491 delete_when, pool, pool)); 3492 3493 err = svn_io_file_write_full(new_file, buf, nbytes, NULL, pool); 3494 3495 if (!err) 3496 err = svn_io_file_flush_to_disk(new_file, pool); 3497 3498 return svn_error_trace( 3499 svn_error_compose_create(err, 3500 svn_io_file_close(new_file, pool))); 3501} 3502 3503 3504svn_error_t * 3505svn_io_file_trunc(apr_file_t *file, apr_off_t offset, apr_pool_t *pool) 3506{ 3507 /* This is a work-around. APR would flush the write buffer 3508 _after_ truncating the file causing now invalid buffered 3509 data to be written behind OFFSET. */ 3510 SVN_ERR(do_io_file_wrapper_cleanup(file, apr_file_flush(file), 3511 N_("Can't flush file '%s'"), 3512 N_("Can't flush stream"), 3513 pool)); 3514 3515 return do_io_file_wrapper_cleanup(file, apr_file_trunc(file, offset), 3516 N_("Can't truncate file '%s'"), 3517 N_("Can't truncate stream"), 3518 pool); 3519} 3520 3521 3522svn_error_t * 3523svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit, 3524 apr_pool_t *pool) 3525{ 3526 /* variables */ 3527 apr_size_t total_read = 0; 3528 svn_boolean_t eof = FALSE; 3529 const char *name; 3530 svn_error_t *err; 3531 apr_size_t buf_size = *limit; 3532 3533 while (buf_size > 0) 3534 { 3535 /* read a fair chunk of data at once. But don't get too ambitious 3536 * as that would result in too much waste. Also make sure we can 3537 * put a NUL after the last byte read. 3538 */ 3539 apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128; 3540 apr_size_t bytes_read = 0; 3541 char *eol; 3542 3543 if (to_read == 0) 3544 break; 3545 3546 /* read data block (or just a part of it) */ 3547 SVN_ERR(svn_io_file_read_full2(file, buf, to_read, 3548 &bytes_read, &eof, pool)); 3549 3550 /* look or a newline char */ 3551 buf[bytes_read] = 0; 3552 eol = strchr(buf, '\n'); 3553 if (eol) 3554 { 3555 apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read; 3556 3557 *eol = 0; 3558 *limit = total_read + (eol - buf); 3559 3560 /* correct the file pointer: 3561 * appear as though we just had read the newline char 3562 */ 3563 SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool)); 3564 3565 return SVN_NO_ERROR; 3566 } 3567 else if (eof) 3568 { 3569 /* no EOL found but we hit the end of the file. 3570 * Generate a nice EOF error object and return it. 3571 */ 3572 char dummy; 3573 SVN_ERR(svn_io_file_getc(&dummy, file, pool)); 3574 } 3575 3576 /* next data chunk */ 3577 buf_size -= bytes_read; 3578 buf += bytes_read; 3579 total_read += bytes_read; 3580 } 3581 3582 /* buffer limit has been exceeded without finding the EOL */ 3583 err = svn_io_file_name_get(&name, file, pool); 3584 if (err) 3585 name = NULL; 3586 svn_error_clear(err); 3587 3588 if (name) 3589 return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, 3590 _("Can't read length line in file '%s'"), 3591 svn_dirent_local_style(name, pool)); 3592 else 3593 return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, 3594 _("Can't read length line in stream")); 3595} 3596 3597 3598svn_error_t * 3599svn_io_stat(apr_finfo_t *finfo, const char *fname, 3600 apr_int32_t wanted, apr_pool_t *pool) 3601{ 3602 apr_status_t status; 3603 const char *fname_apr; 3604 3605 /* APR doesn't like "" directories */ 3606 if (fname[0] == '\0') 3607 fname = "."; 3608 3609 SVN_ERR(cstring_from_utf8(&fname_apr, fname, pool)); 3610 3611 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */ 3612 wanted &= ~SVN__APR_FINFO_MASK_OUT; 3613 3614 status = apr_stat(finfo, fname_apr, wanted, pool); 3615 if (status) 3616 return svn_error_wrap_apr(status, _("Can't stat '%s'"), 3617 svn_dirent_local_style(fname, pool)); 3618 3619 return SVN_NO_ERROR; 3620} 3621 3622 3623svn_error_t * 3624svn_io_file_rename(const char *from_path, const char *to_path, 3625 apr_pool_t *pool) 3626{ 3627 apr_status_t status = APR_SUCCESS; 3628 const char *from_path_apr, *to_path_apr; 3629 3630 SVN_ERR(cstring_from_utf8(&from_path_apr, from_path, pool)); 3631 SVN_ERR(cstring_from_utf8(&to_path_apr, to_path, pool)); 3632 3633 status = apr_file_rename(from_path_apr, to_path_apr, pool); 3634 3635#if defined(WIN32) || defined(__OS2__) 3636 /* If the target file is read only NTFS reports EACCESS and 3637 FAT/FAT32 reports EEXIST */ 3638 if (APR_STATUS_IS_EACCES(status) || APR_STATUS_IS_EEXIST(status)) 3639 { 3640 /* Set the destination file writable because Windows will not 3641 allow us to rename when to_path is read-only, but will 3642 allow renaming when from_path is read only. */ 3643 SVN_ERR(svn_io_set_file_read_write(to_path, TRUE, pool)); 3644 3645 status = apr_file_rename(from_path_apr, to_path_apr, pool); 3646 } 3647 WIN32_RETRY_LOOP(status, apr_file_rename(from_path_apr, to_path_apr, pool)); 3648#endif /* WIN32 || __OS2__ */ 3649 3650 if (status) 3651 return svn_error_wrap_apr(status, _("Can't move '%s' to '%s'"), 3652 svn_dirent_local_style(from_path, pool), 3653 svn_dirent_local_style(to_path, pool)); 3654 3655 return SVN_NO_ERROR; 3656} 3657 3658 3659svn_error_t * 3660svn_io_file_move(const char *from_path, const char *to_path, 3661 apr_pool_t *pool) 3662{ 3663 svn_error_t *err = svn_io_file_rename(from_path, to_path, pool); 3664 3665 if (err && APR_STATUS_IS_EXDEV(err->apr_err)) 3666 { 3667 const char *tmp_to_path; 3668 3669 svn_error_clear(err); 3670 3671 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_to_path, 3672 svn_dirent_dirname(to_path, pool), 3673 svn_io_file_del_none, 3674 pool, pool)); 3675 3676 err = svn_io_copy_file(from_path, tmp_to_path, TRUE, pool); 3677 if (err) 3678 goto failed_tmp; 3679 3680 err = svn_io_file_rename(tmp_to_path, to_path, pool); 3681 if (err) 3682 goto failed_tmp; 3683 3684 err = svn_io_remove_file2(from_path, FALSE, pool); 3685 if (! err) 3686 return SVN_NO_ERROR; 3687 3688 svn_error_clear(svn_io_remove_file2(to_path, FALSE, pool)); 3689 3690 return err; 3691 3692 failed_tmp: 3693 svn_error_clear(svn_io_remove_file2(tmp_to_path, FALSE, pool)); 3694 } 3695 3696 return err; 3697} 3698 3699/* Common implementation of svn_io_dir_make and svn_io_dir_make_hidden. 3700 HIDDEN determines if the hidden attribute 3701 should be set on the newly created directory. */ 3702static svn_error_t * 3703dir_make(const char *path, apr_fileperms_t perm, 3704 svn_boolean_t hidden, svn_boolean_t sgid, apr_pool_t *pool) 3705{ 3706 apr_status_t status; 3707 const char *path_apr; 3708 3709 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 3710 3711 /* APR doesn't like "" directories */ 3712 if (path_apr[0] == '\0') 3713 path_apr = "."; 3714 3715#if (APR_OS_DEFAULT & APR_WSTICKY) 3716 /* The APR shipped with httpd 2.0.50 contains a bug where 3717 APR_OS_DEFAULT encompasses the setuid, setgid, and sticky bits. 3718 There is a special case for file creation, but not directory 3719 creation, so directories wind up getting created with the sticky 3720 bit set. (There is no such thing as a setuid directory, and the 3721 setgid bit is apparently ignored at mkdir() time.) If we detect 3722 this problem, work around it by unsetting those bits if we are 3723 passed APR_OS_DEFAULT. */ 3724 if (perm == APR_OS_DEFAULT) 3725 perm &= ~(APR_USETID | APR_GSETID | APR_WSTICKY); 3726#endif 3727 3728 status = apr_dir_make(path_apr, perm, pool); 3729 WIN32_RETRY_LOOP(status, apr_dir_make(path_apr, perm, pool)); 3730 3731 if (status) 3732 return svn_error_wrap_apr(status, _("Can't create directory '%s'"), 3733 svn_dirent_local_style(path, pool)); 3734 3735#ifdef APR_FILE_ATTR_HIDDEN 3736 if (hidden) 3737 { 3738#ifndef WIN32 3739 status = apr_file_attrs_set(path_apr, 3740 APR_FILE_ATTR_HIDDEN, 3741 APR_FILE_ATTR_HIDDEN, 3742 pool); 3743#else 3744 /* on Windows, use our wrapper so we can also set the 3745 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED attribute */ 3746 status = io_win_file_attrs_set(path_apr, 3747 FILE_ATTRIBUTE_HIDDEN | 3748 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, 3749 FILE_ATTRIBUTE_HIDDEN | 3750 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, 3751 pool); 3752 3753#endif 3754 if (status) 3755 return svn_error_wrap_apr(status, _("Can't hide directory '%s'"), 3756 svn_dirent_local_style(path, pool)); 3757 } 3758#endif 3759 3760/* Windows does not implement sgid. Skip here because retrieving 3761 the file permissions via APR_FINFO_PROT | APR_FINFO_OWNER is documented 3762 to be 'incredibly expensive'. */ 3763#ifndef WIN32 3764 if (sgid) 3765 { 3766 apr_finfo_t finfo; 3767 3768 /* Per our contract, don't do error-checking. Some filesystems 3769 * don't support the sgid bit, and that's okay. */ 3770 status = apr_stat(&finfo, path_apr, APR_FINFO_PROT, pool); 3771 3772 if (!status) 3773 apr_file_perms_set(path_apr, finfo.protection | APR_GSETID); 3774 } 3775#endif 3776 3777 return SVN_NO_ERROR; 3778} 3779 3780svn_error_t * 3781svn_io_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool) 3782{ 3783 return dir_make(path, perm, FALSE, FALSE, pool); 3784} 3785 3786svn_error_t * 3787svn_io_dir_make_hidden(const char *path, apr_fileperms_t perm, 3788 apr_pool_t *pool) 3789{ 3790 return dir_make(path, perm, TRUE, FALSE, pool); 3791} 3792 3793svn_error_t * 3794svn_io_dir_make_sgid(const char *path, apr_fileperms_t perm, 3795 apr_pool_t *pool) 3796{ 3797 return dir_make(path, perm, FALSE, TRUE, pool); 3798} 3799 3800 3801svn_error_t * 3802svn_io_dir_open(apr_dir_t **new_dir, const char *dirname, apr_pool_t *pool) 3803{ 3804 apr_status_t status; 3805 const char *dirname_apr; 3806 3807 /* APR doesn't like "" directories */ 3808 if (dirname[0] == '\0') 3809 dirname = "."; 3810 3811 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool)); 3812 3813 status = apr_dir_open(new_dir, dirname_apr, pool); 3814 if (status) 3815 return svn_error_wrap_apr(status, _("Can't open directory '%s'"), 3816 svn_dirent_local_style(dirname, pool)); 3817 3818 return SVN_NO_ERROR; 3819} 3820 3821svn_error_t * 3822svn_io_dir_remove_nonrecursive(const char *dirname, apr_pool_t *pool) 3823{ 3824 apr_status_t status; 3825 const char *dirname_apr; 3826 3827 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool)); 3828 3829 status = apr_dir_remove(dirname_apr, pool); 3830 3831#ifdef WIN32 3832 { 3833 svn_boolean_t retry = TRUE; 3834 3835 if (APR_TO_OS_ERROR(status) == ERROR_DIR_NOT_EMPTY) 3836 { 3837 apr_status_t empty_status = dir_is_empty(dirname_apr, pool); 3838 3839 if (APR_STATUS_IS_ENOTEMPTY(empty_status)) 3840 retry = FALSE; 3841 } 3842 3843 if (retry) 3844 { 3845 WIN32_RETRY_LOOP(status, apr_dir_remove(dirname_apr, pool)); 3846 } 3847 } 3848#endif 3849 if (status) 3850 return svn_error_wrap_apr(status, _("Can't remove directory '%s'"), 3851 svn_dirent_local_style(dirname, pool)); 3852 3853 return SVN_NO_ERROR; 3854} 3855 3856 3857svn_error_t * 3858svn_io_dir_read(apr_finfo_t *finfo, 3859 apr_int32_t wanted, 3860 apr_dir_t *thedir, 3861 apr_pool_t *pool) 3862{ 3863 apr_status_t status; 3864 3865 status = apr_dir_read(finfo, wanted, thedir); 3866 3867 if (status) 3868 return svn_error_wrap_apr(status, _("Can't read directory")); 3869 3870 /* It would be nice to use entry_name_to_utf8() below, but can we 3871 get the dir's path out of an apr_dir_t? I don't see a reliable 3872 way to do it. */ 3873 3874 if (finfo->fname) 3875 SVN_ERR(svn_path_cstring_to_utf8(&finfo->fname, finfo->fname, pool)); 3876 3877 if (finfo->name) 3878 SVN_ERR(svn_path_cstring_to_utf8(&finfo->name, finfo->name, pool)); 3879 3880 return SVN_NO_ERROR; 3881} 3882 3883svn_error_t * 3884svn_io_dir_close(apr_dir_t *thedir) 3885{ 3886 apr_status_t apr_err = apr_dir_close(thedir); 3887 if (apr_err) 3888 return svn_error_wrap_apr(apr_err, _("Error closing directory")); 3889 3890 return SVN_NO_ERROR; 3891} 3892 3893svn_error_t * 3894svn_io_dir_walk2(const char *dirname, 3895 apr_int32_t wanted, 3896 svn_io_walk_func_t walk_func, 3897 void *walk_baton, 3898 apr_pool_t *pool) 3899{ 3900 apr_status_t apr_err; 3901 apr_dir_t *handle; 3902 apr_pool_t *subpool; 3903 const char *dirname_apr; 3904 apr_finfo_t finfo; 3905 3906 wanted |= APR_FINFO_TYPE | APR_FINFO_NAME; 3907 3908 /* Quoting APR: On NT this request is incredibly expensive, but accurate. */ 3909 wanted &= ~SVN__APR_FINFO_MASK_OUT; 3910 3911 /* The documentation for apr_dir_read used to state that "." and ".." 3912 will be returned as the first two files, but it doesn't 3913 work that way in practice, in particular ext3 on Linux-2.6 doesn't 3914 follow the rules. For details see 3915 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=56666 3916 3917 If APR ever does implement "dot-first" then it would be possible to 3918 remove the svn_io_stat and walk_func calls and use the walk_func 3919 inside the loop. 3920 3921 Note: apr_stat doesn't handle FINFO_NAME but svn_io_dir_walk is 3922 documented to provide it, so we have to do a bit extra. */ 3923 SVN_ERR(svn_io_stat(&finfo, dirname, wanted & ~APR_FINFO_NAME, pool)); 3924 SVN_ERR(cstring_from_utf8(&finfo.name, 3925 svn_dirent_basename(dirname, pool), 3926 pool)); 3927 finfo.valid |= APR_FINFO_NAME; 3928 SVN_ERR((*walk_func)(walk_baton, dirname, &finfo, pool)); 3929 3930 SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool)); 3931 3932 /* APR doesn't like "" directories */ 3933 if (dirname_apr[0] == '\0') 3934 dirname_apr = "."; 3935 3936 apr_err = apr_dir_open(&handle, dirname_apr, pool); 3937 if (apr_err) 3938 return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"), 3939 svn_dirent_local_style(dirname, pool)); 3940 3941 /* iteration subpool */ 3942 subpool = svn_pool_create(pool); 3943 3944 while (1) 3945 { 3946 const char *name_utf8; 3947 const char *full_path; 3948 3949 svn_pool_clear(subpool); 3950 3951 apr_err = apr_dir_read(&finfo, wanted, handle); 3952 if (APR_STATUS_IS_ENOENT(apr_err)) 3953 break; 3954 else if (apr_err) 3955 { 3956 return svn_error_wrap_apr(apr_err, 3957 _("Can't read directory entry in '%s'"), 3958 svn_dirent_local_style(dirname, pool)); 3959 } 3960 3961 if (finfo.filetype == APR_DIR) 3962 { 3963 if (finfo.name[0] == '.' 3964 && (finfo.name[1] == '\0' 3965 || (finfo.name[1] == '.' && finfo.name[2] == '\0'))) 3966 /* skip "." and ".." */ 3967 continue; 3968 3969 /* some other directory. recurse. it will be passed to the 3970 callback inside the recursion. */ 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(svn_io_dir_walk2(full_path, 3975 wanted, 3976 walk_func, 3977 walk_baton, 3978 subpool)); 3979 } 3980 else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK) 3981 { 3982 /* some other directory. pass it to the callback. */ 3983 SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname, 3984 subpool)); 3985 full_path = svn_dirent_join(dirname, name_utf8, subpool); 3986 SVN_ERR((*walk_func)(walk_baton, 3987 full_path, 3988 &finfo, 3989 subpool)); 3990 } 3991 /* else: 3992 Some other type of file; skip it for now. We've reserved the 3993 right to expand our coverage here in the future, though, 3994 without revving this API. 3995 */ 3996 } 3997 3998 svn_pool_destroy(subpool); 3999 4000 apr_err = apr_dir_close(handle); 4001 if (apr_err) 4002 return svn_error_wrap_apr(apr_err, _("Error closing directory '%s'"), 4003 svn_dirent_local_style(dirname, pool)); 4004 4005 return SVN_NO_ERROR; 4006} 4007 4008 4009 4010/** 4011 * Determine if a directory is empty or not. 4012 * @param Return APR_SUCCESS if the dir is empty, else APR_ENOTEMPTY if not. 4013 * @param path The directory. 4014 * @param pool Used for temporary allocation. 4015 * @remark If path is not a directory, or some other error occurs, 4016 * then return the appropriate apr status code. 4017 * 4018 * (This function is written in APR style, in anticipation of 4019 * perhaps someday being moved to APR as 'apr_dir_is_empty'.) 4020 */ 4021static apr_status_t 4022dir_is_empty(const char *dir, apr_pool_t *pool) 4023{ 4024 apr_status_t apr_err; 4025 apr_dir_t *dir_handle; 4026 apr_finfo_t finfo; 4027 apr_status_t retval = APR_SUCCESS; 4028 4029 /* APR doesn't like "" directories */ 4030 if (dir[0] == '\0') 4031 dir = "."; 4032 4033 apr_err = apr_dir_open(&dir_handle, dir, pool); 4034 if (apr_err != APR_SUCCESS) 4035 return apr_err; 4036 4037 for (apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle); 4038 apr_err == APR_SUCCESS; 4039 apr_err = apr_dir_read(&finfo, APR_FINFO_NAME, dir_handle)) 4040 { 4041 /* Ignore entries for this dir and its parent, robustly. 4042 (APR promises that they'll come first, so technically 4043 this guard could be moved outside the loop. But Ryan Bloom 4044 says he doesn't believe it, and I believe him. */ 4045 if (! (finfo.name[0] == '.' 4046 && (finfo.name[1] == '\0' 4047 || (finfo.name[1] == '.' && finfo.name[2] == '\0')))) 4048 { 4049 retval = APR_ENOTEMPTY; 4050 break; 4051 } 4052 } 4053 4054 /* Make sure we broke out of the loop for the right reason. */ 4055 if (apr_err && ! APR_STATUS_IS_ENOENT(apr_err)) 4056 return apr_err; 4057 4058 apr_err = apr_dir_close(dir_handle); 4059 if (apr_err != APR_SUCCESS) 4060 return apr_err; 4061 4062 return retval; 4063} 4064 4065 4066svn_error_t * 4067svn_io_dir_empty(svn_boolean_t *is_empty_p, 4068 const char *path, 4069 apr_pool_t *pool) 4070{ 4071 apr_status_t status; 4072 const char *path_apr; 4073 4074 SVN_ERR(cstring_from_utf8(&path_apr, path, pool)); 4075 4076 status = dir_is_empty(path_apr, pool); 4077 4078 if (!status) 4079 *is_empty_p = TRUE; 4080 else if (APR_STATUS_IS_ENOTEMPTY(status)) 4081 *is_empty_p = FALSE; 4082 else 4083 return svn_error_wrap_apr(status, _("Can't check directory '%s'"), 4084 svn_dirent_local_style(path, pool)); 4085 4086 return SVN_NO_ERROR; 4087} 4088 4089 4090 4091/*** Version/format files ***/ 4092 4093svn_error_t * 4094svn_io_write_version_file(const char *path, 4095 int version, 4096 apr_pool_t *pool) 4097{ 4098 const char *path_tmp; 4099 const char *format_contents = apr_psprintf(pool, "%d\n", version); 4100 4101 SVN_ERR_ASSERT(version >= 0); 4102 4103 SVN_ERR(svn_io_write_unique(&path_tmp, 4104 svn_dirent_dirname(path, pool), 4105 format_contents, strlen(format_contents), 4106 svn_io_file_del_none, pool)); 4107 4108#if defined(WIN32) || defined(__OS2__) 4109 /* make the destination writable, but only on Windows, because 4110 Windows does not let us replace read-only files. */ 4111 SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool)); 4112#endif /* WIN32 || __OS2__ */ 4113 4114 /* rename the temp file as the real destination */ 4115 SVN_ERR(svn_io_file_rename(path_tmp, path, pool)); 4116 4117 /* And finally remove the perms to make it read only */ 4118 return svn_io_set_file_read_only(path, FALSE, pool); 4119} 4120 4121 4122svn_error_t * 4123svn_io_read_version_file(int *version, 4124 const char *path, 4125 apr_pool_t *pool) 4126{ 4127 apr_file_t *format_file; 4128 char buf[80]; 4129 apr_size_t len; 4130 svn_error_t *err; 4131 4132 /* Read a chunk of data from PATH */ 4133 SVN_ERR(svn_io_file_open(&format_file, path, APR_READ, 4134 APR_OS_DEFAULT, pool)); 4135 len = sizeof(buf); 4136 err = svn_io_file_read(format_file, buf, &len, pool); 4137 4138 /* Close the file. */ 4139 SVN_ERR(svn_error_compose_create(err, 4140 svn_io_file_close(format_file, pool))); 4141 4142 /* If there was no data in PATH, return an error. */ 4143 if (len == 0) 4144 return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, 4145 _("Reading '%s'"), 4146 svn_dirent_local_style(path, pool)); 4147 4148 /* Check that the first line contains only digits. */ 4149 { 4150 apr_size_t i; 4151 4152 for (i = 0; i < len; ++i) 4153 { 4154 char c = buf[i]; 4155 4156 if (i > 0 && (c == '\r' || c == '\n')) 4157 { 4158 buf[i] = '\0'; 4159 break; 4160 } 4161 if (! svn_ctype_isdigit(c)) 4162 return svn_error_createf 4163 (SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 4164 _("First line of '%s' contains non-digit"), 4165 svn_dirent_local_style(path, pool)); 4166 } 4167 } 4168 4169 /* Convert to integer. */ 4170 SVN_ERR(svn_cstring_atoi(version, buf)); 4171 4172 return SVN_NO_ERROR; 4173} 4174 4175 4176 4177/* Do a byte-for-byte comparison of FILE1 and FILE2. */ 4178static svn_error_t * 4179contents_identical_p(svn_boolean_t *identical_p, 4180 const char *file1, 4181 const char *file2, 4182 apr_pool_t *pool) 4183{ 4184 svn_error_t *err; 4185 apr_size_t bytes_read1, bytes_read2; 4186 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 4187 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 4188 apr_file_t *file1_h; 4189 apr_file_t *file2_h; 4190 svn_boolean_t eof1 = FALSE; 4191 svn_boolean_t eof2 = FALSE; 4192 4193 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT, 4194 pool)); 4195 4196 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT, 4197 pool); 4198 4199 if (err) 4200 return svn_error_trace( 4201 svn_error_compose_create(err, 4202 svn_io_file_close(file1_h, pool))); 4203 4204 *identical_p = TRUE; /* assume TRUE, until disproved below */ 4205 while (!err && !eof1 && !eof2) 4206 { 4207 err = svn_io_file_read_full2(file1_h, buf1, 4208 SVN__STREAM_CHUNK_SIZE, &bytes_read1, 4209 &eof1, pool); 4210 if (err) 4211 break; 4212 4213 err = svn_io_file_read_full2(file2_h, buf2, 4214 SVN__STREAM_CHUNK_SIZE, &bytes_read2, 4215 &eof2, pool); 4216 if (err) 4217 break; 4218 4219 if ((bytes_read1 != bytes_read2) || memcmp(buf1, buf2, bytes_read1)) 4220 { 4221 *identical_p = FALSE; 4222 break; 4223 } 4224 } 4225 4226 /* Special case: one file being a prefix of the other and the shorter 4227 * file's size is a multiple of SVN__STREAM_CHUNK_SIZE. */ 4228 if (!err && (eof1 != eof2)) 4229 *identical_p = FALSE; 4230 4231 return svn_error_trace( 4232 svn_error_compose_create( 4233 err, 4234 svn_error_compose_create(svn_io_file_close(file1_h, pool), 4235 svn_io_file_close(file2_h, pool)))); 4236} 4237 4238 4239 4240/* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */ 4241static svn_error_t * 4242contents_three_identical_p(svn_boolean_t *identical_p12, 4243 svn_boolean_t *identical_p23, 4244 svn_boolean_t *identical_p13, 4245 const char *file1, 4246 const char *file2, 4247 const char *file3, 4248 apr_pool_t *scratch_pool) 4249{ 4250 svn_error_t *err; 4251 apr_size_t bytes_read1, bytes_read2, bytes_read3; 4252 char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 4253 char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 4254 char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 4255 apr_file_t *file1_h; 4256 apr_file_t *file2_h; 4257 apr_file_t *file3_h; 4258 svn_boolean_t eof1 = FALSE; 4259 svn_boolean_t eof2 = FALSE; 4260 svn_boolean_t eof3 = FALSE; 4261 svn_boolean_t read_1, read_2, read_3; 4262 4263 SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT, 4264 scratch_pool)); 4265 4266 err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT, 4267 scratch_pool); 4268 4269 if (err) 4270 return svn_error_trace( 4271 svn_error_compose_create(err, 4272 svn_io_file_close(file1_h, scratch_pool))); 4273 4274 err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT, 4275 scratch_pool); 4276 4277 if (err) 4278 return svn_error_trace( 4279 svn_error_compose_create( 4280 err, 4281 svn_error_compose_create(svn_io_file_close(file1_h, 4282 scratch_pool), 4283 svn_io_file_close(file2_h, 4284 scratch_pool)))); 4285 4286 /* assume TRUE, until disproved below */ 4287 *identical_p12 = *identical_p23 = *identical_p13 = TRUE; 4288 /* We need to read as long as no error occurs, and as long as one of the 4289 * flags could still change due to a read operation */ 4290 while (!err 4291 && ((*identical_p12 && !eof1 && !eof2) 4292 || (*identical_p23 && !eof2 && !eof3) 4293 || (*identical_p13 && !eof1 && !eof3))) 4294 { 4295 read_1 = read_2 = read_3 = FALSE; 4296 4297 /* As long as a file is not at the end yet, and it is still 4298 * potentially identical to another file, we read the next chunk.*/ 4299 if (!eof1 && (*identical_p12 || *identical_p13)) 4300 { 4301 err = svn_io_file_read_full2(file1_h, buf1, 4302 SVN__STREAM_CHUNK_SIZE, &bytes_read1, 4303 &eof1, scratch_pool); 4304 if (err) 4305 break; 4306 read_1 = TRUE; 4307 } 4308 4309 if (!eof2 && (*identical_p12 || *identical_p23)) 4310 { 4311 err = svn_io_file_read_full2(file2_h, buf2, 4312 SVN__STREAM_CHUNK_SIZE, &bytes_read2, 4313 &eof2, scratch_pool); 4314 if (err) 4315 break; 4316 read_2 = TRUE; 4317 } 4318 4319 if (!eof3 && (*identical_p13 || *identical_p23)) 4320 { 4321 err = svn_io_file_read_full2(file3_h, buf3, 4322 SVN__STREAM_CHUNK_SIZE, &bytes_read3, 4323 &eof3, scratch_pool); 4324 if (err) 4325 break; 4326 read_3 = TRUE; 4327 } 4328 4329 /* If the files are still marked identical, and at least one of them 4330 * is not at the end of file, we check whether they differ, and set 4331 * their flag to false then. */ 4332 if (*identical_p12 4333 && (read_1 || read_2) 4334 && ((eof1 != eof2) 4335 || (bytes_read1 != bytes_read2) 4336 || memcmp(buf1, buf2, bytes_read1))) 4337 { 4338 *identical_p12 = FALSE; 4339 } 4340 4341 if (*identical_p23 4342 && (read_2 || read_3) 4343 && ((eof2 != eof3) 4344 || (bytes_read2 != bytes_read3) 4345 || memcmp(buf2, buf3, bytes_read2))) 4346 { 4347 *identical_p23 = FALSE; 4348 } 4349 4350 if (*identical_p13 4351 && (read_1 || read_3) 4352 && ((eof1 != eof3) 4353 || (bytes_read1 != bytes_read3) 4354 || memcmp(buf1, buf3, bytes_read3))) 4355 { 4356 *identical_p13 = FALSE; 4357 } 4358 } 4359 4360 return svn_error_trace( 4361 svn_error_compose_create( 4362 err, 4363 svn_error_compose_create( 4364 svn_io_file_close(file1_h, scratch_pool), 4365 svn_error_compose_create( 4366 svn_io_file_close(file2_h, scratch_pool), 4367 svn_io_file_close(file3_h, scratch_pool))))); 4368} 4369 4370 4371 4372svn_error_t * 4373svn_io_files_contents_same_p(svn_boolean_t *same, 4374 const char *file1, 4375 const char *file2, 4376 apr_pool_t *pool) 4377{ 4378 svn_boolean_t q; 4379 4380 SVN_ERR(svn_io_filesizes_different_p(&q, file1, file2, pool)); 4381 4382 if (q) 4383 { 4384 *same = FALSE; 4385 return SVN_NO_ERROR; 4386 } 4387 4388 SVN_ERR(contents_identical_p(&q, file1, file2, pool)); 4389 4390 if (q) 4391 *same = TRUE; 4392 else 4393 *same = FALSE; 4394 4395 return SVN_NO_ERROR; 4396} 4397 4398svn_error_t * 4399svn_io_files_contents_three_same_p(svn_boolean_t *same12, 4400 svn_boolean_t *same23, 4401 svn_boolean_t *same13, 4402 const char *file1, 4403 const char *file2, 4404 const char *file3, 4405 apr_pool_t *scratch_pool) 4406{ 4407 svn_boolean_t diff_size12, diff_size23, diff_size13; 4408 4409 SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12, 4410 &diff_size23, 4411 &diff_size13, 4412 file1, 4413 file2, 4414 file3, 4415 scratch_pool)); 4416 4417 if (diff_size12 && diff_size23 && diff_size13) 4418 { 4419 *same12 = *same23 = *same13 = FALSE; 4420 } 4421 else if (diff_size12 && diff_size23) 4422 { 4423 *same12 = *same23 = FALSE; 4424 SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool)); 4425 } 4426 else if (diff_size23 && diff_size13) 4427 { 4428 *same23 = *same13 = FALSE; 4429 SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool)); 4430 } 4431 else if (diff_size12 && diff_size13) 4432 { 4433 *same12 = *same13 = FALSE; 4434 SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool)); 4435 } 4436 else 4437 { 4438 SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13); 4439 SVN_ERR(contents_three_identical_p(same12, same23, same13, 4440 file1, file2, file3, 4441 scratch_pool)); 4442 } 4443 4444 return SVN_NO_ERROR; 4445} 4446 4447#ifdef WIN32 4448/* Counter value of file_mktemp request (used in a threadsafe way), to make 4449 sure that a single process normally never generates the same tempname 4450 twice */ 4451static volatile apr_uint32_t tempname_counter = 0; 4452#endif 4453 4454/* Creates a new temporary file in DIRECTORY with apr flags FLAGS. 4455 Set *NEW_FILE to the file handle and *NEW_FILE_NAME to its name. 4456 Perform temporary allocations in SCRATCH_POOL and the result in 4457 RESULT_POOL. */ 4458static svn_error_t * 4459temp_file_create(apr_file_t **new_file, 4460 const char **new_file_name, 4461 const char *directory, 4462 apr_int32_t flags, 4463 apr_pool_t *result_pool, 4464 apr_pool_t *scratch_pool) 4465{ 4466#ifndef WIN32 4467 const char *templ = svn_dirent_join(directory, "svn-XXXXXX", scratch_pool); 4468 const char *templ_apr; 4469 apr_status_t status; 4470 4471 SVN_ERR(svn_path_cstring_from_utf8(&templ_apr, templ, scratch_pool)); 4472 4473 /* ### svn_path_cstring_from_utf8() guarantees to make a copy of the 4474 data available in POOL and we need a non-const pointer here, 4475 as apr changes the template to return the new filename. */ 4476 status = apr_file_mktemp(new_file, (char *)templ_apr, flags, result_pool); 4477 4478 if (status) 4479 return svn_error_wrap_apr(status, _("Can't create temporary file from " 4480 "template '%s'"), templ); 4481 4482 /* Translate the returned path back to utf-8 before returning it */ 4483 return svn_error_trace(svn_path_cstring_to_utf8(new_file_name, 4484 templ_apr, 4485 result_pool)); 4486#else 4487 /* The Windows implementation of apr_file_mktemp doesn't handle access 4488 denied errors correctly. Therefore we implement our own temp file 4489 creation function here. */ 4490 4491 /* ### Most of this is borrowed from the svn_io_open_uniquely_named(), 4492 ### the function we used before. But we try to guess a more unique 4493 ### name before trying if it exists. */ 4494 4495 /* Offset by some time value and a unique request nr to make the number 4496 +- unique for both this process and on the computer */ 4497 int baseNr = (GetTickCount() << 11) + 7 * svn_atomic_inc(&tempname_counter) 4498 + GetCurrentProcessId(); 4499 int i; 4500 4501 /* ### Maybe use an iterpool? */ 4502 for (i = 0; i <= 99999; i++) 4503 { 4504 apr_uint32_t unique_nr; 4505 const char *unique_name; 4506 const char *unique_name_apr; 4507 apr_file_t *try_file; 4508 apr_status_t apr_err; 4509 4510 /* Generate a number that should be unique for this application and 4511 usually for the entire computer to reduce the number of cycles 4512 through this loop. (A bit of calculation is much cheaper then 4513 disk io) */ 4514 unique_nr = baseNr + 3 * i; 4515 4516 unique_name = svn_dirent_join(directory, 4517 apr_psprintf(scratch_pool, "svn-%X", 4518 unique_nr), 4519 scratch_pool); 4520 4521 SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool)); 4522 4523 apr_err = file_open(&try_file, unique_name_apr, flags, 4524 APR_OS_DEFAULT, FALSE, scratch_pool); 4525 4526 if (APR_STATUS_IS_EEXIST(apr_err)) 4527 continue; 4528 else if (apr_err) 4529 { 4530 /* On Win32, CreateFile fails with an "Access Denied" error 4531 code, rather than "File Already Exists", if the colliding 4532 name belongs to a directory. */ 4533 4534 if (APR_STATUS_IS_EACCES(apr_err)) 4535 { 4536 apr_finfo_t finfo; 4537 apr_status_t apr_err_2 = apr_stat(&finfo, unique_name_apr, 4538 APR_FINFO_TYPE, scratch_pool); 4539 4540 if (!apr_err_2 && finfo.filetype == APR_DIR) 4541 continue; 4542 4543 apr_err_2 = APR_TO_OS_ERROR(apr_err); 4544 4545 if (apr_err_2 == ERROR_ACCESS_DENIED || 4546 apr_err_2 == ERROR_SHARING_VIOLATION) 4547 { 4548 /* The file is in use by another process or is hidden; 4549 create a new name, but don't do this 99999 times in 4550 case the folder is not writable */ 4551 i += 797; 4552 continue; 4553 } 4554 4555 /* Else fall through and return the original error. */ 4556 } 4557 4558 return svn_error_wrap_apr(apr_err, _("Can't open '%s'"), 4559 svn_dirent_local_style(unique_name, 4560 scratch_pool)); 4561 } 4562 else 4563 { 4564 /* Move file to the right pool */ 4565 apr_err = apr_file_setaside(new_file, try_file, result_pool); 4566 4567 if (apr_err) 4568 return svn_error_wrap_apr(apr_err, _("Can't set aside '%s'"), 4569 svn_dirent_local_style(unique_name, 4570 scratch_pool)); 4571 4572 *new_file_name = apr_pstrdup(result_pool, unique_name); 4573 4574 return SVN_NO_ERROR; 4575 } 4576 } 4577 4578 return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED, 4579 NULL, 4580 _("Unable to make name in '%s'"), 4581 svn_dirent_local_style(directory, scratch_pool)); 4582#endif 4583} 4584 4585/* Wrapper for apr_file_name_get(), passing out a UTF8-encoded filename. */ 4586svn_error_t * 4587svn_io_file_name_get(const char **filename, 4588 apr_file_t *file, 4589 apr_pool_t *pool) 4590{ 4591 const char *fname_apr; 4592 apr_status_t status; 4593 4594 status = apr_file_name_get(&fname_apr, file); 4595 if (status) 4596 return svn_error_wrap_apr(status, _("Can't get file name")); 4597 4598 if (fname_apr) 4599 SVN_ERR(svn_path_cstring_to_utf8(filename, fname_apr, pool)); 4600 else 4601 *filename = NULL; 4602 4603 return SVN_NO_ERROR; 4604} 4605 4606 4607svn_error_t * 4608svn_io_open_unique_file3(apr_file_t **file, 4609 const char **unique_path, 4610 const char *dirpath, 4611 svn_io_file_del_t delete_when, 4612 apr_pool_t *result_pool, 4613 apr_pool_t *scratch_pool) 4614{ 4615 apr_file_t *tempfile; 4616 const char *tempname; 4617 struct temp_file_cleanup_s *baton = NULL; 4618 apr_int32_t flags = (APR_READ | APR_WRITE | APR_CREATE | APR_EXCL | 4619 APR_BUFFERED | APR_BINARY); 4620#if !defined(WIN32) && !defined(__OS2__) 4621 apr_fileperms_t perms; 4622 svn_boolean_t using_system_temp_dir = FALSE; 4623#endif 4624 4625 SVN_ERR_ASSERT(file || unique_path); 4626 if (file) 4627 *file = NULL; 4628 if (unique_path) 4629 *unique_path = NULL; 4630 4631 if (dirpath == NULL) 4632 { 4633#if !defined(WIN32) && !defined(__OS2__) 4634 using_system_temp_dir = TRUE; 4635#endif 4636 SVN_ERR(svn_io_temp_dir(&dirpath, scratch_pool)); 4637 } 4638 4639 switch (delete_when) 4640 { 4641 case svn_io_file_del_on_pool_cleanup: 4642 baton = apr_palloc(result_pool, sizeof(*baton)); 4643 baton->pool = result_pool; 4644 baton->fname_apr = NULL; 4645 4646 /* Because cleanups are run LIFO, we need to make sure to register 4647 our cleanup before the apr_file_close cleanup: 4648 4649 On Windows, you can't remove an open file. 4650 */ 4651 apr_pool_cleanup_register(result_pool, baton, 4652 temp_file_plain_cleanup_handler, 4653 temp_file_child_cleanup_handler); 4654 4655 break; 4656 case svn_io_file_del_on_close: 4657 flags |= APR_DELONCLOSE; 4658 break; 4659 default: 4660 break; 4661 } 4662 4663 SVN_ERR(temp_file_create(&tempfile, &tempname, dirpath, flags, 4664 result_pool, scratch_pool)); 4665 4666#if !defined(WIN32) && !defined(__OS2__) 4667 /* apr_file_mktemp() creates files with mode 0600. 4668 * This is appropriate if we're using a system temp dir since we don't 4669 * want to leak sensitive data into temp files other users can read. 4670 * If we're not using a system temp dir we're probably using the 4671 * .svn/tmp area and it's likely that the tempfile will end up being 4672 * copied or renamed into the working copy. 4673 * This would cause working files having mode 0600 while users might 4674 * expect to see 0644 or 0664. So we tweak perms of the tempfile in this 4675 * case, but only if the umask allows it. */ 4676 if (!using_system_temp_dir) 4677 { 4678 SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool)); 4679 SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool)); 4680 } 4681#endif 4682 4683 if (file) 4684 *file = tempfile; 4685 else 4686 SVN_ERR(svn_io_file_close(tempfile, scratch_pool)); 4687 4688 if (unique_path) 4689 *unique_path = tempname; /* Was allocated in result_pool */ 4690 4691 if (baton) 4692 SVN_ERR(cstring_from_utf8(&baton->fname_apr, tempname, result_pool)); 4693 4694 return SVN_NO_ERROR; 4695} 4696 4697svn_error_t * 4698svn_io_file_readline(apr_file_t *file, 4699 svn_stringbuf_t **stringbuf, 4700 const char **eol, 4701 svn_boolean_t *eof, 4702 apr_size_t max_len, 4703 apr_pool_t *result_pool, 4704 apr_pool_t *scratch_pool) 4705{ 4706 svn_stringbuf_t *str; 4707 const char *eol_str; 4708 apr_size_t numbytes; 4709 char c; 4710 apr_size_t len; 4711 svn_boolean_t found_eof; 4712 4713 str = svn_stringbuf_create_ensure(80, result_pool); 4714 4715 /* Read bytes into STR up to and including, but not storing, 4716 * the next EOL sequence. */ 4717 eol_str = NULL; 4718 numbytes = 1; 4719 len = 0; 4720 found_eof = FALSE; 4721 while (!found_eof) 4722 { 4723 if (len < max_len) 4724 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes, 4725 &found_eof, scratch_pool)); 4726 len++; 4727 if (numbytes != 1 || len > max_len) 4728 { 4729 found_eof = TRUE; 4730 break; 4731 } 4732 4733 if (c == '\n') 4734 { 4735 eol_str = "\n"; 4736 } 4737 else if (c == '\r') 4738 { 4739 eol_str = "\r"; 4740 4741 if (!found_eof && len < max_len) 4742 { 4743 apr_off_t pos; 4744 4745 /* Check for "\r\n" by peeking at the next byte. */ 4746 pos = 0; 4747 SVN_ERR(svn_io_file_seek(file, APR_CUR, &pos, scratch_pool)); 4748 SVN_ERR(svn_io_file_read_full2(file, &c, sizeof(c), &numbytes, 4749 &found_eof, scratch_pool)); 4750 if (numbytes == 1 && c == '\n') 4751 { 4752 eol_str = "\r\n"; 4753 len++; 4754 } 4755 else 4756 { 4757 /* Pretend we never peeked. */ 4758 SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, scratch_pool)); 4759 found_eof = FALSE; 4760 numbytes = 1; 4761 } 4762 } 4763 } 4764 else 4765 svn_stringbuf_appendbyte(str, c); 4766 4767 if (eol_str) 4768 break; 4769 } 4770 4771 if (eol) 4772 *eol = eol_str; 4773 if (eof) 4774 *eof = found_eof; 4775 *stringbuf = str; 4776 4777 return SVN_NO_ERROR; 4778} 4779